diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7d1a74e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,17 @@ +name: build +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install flake8 + run: pip install flake8 flake8-import-order flake8-future-import flake8-commas flake8-logging-format + - name: Lint with flake8 + run: | + flake8 --version + flake8 diff --git a/.github/workflows/compilemessages.yml b/.github/workflows/compilemessages.yml new file mode 100644 index 0000000..edcf2bd --- /dev/null +++ b/.github/workflows/compilemessages.yml @@ -0,0 +1,40 @@ +name: compilemessages +on: + push: + paths: + - 'locale/**' + pull_request: + paths: + - 'locale/**' +jobs: + compilemessages: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Checkout submodules + run: | + git submodule init + git submodule update + - name: Install requirements + run: | + sudo apt-get install gettext + pip install -r requirements.txt + pip install pymysql + - name: Check .po file validity + run: | + fail=0 + while read -r file; do + if ! msgfmt --check-format "$file"; then + fail=$((fail + 1)) + fi + done < <(find locale -name '*.po') + exit "$fail" + shell: bash + - name: Compile messages + run: | + echo "STATIC_ROOT = '/tmp'" > dmoj/local_settings.py + python manage.py compilemessages diff --git a/.github/workflows/init.yml b/.github/workflows/init.yml deleted file mode 100644 index 23c4b63..0000000 --- a/.github/workflows/init.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Lint - -on: - # Trigger the workflow on push or pull request, - # but only for the main branch - push: - branches: - - master - pull_request: - branches: - - master - -jobs: - run-linters: - name: Run linters - runs-on: ubuntu-latest - - steps: - - name: Check out Git repository - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v1 - with: - python-version: 3.8 - - - name: Install Python dependencies - run: pip install black - - - name: Run linters - uses: wearerequired/lint-action@v2 - with: - black: true diff --git a/.github/workflows/makemessages.yml b/.github/workflows/makemessages.yml new file mode 100644 index 0000000..fdf375d --- /dev/null +++ b/.github/workflows/makemessages.yml @@ -0,0 +1,53 @@ +name: makemessages +on: + push: + branches: + - master +jobs: + makemessages: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Checkout submodules + run: | + git submodule init + git submodule update + - name: Install requirements + run: | + sudo apt-get install gettext + curl -O https://artifacts.crowdin.com/repo/deb/crowdin.deb + sudo dpkg -i crowdin.deb + pip install -r requirements.txt + pip install pymysql + - name: Collect localizable strings + run: | + echo "STATIC_ROOT = '/tmp'" > dmoj/local_settings.py + python manage.py makemessages -l en -e py,html,txt + python manage.py makemessages -l en -d djangojs + - name: Upload strings to Crowdin + env: + CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }} + run: | + cat > crowdin.yaml <> crowdin.yaml + crowdin upload sources diff --git a/.github/workflows/updatemessages.yml b/.github/workflows/updatemessages.yml new file mode 100644 index 0000000..b000444 --- /dev/null +++ b/.github/workflows/updatemessages.yml @@ -0,0 +1,67 @@ +name: updatemessages +on: + schedule: + - cron: '0 * * * *' +jobs: + updatemessages: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Checkout submodules + run: | + git submodule init + git submodule update + - name: Install requirements + run: | + sudo apt-get install gettext + curl -O https://artifacts.crowdin.com/repo/deb/crowdin.deb + sudo dpkg -i crowdin.deb + pip install -r requirements.txt + pip install pymysql + - name: Download strings from Crowdin + env: + CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }} + run: | + cat > crowdin.yaml <> crowdin.yaml + crowdin download + rm crowdin.yaml + - name: Cleanup + run: | + rm -rf src/ + git add locale + git checkout . + git clean -fd + - name: Create pull request + uses: peter-evans/create-pull-request@v1.4.1-multi + env: + GITHUB_TOKEN: ${{ secrets.REPO_SCOPED_TOKEN }} + COMMIT_MESSAGE: 'i18n: update translations from Crowdin' + PULL_REQUEST_TITLE: 'Update translations from Crowdin' + PULL_REQUEST_BODY: This PR has been auto-generated to pull in latest translations from [Crowdin](https://translate.dmoj.ca). + PULL_REQUEST_LABELS: i18n, enhancement + PULL_REQUEST_REVIEWERS: Xyene, quantum5 + PULL_REQUEST_BRANCH: update-i18n + BRANCH_SUFFIX: none diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0bea662 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,8 @@ +[submodule "resources/pagedown"] + path = resources/pagedown + url = https://github.com/DMOJ/dmoj-pagedown.git + branch = master +[submodule "resources/libs"] + path = resources/libs + url = https://github.com/DMOJ/site-assets.git + branch = master diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 8740838..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,17 +0,0 @@ -repos: - - repo: https://github.com/rtts/djhtml - rev: 'v1.5.2' # replace with the latest tag on GitHub - hooks: - - id: djhtml - entry: djhtml -i -t 2 - files: templates/. - - id: djcss - types: [scss] - - repo: https://github.com/psf/black - rev: 22.12.0 - hooks: - - id: black - - repo: https://github.com/hadialqattan/pycln - rev: 'v2.3.0' - hooks: - - id: pycln diff --git a/502.html b/502.html index a65c3cd..98f4dbb 100644 --- a/502.html +++ b/502.html @@ -49,7 +49,7 @@
diff --git a/README.md b/README.md index 81fe6e5..0d5a159 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,11 @@ Supported languages: - Assembly (x64) - AWK - C -- C++03 / C++11 / C++14 / C++17 / C++20 +- C++03 / C++11 / C++14 / C++17 - Java 11 - Pascal - Perl - Python 2 / Python 3 -- PyPy 2 / PyPy 3 Support plagiarism detection via [Stanford MOSS](https://theory.stanford.edu/~aiken/moss/). @@ -31,196 +30,10 @@ Support plagiarism detection via [Stanford MOSS](https://theory.stanford.edu/~ai Most of the setup are the same as DMOJ installations. You can view the installation guide of DMOJ here: https://docs.dmoj.ca/#/site/installation. There is one minor change: Instead of `git clone https://github.com/DMOJ/site.git`, you clone this repo `git clone https://github.com/LQDJudge/online-judge.git`. +### Additional Steps in Production: -- Bước 1: cài các thư viện cần thiết - - $ ở đây nghĩa là sudo. Ví dụ dòng đầu nghĩa là chạy lệnh `sudo apt update` - -```jsx -$ apt update -$ apt install git gcc g++ make python3-dev python3-pip libxml2-dev libxslt1-dev zlib1g-dev gettext curl redis-server -$ curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - -$ apt install nodejs -$ npm install -g sass postcss-cli postcss autoprefixer -``` - -- Bước 2: tạo DB - - Server đang dùng MariaDB ≥ 10.5, các bạn cũng có thể dùng Mysql nếu bị conflict - - Nếu các bạn chạy lệnh dưới này xong mà version mariadb bị cũ (< 10.5) thì có thể tra google cách cài MariaDB mới nhất (10.5 hoặc 10.6). - - Các bạn có thể thấy version MariaDB bằng cách gõ lệnh `sudo mysql` (Ctrl + C để quit) - -```jsx -$ apt update -$ apt install mariadb-server libmysqlclient-dev -``` - -- Bước 3: tạo table trong DB - - Các bạn có thể thay tên table và password - -```jsx -$ sudo mysql -mariadb> CREATE DATABASE dmoj DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; -mariadb> GRANT ALL PRIVILEGES ON dmoj.* TO 'dmoj'@'localhost' IDENTIFIED BY ''; -mariadb> exit -``` - -- Bước 4: Cài đặt môi trường ảo (virtual env) và pull code - - Nếu `pip3 install mysqlclient` bị lỗi thì thử chạy `pip3 install mysqlclient==2.1.1` - -```jsx -$ python3 -m venv dmojsite -$ . dmojsite/bin/activate - -(dmojsite) $ git clone https://github.com/LQDJudge/online-judge.git -(dmojsite) $ cd online-judge -(dmojsite) $ git submodule init -(dmojsite) $ git submodule update -(dmojsite) $ pip3 install -r requirements.txt -(dmojsite) $ pip3 install mysqlclient -(dmojsite) $ pre-commit install -``` - -- Bước 5: tạo local_settings.py. Đây là file để custom setting cho Django. Các bạn tạo file vào `online-judge/dmoj/local_settings.py` - - File mẫu: https://github.com/DMOJ/docs/blob/master/sample_files/local_settings.py - - Nếu bạn đổi tên hoặc mật khẩu table databases thì thay đổi thông tin tương ứng trong `Databases` - - Sau khi xong, chạy lệnh `(dmojsite) $ python3 manage.py check` để kiểm tra -- Bước 6: Compile CSS và translation - - Giải thích: - - Lệnh 1 và 2 gọi sau mỗi lần thay đổi 1 file css hoặc file js (file html thì không cần) - - Lệnh 3 và 4 gọi sau mỗi lần thay đổi file dịch - - Note: Sau khi chạy lệnh này, folder tương ứng với STATIC_ROOT trong local_settings phải được tạo. Nếu chưa được tạo thì mình cần tạo folder đó trước khi chạy 2 lệnh đầu. - -```jsx -(dmojsite) $ ./make_style.sh -(dmojsite) $ python3 manage.py collectstatic -(dmojsite) $ python3 manage.py compilemessages -(dmojsite) $ python3 manage.py compilejsi18n -``` - -- Bước 7: Thêm dữ liệu vào DB - -```jsx -(dmojsite) $ python3 manage.py migrate -(dmojsite) $ python3 manage.py loaddata navbar -(dmojsite) $ python3 manage.py loaddata language_small -(dmojsite) $ python3 manage.py loaddata demo -``` - -- Bước 8: Chạy site. Đến đây thì cơ bản đã hoàn thành (chưa có judge, websocket, celery). Các bạn có thể truy cập tại `localhost:8000` - -```jsx -python3 manage.py runserver 0.0.0.0:8000 -``` - -**Một số lưu ý:** - -1. (WSL) có thể tải ứng dụng Terminal trong Windows Store -2. (WSL) mỗi lần mở ubuntu, các bạn cần chạy lệnh sau để mariadb khởi động: `sudo service mysql restart` (tương tự cho một số service khác như memcached, celery) -3. Sau khi cài đặt, các bạn chỉ cần activate virtual env và chạy lệnh runserver là ok -```jsx -. dmojsite/bin/activate -python3 manage.py runserver -``` -5. Đối với nginx, sau khi config xong theo guide của DMOJ, bạn cần thêm location như sau để sử dụng được tính năng profile image, thay thế `path/to/oj` thành đường dẫn nơi bạn đã clone source code. - -``` -location /profile_images/ { - root /path/to/oj; -} -``` - -6. Quy trình dev: - 1. Sau khi thay đổi code thì django tự build lại, các bạn chỉ cần F5 - 2. Một số style nằm trong các file .scss. Các bạn cần recompile css thì mới thấy được thay đổi. - -**Optional:** - -************Alias:************ Các bạn có thể lưu các alias này để sau này dùng cho nhanh - -- mtrans: để generate translation khi các bạn add một string trong code -- trans: compile translation (sau khi bạn đã dịch tiếng Việt) -- cr: chuyển tới folder OJ -- pr: chạy server -- sm: restart service (chủ yếu dùng cho WSL) -- sd: activate virtual env -- css: compile các file css - -```jsx -alias mtrans='python3 manage.py makemessages -l vi && python3 manage.py makedmojmessages -l vi' -alias pr='python3 manage.py runserver' -alias sd='source ~/LQDOJ/dmojsite/bin/activate' -alias sm='sudo service mysql restart && sudo service redis-server start && sudo service memcached start' -alias trans='python3 manage.py compilemessages -l vi && python3 manage.py compilejsi18n -l vi' -alias cr='cd ~/LQDOJ/online-judge' -alias css='./make_style.sh && python3 manage.py collectstatic --noinput' -``` - -**Memcached:** dùng cho in-memory cache - -```jsx -$ sudo apt install memcached -``` - -**Websocket:** dùng để live update (như chat) - -- Tạo file online-judge/websocket/config.js - -```jsx -module.exports = { - get_host: '127.0.0.1', - get_port: 15100, - post_host: '127.0.0.1', - post_port: 15101, - http_host: '127.0.0.1', - http_port: 15102, - long_poll_timeout: 29000, -}; -``` - -- Cài các thư viện - -```jsx -(dmojsite) $ npm install qu ws simplesets -(dmojsite) $ pip3 install websocket-client -``` - -- Khởi động (trong 1 tab riêng) - -```jsx -(dmojsite) $ node websocket/daemon.js -``` - -**************Celery:************** (dùng cho một số task như batch rejudge_ - -```jsx -celery -A dmoj_celery worker -``` - -**************Judge:************** - -- Cài đặt ở 1 folder riêng bên ngoài site: - -```jsx -$ apt install python3-dev python3-pip build-essential libseccomp-dev -$ git clone https://github.com/LQDJudge/judge-server.git -$ cd judge-server -$ sudo pip3 install -e . -``` - -- Tạo một file judge.yml ở bên ngoài folder judge-server (file mẫu https://github.com/DMOJ/docs/blob/master/sample_files/judge_conf.yml) -- Thêm judge vào site bằng UI: Admin → Judge → Thêm Judge → nhập id và key (chỉ cần thêm 1 lần) hoặc dùng lệnh `(dmojsite) $ python3 managed.py addjudge `. -- Chạy Bridge (cầu nối giữa judge và site) trong 1 tab riêng trong folder online-judge: - -```jsx -(dmojsite) $ python3 managed.py runbridged -``` - -- Khởi động Judge (trong 1 tab riêng): - -```jsx -$ dmoj -c judge.yml localhost -``` - -- Lưu ý: mỗi lần sau này muốn chạy judge thì mở 1 tab cho bridge và n tab cho judge. Mỗi judge cần 1 file yml khác nhau (chứa authentication khác nhau) +1. To use newsletter (email sending), go to admin and create a newsletter. +2. Change the domain name and website name in Admin page: Navigation Bars/Sites ### Some frequent difficulties when installation: @@ -228,21 +41,6 @@ $ dmoj -c judge.yml localhost 2. Missing the problem folder in `local_settings.py`. You need to create a folder to contain all problem packages and configure in `local_settings.py`. 3. Missing static folder in `local_settings.py`. Similar to problem folder, make sure to configure `STATIC_FILES` inside `local_settings.py`. 4. Missing configure file for judges. Each judge must have a seperate configure file. To create this file, you can run `python dmojauto-conf`. Checkout all sample files here https://github.com/DMOJ/docs/blob/master/sample_files. -5. Missing timezone data for SQL. If you're using Ubuntu and you're following DMOJ's installation guide for the server, and you are getting the error mentioned in https://github.com/LQDJudge/online-judge/issues/45, then you can follow this method to fix: -``` -mysql --- You may have to do this if you haven't set root password for MySQL, replace mypass with your password --- SET PASSWORD FOR 'root'@'localhost' = PASSWORD('mypass'); --- FLUSH PRIVILEGES; -exit -mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -D mysql -u root -p -mysql -u root -p -e "flush tables;" mysql -``` -6. Missing the chat secret key, you must generate a Fernet key, and assign a variable in `local_settings.py` like this -```python -CHAT_SECRET_KEY = "81HqDtbqAywKSOumSxxxxxxxxxxxxxxxxx=" -``` - ## Usage @@ -255,12 +53,12 @@ source dmojsite/bin/activate 2. Run server: ```bash -python3 manage.py runserver 0.0.0.0:8000 +python manage.py runserver 0.0.0.0:8000 ``` 3. Create a bridge (this is opened in a different terminal with the second step if you are using the same machine) ```bash -python3 manage.py runbridged +python manage.py runbridged ``` 4. Create a judge (another terminal) @@ -281,22 +79,20 @@ celery -A dmoj_celery worker node websocket/daemon.js ``` -7. To use subdomain for each organization, go to admin page -> navigation bar -> sites, add domain name (e.g, "localhost:8000"). Then go to add `USE_SUBDOMAIN = True` to local_settings.py. - ## Deploy Most of the steps are similar to Django tutorials. Here are two usual steps: 1. Update vietnamese translation: - - If you add any new phrases in the code, ```python3 manage.py makemessages``` + - If you add any new phrases in the code, ```python manage.py makemessages``` - go to `locale/vi` - modify `.po` file - - ```python3 manage.py compilemessages``` - - ```python3 manage.py compilejsi18n``` + - ```python manage.py compilemessages``` + - ```python manage.py compilejsi18n``` 2. Update styles (using SASS) - Change .css/.scss files in `resources` folder - - ```./make_style.sh && python3 manage.py collectstatic``` - - Sometimes you need to press `Ctrl + F5` to see the new user interface in browser. + - ```./make_style && python manage.py collectstatic``` + - Sometimes you need to `Ctrl + F5` to see the new user interface in browser. ## Screenshots @@ -304,8 +100,7 @@ Most of the steps are similar to Django tutorials. Here are two usual steps: Leaderboard with information about contest rating, performance points and real name of all users. -![](https://raw.githubusercontent.com/emladevops/LQDOJ-image/main/brave_SK67WA26FA.png#gh-light-mode-only) -![](https://raw.githubusercontent.com/emladevops/LQDOJ-image/main/brave_cmqqCnwaFc.png#gh-dark-mode-only) +![](https://i.imgur.com/ampxHXM.png) ### Admin dashboard @@ -323,5 +118,4 @@ You can write the problems' statement in Markdown with LaTeX figures and formula Users can communicate with each other and can see who's online. -![](https://raw.githubusercontent.com/emladevops/LQDOJ-image/main/brave_kPsC5bJluc.png#gh-light-mode-only) -![](https://raw.githubusercontent.com/emladevops/LQDOJ-image/main/brave_AtrEzXzEAx.png#gh-dark-mode-only) +![](https://i.imgur.com/y9SGCgl.png) diff --git a/chat_box/apps.py b/chat_box/apps.py index e49c8ad..fbc3ca1 100644 --- a/chat_box/apps.py +++ b/chat_box/apps.py @@ -2,7 +2,4 @@ from django.apps import AppConfig class ChatBoxConfig(AppConfig): - name = "chat_box" - - def ready(self): - from . import models + name = 'chat_box' diff --git a/chat_box/migrations/0001_initial.py b/chat_box/migrations/0001_initial.py index f738c77..b311ab5 100644 --- a/chat_box/migrations/0001_initial.py +++ b/chat_box/migrations/0001_initial.py @@ -9,43 +9,22 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ("judge", "0100_auto_20200127_0059"), + ('judge', '0100_auto_20200127_0059'), ] operations = [ migrations.CreateModel( - name="Message", + name='Message', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "time", - models.DateTimeField(auto_now_add=True, verbose_name="posted time"), - ), - ( - "body", - models.TextField(max_length=8192, verbose_name="body of comment"), - ), - ( - "author", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Profile", - verbose_name="user", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('time', models.DateTimeField(auto_now_add=True, verbose_name='posted time')), + ('body', models.TextField(max_length=8192, verbose_name='body of comment')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Profile', verbose_name='user')), ], options={ - "verbose_name": "message", - "verbose_name_plural": "messages", - "ordering": ("-time",), + 'verbose_name': 'message', + 'verbose_name_plural': 'messages', + 'ordering': ('-time',), }, ), ] diff --git a/chat_box/migrations/0002_message_hidden.py b/chat_box/migrations/0002_message_hidden.py index baef87e..23c02da 100644 --- a/chat_box/migrations/0002_message_hidden.py +++ b/chat_box/migrations/0002_message_hidden.py @@ -6,13 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("chat_box", "0001_initial"), + ('chat_box', '0001_initial'), ] operations = [ migrations.AddField( - model_name="message", - name="hidden", - field=models.BooleanField(default=False, verbose_name="is hidden"), + model_name='message', + name='hidden', + field=models.BooleanField(default=False, verbose_name='is hidden'), ), ] diff --git a/chat_box/migrations/0003_auto_20200505_2306.py b/chat_box/migrations/0003_auto_20200505_2306.py index 7af087e..28efdb9 100644 --- a/chat_box/migrations/0003_auto_20200505_2306.py +++ b/chat_box/migrations/0003_auto_20200505_2306.py @@ -6,13 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("chat_box", "0002_message_hidden"), + ('chat_box', '0002_message_hidden'), ] operations = [ migrations.AlterField( - model_name="message", - name="hidden", - field=models.BooleanField(default=True, verbose_name="is hidden"), + model_name='message', + name='hidden', + field=models.BooleanField(default=True, verbose_name='is hidden'), ), ] diff --git a/chat_box/migrations/0004_auto_20200505_2336.py b/chat_box/migrations/0004_auto_20200505_2336.py index 7475262..3ecc44c 100644 --- a/chat_box/migrations/0004_auto_20200505_2336.py +++ b/chat_box/migrations/0004_auto_20200505_2336.py @@ -6,13 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("chat_box", "0003_auto_20200505_2306"), + ('chat_box', '0003_auto_20200505_2306'), ] operations = [ migrations.AlterField( - model_name="message", - name="hidden", - field=models.BooleanField(default=False, verbose_name="is hidden"), + model_name='message', + name='hidden', + field=models.BooleanField(default=False, verbose_name='is hidden'), ), ] diff --git a/chat_box/migrations/0005_auto_20211011_0714.py b/chat_box/migrations/0005_auto_20211011_0714.py index a28957e..eaf26cb 100644 --- a/chat_box/migrations/0005_auto_20211011_0714.py +++ b/chat_box/migrations/0005_auto_20211011_0714.py @@ -7,52 +7,22 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ("judge", "0116_auto_20211011_0645"), - ("chat_box", "0004_auto_20200505_2336"), + ('judge', '0116_auto_20211011_0645'), + ('chat_box', '0004_auto_20200505_2336'), ] operations = [ migrations.CreateModel( - name="Room", + name='Room', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "user_one", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="user_one", - to="judge.Profile", - verbose_name="user 1", - ), - ), - ( - "user_two", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="user_two", - to="judge.Profile", - verbose_name="user 2", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_one', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_one', to='judge.Profile', verbose_name='user 1')), + ('user_two', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_two', to='judge.Profile', verbose_name='user 2')), ], ), migrations.AddField( - model_name="message", - name="room", - field=models.ForeignKey( - default=None, - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="chat_box.Room", - verbose_name="room id", - ), + model_name='message', + name='room', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='chat_box.Room', verbose_name='room id'), ), ] diff --git a/chat_box/migrations/0006_userroom.py b/chat_box/migrations/0006_userroom.py index 534e1a3..eff219c 100644 --- a/chat_box/migrations/0006_userroom.py +++ b/chat_box/migrations/0006_userroom.py @@ -7,42 +7,18 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ("judge", "0116_auto_20211011_0645"), - ("chat_box", "0005_auto_20211011_0714"), + ('judge', '0116_auto_20211011_0645'), + ('chat_box', '0005_auto_20211011_0714'), ] operations = [ migrations.CreateModel( - name="UserRoom", + name='UserRoom', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("last_seen", models.DateTimeField(verbose_name="last seen")), - ( - "room", - models.ForeignKey( - default=None, - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="chat_box.Room", - verbose_name="room id", - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Profile", - verbose_name="user", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('last_seen', models.DateTimeField(verbose_name='last seen')), + ('room', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='chat_box.Room', verbose_name='room id')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Profile', verbose_name='user')), ], ), ] diff --git a/chat_box/migrations/0007_auto_20211112_1255.py b/chat_box/migrations/0007_auto_20211112_1255.py index c38e475..49a39f0 100644 --- a/chat_box/migrations/0007_auto_20211112_1255.py +++ b/chat_box/migrations/0007_auto_20211112_1255.py @@ -6,13 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("chat_box", "0006_userroom"), + ('chat_box', '0006_userroom'), ] operations = [ migrations.AlterField( - model_name="userroom", - name="last_seen", - field=models.DateTimeField(auto_now_add=True, verbose_name="last seen"), + model_name='userroom', + name='last_seen', + field=models.DateTimeField(auto_now_add=True, verbose_name='last seen'), ), ] diff --git a/chat_box/migrations/0008_ignore.py b/chat_box/migrations/0008_ignore.py index 6648d1e..723dd3b 100644 --- a/chat_box/migrations/0008_ignore.py +++ b/chat_box/migrations/0008_ignore.py @@ -7,33 +7,17 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ("judge", "0116_auto_20211011_0645"), - ("chat_box", "0007_auto_20211112_1255"), + ('judge', '0116_auto_20211011_0645'), + ('chat_box', '0007_auto_20211112_1255'), ] operations = [ migrations.CreateModel( - name="Ignore", + name='Ignore', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("ignored_users", models.ManyToManyField(to="judge.Profile")), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="ignored_chat_users", - to="judge.Profile", - verbose_name="user", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ignored_users', models.ManyToManyField(to='judge.Profile')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ignored_chat_users', to='judge.Profile', verbose_name='user')), ], ), ] diff --git a/chat_box/migrations/0009_auto_20220618_1452.py b/chat_box/migrations/0009_auto_20220618_1452.py deleted file mode 100644 index f0d5e7e..0000000 --- a/chat_box/migrations/0009_auto_20220618_1452.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.25 on 2022-06-18 07:52 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("chat_box", "0008_ignore"), - ] - - operations = [ - migrations.AlterField( - model_name="ignore", - name="user", - field=models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="ignored_chat_users", - to="judge.Profile", - verbose_name="user", - ), - ), - ] diff --git a/chat_box/migrations/0010_auto_20221028_0300.py b/chat_box/migrations/0010_auto_20221028_0300.py deleted file mode 100644 index df93cd1..0000000 --- a/chat_box/migrations/0010_auto_20221028_0300.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.25 on 2022-10-27 20:00 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0135_auto_20221028_0300"), - ("chat_box", "0009_auto_20220618_1452"), - ] - - operations = [ - migrations.AlterUniqueTogether( - name="userroom", - unique_together={("user", "room")}, - ), - ] diff --git a/chat_box/migrations/0011_alter_message_hidden.py b/chat_box/migrations/0011_alter_message_hidden.py deleted file mode 100644 index 1393e82..0000000 --- a/chat_box/migrations/0011_alter_message_hidden.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.18 on 2023-02-18 21:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("chat_box", "0010_auto_20221028_0300"), - ] - - operations = [ - migrations.AlterField( - model_name="message", - name="hidden", - field=models.BooleanField( - db_index=True, default=False, verbose_name="is hidden" - ), - ), - ] diff --git a/chat_box/migrations/0012_auto_20230308_1417.py b/chat_box/migrations/0012_auto_20230308_1417.py deleted file mode 100644 index bd3ec5a..0000000 --- a/chat_box/migrations/0012_auto_20230308_1417.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-08 07:17 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0154_add_submission_indexes"), - ("chat_box", "0011_alter_message_hidden"), - ] - - operations = [ - migrations.AlterModelOptions( - name="message", - options={ - "ordering": ("-id",), - "verbose_name": "message", - "verbose_name_plural": "messages", - }, - ), - migrations.AlterField( - model_name="message", - name="hidden", - field=models.BooleanField(default=False, verbose_name="is hidden"), - ), - migrations.AddIndex( - model_name="message", - index=models.Index( - fields=["hidden", "room", "-id"], name="chat_box_me_hidden_b2307a_idx" - ), - ), - ] diff --git a/chat_box/migrations/0013_alter_message_time.py b/chat_box/migrations/0013_alter_message_time.py deleted file mode 100644 index 0f4ddc3..0000000 --- a/chat_box/migrations/0013_alter_message_time.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-28 01:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("chat_box", "0012_auto_20230308_1417"), - ] - - operations = [ - migrations.AlterField( - model_name="message", - name="time", - field=models.DateTimeField( - auto_now_add=True, db_index=True, verbose_name="posted time" - ), - ), - ] diff --git a/chat_box/migrations/0014_userroom_unread_count.py b/chat_box/migrations/0014_userroom_unread_count.py deleted file mode 100644 index 8f407b3..0000000 --- a/chat_box/migrations/0014_userroom_unread_count.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-28 06:02 - -from django.db import migrations, models - - -def migrate(apps, schema_editor): - UserRoom = apps.get_model("chat_box", "UserRoom") - Message = apps.get_model("chat_box", "Message") - - for ur in UserRoom.objects.all(): - if not ur.room: - continue - messages = ur.room.message_set - last_msg = messages.first() - try: - if last_msg and last_msg.author != ur.user: - ur.unread_count = messages.filter(time__gte=ur.last_seen).count() - else: - ur.unread_count = 0 - ur.save() - except: - continue - - -class Migration(migrations.Migration): - - dependencies = [ - ("chat_box", "0013_alter_message_time"), - ] - - operations = [ - migrations.AddField( - model_name="userroom", - name="unread_count", - field=models.IntegerField(db_index=True, default=0), - ), - migrations.RunPython(migrate, migrations.RunPython.noop, atomic=True), - ] diff --git a/chat_box/migrations/0015_room_last_msg_time.py b/chat_box/migrations/0015_room_last_msg_time.py deleted file mode 100644 index a32f21b..0000000 --- a/chat_box/migrations/0015_room_last_msg_time.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.2.18 on 2023-11-02 01:41 - -from django.db import migrations, models - - -def migrate(apps, schema_editor): - Room = apps.get_model("chat_box", "Room") - Message = apps.get_model("chat_box", "Message") - - for room in Room.objects.all(): - messages = room.message_set - last_msg = messages.first() - if last_msg: - room.last_msg_time = last_msg.time - room.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("chat_box", "0014_userroom_unread_count"), - ] - - operations = [ - migrations.AddField( - model_name="room", - name="last_msg_time", - field=models.DateTimeField( - db_index=True, null=True, verbose_name="last seen" - ), - ), - migrations.RunPython(migrate, migrations.RunPython.noop, atomic=True), - ] diff --git a/chat_box/migrations/0016_alter_room_unique_together.py b/chat_box/migrations/0016_alter_room_unique_together.py deleted file mode 100644 index 14b784a..0000000 --- a/chat_box/migrations/0016_alter_room_unique_together.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 3.2.18 on 2024-08-22 03:12 - -from django.db import migrations, models - - -def remove_duplicates(apps, schema_editor): - Room = apps.get_model("chat_box", "Room") - seen = set() - - for room in Room.objects.all(): - pair = (room.user_one_id, room.user_two_id) - reverse_pair = (room.user_two_id, room.user_one_id) - - if pair in seen or reverse_pair in seen: - room.delete() - else: - seen.add(pair) - - -class Migration(migrations.Migration): - - dependencies = [ - ("chat_box", "0015_room_last_msg_time"), - ] - - operations = [ - migrations.RunPython(remove_duplicates), - migrations.AlterUniqueTogether( - name="room", - unique_together={("user_one", "user_two")}, - ), - ] diff --git a/chat_box/models.py b/chat_box/models.py index 6d86125..5958347 100644 --- a/chat_box/models.py +++ b/chat_box/models.py @@ -1,113 +1,57 @@ from django.db import models -from django.db.models import CASCADE, Q +from django.db.models import CASCADE from django.utils.translation import gettext_lazy as _ -from django.utils.functional import cached_property from judge.models.profile import Profile -from judge.caching import cache_wrapper -__all__ = ["Message", "Room", "UserRoom", "Ignore"] - +__all__ = ['Message'] class Room(models.Model): - user_one = models.ForeignKey( - Profile, related_name="user_one", verbose_name="user 1", on_delete=CASCADE - ) - user_two = models.ForeignKey( - Profile, related_name="user_two", verbose_name="user 2", on_delete=CASCADE - ) - last_msg_time = models.DateTimeField( - verbose_name=_("last seen"), null=True, db_index=True - ) - - class Meta: - app_label = "chat_box" - unique_together = ("user_one", "user_two") - - @cached_property - def _cached_info(self): - return get_room_info(self.id) + user_one = models.ForeignKey(Profile, related_name="user_one", verbose_name='user 1', on_delete=CASCADE) + user_two = models.ForeignKey(Profile, related_name="user_two", verbose_name='user 2', on_delete=CASCADE) def contain(self, profile): - return profile.id in [self.user_one_id, self.user_two_id] - + return self.user_one == profile or self.user_two == profile def other_user(self, profile): return self.user_one if profile == self.user_two else self.user_two - - def other_user_id(self, profile): - user_ids = [self.user_one_id, self.user_two_id] - return sum(user_ids) - profile.id - def users(self): return [self.user_one, self.user_two] - def last_message_body(self): - return self._cached_info["last_message"] - - @classmethod - def prefetch_room_cache(self, room_ids): - get_room_info.prefetch_multi([(i,) for i in room_ids]) - - class Message(models.Model): - author = models.ForeignKey(Profile, verbose_name=_("user"), on_delete=CASCADE) - time = models.DateTimeField( - verbose_name=_("posted time"), auto_now_add=True, db_index=True - ) - body = models.TextField(verbose_name=_("body of comment"), max_length=8192) - hidden = models.BooleanField(verbose_name="is hidden", default=False) - room = models.ForeignKey( - Room, verbose_name="room id", on_delete=CASCADE, default=None, null=True - ) + author = models.ForeignKey(Profile, verbose_name=_('user'), on_delete=CASCADE) + time = models.DateTimeField(verbose_name=_('posted time'), auto_now_add=True) + body = models.TextField(verbose_name=_('body of comment'), max_length=8192) + hidden = models.BooleanField(verbose_name='is hidden', default=False) + room = models.ForeignKey(Room, verbose_name='room id', on_delete=CASCADE, default=None, null=True) def save(self, *args, **kwargs): + new_message = self.id self.body = self.body.strip() super(Message, self).save(*args, **kwargs) class Meta: - verbose_name = "message" - verbose_name_plural = "messages" - ordering = ("-id",) - indexes = [ - models.Index(fields=["hidden", "room", "-id"]), - ] - app_label = "chat_box" - + app_label = 'chat_box' + verbose_name = 'message' + verbose_name_plural = 'messages' + ordering = ('-time',) class UserRoom(models.Model): - user = models.ForeignKey(Profile, verbose_name=_("user"), on_delete=CASCADE) - room = models.ForeignKey( - Room, verbose_name="room id", on_delete=CASCADE, default=None, null=True - ) - last_seen = models.DateTimeField(verbose_name=_("last seen"), auto_now_add=True) - unread_count = models.IntegerField(default=0, db_index=True) - - class Meta: - unique_together = ("user", "room") - app_label = "chat_box" + user = models.ForeignKey(Profile, verbose_name=_('user'), on_delete=CASCADE) + room = models.ForeignKey(Room, verbose_name='room id', on_delete=CASCADE, default=None, null=True) + last_seen = models.DateTimeField(verbose_name=_('last seen'), auto_now_add=True) class Ignore(models.Model): - user = models.OneToOneField( - Profile, - related_name="ignored_chat_users", - verbose_name=_("user"), - on_delete=CASCADE, - db_index=True, - ) + user = models.ForeignKey(Profile, related_name="ignored_chat_users", verbose_name=_('user'), on_delete=CASCADE) ignored_users = models.ManyToManyField(Profile) - class Meta: - app_label = "chat_box" - @classmethod def is_ignored(self, current_user, new_friend): try: - return current_user.ignored_chat_users.ignored_users.filter( - id=new_friend.id - ).exists() + return current_user.ignored_chat_users.get().ignored_users \ + .filter(id=new_friend.id).exists() except: return False @@ -115,40 +59,26 @@ class Ignore(models.Model): def get_ignored_users(self, user): try: return self.objects.get(user=user).ignored_users.all() - except: + except Ignore.DoesNotExist: return Profile.objects.none() - @classmethod - def get_ignored_rooms(self, user): - try: - ignored_users = self.objects.get(user=user).ignored_users.all() - return Room.objects.filter(Q(user_one=user) | Q(user_two=user)).filter( - Q(user_one__in=ignored_users) | Q(user_two__in=ignored_users) - ) - except: - return Room.objects.none() - @classmethod def add_ignore(self, current_user, friend): - ignore, created = self.objects.get_or_create(user=current_user) + ignore, created = self.objects.get_or_create( + user = current_user + ) ignore.ignored_users.add(friend) @classmethod def remove_ignore(self, current_user, friend): - ignore, created = self.objects.get_or_create(user=current_user) + ignore, created = self.objects.get_or_create( + user = current_user + ) ignore.ignored_users.remove(friend) @classmethod def toggle_ignore(self, current_user, friend): - if self.is_ignored(current_user, friend): + if (self.is_ignored(current_user, friend)): self.remove_ignore(current_user, friend) else: - self.add_ignore(current_user, friend) - - -@cache_wrapper(prefix="Rinfo") -def get_room_info(room_id): - last_msg = Message.objects.filter(room_id=room_id).first() - return { - "last_message": last_msg.body if last_msg else None, - } + self.add_ignore(current_user, friend) \ No newline at end of file diff --git a/chat_box/utils.py b/chat_box/utils.py index f3f27b4..5ebb16e 100644 --- a/chat_box/utils.py +++ b/chat_box/utils.py @@ -1,51 +1,18 @@ from cryptography.fernet import Fernet -import hmac -import hashlib from django.conf import settings -from django.db.models import OuterRef, Count, Subquery, IntegerField, Q -from django.db.models.functions import Coalesce - -from chat_box.models import Ignore, Message, UserRoom, Room - -from judge.caching import cache_wrapper secret_key = settings.CHAT_SECRET_KEY fernet = Fernet(secret_key) - def encrypt_url(creator_id, other_id): - message = str(creator_id) + "_" + str(other_id) + message = str(creator_id) + '_' + str(other_id) return fernet.encrypt(message.encode()).decode() - def decrypt_url(message_encrypted): try: dec_message = fernet.decrypt(message_encrypted.encode()).decode() - creator_id, other_id = dec_message.split("_") + creator_id, other_id = dec_message.split('_') return int(creator_id), int(other_id) except Exception as e: - return None, None - - -def encrypt_channel(channel): - return ( - hmac.new( - settings.CHAT_SECRET_KEY.encode(), - channel.encode(), - hashlib.sha512, - ).hexdigest()[:16] - + "%s" % channel - ) - - -@cache_wrapper(prefix="gub") -def get_unread_boxes(profile): - ignored_rooms = Ignore.get_ignored_rooms(profile) - unread_boxes = ( - UserRoom.objects.filter(user=profile, unread_count__gt=0) - .exclude(room__in=ignored_rooms) - .count() - ) - - return unread_boxes + return None, None \ No newline at end of file diff --git a/chat_box/views.py b/chat_box/views.py index e3d1646..f64fb15 100644 --- a/chat_box/views.py +++ b/chat_box/views.py @@ -1,77 +1,47 @@ from django.utils.translation import gettext as _ from django.views.generic import ListView -from django.http import ( - HttpResponse, - JsonResponse, - HttpResponseBadRequest, - HttpResponsePermanentRedirect, - HttpResponseRedirect, -) +from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest, HttpResponsePermanentRedirect, HttpResponseRedirect from django.core.paginator import Paginator from django.core.exceptions import PermissionDenied from django.shortcuts import render from django.forms.models import model_to_dict -from django.db.models import ( - Case, - BooleanField, - When, - Q, - Subquery, - OuterRef, - Exists, - Count, - IntegerField, - F, - Max, -) +from django.db.models import Case, BooleanField, When, Q, Subquery, OuterRef, Exists, Count, IntegerField from django.db.models.functions import Coalesce from django.utils import timezone from django.contrib.auth.decorators import login_required from django.urls import reverse +import datetime from judge import event_poster as event from judge.jinja2.gravatar import gravatar from judge.models import Friend -from chat_box.models import Message, Profile, Room, UserRoom, Ignore, get_room_info -from chat_box.utils import encrypt_url, decrypt_url, encrypt_channel, get_unread_boxes +from chat_box.models import Message, Profile, Room, UserRoom, Ignore +from chat_box.utils import encrypt_url, decrypt_url -from reversion import revisions +import json class ChatView(ListView): - context_object_name = "message" - template_name = "chat/chat.html" - title = _("LQDOJ Chat") - + context_object_name = 'message' + template_name = 'chat/chat.html' + title = _('Chat Box') + def __init__(self): super().__init__() self.room_id = None self.room = None + self.paginate_by = 50 self.messages = None - self.first_page_size = 20 # only for first request - self.follow_up_page_size = 50 + self.paginator = None def get_queryset(self): return self.messages - def has_next(self): - try: - msg = Message.objects.filter(room=self.room_id).earliest("id") - except Exception as e: - return False - return msg not in self.messages - def get(self, request, *args, **kwargs): - request_room = kwargs["room_id"] - page_size = self.follow_up_page_size - try: - last_id = int(request.GET.get("last_id")) - except Exception: - last_id = 1e15 - page_size = self.first_page_size - only_messages = request.GET.get("only_messages") + request_room = kwargs['room_id'] + page = request.GET.get('page') if request_room: try: @@ -83,261 +53,180 @@ class ChatView(ListView): else: request_room = None - self.room_id = request_room - self.messages = ( - Message.objects.filter(hidden=False, room=self.room_id, id__lt=last_id) - .select_related("author") - .only("body", "time", "author__rating", "author__display_rank")[:page_size] - ) - if not only_messages: + if request_room != self.room_id or not self.messages: + self.room_id = request_room + self.messages = Message.objects.filter(hidden=False, room=self.room_id) + self.paginator = Paginator(self.messages, self.paginate_by) + + if page == None: + update_last_seen(request, **kwargs) return super().get(request, *args, **kwargs) - return render( - request, - "chat/message_list.html", - { - "object_list": self.messages, - "has_next": self.has_next(), - }, - ) + cur_page = self.paginator.get_page(page) + + return render(request, 'chat/message_list.html', { + 'object_list': cur_page.object_list, + 'num_pages': self.paginator.num_pages + }) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["title"] = self.title - context["last_msg"] = event.last() - context["status_sections"] = get_status_context(self.request.profile) - context["room"] = self.room_id - context["has_next"] = self.has_next() - context["unread_count_lobby"] = get_unread_count(None, self.request.profile) - context["chat_channel"] = encrypt_channel( - "chat_" + str(self.request.profile.id) - ) - context["chat_lobby_channel"] = encrypt_channel("chat_lobby") + context['title'] = self.title + context['last_msg'] = event.last() + context['status_sections'] = get_status_context(self.request) + context['room'] = self.room_id + context['unread_count_lobby'] = get_unread_count(None, self.request.profile) if self.room: users_room = [self.room.user_one, self.room.user_two] users_room.remove(self.request.profile) - context["other_user"] = users_room[0] - context["other_online"] = get_user_online_status(context["other_user"]) - context["is_ignored"] = Ignore.is_ignored( - self.request.profile, context["other_user"] - ) + context['other_user'] = users_room[0] + context['other_online'] = get_user_online_status(context['other_user']) + context['is_ignored'] = Ignore.is_ignored(self.request.profile, context['other_user']) else: - context["online_count"] = get_online_count() - context["message_template"] = { - "author": self.request.profile, - "id": "$id", - "time": timezone.now(), - "body": "$body", - } + context['online_count'] = get_online_count() + context['message_template'] = { + 'author': self.request.profile, + 'id': '$id', + 'time': timezone.now(), + 'body': '$body' + } return context def delete_message(request): - ret = {"delete": "done"} - - if request.method == "GET": - return HttpResponseBadRequest() - - try: - messid = int(request.POST.get("message")) - mess = Message.objects.get(id=messid) - except: - return HttpResponseBadRequest() - - if not request.user.is_staff and request.profile != mess.author: - return HttpResponseBadRequest() - - mess.hidden = True - mess.save() + ret = {'delete': 'done'} + + if request.method == 'GET': + return JsonResponse(ret) + if request.user.is_staff: + try: + messid = int(request.POST.get('message')) + mess = Message.objects.get(id=messid) + except: + return HttpResponseBadRequest() + + mess.hidden = True + mess.save() + + return JsonResponse(ret) + return JsonResponse(ret) -def mute_message(request): - ret = {"mute": "done"} - - if request.method == "GET": - return HttpResponseBadRequest() - - if not request.user.is_staff: - return HttpResponseBadRequest() - - try: - messid = int(request.POST.get("message")) - mess = Message.objects.get(id=messid) - except: - return HttpResponseBadRequest() - - with revisions.create_revision(): - revisions.set_comment(_("Mute chat") + ": " + mess.body) - revisions.set_user(request.user) - mess.author.mute = True - mess.author.save() - - Message.objects.filter(room=None, author=mess.author).update(hidden=True) - - return JsonResponse(ret) - - -def check_valid_message(request, room): - if not room and len(request.POST["body"]) > 200: - return False - - if not can_access_room(request, room) or request.profile.mute: - return False - - last_msg = Message.objects.filter(room=room).first() - if ( - last_msg - and last_msg.author == request.profile - and last_msg.body == request.POST["body"].strip() - ): - return False - - if not room: - four_last_msg = Message.objects.filter(room=room).order_by("-id")[:4] - if len(four_last_msg) >= 4: - same_author = all(msg.author == request.profile for msg in four_last_msg) - time_diff = timezone.now() - four_last_msg[3].time - if same_author and time_diff.total_seconds() < 300: - return False - - return True - - @login_required def post_message(request): - ret = {"msg": "posted"} - - if request.method != "POST": - return HttpResponseBadRequest() - if len(request.POST["body"]) > 5000 or len(request.POST["body"].strip()) == 0: + ret = {'msg': 'posted'} + if request.method != 'POST': return HttpResponseBadRequest() room = None - if request.POST["room"]: - room = Room.objects.get(id=request.POST["room"]) - - if not check_valid_message(request, room): + if request.POST['room']: + room = Room.objects.get(id=request.POST['room']) + + if not can_access_room(request, room) or request.profile.mute: return HttpResponseBadRequest() - new_message = Message(author=request.profile, body=request.POST["body"], room=room) + new_message = Message(author=request.profile, + body=request.POST['body'], + room=room) new_message.save() if not room: - event.post( - encrypt_channel("chat_lobby"), - { - "type": "lobby", - "author_id": request.profile.id, - "message": new_message.id, - "room": "None", - "tmp_id": request.POST.get("tmp_id"), - }, - ) + event.post('chat_lobby', { + 'type': 'lobby', + 'author_id': request.profile.id, + 'message': new_message.id, + 'room': 'None', + 'tmp_id': request.POST.get('tmp_id') + }) else: - get_room_info.dirty(room.id) - room.last_msg_time = new_message.time - room.save() - for user in room.users(): - event.post( - encrypt_channel("chat_" + str(user.id)), - { - "type": "private", - "author_id": request.profile.id, - "message": new_message.id, - "room": room.id, - "tmp_id": request.POST.get("tmp_id"), - }, - ) - if user != request.profile: - UserRoom.objects.filter(user=user, room=room).update( - unread_count=F("unread_count") + 1 - ) - get_unread_boxes.dirty(user) - + event.post('chat_' + str(user.id), { + 'type': 'private', + 'author_id': request.profile.id, + 'message': new_message.id, + 'room': room.id, + 'tmp_id': request.POST.get('tmp_id') + }) + return JsonResponse(ret) def can_access_room(request, room): - return not room or room.contain(request.profile) + return not room or room.user_one == request.profile or room.user_two == request.profile @login_required def chat_message_ajax(request): - if request.method != "GET": + if request.method != 'GET': return HttpResponseBadRequest() try: - message_id = request.GET["message"] + message_id = request.GET['message'] except KeyError: return HttpResponseBadRequest() try: message = Message.objects.filter(hidden=False).get(id=message_id) room = message.room - if not can_access_room(request, room): - return HttpResponse("Unauthorized", status=401) + if room and not room.contain(request.profile): + return HttpResponse('Unauthorized', status=401) except Message.DoesNotExist: return HttpResponseBadRequest() - return render( - request, - "chat/message.html", - { - "message": message, - }, - ) + return render(request, 'chat/message.html', { + 'message': message, + }) @login_required def update_last_seen(request, **kwargs): - if "room_id" in kwargs: - room_id = kwargs["room_id"] - elif request.method == "GET": - room_id = request.GET.get("room") - elif request.method == "POST": - room_id = request.POST.get("room") + if 'room_id' in kwargs: + room_id = kwargs['room_id'] + elif request.method == 'GET': + room_id = request.GET.get('room') + elif request.method == 'POST': + room_id = request.POST.get('room') else: return HttpResponseBadRequest() + try: profile = request.profile room = None if room_id: - room = Room.objects.filter(id=int(room_id)).first() + room = Room.objects.get(id=int(room_id)) except Room.DoesNotExist: return HttpResponseBadRequest() + except Exception as e: + return HttpResponseBadRequest() - if not can_access_room(request, room): + if room and not room.contain(profile): return HttpResponseBadRequest() user_room, _ = UserRoom.objects.get_or_create(user=profile, room=room) user_room.last_seen = timezone.now() - user_room.unread_count = 0 user_room.save() - get_unread_boxes.dirty(profile) - - return JsonResponse({"msg": "updated"}) + return JsonResponse({'msg': 'updated'}) def get_online_count(): - last_5_minutes = timezone.now() - timezone.timedelta(minutes=5) - return Profile.objects.filter(last_access__gte=last_5_minutes).count() + last_two_minutes = timezone.now()-timezone.timedelta(minutes=2) + return Profile.objects.filter(last_access__gte=last_two_minutes).count() def get_user_online_status(user): time_diff = timezone.now() - user.last_access - is_online = time_diff <= timezone.timedelta(minutes=5) + is_online = time_diff <= timezone.timedelta(minutes=2) return is_online def user_online_status_ajax(request): - if request.method != "GET": + if request.method != 'GET': return HttpResponseBadRequest() - user_id = request.GET.get("user") + user_id = request.GET.get('user') if user_id: try: @@ -347,126 +236,109 @@ def user_online_status_ajax(request): return HttpResponseBadRequest() is_online = get_user_online_status(user) - return render( - request, - "chat/user_online_status.html", - { - "other_user": user, - "other_online": is_online, - "is_ignored": Ignore.is_ignored(request.profile, user), - }, - ) + return render(request, 'chat/user_online_status.html', { + 'other_user': user, + 'other_online': is_online, + 'is_ignored': Ignore.is_ignored(request.profile, user) + }) else: - return render( - request, - "chat/user_online_status.html", - { - "online_count": get_online_count(), - }, - ) + return render(request, 'chat/user_online_status.html', { + 'online_count': get_online_count(), + }) -def get_online_status(profile, other_profile_ids, rooms=None): - if not other_profile_ids: +def get_online_status(request_user, queryset, rooms=None): + if not queryset: return None - Profile.prefetch_profile_cache(other_profile_ids) - - joined_ids = ",".join([str(id) for id in other_profile_ids]) - other_profiles = Profile.objects.raw( - f"SELECT * from judge_profile where id in ({joined_ids}) order by field(id,{joined_ids})" - ) - last_5_minutes = timezone.now() - timezone.timedelta(minutes=5) + last_two_minutes = timezone.now()-timezone.timedelta(minutes=2) ret = [] - if rooms: - unread_count = get_unread_count(rooms, profile) - count = {} - last_msg = {} - room_of_user = {} - for i in unread_count: - room = Room.objects.get(id=i["room"]) - other_profile = room.other_user(profile) - count[other_profile.id] = i["unread_count"] - rooms = Room.objects.filter(id__in=rooms) - for room in rooms: - other_profile_id = room.other_user_id(profile) - last_msg[other_profile_id] = room.last_message_body() - room_of_user[other_profile_id] = room.id - for other_profile in other_profiles: + if rooms: + unread_count = get_unread_count(rooms, request_user) + count = {} + for i in unread_count: + count[i['other_user']] = i['unread_count'] + + for user in queryset: is_online = False - if other_profile.last_access >= last_5_minutes: + if (user.last_access >= last_two_minutes): is_online = True - user_dict = {"user": other_profile, "is_online": is_online} - if rooms: - user_dict.update( - { - "unread_count": count.get(other_profile.id), - "last_msg": last_msg.get(other_profile.id), - "room": room_of_user.get(other_profile.id), - } - ) - user_dict["url"] = encrypt_url(profile.id, other_profile.id) + user_dict = {'user': user, 'is_online': is_online} + if rooms and user.id in count: + user_dict['unread_count'] = count[user.id] + user_dict['url'] = encrypt_url(request_user.id, user.id) ret.append(user_dict) return ret -def get_status_context(profile, include_ignored=False): +def get_status_context(request, include_ignored=False): if include_ignored: - ignored_users = [] + ignored_users = Profile.objects.none() queryset = Profile.objects else: - ignored_users = list( - Ignore.get_ignored_users(profile).values_list("id", flat=True) - ) + ignored_users = Ignore.get_ignored_users(request.profile) queryset = Profile.objects.exclude(id__in=ignored_users) - last_5_minutes = timezone.now() - timezone.timedelta(minutes=5) - recent_profile = ( - Room.objects.filter(Q(user_one=profile) | Q(user_two=profile)) - .annotate( - other_user=Case( - When(user_one=profile, then="user_two"), - default="user_one", + last_two_minutes = timezone.now()-timezone.timedelta(minutes=2) + recent_profile = Room.objects.filter( + Q(user_one=request.profile) | Q(user_two=request.profile) + ).annotate( + last_msg_time=Subquery( + Message.objects.filter(room=OuterRef('pk')).values('time')[:1] ), - ) - .filter(last_msg_time__isnull=False) - .exclude(other_user__in=ignored_users) - .order_by("-last_msg_time") - .values("other_user", "id")[:20] - ) - - recent_profile_ids = [str(i["other_user"]) for i in recent_profile] - recent_rooms = [int(i["id"]) for i in recent_profile] - Room.prefetch_room_cache(recent_rooms) - - admin_list = ( - queryset.filter(display_rank="admin") - .exclude(id__in=recent_profile_ids) - .values_list("id", flat=True) - ) + other_user=Case( + When(user_one=request.profile, then='user_two'), + default='user_one', + ) + ).filter(last_msg_time__isnull=False)\ + .exclude(other_user__in=ignored_users)\ + .order_by('-last_msg_time').values('other_user', 'id')[:20] + recent_profile_id = [str(i['other_user']) for i in recent_profile] + joined_id = ','.join(recent_profile_id) + recent_rooms = [int(i['id']) for i in recent_profile] + recent_list = None + if joined_id: + recent_list = Profile.objects.raw( + f'SELECT * from judge_profile where id in ({joined_id}) order by field(id,{joined_id})') + friend_list = Friend.get_friend_profiles(request.profile).exclude(id__in=recent_profile_id)\ + .exclude(id__in=ignored_users)\ + .order_by('-last_access') + admin_list = queryset.filter(display_rank='admin')\ + .exclude(id__in=friend_list).exclude(id__in=recent_profile_id) + all_user_status = queryset\ + .filter(display_rank='user', + last_access__gte = last_two_minutes)\ + .annotate(is_online=Case(default=True,output_field=BooleanField()))\ + .order_by('-rating').exclude(id__in=friend_list).exclude(id__in=admin_list)\ + .exclude(id__in=recent_profile_id)[:30] + return [ { - "title": _("Recent"), - "user_list": get_online_status(profile, recent_profile_ids, recent_rooms), + 'title': 'Recent', + 'user_list': get_online_status(request.profile, recent_list, recent_rooms), }, { - "title": _("Admin"), - "user_list": get_online_status(profile, admin_list), + 'title': 'Following', + 'user_list': get_online_status(request.profile, friend_list), + }, + { + 'title': 'Admin', + 'user_list': get_online_status(request.profile, admin_list), + }, + { + 'title': 'Other', + 'user_list': get_online_status(request.profile, all_user_status), }, ] @login_required def online_status_ajax(request): - return render( - request, - "chat/online_status.html", - { - "status_sections": get_status_context(request.profile), - "unread_count_lobby": get_unread_count(None, request.profile), - }, - ) + return render(request, 'chat/online_status.html', { + 'status_sections': get_status_context(request), + 'unread_count_lobby': get_unread_count(None, request.profile), + }) @login_required @@ -479,14 +351,15 @@ def get_room(user_one, user_two): @login_required def get_or_create_room(request): - if request.method == "GET": - decrypted_other_id = request.GET.get("other") - elif request.method == "POST": - decrypted_other_id = request.POST.get("other") + if request.method == 'GET': + decrypted_other_id = request.GET.get('other') + elif request.method == 'POST': + decrypted_other_id = request.POST.get('other') else: return HttpResponseBadRequest() request_id, other_id = decrypt_url(decrypted_other_id) + if not other_id or not request_id or request_id != request.profile.id: return HttpResponseBadRequest() @@ -496,7 +369,7 @@ def get_or_create_room(request): return HttpResponseBadRequest() user = request.profile - + if not other_user or not user: return HttpResponseBadRequest() # TODO: each user can only create <= 300 rooms @@ -507,41 +380,47 @@ def get_or_create_room(request): user_room.last_seen = timezone.now() user_room.save() - room_url = reverse("chat", kwargs={"room_id": room.id}) - if request.method == "GET": - return JsonResponse( - { - "room": room.id, - "other_user_id": other_user.id, - "url": room_url, - } - ) - return HttpResponseRedirect(room_url) + if request.method == 'GET': + return JsonResponse({'room': room.id, 'other_user_id': other_user.id}) + return HttpResponseRedirect(reverse('chat', kwargs={'room_id': room.id})) def get_unread_count(rooms, user): if rooms: - return UserRoom.objects.filter( - user=user, room__in=rooms, unread_count__gt=0 - ).values("unread_count", "room") - else: # lobby - user_room = UserRoom.objects.filter(user=user, room__isnull=True).first() - if not user_room: - return 0 - last_seen = user_room.last_seen - res = ( - Message.objects.filter(room__isnull=True, time__gte=last_seen) - .exclude(author=user) - .exclude(hidden=True) - .count() - ) + mess = Message.objects.filter(room=OuterRef('room'), + time__gte=OuterRef('last_seen'))\ + .exclude(author=user)\ + .order_by().values('room')\ + .annotate(unread_count=Count('pk')).values('unread_count') - return res + return UserRoom.objects\ + .filter(user=user, room__in=rooms)\ + .annotate( + unread_count=Coalesce(Subquery(mess, output_field=IntegerField()), 0), + other_user=Case( + When(room__user_one=user, then='room__user_two'), + default='room__user_one', + ) + ).filter(unread_count__gte=1).values('other_user', 'unread_count') + else: # lobby + mess = Message.objects.filter(room__isnull=True, + time__gte=OuterRef('last_seen'))\ + .exclude(author=user)\ + .order_by().values('room')\ + .annotate(unread_count=Count('pk')).values('unread_count') + + res = UserRoom.objects\ + .filter(user=user, room__isnull=True)\ + .annotate( + unread_count=Coalesce(Subquery(mess, output_field=IntegerField()), 0), + ).values_list('unread_count', flat=True) + + return res[0] if len(res) else 0 @login_required def toggle_ignore(request, **kwargs): - user_id = kwargs["user_id"] + user_id = kwargs['user_id'] if not user_id: return HttpResponseBadRequest() try: @@ -550,5 +429,25 @@ def toggle_ignore(request, **kwargs): return HttpResponseBadRequest() Ignore.toggle_ignore(request.profile, other_user) - next_url = request.GET.get("next", "/") + next_url = request.GET.get('next', '/') return HttpResponseRedirect(next_url) + + +@login_required +def get_unread_boxes(request): + if (request.method != 'GET'): + return HttpResponseBadRequest() + + mess = Message.objects.filter(room=OuterRef('room'), + time__gte=OuterRef('last_seen'))\ + .exclude(author=request.profile)\ + .order_by().values('room')\ + .annotate(unread_count=Count('pk')).values('unread_count') + + unread_boxes = UserRoom.objects\ + .filter(user=request.profile, room__isnull=False)\ + .annotate( + unread_count=Coalesce(Subquery(mess, output_field=IntegerField()), 0), + ).filter(unread_count__gte=1).count() + + return JsonResponse({'unread_boxes': unread_boxes}) \ No newline at end of file diff --git a/django_2_2_pymysql_patch.py b/django_2_2_pymysql_patch.py index c39c823..4470180 100644 --- a/django_2_2_pymysql_patch.py +++ b/django_2_2_pymysql_patch.py @@ -12,6 +12,6 @@ if (2, 2) <= django.VERSION < (3,): # attribute where the exact query sent to the database is saved. # See MySQLdb/cursors.py in the source distribution. # MySQLdb returns string, PyMySQL bytes. - return force_text(getattr(cursor, "_executed", None), errors="replace") + return force_text(getattr(cursor, '_executed', None), errors='replace') DatabaseOperations.last_executed_query = last_executed_query diff --git a/django_ace/static/django_ace/widget.css b/django_ace/static/django_ace/widget.css index a77a288..09ad946 100644 --- a/django_ace/static/django_ace/widget.css +++ b/django_ace/static/django_ace/widget.css @@ -11,12 +11,6 @@ bottom: 0; } -.ace_editor { - overflow: hidden; - font: 12px/normal 'Fira Code', 'Monaco', 'Menlo', monospace; - direction: 1tr; -} - .django-ace-widget.loading { display: none; } @@ -61,4 +55,4 @@ .django-ace-editor-fullscreen .django-ace-max_min { background-image: url(img/contract.png); -} +} \ No newline at end of file diff --git a/django_ace/static/django_ace/widget.js b/django_ace/static/django_ace/widget.js index d5226dd..d097b06 100644 --- a/django_ace/static/django_ace/widget.js +++ b/django_ace/static/django_ace/widget.js @@ -77,17 +77,15 @@ mode = widget.getAttribute('data-mode'), theme = widget.getAttribute('data-theme'), wordwrap = widget.getAttribute('data-wordwrap'), - toolbar = prev(widget); - var main_block = div.parentNode.parentNode; + toolbar = prev(widget), + main_block = toolbar.parentNode; - if (toolbar != null) { - // Toolbar maximize/minimize button - var min_max = toolbar.getElementsByClassName('django-ace-max_min'); - min_max[0].onclick = function () { - minimizeMaximize(widget, main_block, editor); - return false; - }; - } + // Toolbar maximize/minimize button + var min_max = toolbar.getElementsByClassName('django-ace-max_min'); + min_max[0].onclick = function () { + minimizeMaximize(widget, main_block, editor); + return false; + }; editor.getSession().setValue(textarea.value); @@ -162,7 +160,7 @@ ]); window[widget.id] = editor; - setTimeout(() => $(widget).trigger('ace_load', [editor]), 100); + $(widget).trigger('ace_load', [editor]); } function init() { diff --git a/django_ace/widgets.py b/django_ace/widgets.py index 659ec75..91369d0 100644 --- a/django_ace/widgets.py +++ b/django_ace/widgets.py @@ -11,33 +11,22 @@ from django.utils.safestring import mark_safe class AceWidget(forms.Textarea): - def __init__( - self, - mode=None, - theme=None, - wordwrap=False, - width="100%", - height="300px", - no_ace_media=False, - toolbar=True, - *args, - **kwargs - ): + def __init__(self, mode=None, theme=None, wordwrap=False, width='100%', height='300px', + no_ace_media=False, *args, **kwargs): self.mode = mode self.theme = theme self.wordwrap = wordwrap self.width = width self.height = height self.ace_media = not no_ace_media - self.toolbar = toolbar super(AceWidget, self).__init__(*args, **kwargs) @property def media(self): - js = [urljoin(settings.ACE_URL, "ace.js")] if self.ace_media else [] - js.append("django_ace/widget.js") + js = [urljoin(settings.ACE_URL, 'ace.js')] if self.ace_media else [] + js.append('django_ace/widget.js') css = { - "screen": ["django_ace/widget.css"], + 'screen': ['django_ace/widget.css'], } return forms.Media(js=js, css=css) @@ -45,32 +34,24 @@ class AceWidget(forms.Textarea): attrs = attrs or {} ace_attrs = { - "class": "django-ace-widget loading", - "style": "width:%s; height:%s" % (self.width, self.height), - "id": "ace_%s" % name, + 'class': 'django-ace-widget loading', + 'style': 'width:%s; height:%s' % (self.width, self.height), + 'id': 'ace_%s' % name, } if self.mode: - ace_attrs["data-mode"] = self.mode + ace_attrs['data-mode'] = self.mode if self.theme: - ace_attrs["data-theme"] = self.theme + ace_attrs['data-theme'] = self.theme if self.wordwrap: - ace_attrs["data-wordwrap"] = "true" + ace_attrs['data-wordwrap'] = 'true' - attrs.update( - style="width: 100%; min-width: 100%; max-width: 100%; resize: none" - ) + attrs.update(style='width: 100%; min-width: 100%; max-width: 100%; resize: none') textarea = super(AceWidget, self).render(name, value, attrs) - html = "
%s" % (flatatt(ace_attrs), textarea) + html = '
%s' % (flatatt(ace_attrs), textarea) - if self.toolbar: - toolbar = ( - '
' - '' - "
" - ).format(self.width) - html = toolbar + html - - html = '
{}
'.format(html) + # add toolbar + html = ('
' + '
%s
') % html return mark_safe(html) diff --git a/dmoj/celery.py b/dmoj/celery.py index 5f0c87d..96718ea 100644 --- a/dmoj/celery.py +++ b/dmoj/celery.py @@ -4,30 +4,24 @@ import socket from celery import Celery from celery.signals import task_failure -app = Celery("dmoj") +app = Celery('dmoj') from django.conf import settings # noqa: E402, I202, django must be imported here +app.config_from_object(settings, namespace='CELERY') -app.config_from_object(settings, namespace="CELERY") - -if hasattr(settings, "CELERY_BROKER_URL_SECRET"): +if hasattr(settings, 'CELERY_BROKER_URL_SECRET'): app.conf.broker_url = settings.CELERY_BROKER_URL_SECRET -if hasattr(settings, "CELERY_RESULT_BACKEND_SECRET"): +if hasattr(settings, 'CELERY_RESULT_BACKEND_SECRET'): app.conf.result_backend = settings.CELERY_RESULT_BACKEND_SECRET # Load task modules from all registered Django app configs. app.autodiscover_tasks() # Logger to enable errors be reported. -logger = logging.getLogger("judge.celery") +logger = logging.getLogger('judge.celery') @task_failure.connect() def celery_failure_log(sender, task_id, exception, traceback, *args, **kwargs): - logger.error( - "Celery Task %s: %s on %s", - sender.name, - task_id, - socket.gethostname(), # noqa: G201 - exc_info=(type(exception), exception, traceback), - ) + logger.error('Celery Task %s: %s on %s', sender.name, task_id, socket.gethostname(), # noqa: G201 + exc_info=(type(exception), exception, traceback)) diff --git a/dmoj/settings.py b/dmoj/settings.py index 2867a2a..86377a7 100644 --- a/dmoj/settings.py +++ b/dmoj/settings.py @@ -22,7 +22,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "5*9f5q57mqmlz2#f$x1h76&jxy#yortjl1v+l*6hd18$d*yx#0" +SECRET_KEY = '5*9f5q57mqmlz2#f$x1h76&jxy#yortjl1v+l*6hd18$d*yx#0' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -30,10 +30,9 @@ DEBUG = True ALLOWED_HOSTS = [] SITE_ID = 1 -SITE_NAME = "LQDOJ" -SITE_LONG_NAME = "LQDOJ: Le Quy Don Online Judge" +SITE_NAME = 'LQDOJ' +SITE_LONG_NAME = 'LQDOJ: Le Quy Don Online Judge' SITE_ADMIN_EMAIL = False -SITE_DOMAIN = "lqdoj.edu.vn" DMOJ_REQUIRE_STAFF_2FA = True @@ -45,11 +44,13 @@ DMOJ_SSL = 0 # Refer to dmoj.ca/post/103-point-system-rework DMOJ_PP_STEP = 0.95 DMOJ_PP_ENTRIES = 100 -DMOJ_PP_BONUS_FUNCTION = lambda n: 300 * (1 - 0.997**n) # noqa: E731 +DMOJ_PP_BONUS_FUNCTION = lambda n: 300 * (1 - 0.997 ** n) # noqa: E731 -NODEJS = "/usr/bin/node" -EXIFTOOL = "/usr/bin/exiftool" -ACE_URL = "//cdnjs.cloudflare.com/ajax/libs/ace/1.1.3" +NODEJS = '/usr/bin/node' +EXIFTOOL = '/usr/bin/exiftool' +ACE_URL = '//cdnjs.cloudflare.com/ajax/libs/ace/1.1.3' +SELECT2_JS_URL = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js' +DEFAULT_SELECT2_CSS = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css' DMOJ_CAMO_URL = None DMOJ_CAMO_KEY = None @@ -61,7 +62,6 @@ DMOJ_PROBLEM_MAX_TIME_LIMIT = 60 # seconds DMOJ_PROBLEM_MIN_MEMORY_LIMIT = 0 # kilobytes DMOJ_PROBLEM_MAX_MEMORY_LIMIT = 1048576 # kilobytes DMOJ_PROBLEM_MIN_PROBLEM_POINTS = 0 -DMOJ_SUBMISSION_ROOT = "/tmp" DMOJ_RATING_COLORS = True DMOJ_EMAIL_THROTTLING = (10, 60) DMOJ_STATS_LANGUAGE_THRESHOLD = 10 @@ -73,20 +73,16 @@ DMOJ_BLOG_NEW_CONTEST_COUNT = 7 DMOJ_BLOG_RECENTLY_ATTEMPTED_PROBLEMS_COUNT = 7 DMOJ_TOTP_TOLERANCE_HALF_MINUTES = 1 DMOJ_USER_MAX_ORGANIZATION_COUNT = 10 -DMOJ_USER_MAX_ORGANIZATION_ADD = 5 DMOJ_COMMENT_VOTE_HIDE_THRESHOLD = -5 -DMOJ_PDF_PROBLEM_CACHE = "" +DMOJ_PDF_PROBLEM_CACHE = '' DMOJ_PDF_PROBLEM_TEMP_DIR = tempfile.gettempdir() DMOJ_STATS_SUBMISSION_RESULT_COLORS = { - "TLE": "#a3bcbd", - "AC": "#00a92a", - "WA": "#ed4420", - "CE": "#42586d", - "ERR": "#ffa71c", + 'TLE': '#a3bcbd', + 'AC': '#00a92a', + 'WA': '#ed4420', + 'CE': '#42586d', + 'ERR': '#ffa71c', } -DMOJ_PROFILE_IMAGE_ROOT = "profile_images" -DMOJ_ORGANIZATION_IMAGE_ROOT = "organization_images" -DMOJ_TEST_FORMATTER_ROOT = "test_formatter" MARKDOWN_STYLES = {} MARKDOWN_DEFAULT_STYLE = {} @@ -94,15 +90,16 @@ MARKDOWN_DEFAULT_STYLE = {} MATHOID_URL = False MATHOID_GZIP = False MATHOID_MML_CACHE = None -MATHOID_CSS_CACHE = "default" -MATHOID_DEFAULT_TYPE = "auto" +MATHOID_CSS_CACHE = 'default' +MATHOID_DEFAULT_TYPE = 'auto' MATHOID_MML_CACHE_TTL = 86400 -MATHOID_CACHE_ROOT = tempfile.gettempdir() + "/mathoidCache" +MATHOID_CACHE_ROOT = tempfile.gettempdir() + '/mathoidCache' MATHOID_CACHE_URL = False TEXOID_GZIP = False -TEXOID_META_CACHE = "default" +TEXOID_META_CACHE = 'default' TEXOID_META_CACHE_TTL = 86400 +DMOJ_NEWSLETTER_ID_ON_REGISTER = 1 BAD_MAIL_PROVIDERS = () BAD_MAIL_PROVIDER_REGEX = () @@ -113,30 +110,31 @@ TIMEZONE_MAP = None TIMEZONE_DETECT_BACKEND = None TERMS_OF_SERVICE_URL = None -DEFAULT_USER_LANGUAGE = "PY3" +DEFAULT_USER_LANGUAGE = 'PY3' -PHANTOMJS = "" +PHANTOMJS = '' PHANTOMJS_PDF_ZOOM = 0.75 PHANTOMJS_PDF_TIMEOUT = 5.0 -PHANTOMJS_PAPER_SIZE = "Letter" +PHANTOMJS_PAPER_SIZE = 'Letter' -SLIMERJS = "" +SLIMERJS = '' SLIMERJS_PDF_ZOOM = 0.75 -SLIMERJS_FIREFOX_PATH = "" -SLIMERJS_PAPER_SIZE = "Letter" +SLIMERJS_FIREFOX_PATH = '' +SLIMERJS_PAPER_SIZE = 'Letter' -PUPPETEER_MODULE = "/usr/lib/node_modules/puppeteer" -PUPPETEER_PAPER_SIZE = "Letter" +PUPPETEER_MODULE = '/usr/lib/node_modules/puppeteer' +PUPPETEER_PAPER_SIZE = 'Letter' USE_SELENIUM = False SELENIUM_CUSTOM_CHROME_PATH = None -SELENIUM_CHROMEDRIVER_PATH = "chromedriver" +SELENIUM_CHROMEDRIVER_PATH = 'chromedriver' +PYGMENT_THEME = 'pygment-github.css' INLINE_JQUERY = True INLINE_FONTAWESOME = True -JQUERY_JS = "//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js" -FONTAWESOME_CSS = "//cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" -DMOJ_CANONICAL = "" +JQUERY_JS = '//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js' +FONTAWESOME_CSS = '//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css' +DMOJ_CANONICAL = '' # Application definition @@ -148,315 +146,356 @@ except ImportError: pass else: del wpadmin - INSTALLED_APPS += ("wpadmin",) + INSTALLED_APPS += ('wpadmin',) WPADMIN = { - "admin": { - "title": "LQDOJ Admin", - "menu": { - "top": "wpadmin.menu.menus.BasicTopMenu", - "left": "wpadmin.menu.custom.CustomModelLeftMenuWithDashboard", + 'admin': { + 'title': 'LQDOJ Admin', + 'menu': { + 'top': 'wpadmin.menu.menus.BasicTopMenu', + 'left': 'wpadmin.menu.custom.CustomModelLeftMenuWithDashboard', }, - "custom_menu": [ + 'custom_menu': [ { - "model": "judge.Problem", - "icon": "fa-question-circle", - "children": [ - "judge.ProblemGroup", - "judge.ProblemType", - "judge.ProblemPointsVote", + 'model': 'judge.Problem', + 'icon': 'fa-question-circle', + 'children': [ + 'judge.ProblemGroup', + 'judge.ProblemType', ], }, { - "model": "judge.Submission", - "icon": "fa-check-square", - "children": [ - "judge.Language", - "judge.Judge", + 'model': 'judge.Submission', + 'icon': 'fa-check-square-o', + 'children': [ + 'judge.Language', + 'judge.Judge', ], }, { - "model": "judge.Contest", - "icon": "fa-bar-chart", - "children": [ - "judge.ContestParticipation", - "judge.ContestTag", + 'model': 'judge.Contest', + 'icon': 'fa-bar-chart', + 'children': [ + 'judge.ContestParticipation', + 'judge.ContestTag', ], }, { - "model": "auth.User", - "icon": "fa-user", - "children": [ - "auth.Group", - "registration.RegistrationProfile", + 'model': 'auth.User', + 'icon': 'fa-user', + 'children': [ + 'auth.Group', + 'registration.RegistrationProfile', ], }, { - "model": "judge.Profile", - "icon": "fa-user-plus", - "children": [ - "judge.Organization", - "judge.OrganizationRequest", + 'model': 'judge.Profile', + 'icon': 'fa-user-plus', + 'children': [ + 'judge.Organization', + 'judge.OrganizationRequest', ], }, { - "model": "judge.NavigationBar", - "icon": "fa-bars", - "children": [ - "judge.MiscConfig", - "judge.License", - "sites.Site", - "redirects.Redirect", + 'model': 'judge.NavigationBar', + 'icon': 'fa-bars', + 'children': [ + 'judge.MiscConfig', + 'judge.License', + 'sites.Site', + 'redirects.Redirect', ], }, - ("judge.BlogPost", "fa-rss-square"), - ("judge.Ticket", "fa-exclamation-circle"), - ("admin.LogEntry", "fa-empire"), + ('judge.BlogPost', 'fa-rss-square'), + ('judge.Comment', 'fa-comment-o'), + ('judge.Ticket', 'fa-exclamation-circle'), + ('flatpages.FlatPage', 'fa-file-text-o'), + ('judge.Solution', 'fa-pencil'), ], - "dashboard": { - "breadcrumbs": True, + 'dashboard': { + 'breadcrumbs': True, }, }, } INSTALLED_APPS += ( - "django.contrib.admin", - "judge", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.flatpages", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.redirects", - "django.contrib.staticfiles", - "django.contrib.sites", - "django.contrib.sitemaps", - "registration", - "mptt", - "reversion", - "reversion_compare", - "django_social_share", - "social_django", - "compressor", - "django_ace", - "pagedown", - "sortedm2m", - "statici18n", - "impersonate", - "django_jinja", - "chat_box", - "django.forms", + 'django.contrib.admin', + 'judge', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.flatpages', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.redirects', + 'django.contrib.staticfiles', + 'django.contrib.sites', + 'django.contrib.sitemaps', + 'registration', + 'mptt', + 'reversion', + 'django_social_share', + 'social_django', + 'compressor', + 'django_ace', + 'pagedown', + 'sortedm2m', + 'statici18n', + 'impersonate', + 'django_jinja', + 'chat_box', + 'newsletter', + 'django.forms', ) MIDDLEWARE = ( - "judge.middleware.SlowRequestMiddleware", - "judge.middleware.ShortCircuitMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.locale.LocaleMiddleware", - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "judge.middleware.DMOJLoginMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", - "judge.user_log.LogUserAccessMiddleware", - "judge.timezone.TimezoneMiddleware", - "impersonate.middleware.ImpersonateMiddleware", - "judge.middleware.DMOJImpersonationMiddleware", - "judge.middleware.ContestMiddleware", - "judge.middleware.DarkModeMiddleware", - "judge.middleware.SubdomainMiddleware", - "django.contrib.flatpages.middleware.FlatpageFallbackMiddleware", - "judge.social_auth.SocialAuthExceptionMiddleware", - "django.contrib.redirects.middleware.RedirectFallbackMiddleware", + 'judge.middleware.ShortCircuitMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'judge.middleware.DMOJLoginMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'judge.user_log.LogUserAccessMiddleware', + 'judge.timezone.TimezoneMiddleware', + 'impersonate.middleware.ImpersonateMiddleware', + 'judge.middleware.DMOJImpersonationMiddleware', + 'judge.middleware.ContestMiddleware', + 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', + 'judge.social_auth.SocialAuthExceptionMiddleware', + 'django.contrib.redirects.middleware.RedirectFallbackMiddleware', ) -X_FRAME_OPTIONS = "SAMEORIGIN" +FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' -LANGUAGE_COOKIE_AGE = 8640000 - -FORM_RENDERER = "django.forms.renderers.TemplatesSetting" - -IMPERSONATE = { - "REQUIRE_SUPERUSER": True, - "DISABLE_LOGGING": True, - "ADMIN_DELETE_PERMISSION": True, -} +IMPERSONATE_REQUIRE_SUPERUSER = True +IMPERSONATE_DISABLE_LOGGING = True ACCOUNT_ACTIVATION_DAYS = 7 AUTH_PASSWORD_VALIDATORS = [ { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { - "NAME": "judge.utils.pwned.PwnedPasswordsValidator", + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + 'NAME': 'judge.utils.pwned.PwnedPasswordsValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] -SILENCED_SYSTEM_CHECKS = ["urls.W002", "fields.W342"] +SILENCED_SYSTEM_CHECKS = ['urls.W002', 'fields.W342'] -ROOT_URLCONF = "dmoj.urls" -LOGIN_REDIRECT_URL = "/user" -WSGI_APPLICATION = "dmoj.wsgi.application" +ROOT_URLCONF = 'dmoj.urls' +LOGIN_REDIRECT_URL = '/user' +WSGI_APPLICATION = 'dmoj.wsgi.application' TEMPLATES = [ { - "BACKEND": "django_jinja.backend.Jinja2", - "DIRS": [ - os.path.join(BASE_DIR, "templates"), + 'BACKEND': 'django_jinja.backend.Jinja2', + 'DIRS': [ + os.path.join(BASE_DIR, 'templates'), ], - "APP_DIRS": False, - "OPTIONS": { - "match_extension": (".html", ".txt"), - "match_regex": "^(?!admin/)", - "context_processors": [ - "django.template.context_processors.media", - "django.template.context_processors.tz", - "django.template.context_processors.i18n", - "django.template.context_processors.request", - "django.contrib.messages.context_processors.messages", - "judge.template_context.comet_location", - "judge.template_context.get_resource", - "judge.template_context.general_info", - "judge.template_context.site", - "judge.template_context.site_name", - "judge.template_context.misc_config", - "social_django.context_processors.backends", - "social_django.context_processors.login_redirect", + 'APP_DIRS': False, + 'OPTIONS': { + 'match_extension': ('.html', '.txt'), + 'match_regex': '^(?!admin/)', + 'context_processors': [ + 'django.template.context_processors.media', + 'django.template.context_processors.tz', + 'django.template.context_processors.i18n', + 'django.template.context_processors.request', + 'django.contrib.messages.context_processors.messages', + 'judge.template_context.comet_location', + 'judge.template_context.get_resource', + 'judge.template_context.general_info', + 'judge.template_context.site', + 'judge.template_context.site_name', + 'judge.template_context.misc_config', + 'judge.template_context.math_setting', + 'social_django.context_processors.backends', + 'social_django.context_processors.login_redirect', ], - "autoescape": select_autoescape(["html", "xml"]), - "trim_blocks": True, - "lstrip_blocks": True, - "extensions": DEFAULT_EXTENSIONS - + [ - "compressor.contrib.jinja2ext.CompressorExtension", - "judge.jinja2.DMOJExtension", - "judge.jinja2.spaceless.SpacelessExtension", + 'autoescape': select_autoescape(['html', 'xml']), + 'trim_blocks': True, + 'lstrip_blocks': True, + 'extensions': DEFAULT_EXTENSIONS + [ + 'compressor.contrib.jinja2ext.CompressorExtension', + 'judge.jinja2.DMOJExtension', + 'judge.jinja2.spaceless.SpacelessExtension', ], }, }, { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "APP_DIRS": True, - "DIRS": [ - os.path.join(BASE_DIR, "templates"), + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + 'DIRS': [ + os.path.join(BASE_DIR, 'templates'), ], - "OPTIONS": { - "context_processors": [ - "django.contrib.auth.context_processors.auth", - "django.template.context_processors.media", - "django.template.context_processors.tz", - "django.template.context_processors.i18n", - "django.template.context_processors.request", - "django.contrib.messages.context_processors.messages", + 'OPTIONS': { + 'context_processors': [ + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.media', + 'django.template.context_processors.tz', + 'django.template.context_processors.i18n', + 'django.template.context_processors.request', + 'django.contrib.messages.context_processors.messages', ], }, }, ] LOCALE_PATHS = [ - os.path.join(BASE_DIR, "locale"), + os.path.join(BASE_DIR, 'locale'), ] LANGUAGES = [ - ("vi", _("Vietnamese")), - ("en", _("English")), + ('de', _('German')), + ('en', _('English')), + ('es', _('Spanish')), + ('fr', _('French')), + ('hr', _('Croatian')), + ('hu', _('Hungarian')), + ('ja', _('Japanese')), + ('ko', _('Korean')), + ('pt', _('Brazilian Portuguese')), + ('ro', _('Romanian')), + ('ru', _('Russian')), + ('sr-latn', _('Serbian (Latin)')), + ('tr', _('Turkish')), + ('vi', _('Vietnamese')), + ('zh-hans', _('Simplified Chinese')), + ('zh-hant', _('Traditional Chinese')), ] +MARKDOWN_ADMIN_EDITABLE_STYLE = { + 'safe_mode': False, + 'use_camo': True, + 'texoid': True, + 'math': True, +} + +MARKDOWN_DEFAULT_STYLE = { + 'safe_mode': True, + 'nofollow': True, + 'use_camo': True, + 'math': True, +} + +MARKDOWN_USER_LARGE_STYLE = { + 'safe_mode': True, + 'nofollow': True, + 'use_camo': True, + 'math': True, +} + +MARKDOWN_STYLES = { + 'comment': MARKDOWN_DEFAULT_STYLE, + 'self-description': MARKDOWN_USER_LARGE_STYLE, + 'problem': MARKDOWN_ADMIN_EDITABLE_STYLE, + 'contest': MARKDOWN_ADMIN_EDITABLE_STYLE, + 'language': MARKDOWN_ADMIN_EDITABLE_STYLE, + 'license': MARKDOWN_ADMIN_EDITABLE_STYLE, + 'judge': MARKDOWN_ADMIN_EDITABLE_STYLE, + 'blog': MARKDOWN_ADMIN_EDITABLE_STYLE, + 'solution': MARKDOWN_ADMIN_EDITABLE_STYLE, + 'contest_tag': MARKDOWN_ADMIN_EDITABLE_STYLE, + 'organization-about': MARKDOWN_USER_LARGE_STYLE, + 'ticket': MARKDOWN_USER_LARGE_STYLE, +} + # Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": os.path.join(BASE_DIR, "db.sqlite3"), + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, } ENABLE_FTS = False # Bridged configuration -BRIDGED_JUDGE_ADDRESS = [("localhost", 9999)] +BRIDGED_JUDGE_ADDRESS = [('localhost', 9999)] BRIDGED_JUDGE_PROXIES = None -BRIDGED_DJANGO_ADDRESS = [("localhost", 9998)] +BRIDGED_DJANGO_ADDRESS = [('localhost', 9998)] BRIDGED_DJANGO_CONNECT = None -BRIDGED_AUTO_CREATE_JUDGE = False # Event Server configuration EVENT_DAEMON_USE = False -EVENT_DAEMON_POST = "ws://localhost:9997/" -EVENT_DAEMON_GET = "ws://localhost:9996/" -EVENT_DAEMON_POLL = "/channels/" +EVENT_DAEMON_POST = 'ws://localhost:9997/' +EVENT_DAEMON_GET = 'ws://localhost:9996/' +EVENT_DAEMON_POLL = '/channels/' EVENT_DAEMON_KEY = None -EVENT_DAEMON_AMQP_EXCHANGE = "dmoj-events" -EVENT_DAEMON_SUBMISSION_KEY = ( - "6Sdmkx^%pk@GsifDfXcwX*Y7LRF%RGT8vmFpSxFBT$fwS7trc8raWfN#CSfQuKApx&$B#Gh2L7p%W!Ww" -) +EVENT_DAEMON_AMQP_EXCHANGE = 'dmoj-events' +EVENT_DAEMON_SUBMISSION_KEY = '6Sdmkx^%pk@GsifDfXcwX*Y7LRF%RGT8vmFpSxFBT$fwS7trc8raWfN#CSfQuKApx&$B#Gh2L7p%W!Ww' # Internationalization # https://docs.djangoproject.com/en/1.11/topics/i18n/ # Whatever you do, this better be one of the entries in `LANGUAGES`. -LANGUAGE_CODE = "vi" -TIME_ZONE = "Asia/Ho_Chi_Minh" -DEFAULT_USER_TIME_ZONE = "Asia/Ho_Chi_Minh" +LANGUAGE_CODE = 'vi' +TIME_ZONE = 'Asia/Ho_Chi_Minh' +DEFAULT_USER_TIME_ZONE = 'Asia/Ho_Chi_Minh' USE_I18N = True USE_L10N = True USE_TZ = True # Cookies -SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" +SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.11/howto/static-files/ -DMOJ_RESOURCES = os.path.join(BASE_DIR, "resources") +DMOJ_RESOURCES = os.path.join(BASE_DIR, 'resources') STATICFILES_FINDERS = ( - "django.contrib.staticfiles.finders.FileSystemFinder", - "django.contrib.staticfiles.finders.AppDirectoriesFinder", + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', ) STATICFILES_DIRS = [ - os.path.join(BASE_DIR, "resources"), + os.path.join(BASE_DIR, 'resources'), ] -STATIC_URL = "/static/" +STATIC_URL = '/static/' # Define a cache CACHES = {} # Authentication AUTHENTICATION_BACKENDS = ( - "social_core.backends.google.GoogleOAuth2", - "social_core.backends.facebook.FacebookOAuth2", - "judge.social_auth.GitHubSecureEmailOAuth2", - "judge.authentication.CustomModelBackend", + 'social_core.backends.google.GoogleOAuth2', + 'social_core.backends.facebook.FacebookOAuth2', + 'judge.social_auth.GitHubSecureEmailOAuth2', + 'django.contrib.auth.backends.ModelBackend', ) SOCIAL_AUTH_PIPELINE = ( - "social_core.pipeline.social_auth.social_details", - "social_core.pipeline.social_auth.social_uid", - "social_core.pipeline.social_auth.auth_allowed", - "judge.social_auth.verify_email", - "social_core.pipeline.social_auth.social_user", - "social_core.pipeline.user.get_username", - "social_core.pipeline.social_auth.associate_by_email", - "judge.social_auth.choose_username", - "social_core.pipeline.user.create_user", - "judge.social_auth.make_profile", - "social_core.pipeline.social_auth.associate_user", - "social_core.pipeline.social_auth.load_extra_data", - "social_core.pipeline.user.user_details", + 'social_core.pipeline.social_auth.social_details', + 'social_core.pipeline.social_auth.social_uid', + 'social_core.pipeline.social_auth.auth_allowed', + 'judge.social_auth.verify_email', + 'social_core.pipeline.social_auth.social_user', + 'social_core.pipeline.user.get_username', + 'social_core.pipeline.social_auth.associate_by_email', + 'judge.social_auth.choose_username', + 'social_core.pipeline.user.create_user', + 'judge.social_auth.make_profile', + 'social_core.pipeline.social_auth.associate_user', + 'social_core.pipeline.social_auth.load_extra_data', + 'social_core.pipeline.user.user_details', ) -SOCIAL_AUTH_PROTECTED_USER_FIELDS = ["first_name", "last_name"] -SOCIAL_AUTH_GOOGLE_OAUTH2_USER_FIELDS = ["email", "username"] -SOCIAL_AUTH_GITHUB_SECURE_SCOPE = ["user:email"] -SOCIAL_AUTH_FACEBOOK_SCOPE = ["email"] +SOCIAL_AUTH_PROTECTED_USER_FIELDS = ['first_name', 'last_name'] +SOCIAL_AUTH_GOOGLE_OAUTH2_USER_FIELDS = ['email', 'username'] +SOCIAL_AUTH_GITHUB_SECURE_SCOPE = ['user:email'] +SOCIAL_AUTH_FACEBOOK_SCOPE = ['email'] SOCIAL_AUTH_SLUGIFY_USERNAMES = True -SOCIAL_AUTH_SLUGIFY_FUNCTION = "judge.social_auth.slugify_username" +SOCIAL_AUTH_SLUGIFY_FUNCTION = 'judge.social_auth.slugify_username' JUDGE_AMQP_PATH = None @@ -473,29 +512,22 @@ FILE_UPLOAD_PERMISSIONS = 0o644 MESSAGES_TO_LOAD = 15 -ML_OUTPUT_PATH = None +NEWSLETTER_CONFIRM_EMAIL = False -# Use subdomain for organizations -USE_SUBDOMAIN = False +# Amount of seconds to wait between each email. Here 100ms is used. +NEWSLETTER_EMAIL_DELAY = 0.1 -# Chat -CHAT_SECRET_KEY = "QUdVFsxk6f5-Hd8g9BXv81xMqvIZFRqMl-KbRzztW-U=" +# Amount of seconds to wait between each batch. Here one minute is used. +NEWSLETTER_BATCH_DELAY = 60 -# Nginx -META_REMOTE_ADDRESS_KEY = "REMOTE_ADDR" - -DEFAULT_AUTO_FIELD = "django.db.models.AutoField" - -# Chunk upload -CHUNK_UPLOAD_DIR = "/tmp/chunk_upload_tmp" - -# Rate limit -RL_VOTE = "200/h" -RL_COMMENT = "30/h" +# Number of emails in one batch +NEWSLETTER_BATCH_SIZE = 100 +# Google form to request name +REGISTER_NAME_URL = None try: - with open(os.path.join(os.path.dirname(__file__), "local_settings.py")) as f: + with open(os.path.join(os.path.dirname(__file__), 'local_settings.py')) as f: exec(f.read(), globals()) except IOError: pass diff --git a/dmoj/throttle_mail.py b/dmoj/throttle_mail.py index 047e261..7346a21 100644 --- a/dmoj/throttle_mail.py +++ b/dmoj/throttle_mail.py @@ -8,8 +8,8 @@ DEFAULT_THROTTLE = (10, 60) def new_email(): - cache.add("error_email_throttle", 0, settings.DMOJ_EMAIL_THROTTLING[1]) - return cache.incr("error_email_throttle") + cache.add('error_email_throttle', 0, settings.DMOJ_EMAIL_THROTTLING[1]) + return cache.incr('error_email_throttle') class ThrottledEmailHandler(AdminEmailHandler): diff --git a/dmoj/urls.py b/dmoj/urls.py index 1f077f4..1fad04d 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -12,1256 +12,420 @@ from django.utils.functional import lazystr from django.utils.translation import ugettext_lazy as _ from django.views.generic import RedirectView from django.contrib.auth.decorators import login_required -from django.conf.urls.static import static as url_static +from judge.feed import AtomBlogFeed, AtomCommentFeed, AtomProblemFeed, BlogFeed, CommentFeed, ProblemFeed from judge.forms import CustomAuthenticationForm -from judge.sitemap import ( - BlogPostSitemap, - ContestSitemap, - HomePageSitemap, - OrganizationSitemap, - ProblemSitemap, - SolutionSitemap, - UrlSitemap, - UserSitemap, -) -from judge.views import ( - TitledTemplateView, - about, - api, - blog, - comment, - contests, - language, - license, - mailgun, - markdown_editor, - test_formatter, - notification, - organization, - preview, - problem, - problem_manage, - ranked_submission, - register, - stats, - status, - submission, - tasks, - ticket, - totp, - user, - volunteer, - pagevote, - bookmark, - widgets, - internal, - resolver, - course, - email, - custom_file_upload, -) -from judge import authentication - -from judge.views.test_formatter import test_formatter - -from judge.views.problem_data import ( - ProblemDataView, - ProblemSubmissionDiff, - problem_data_file, - problem_init_view, - ProblemZipUploadView, -) +from judge.sitemap import BlogPostSitemap, ContestSitemap, HomePageSitemap, OrganizationSitemap, ProblemSitemap, \ + SolutionSitemap, UrlSitemap, UserSitemap +from judge.views import TitledTemplateView, about, api, blog, comment, contests, ide, language, license, mailgun, \ + notification, organization, preview, problem, problem_manage, ranked_submission, register, stats, status, submission, tasks, \ + ticket, totp, user, widgets +from judge.views.problem_data import ProblemDataView, ProblemSubmissionDiff, \ + problem_data_file, problem_init_view, ProblemZipUploadView from judge.views.register import ActivationView, RegistrationView -from judge.views.select2 import ( - AssigneeSelect2View, - ChatUserSearchSelect2View, - ContestSelect2View, - ContestUserSearchSelect2View, - OrganizationSelect2View, - ProblemSelect2View, - TicketUserSelect2View, - UserSearchSelect2View, - UserSelect2View, - ProblemAuthorSearchSelect2View, -) +from judge.views.select2 import AssigneeSelect2View, ChatUserSearchSelect2View, CommentSelect2View, \ + ContestSelect2View, ContestUserSearchSelect2View, OrganizationSelect2View, ProblemSelect2View, TicketUserSelect2View, \ + UserSearchSelect2View, UserSelect2View admin.autodiscover() register_patterns = [ - url( - r"^activate/complete/$", - TitledTemplateView.as_view( - template_name="registration/activation_complete.html", - title="Activation Successful!", - ), - name="registration_activation_complete", - ), + url(r'^activate/complete/$', + TitledTemplateView.as_view(template_name='registration/activation_complete.html', + title='Activation Successful!'), + name='registration_activation_complete'), # Activation keys get matched by \w+ instead of the more specific # [a-fA-F0-9]{40} because a bad activation key should still get to the view; # that way it can return a sensible "invalid key" message instead of a # confusing 404. - url( - r"^activate/(?P\w+)/$", - ActivationView.as_view(title=_("Activation key invalid")), - name="registration_activate", - ), - url( - r"^register/$", - RegistrationView.as_view(title=_("Register")), - name="registration_register", - ), - url( - r"^register/complete/$", - TitledTemplateView.as_view( - template_name="registration/registration_complete.html", - title=_("Registration Completed"), - ), - name="registration_complete", - ), - url( - r"^register/closed/$", - TitledTemplateView.as_view( - template_name="registration/registration_closed.html", - title=_("Registration not allowed"), - ), - name="registration_disallowed", - ), - url( - r"^login/$", - auth_views.LoginView.as_view( - template_name="registration/login.html", - extra_context={"title": _("Login")}, - authentication_form=CustomAuthenticationForm, - redirect_authenticated_user=True, - ), - name="auth_login", - ), - url(r"^logout/$", user.UserLogoutView.as_view(), name="auth_logout"), - url( - r"^password/change/$", - authentication.CustomPasswordChangeView.as_view(), - name="password_change", - ), - url( - r"^password/change/done/$", - auth_views.PasswordChangeDoneView.as_view( - template_name="registration/password_change_done.html", - ), - name="password_change_done", - ), - url( - r"^password/reset/$", - auth_views.PasswordResetView.as_view( - template_name="registration/password_reset.html", - html_email_template_name="registration/password_reset_email.html", - email_template_name="registration/password_reset_email.txt", - ), - name="password_reset", - ), - url( - r"^password/reset/confirm/(?P[0-9A-Za-z]+)-(?P.+)/$", + url(r'^activate/(?P\w+)/$', + ActivationView.as_view(title='Activation key invalid'), + name='registration_activate'), + url(r'^register/$', + RegistrationView.as_view(title='Register'), + name='registration_register'), + url(r'^register/complete/$', + TitledTemplateView.as_view(template_name='registration/registration_complete.html', + title='Registration Completed'), + name='registration_complete'), + url(r'^register/closed/$', + TitledTemplateView.as_view(template_name='registration/registration_closed.html', + title='Registration not allowed'), + name='registration_disallowed'), + url(r'^login/$', auth_views.LoginView.as_view( + template_name='registration/login.html', + extra_context={'title': _('Login')}, + authentication_form=CustomAuthenticationForm, + redirect_authenticated_user=True, + ), name='auth_login'), + url(r'^logout/$', user.UserLogoutView.as_view(), name='auth_logout'), + url(r'^password/change/$', auth_views.PasswordChangeView.as_view( + template_name='registration/password_change_form.html', + ), name='password_change'), + url(r'^password/change/done/$', auth_views.PasswordChangeDoneView.as_view( + template_name='registration/password_change_done.html', + ), name='password_change_done'), + url(r'^password/reset/$', auth_views.PasswordResetView.as_view( + template_name='registration/password_reset.html', + html_email_template_name='registration/password_reset_email.html', + email_template_name='registration/password_reset_email.txt', + ), name='password_reset'), + url(r'^password/reset/confirm/(?P[0-9A-Za-z]+)-(?P.+)/$', auth_views.PasswordResetConfirmView.as_view( - template_name="registration/password_reset_confirm.html", - ), - name="password_reset_confirm", - ), - url( - r"^password/reset/complete/$", - auth_views.PasswordResetCompleteView.as_view( - template_name="registration/password_reset_complete.html", - ), - name="password_reset_complete", - ), - url( - r"^password/reset/done/$", - auth_views.PasswordResetDoneView.as_view( - template_name="registration/password_reset_done.html", - ), - name="password_reset_done", - ), - url(r"^email/change/$", email.email_change_view, name="email_change"), - url( - r"^email/change/verify/(?P[0-9A-Za-z]+)-(?P.+)/$", - email.verify_email_view, - name="email_change_verify", - ), - url( - r"^email/change/pending$", - email.email_change_pending_view, - name="email_change_pending", - ), - url(r"^social/error/$", register.social_auth_error, name="social_auth_error"), - url(r"^2fa/$", totp.TOTPLoginView.as_view(), name="login_2fa"), - url(r"^2fa/enable/$", totp.TOTPEnableView.as_view(), name="enable_2fa"), - url(r"^2fa/disable/$", totp.TOTPDisableView.as_view(), name="disable_2fa"), + template_name='registration/password_reset_confirm.html', + ), name='password_reset_confirm'), + url(r'^password/reset/complete/$', auth_views.PasswordResetCompleteView.as_view( + template_name='registration/password_reset_complete.html', + ), name='password_reset_complete'), + url(r'^password/reset/done/$', auth_views.PasswordResetDoneView.as_view( + template_name='registration/password_reset_done.html', + ), name='password_reset_done'), + url(r'^social/error/$', register.social_auth_error, name='social_auth_error'), + + url(r'^2fa/$', totp.TOTPLoginView.as_view(), name='login_2fa'), + url(r'^2fa/enable/$', totp.TOTPEnableView.as_view(), name='enable_2fa'), + url(r'^2fa/disable/$', totp.TOTPDisableView.as_view(), name='disable_2fa'), ] def exception(request): if not request.user.is_superuser: raise Http404() - raise RuntimeError("@Xyene asked me to cause this") + raise RuntimeError('@Xyene asked me to cause this') -def paged_list_view(view, name, **kwargs): - return include( - [ - url(r"^$", view.as_view(**kwargs), name=name), - url(r"^(?P\d+)$", view.as_view(**kwargs), name=name), - ] - ) +def paged_list_view(view, name): + return include([ + url(r'^$', view.as_view(), name=name), + url(r'^(?P\d+)$', view.as_view(), name=name), + ]) urlpatterns = [ - url("", include("pagedown.urls")), - url( - r"^$", - blog.PostList.as_view(template_name="home.html", title=_("Home")), - kwargs={"page": 1}, - name="home", - ), - url(r"^500/$", exception), - url(r"^toggle_darkmode/$", user.toggle_darkmode, name="toggle_darkmode"), - url(r"^admin/", admin.site.urls), - url(r"^i18n/", include("django.conf.urls.i18n")), - url(r"^accounts/", include(register_patterns)), - url(r"^", include("social_django.urls")), - url( - r"^feed/", - include( - [ - url(r"^tickets/$", blog.TicketFeed.as_view(), name="ticket_feed"), - url(r"^comments/$", blog.CommentFeed.as_view(), name="comment_feed"), - ] - ), - ), - url(r"^problems/", paged_list_view(problem.ProblemList, "problem_list")), - url(r"^problems/random/$", problem.RandomProblem.as_view(), name="problem_random"), - url( - r"^problems/feed/$", - problem.ProblemFeed.as_view(feed_type="for_you"), - name="problem_feed", - ), - url( - r"^problems/feed/new/$", - problem.ProblemFeed.as_view(feed_type="new"), - name="problem_feed_new", - ), - url( - r"^problems/feed/volunteer/$", - problem.ProblemFeed.as_view(feed_type="volunteer"), - name="problem_feed_volunteer", - ), - url( - r"^problem/(?P[^/]+)", - include( - [ - url(r"^$", problem.ProblemDetail.as_view(), name="problem_detail"), - url( - r"^/editorial$", - problem.ProblemSolution.as_view(), - name="problem_editorial", - ), - url(r"^/raw$", problem.ProblemRaw.as_view(), name="problem_raw"), - url(r"^/pdf$", problem.ProblemPdfView.as_view(), name="problem_pdf"), - url( - r"^/pdf/(?P[a-z-]+)$", - problem.ProblemPdfView.as_view(), - name="problem_pdf", - ), - url( - r"^/pdf_description$", - problem.ProblemPdfDescriptionView.as_view(), - name="problem_pdf_description", - ), - url(r"^/clone", problem.ProblemClone.as_view(), name="problem_clone"), - url(r"^/submit$", problem.problem_submit, name="problem_submit"), - url( - r"^/resubmit/(?P\d+)$", - problem.problem_submit, - name="problem_submit", - ), - url( - r"^/rank/", - paged_list_view( - ranked_submission.RankedSubmissions, "ranked_submissions" - ), - ), - url( - r"^/submissions/", - paged_list_view( - submission.ProblemSubmissions, "chronological_submissions" - ), - ), - url( - r"^/submissions/(?P\w+)/", - paged_list_view( - submission.UserProblemSubmissions, "user_submissions" - ), - ), - url( - r"^/$", - lambda _, problem: HttpResponsePermanentRedirect( - reverse("problem_detail", args=[problem]) - ), - ), - url(r"^/test_data$", ProblemDataView.as_view(), name="problem_data"), - url(r"^/test_data/init$", problem_init_view, name="problem_data_init"), - url( - r"^/test_data/diff$", - ProblemSubmissionDiff.as_view(), - name="problem_submission_diff", - ), - url( - r"^/test_data/upload$", - ProblemZipUploadView.as_view(), - name="problem_zip_upload", - ), - url( - r"^/data/(?P.+)$", problem_data_file, name="problem_data_file" - ), - url( - r"^/tickets$", - ticket.ProblemTicketListView.as_view(), - name="problem_ticket_list", - ), - url( - r"^/tickets/new$", - ticket.NewProblemTicketView.as_view(), - name="new_problem_ticket", - ), - url( - r"^/manage/submission", - include( - [ - url( - "^$", - problem_manage.ManageProblemSubmissionView.as_view(), - name="problem_manage_submissions", - ), - url( - "^/action$", - problem_manage.ActionSubmissionsView.as_view(), - name="problem_submissions_action", - ), - url( - "^/action/preview$", - problem_manage.PreviewActionSubmissionsView.as_view(), - name="problem_submissions_rejudge_preview", - ), - url( - "^/rejudge/success/(?P[A-Za-z0-9-]*)$", - problem_manage.rejudge_success, - name="problem_submissions_rejudge_success", - ), - url( - "^/rescore/all$", - problem_manage.RescoreAllSubmissionsView.as_view(), - name="problem_submissions_rescore_all", - ), - url( - "^/rescore/success/(?P[A-Za-z0-9-]*)$", - problem_manage.rescore_success, - name="problem_submissions_rescore_success", - ), - ] - ), - ), - ] - ), - ), - url( - r"^submissions/", paged_list_view(submission.AllSubmissions, "all_submissions") - ), - url( - r"^submissions/user/(?P\w+)/", - paged_list_view(submission.AllUserSubmissions, "all_user_submissions"), - ), - url( - r"^submissions/friends/", - paged_list_view(submission.AllFriendSubmissions, "all_friend_submissions"), - ), - url( - r"^src/(?P\d+)/raw$", - submission.SubmissionSourceRaw.as_view(), - name="submission_source_raw", - ), - url( - r"^submission/(?P\d+)", - include( - [ - url( - r"^$", - submission.SubmissionStatus.as_view(), - name="submission_status", - ), - url(r"^/abort$", submission.abort_submission, name="submission_abort"), - ] - ), - ), - url( - r"^test_formatter/", - include( - [ - url( - r"^$", - login_required(test_formatter.TestFormatter.as_view()), - name="test_formatter", - ), - url( - r"^edit_page$", - login_required(test_formatter.EditTestFormatter.as_view()), - name="test_formatter_edit", - ), - url( - r"^download_page$", - login_required(test_formatter.DownloadTestFormatter.as_view()), - name="test_formatter_download", - ), - ] - ), - ), - url( - r"^markdown_editor/", - markdown_editor.MarkdownEditor.as_view(), - name="markdown_editor", - ), - url( - r"^submission_source_file/(?P(\w|\.)+)", - submission.SubmissionSourceFileView.as_view(), - name="submission_source_file", - ), - url( - r"^users/", - include( - [ - url(r"^$", user.users, name="user_list"), - url( - r"^(?P\d+)$", - lambda request, page: HttpResponsePermanentRedirect( - "%s?page=%s" % (reverse("user_list"), page) - ), - ), - url( - r"^find$", user.user_ranking_redirect, name="user_ranking_redirect" - ), - ] - ), - ), - url(r"^user$", user.UserAboutPage.as_view(), name="user_page"), - url(r"^edit/profile/$", user.edit_profile, name="user_edit_profile"), - url(r"^user/bookmarks", user.UserBookMarkPage.as_view(), name="user_bookmark"), - url( - r"^user/(?P\w+)", - include( - [ - url(r"^$", user.UserAboutPage.as_view(), name="user_page"), - url( - r"^/solved", - include( - [ - url( - r"^$", - user.UserProblemsPage.as_view(), - name="user_problems", - ), - url( - r"/ajax$", - user.UserPerformancePointsAjax.as_view(), - name="user_pp_ajax", - ), - ] - ), - ), - url( - r"^/submissions/", - paged_list_view( - submission.AllUserSubmissions, "all_user_submissions_old" - ), - ), - url( - r"^/submissions/", - lambda _, user: HttpResponsePermanentRedirect( - reverse("all_user_submissions", args=[user]) - ), - ), - url(r"^/toggle_follow/", user.toggle_follow, name="user_toggle_follow"), - url( - r"^/$", - lambda _, user: HttpResponsePermanentRedirect( - reverse("user_page", args=[user]) - ), - ), - ] - ), - ), - url(r"^pagevotes/upvote/$", pagevote.upvote_page, name="pagevote_upvote"), - url(r"^pagevotes/downvote/$", pagevote.downvote_page, name="pagevote_downvote"), - url(r"^bookmarks/dobookmark/$", bookmark.dobookmark_page, name="dobookmark"), - url(r"^bookmarks/undobookmark/$", bookmark.undobookmark_page, name="undobookmark"), - url(r"^comments/upvote/$", comment.upvote_comment, name="comment_upvote"), - url(r"^comments/downvote/$", comment.downvote_comment, name="comment_downvote"), - url(r"^comments/hide/$", comment.comment_hide, name="comment_hide"), - url(r"^comments/get_replies/$", comment.get_replies, name="comment_get_replies"), - url(r"^comments/show_more/$", comment.get_show_more, name="comment_show_more"), - url( - r"^comments/(?P\d+)/", - include( - [ - url(r"^edit$", comment.CommentEdit.as_view(), name="comment_edit"), - url( - r"^history/ajax$", - comment.CommentRevisionAjax.as_view(), - name="comment_revision_ajax", - ), - url( - r"^edit/ajax$", - comment.CommentEditAjax.as_view(), - name="comment_edit_ajax", - ), - url( - r"^votes/ajax$", - comment.CommentVotesAjax.as_view(), - name="comment_votes_ajax", - ), - url( - r"^render$", - comment.CommentContent.as_view(), - name="comment_content", - ), - ] - ), - ), - url(r"^contests/", paged_list_view(contests.ContestList, "contest_list")), - url( - r"^contests/summary/(?P\w+)/", - paged_list_view(contests.ContestsSummaryView, "contests_summary"), - ), - url( - r"^contests/official", - paged_list_view(contests.OfficialContestList, "official_contest_list"), - ), - url(r"^courses/", paged_list_view(course.CourseList, "course_list")), - url( - r"^course/(?P[\w-]*)", - include( - [ - url(r"^$", course.CourseDetail.as_view(), name="course_detail"), - url( - r"^/lesson/(?P\d+)$", - course.CourseLessonDetail.as_view(), - name="course_lesson_detail", - ), - url( - r"^/edit_lessons$", - course.EditCourseLessonsView.as_view(), - name="edit_course_lessons", - ), - url( - r"^/grades$", - course.CourseStudentResults.as_view(), - name="course_grades", - ), - url( - r"^/grades/lesson/(?P\d+)$", - course.CourseStudentResultsLesson.as_view(), - name="course_grades_lesson", - ), - url( - r"^/add_contest$", - course.AddCourseContest.as_view(), - name="add_course_contest", - ), - url( - r"^/edit_contest/(?P\w+)$", - course.EditCourseContest.as_view(), - name="edit_course_contest", - ), - url( - r"^/contests$", - course.CourseContestList.as_view(), - name="course_contest_list", - ), - ] - ), - ), - url( - r"^contests/(?P\d+)/(?P\d+)/$", - contests.ContestCalendar.as_view(), - name="contest_calendar", - ), - url( - r"^contests/tag/(?P[a-z-]+)", - include( - [ - url(r"^$", contests.ContestTagDetail.as_view(), name="contest_tag"), - url( - r"^/ajax$", - contests.ContestTagDetailAjax.as_view(), - name="contest_tag_ajax", - ), - ] - ), - ), - url( - r"^contest/(?P\w+)", - include( - [ - url(r"^$", contests.ContestDetail.as_view(), name="contest_view"), - url( - r"^/moss$", contests.ContestMossView.as_view(), name="contest_moss" - ), - url( - r"^/moss/delete$", - contests.ContestMossDelete.as_view(), - name="contest_moss_delete", - ), - url(r"^/clone$", contests.ContestClone.as_view(), name="contest_clone"), - url( - r"^/ranking/$", - contests.ContestRanking.as_view(), - name="contest_ranking", - ), - url( - r"^/final_ranking/$", - contests.ContestFinalRanking.as_view(), - name="contest_final_ranking", - ), - url(r"^/join$", contests.ContestJoin.as_view(), name="contest_join"), - url(r"^/leave$", contests.ContestLeave.as_view(), name="contest_leave"), - url(r"^/stats$", contests.ContestStats.as_view(), name="contest_stats"), - url( - r"^/submissions/(?P\w+)/(?P\w+)", - paged_list_view( - submission.UserContestSubmissions, "contest_user_submissions" - ), - ), - url( - r"^/submissions/(?P\d+)/(?P\w+)/ajax", - paged_list_view( - submission.UserContestSubmissionsAjax, - "contest_user_submissions_ajax", - ), - ), - url( - r"^/submissions", - paged_list_view( - submission.ContestSubmissions, - "contest_submissions", - ), - ), - url( - r"^/participations$", - contests.ContestParticipationList.as_view(), - name="contest_participation_own", - ), - url( - r"^/participations/(?P\w+)$", - contests.ContestParticipationList.as_view(), - name="contest_participation", - ), - url( - r"^/participation/disqualify$", - contests.ContestParticipationDisqualify.as_view(), - name="contest_participation_disqualify", - ), - url( - r"^/clarification$", - contests.NewContestClarificationView.as_view(), - name="new_contest_clarification", - ), - url( - r"^/clarification/ajax$", - contests.ContestClarificationAjax.as_view(), - name="contest_clarification_ajax", - ), - url( - r"^/$", - lambda _, contest: HttpResponsePermanentRedirect( - reverse("contest_view", args=[contest]) - ), - ), - ] - ), - ), - url( - r"^organizations/$", - organization.OrganizationList.as_view(), - name="organization_list", - ), - url( - r"^organizations/add/$", - organization.AddOrganization.as_view(), - name="organization_add", - ), - url( - r"^organization/(?P\d+)-(?P[\w-]*)", - include( - [ - url( - r"^$", - organization.OrganizationHome.as_view(), - name="organization_home", - ), - url( - r"^/users/", - paged_list_view( - organization.OrganizationUsers, - "organization_users", - ), - ), - url( - r"^/problems/", - paged_list_view( - organization.OrganizationProblems, "organization_problems" - ), - ), - url( - r"^/contests/", - paged_list_view( - organization.OrganizationContests, "organization_contests" - ), - ), - url( - r"^/contest/add", - organization.AddOrganizationContest.as_view(), - name="organization_contest_add", - ), - url( - r"^/contest/edit/(?P\w+)", - organization.EditOrganizationContest.as_view(), - name="organization_contest_edit", - ), - url( - r"^/submissions/", - paged_list_view( - organization.OrganizationSubmissions, "organization_submissions" - ), - ), - url( - r"^/join$", - organization.JoinOrganization.as_view(), - name="join_organization", - ), - url( - r"^/leave$", - organization.LeaveOrganization.as_view(), - name="leave_organization", - ), - url( - r"^/edit$", - organization.EditOrganization.as_view(), - name="edit_organization", - ), - url( - r"^/kick$", - organization.KickUserWidgetView.as_view(), - name="organization_user_kick", - ), - url( - r"^/add_member$", - organization.AddOrganizationMember.as_view(), - name="add_organization_member", - ), - url( - r"^/blog/add$", - organization.AddOrganizationBlog.as_view(), - name="add_organization_blog", - ), - url( - r"^/blog/edit/(?P\d+)$", - organization.EditOrganizationBlog.as_view(), - name="edit_organization_blog", - ), - url( - r"^/blog/pending$", - organization.PendingBlogs.as_view(), - name="organization_pending_blogs", - ), - url( - r"^/request$", - organization.RequestJoinOrganization.as_view(), - name="request_organization", - ), - url( - r"^/request/(?P\d+)$", - organization.OrganizationRequestDetail.as_view(), - name="request_organization_detail", - ), - url( - r"^/requests/", - include( - [ - url( - r"^pending$", - organization.OrganizationRequestView.as_view(), - name="organization_requests_pending", - ), - url( - r"^log$", - organization.OrganizationRequestLog.as_view(), - name="organization_requests_log", - ), - url( - r"^approved$", - organization.OrganizationRequestLog.as_view( - states=("A",), tab="approved" - ), - name="organization_requests_approved", - ), - url( - r"^rejected$", - organization.OrganizationRequestLog.as_view( - states=("R",), tab="rejected" - ), - name="organization_requests_rejected", - ), - ] - ), - ), - url( - r"^/$", - lambda _, pk, slug: HttpResponsePermanentRedirect( - reverse("organization_home", args=[pk, slug]) - ), - ), - ] - ), - ), - url(r"^runtimes/$", language.LanguageList.as_view(), name="runtime_list"), - url(r"^runtimes/matrix/$", status.version_matrix, name="version_matrix"), - url(r"^status/$", status.status_all, name="status_all"), - url( - r"^api/", - include( - [ - url(r"^contest/list$", api.api_v1_contest_list), - url(r"^contest/info/(\w+)$", api.api_v1_contest_detail), - url(r"^problem/list$", api.api_v1_problem_list), - url(r"^problem/info/(\w+)$", api.api_v1_problem_info), - url(r"^user/list$", api.api_v1_user_list), - url(r"^user/info/(\w+)$", api.api_v1_user_info), - url(r"^user/submissions/(\w+)$", api.api_v1_user_submissions), - ] - ), - ), - url(r"^blog/", blog.PostList.as_view(), name="blog_post_list"), - url(r"^post/(?P\d+)-(?P.*)$", blog.PostView.as_view(), name="blog_post"), - url(r"^license/(?P[-\w.]+)$", license.LicenseDetail.as_view(), name="license"), - url( - r"^mailgun/mail_activate/$", - mailgun.MailgunActivationView.as_view(), - name="mailgun_activate", - ), - url( - r"^widgets/", - include( - [ - url( - r"^contest_mode$", - contests.update_contest_mode, - name="contest_mode_ajax", - ), - url( - r"^rejudge$", widgets.rejudge_submission, name="submission_rejudge" - ), - url( - r"^single_submission$", - submission.single_submission_query, - name="submission_single_query", - ), - url( - r"^submission_testcases$", - submission.SubmissionTestCaseQuery.as_view(), - name="submission_testcases_query", - ), - url( - r"^detect_timezone$", - widgets.DetectTimezone.as_view(), - name="detect_timezone", - ), - url(r"^status-table$", status.status_table, name="status_table"), - url( - r"^template$", - problem.LanguageTemplateAjax.as_view(), - name="language_template_ajax", - ), - url( - r"^select2/", - include( - [ - url( - r"^user_search$", - UserSearchSelect2View.as_view(), - name="user_search_select2_ajax", - ), - url( - r"^user_search_chat$", - ChatUserSearchSelect2View.as_view(), - name="chat_user_search_select2_ajax", - ), - url( - r"^contest_users/(?P\w+)$", - ContestUserSearchSelect2View.as_view(), - name="contest_user_search_select2_ajax", - ), - url( - r"^ticket_user$", - TicketUserSelect2View.as_view(), - name="ticket_user_select2_ajax", - ), - url( - r"^ticket_assignee$", - AssigneeSelect2View.as_view(), - name="ticket_assignee_select2_ajax", - ), - url( - r"^problem_authors$", - ProblemAuthorSearchSelect2View.as_view(), - name="problem_authors_select2_ajax", - ), - ] - ), - ), - url( - r"^preview/", - include( - [ - url( - r"^problem$", - preview.ProblemMarkdownPreviewView.as_view(), - name="problem_preview", - ), - url( - r"^blog$", - preview.BlogMarkdownPreviewView.as_view(), - name="blog_preview", - ), - url( - r"^contest$", - preview.ContestMarkdownPreviewView.as_view(), - name="contest_preview", - ), - url( - r"^comment$", - preview.CommentMarkdownPreviewView.as_view(), - name="comment_preview", - ), - url( - r"^profile$", - preview.ProfileMarkdownPreviewView.as_view(), - name="profile_preview", - ), - url( - r"^organization$", - preview.OrganizationMarkdownPreviewView.as_view(), - name="organization_preview", - ), - url( - r"^solution$", - preview.SolutionMarkdownPreviewView.as_view(), - name="solution_preview", - ), - url( - r"^license$", - preview.LicenseMarkdownPreviewView.as_view(), - name="license_preview", - ), - url( - r"^ticket$", - preview.TicketMarkdownPreviewView.as_view(), - name="ticket_preview", - ), - ] - ), - ), - ] - ), - ), - url( - r"^stats/", - include( - [ - url( - "^language/", - include( - [ - url( - "^$", - stats.StatLanguage.as_view(), - name="language_stats", - ), - ] - ), - ), - url( - "^site/", - include( - [ - url("^$", stats.StatSite.as_view(), name="site_stats"), - ] - ), - ), - ] - ), - ), - url( - r"^tickets/", - include( - [ - url(r"^$", ticket.TicketList.as_view(), name="ticket_list"), - url(r"^ajax$", ticket.TicketListDataAjax.as_view(), name="ticket_ajax"), - ] - ), - ), - url( - r"^ticket/(?P\d+)", - include( - [ - url(r"^$", ticket.TicketView.as_view(), name="ticket"), - url( - r"^/ajax$", - ticket.TicketMessageDataAjax.as_view(), - name="ticket_message_ajax", - ), - url( - r"^/open$", - ticket.TicketStatusChangeView.as_view(open=True), - name="ticket_open", - ), - url( - r"^/close$", - ticket.TicketStatusChangeView.as_view(open=False), - name="ticket_close", - ), - url( - r"^/notes$", - ticket.TicketNotesEditView.as_view(), - name="ticket_notes", - ), - ] - ), - ), - url( - r"^sitemap\.xml$", - sitemap, - { - "sitemaps": { - "problem": ProblemSitemap, - "user": UserSitemap, - "home": HomePageSitemap, - "contest": ContestSitemap, - "organization": OrganizationSitemap, - "blog": BlogPostSitemap, - "solutions": SolutionSitemap, - "pages": UrlSitemap( - [ - {"location": "/about/", "priority": 0.9}, - ] - ), - } - }, - ), - url( - r"^judge-select2/", - include( - [ - url(r"^profile/$", UserSelect2View.as_view(), name="profile_select2"), - url( - r"^organization/$", - OrganizationSelect2View.as_view(), - name="organization_select2", - ), - url( - r"^problem/$", ProblemSelect2View.as_view(), name="problem_select2" - ), - url( - r"^contest/$", ContestSelect2View.as_view(), name="contest_select2" - ), - ] - ), - ), - url( - r"^tasks/", - include( - [ - url( - r"^status/(?P[A-Za-z0-9-]*)$", - tasks.task_status, - name="task_status", - ), - url(r"^ajax_status$", tasks.task_status_ajax, name="task_status_ajax"), - url(r"^success$", tasks.demo_success), - url(r"^failure$", tasks.demo_failure), - url(r"^progress$", tasks.demo_progress), - ] - ), - ), - url(r"^about/", about.about, name="about"), - url( - r"^custom_checker_sample/", - about.custom_checker_sample, - name="custom_checker_sample", - ), - url( - r"^chat/", - include( - [ - url( - r"^(?P\d*)$", - login_required(chat.ChatView.as_view()), - name="chat", - ), - url(r"^delete/$", chat.delete_message, name="delete_chat_message"), - url(r"^mute/$", chat.mute_message, name="mute_chat_message"), - url(r"^post/$", chat.post_message, name="post_chat_message"), - url(r"^ajax$", chat.chat_message_ajax, name="chat_message_ajax"), - url( - r"^online_status/ajax$", - chat.online_status_ajax, - name="online_status_ajax", - ), - url( - r"^get_or_create_room$", - chat.get_or_create_room, - name="get_or_create_room", - ), - url( - r"^update_last_seen$", - chat.update_last_seen, - name="update_last_seen", - ), - url( - r"^online_status/user/ajax$", - chat.user_online_status_ajax, - name="user_online_status_ajax", - ), - url( - r"^toggle_ignore/(?P\d+)$", - chat.toggle_ignore, - name="toggle_ignore", - ), - ] - ), - ), - url( - r"^internal/", - include( - [ - url( - r"^problem$", - internal.InternalProblem.as_view(), - name="internal_problem", - ), - url( - r"^problem_votes$", - internal.get_problem_votes, - name="internal_problem_votes", - ), - url( - r"^request_time$", - internal.InternalRequestTime.as_view(), - name="internal_request_time", - ), - url( - r"^request_time_detail$", - internal.InternalRequestTimeDetail.as_view(), - name="internal_request_time_detail", - ), - url( - r"^internal_slow_request$", - internal.InternalSlowRequest.as_view(), - name="internal_slow_request", - ), - url( - r"^internal_slow_request_detail$", - internal.InternalSlowRequestDetail.as_view(), - name="internal_slow_request_detail", - ), - ] - ), - ), - url( - r"^notifications/", - paged_list_view(notification.NotificationList, "notification"), - ), - url( - r"^import_users/", - include( - [ - url(r"^$", user.ImportUsersView.as_view(), name="import_users"), - url( - r"post_file/$", - user.import_users_post_file, - name="import_users_post_file", - ), - url(r"submit/$", user.import_users_submit, name="import_users_submit"), - url(r"sample/$", user.sample_import_users, name="import_users_sample"), - ] - ), - ), - url( - r"^volunteer/", - include( - [ - url( - r"^problem/vote$", - volunteer.vote_problem, - name="volunteer_problem_vote", - ), - ] - ), - ), - url(r"^resolver/(?P\w+)", resolver.Resolver.as_view(), name="resolver"), - url(r"^upload/$", custom_file_upload.file_upload, name="custom_file_upload"), -] + url_static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + url(r'^$', blog.PostList.as_view(template_name='home.html', title=_('Home')), kwargs={'page': 1}, name='home'), + url(r'^500/$', exception), + url(r'^admin/', admin.site.urls), + url(r'^i18n/', include('django.conf.urls.i18n')), + url(r'^accounts/', include(register_patterns)), + url(r'^', include('social_django.urls')), -# if hasattr(settings, "INTERNAL_IPS"): -# urlpatterns.append(url("__debug__/", include("debug_toolbar.urls"))) + url(r'^problems/$', problem.ProblemList.as_view(), name='problem_list'), + url(r'^problems/random/$', problem.RandomProblem.as_view(), name='problem_random'), -favicon_paths = [ - "apple-touch-icon-180x180.png", - "apple-touch-icon-114x114.png", - "android-chrome-72x72.png", - "apple-touch-icon-57x57.png", - "apple-touch-icon-72x72.png", - "apple-touch-icon.png", - "mstile-70x70.png", - "android-chrome-36x36.png", - "apple-touch-icon-precomposed.png", - "apple-touch-icon-76x76.png", - "apple-touch-icon-60x60.png", - "android-chrome-96x96.png", - "mstile-144x144.png", - "mstile-150x150.png", - "safari-pinned-tab.svg", - "android-chrome-144x144.png", - "apple-touch-icon-152x152.png", - "favicon-96x96.png", - "favicon-32x32.png", - "favicon-16x16.png", - "android-chrome-192x192.png", - "android-chrome-512x512.png", - "android-chrome-48x48.png", - "mstile-310x150.png", - "apple-touch-icon-144x144.png", - "browserconfig.xml", - "manifest.json", - "apple-touch-icon-120x120.png", - "mstile-310x310.png", - "reload.png", + url(r'^problem/(?P[^/]+)', include([ + url(r'^$', problem.ProblemDetail.as_view(), name='problem_detail'), + url(r'^/editorial$', problem.ProblemSolution.as_view(), name='problem_editorial'), + url(r'^/comments$', problem.ProblemComments.as_view(), name='problem_comments'), + url(r'^/raw$', problem.ProblemRaw.as_view(), name='problem_raw'), + url(r'^/pdf$', problem.ProblemPdfView.as_view(), name='problem_pdf'), + url(r'^/pdf/(?P[a-z-]+)$', problem.ProblemPdfView.as_view(), name='problem_pdf'), + url(r'^/clone', problem.ProblemClone.as_view(), name='problem_clone'), + url(r'^/submit$', problem.problem_submit, name='problem_submit'), + url(r'^/resubmit/(?P\d+)$', problem.problem_submit, name='problem_submit'), + + url(r'^/rank/', paged_list_view(ranked_submission.RankedSubmissions, 'ranked_submissions')), + url(r'^/submissions/', paged_list_view(submission.ProblemSubmissions, 'chronological_submissions')), + url(r'^/submissions/(?P\w+)/', paged_list_view(submission.UserProblemSubmissions, 'user_submissions')), + + url(r'^/$', lambda _, problem: HttpResponsePermanentRedirect(reverse('problem_detail', args=[problem]))), + + url(r'^/test_data$', ProblemDataView.as_view(), name='problem_data'), + url(r'^/test_data/init$', problem_init_view, name='problem_data_init'), + url(r'^/test_data/diff$', ProblemSubmissionDiff.as_view(), name='problem_submission_diff'), + url(r'^/test_data/upload$', ProblemZipUploadView.as_view(), name='problem_zip_upload'), + url(r'^/data/(?P.+)$', problem_data_file, name='problem_data_file'), + + url(r'^/tickets$', ticket.ProblemTicketListView.as_view(), name='problem_ticket_list'), + url(r'^/tickets/new$', ticket.NewProblemTicketView.as_view(), name='new_problem_ticket'), + + url(r'^/manage/submission', include([ + url('^$', problem_manage.ManageProblemSubmissionView.as_view(), name='problem_manage_submissions'), + url('^/action$', problem_manage.ActionSubmissionsView.as_view(), name='problem_submissions_action'), + url('^/action/preview$', problem_manage.PreviewActionSubmissionsView.as_view(), + name='problem_submissions_rejudge_preview'), + url('^/rejudge/success/(?P[A-Za-z0-9-]*)$', problem_manage.rejudge_success, + name='problem_submissions_rejudge_success'), + url('^/rescore/all$', problem_manage.RescoreAllSubmissionsView.as_view(), + name='problem_submissions_rescore_all'), + url('^/rescore/success/(?P[A-Za-z0-9-]*)$', problem_manage.rescore_success, + name='problem_submissions_rescore_success'), + ])), + ])), + + url(r'^submissions/', paged_list_view(submission.AllSubmissions, 'all_submissions')), + url(r'^submissions/user/(?P\w+)/', paged_list_view(submission.AllUserSubmissions, 'all_user_submissions')), + + url(r'^src/(?P\d+)$', submission.SubmissionSource.as_view(), name='submission_source'), + url(r'^src/(?P\d+)/raw$', submission.SubmissionSourceRaw.as_view(), name='submission_source_raw'), + + url(r'^submission/(?P\d+)', include([ + url(r'^$', submission.SubmissionStatus.as_view(), name='submission_status'), + url(r'^/abort$', submission.abort_submission, name='submission_abort'), + url(r'^/html$', submission.single_submission), + ])), + + url(r'^users/', include([ + url(r'^$', user.users, name='user_list'), + url(r'^(?P\d+)$', lambda request, page: + HttpResponsePermanentRedirect('%s?page=%s' % (reverse('user_list'), page))), + url(r'^find$', user.user_ranking_redirect, name='user_ranking_redirect'), + ])), + + url(r'^user$', user.UserAboutPage.as_view(), name='user_page'), + url(r'^edit/profile/$', user.edit_profile, name='user_edit_profile'), + url(r'^user/(?P\w+)', include([ + url(r'^$', user.UserAboutPage.as_view(), name='user_page'), + url(r'^/solved', include([ + url(r'^$', user.UserProblemsPage.as_view(), name='user_problems'), + url(r'/ajax$', user.UserPerformancePointsAjax.as_view(), name='user_pp_ajax'), + ])), + url(r'^/submissions/', paged_list_view(submission.AllUserSubmissions, 'all_user_submissions_old')), + url(r'^/submissions/', lambda _, user: + HttpResponsePermanentRedirect(reverse('all_user_submissions', args=[user]))), + + url(r'^/$', lambda _, user: HttpResponsePermanentRedirect(reverse('user_page', args=[user]))), + ])), + + url(r'^comments/upvote/$', comment.upvote_comment, name='comment_upvote'), + url(r'^comments/downvote/$', comment.downvote_comment, name='comment_downvote'), + url(r'^comments/hide/$', comment.comment_hide, name='comment_hide'), + url(r'^comments/(?P\d+)/', include([ + url(r'^edit$', comment.CommentEdit.as_view(), name='comment_edit'), + url(r'^history/ajax$', comment.CommentRevisionAjax.as_view(), name='comment_revision_ajax'), + url(r'^edit/ajax$', comment.CommentEditAjax.as_view(), name='comment_edit_ajax'), + url(r'^votes/ajax$', comment.CommentVotesAjax.as_view(), name='comment_votes_ajax'), + url(r'^render$', comment.CommentContent.as_view(), name='comment_content'), + ])), + + url(r'^contests/', paged_list_view(contests.ContestList, 'contest_list')), + url(r'^contests/(?P\d+)/(?P\d+)/$', contests.ContestCalendar.as_view(), name='contest_calendar'), + url(r'^contests/tag/(?P[a-z-]+)', include([ + url(r'^$', contests.ContestTagDetail.as_view(), name='contest_tag'), + url(r'^/ajax$', contests.ContestTagDetailAjax.as_view(), name='contest_tag_ajax'), + ])), + + url(r'^contest/(?P\w+)', include([ + url(r'^$', contests.ContestDetail.as_view(), name='contest_view'), + url(r'^/moss$', contests.ContestMossView.as_view(), name='contest_moss'), + url(r'^/moss/delete$', contests.ContestMossDelete.as_view(), name='contest_moss_delete'), + url(r'^/clone$', contests.ContestClone.as_view(), name='contest_clone'), + url(r'^/ranking/$', contests.ContestRanking.as_view(), name='contest_ranking'), + url(r'^/ranking/ajax$', contests.contest_ranking_ajax, name='contest_ranking_ajax'), + url(r'^/join$', contests.ContestJoin.as_view(), name='contest_join'), + url(r'^/leave$', contests.ContestLeave.as_view(), name='contest_leave'), + url(r'^/stats$', contests.ContestStats.as_view(), name='contest_stats'), + + url(r'^/rank/(?P\w+)/', + paged_list_view(ranked_submission.ContestRankedSubmission, 'contest_ranked_submissions')), + + url(r'^/submissions/(?P\w+)/(?P\w+)/', + paged_list_view(submission.UserContestSubmissions, 'contest_user_submissions')), + + url(r'^/participations$', contests.ContestParticipationList.as_view(), name='contest_participation_own'), + url(r'^/participations/(?P\w+)$', + contests.ContestParticipationList.as_view(), name='contest_participation'), + url(r'^/participation/disqualify$', contests.ContestParticipationDisqualify.as_view(), + name='contest_participation_disqualify'), + + url(r'^/clarification$', contests.NewContestClarificationView.as_view(), name='new_contest_clarification'), + url(r'^/clarification/ajax$', contests.ContestClarificationAjax.as_view(), name='contest_clarification_ajax'), + + url(r'^/$', lambda _, contest: HttpResponsePermanentRedirect(reverse('contest_view', args=[contest]))), + ])), + + url(r'^organizations/$', organization.OrganizationList.as_view(), name='organization_list'), + url(r'^organization/(?P\d+)-(?P[\w-]*)', include([ + url(r'^$', organization.OrganizationHome.as_view(), name='organization_home'), + url(r'^/users$', organization.OrganizationUsers.as_view(), name='organization_users'), + url(r'^/join$', organization.JoinOrganization.as_view(), name='join_organization'), + url(r'^/leave$', organization.LeaveOrganization.as_view(), name='leave_organization'), + url(r'^/edit$', organization.EditOrganization.as_view(), name='edit_organization'), + url(r'^/kick$', organization.KickUserWidgetView.as_view(), name='organization_user_kick'), + + url(r'^/request$', organization.RequestJoinOrganization.as_view(), name='request_organization'), + url(r'^/request/(?P\d+)$', organization.OrganizationRequestDetail.as_view(), + name='request_organization_detail'), + url(r'^/requests/', include([ + url(r'^pending$', organization.OrganizationRequestView.as_view(), name='organization_requests_pending'), + url(r'^log$', organization.OrganizationRequestLog.as_view(), name='organization_requests_log'), + url(r'^approved$', organization.OrganizationRequestLog.as_view(states=('A',), tab='approved'), + name='organization_requests_approved'), + url(r'^rejected$', organization.OrganizationRequestLog.as_view(states=('R',), tab='rejected'), + name='organization_requests_rejected'), + ])), + + url(r'^/$', lambda _, pk, slug: HttpResponsePermanentRedirect(reverse('organization_home', args=[pk, slug]))), + ])), + + url(r'^runtimes/$', language.LanguageList.as_view(), name='runtime_list'), + url(r'^runtimes/matrix/$', status.version_matrix, name='version_matrix'), + url(r'^status/$', status.status_all, name='status_all'), + + url(r'^api/', include([ + url(r'^contest/list$', api.api_v1_contest_list), + url(r'^contest/info/(\w+)$', api.api_v1_contest_detail), + url(r'^problem/list$', api.api_v1_problem_list), + url(r'^problem/info/(\w+)$', api.api_v1_problem_info), + url(r'^user/list$', api.api_v1_user_list), + url(r'^user/info/(\w+)$', api.api_v1_user_info), + url(r'^user/submissions/(\w+)$', api.api_v1_user_submissions), + ])), + + url(r'^blog/', paged_list_view(blog.PostList, 'blog_post_list')), + url(r'^post/(?P\d+)-(?P.*)$', blog.PostView.as_view(), name='blog_post'), + + url(r'^license/(?P[-\w.]+)$', license.LicenseDetail.as_view(), name='license'), + + url(r'^mailgun/mail_activate/$', mailgun.MailgunActivationView.as_view(), name='mailgun_activate'), + + url(r'^widgets/', include([ + url(r'^contest_mode$', contests.update_contest_mode, name='contest_mode_ajax'), + url(r'^rejudge$', widgets.rejudge_submission, name='submission_rejudge'), + url(r'^single_submission$', submission.single_submission_query, name='submission_single_query'), + url(r'^submission_testcases$', submission.SubmissionTestCaseQuery.as_view(), name='submission_testcases_query'), + url(r'^detect_timezone$', widgets.DetectTimezone.as_view(), name='detect_timezone'), + url(r'^status-table$', status.status_table, name='status_table'), + + url(r'^template$', problem.LanguageTemplateAjax.as_view(), name='language_template_ajax'), + + url(r'^select2/', include([ + url(r'^user_search$', UserSearchSelect2View.as_view(), name='user_search_select2_ajax'), + url(r'^user_search_chat$', ChatUserSearchSelect2View.as_view(), name='chat_user_search_select2_ajax'), + url(r'^contest_users/(?P\w+)$', ContestUserSearchSelect2View.as_view(), + name='contest_user_search_select2_ajax'), + url(r'^ticket_user$', TicketUserSelect2View.as_view(), name='ticket_user_select2_ajax'), + url(r'^ticket_assignee$', AssigneeSelect2View.as_view(), name='ticket_assignee_select2_ajax'), + ])), + + url(r'^preview/', include([ + url(r'^problem$', preview.ProblemMarkdownPreviewView.as_view(), name='problem_preview'), + url(r'^blog$', preview.BlogMarkdownPreviewView.as_view(), name='blog_preview'), + url(r'^contest$', preview.ContestMarkdownPreviewView.as_view(), name='contest_preview'), + url(r'^comment$', preview.CommentMarkdownPreviewView.as_view(), name='comment_preview'), + url(r'^profile$', preview.ProfileMarkdownPreviewView.as_view(), name='profile_preview'), + url(r'^organization$', preview.OrganizationMarkdownPreviewView.as_view(), name='organization_preview'), + url(r'^solution$', preview.SolutionMarkdownPreviewView.as_view(), name='solution_preview'), + url(r'^license$', preview.LicenseMarkdownPreviewView.as_view(), name='license_preview'), + url(r'^ticket$', preview.TicketMarkdownPreviewView.as_view(), name='ticket_preview'), + ])), + ])), + + url(r'^feed/', include([ + url(r'^problems/rss/$', ProblemFeed(), name='problem_rss'), + url(r'^problems/atom/$', AtomProblemFeed(), name='problem_atom'), + url(r'^comment/rss/$', CommentFeed(), name='comment_rss'), + url(r'^comment/atom/$', AtomCommentFeed(), name='comment_atom'), + url(r'^blog/rss/$', BlogFeed(), name='blog_rss'), + url(r'^blog/atom/$', AtomBlogFeed(), name='blog_atom'), + ])), + + url(r'^stats/', include([ + url('^language/', include([ + url('^$', stats.language, name='language_stats'), + url('^data/all/$', stats.language_data, name='language_stats_data_all'), + url('^data/ac/$', stats.ac_language_data, name='language_stats_data_ac'), + url('^data/status/$', stats.status_data, name='stats_data_status'), + url('^data/ac_rate/$', stats.ac_rate, name='language_stats_data_ac_rate'), + ])), + ])), + + url(r'^tickets/', include([ + url(r'^$', ticket.TicketList.as_view(), name='ticket_list'), + url(r'^ajax$', ticket.TicketListDataAjax.as_view(), name='ticket_ajax'), + ])), + + url(r'^ticket/(?P\d+)', include([ + url(r'^$', ticket.TicketView.as_view(), name='ticket'), + url(r'^/ajax$', ticket.TicketMessageDataAjax.as_view(), name='ticket_message_ajax'), + url(r'^/open$', ticket.TicketStatusChangeView.as_view(open=True), name='ticket_open'), + url(r'^/close$', ticket.TicketStatusChangeView.as_view(open=False), name='ticket_close'), + url(r'^/notes$', ticket.TicketNotesEditView.as_view(), name='ticket_notes'), + ])), + + url(r'^sitemap\.xml$', sitemap, {'sitemaps': { + 'problem': ProblemSitemap, + 'user': UserSitemap, + 'home': HomePageSitemap, + 'contest': ContestSitemap, + 'organization': OrganizationSitemap, + 'blog': BlogPostSitemap, + 'solutions': SolutionSitemap, + 'pages': UrlSitemap([ + {'location': '/about/', 'priority': 0.9}, + ]), + }}), + + url(r'^judge-select2/', include([ + url(r'^profile/$', UserSelect2View.as_view(), name='profile_select2'), + url(r'^organization/$', OrganizationSelect2View.as_view(), name='organization_select2'), + url(r'^problem/$', ProblemSelect2View.as_view(), name='problem_select2'), + url(r'^contest/$', ContestSelect2View.as_view(), name='contest_select2'), + url(r'^comment/$', CommentSelect2View.as_view(), name='comment_select2'), + ])), + + url(r'^tasks/', include([ + url(r'^status/(?P[A-Za-z0-9-]*)$', tasks.task_status, name='task_status'), + url(r'^ajax_status$', tasks.task_status_ajax, name='task_status_ajax'), + url(r'^success$', tasks.demo_success), + url(r'^failure$', tasks.demo_failure), + url(r'^progress$', tasks.demo_progress), + ])), + + url(r'^about/', about.about, name='about'), + + url(r'^custom_checker_sample/', about.custom_checker_sample, name='custom_checker_sample'), + + url(r'^chat/', include([ + url(r'^(?P\d*)$', login_required(chat.ChatView.as_view()), name='chat'), + url(r'^delete/$', chat.delete_message, name='delete_chat_message'), + url(r'^post/$', chat.post_message, name='post_chat_message'), + url(r'^ajax$', chat.chat_message_ajax, name='chat_message_ajax'), + url(r'^online_status/ajax$', chat.online_status_ajax, name='online_status_ajax'), + url(r'^get_or_create_room$', chat.get_or_create_room, name='get_or_create_room'), + url(r'^update_last_seen$', chat.update_last_seen, name='update_last_seen'), + url(r'^online_status/user/ajax$', chat.user_online_status_ajax, name='user_online_status_ajax'), + url(r'^toggle_ignore/(?P\d+)$', chat.toggle_ignore, name='toggle_ignore'), + url(r'^get_unread_boxes$', chat.get_unread_boxes, name='get_unread_boxes'), + ])), + + url(r'^notifications/', + login_required(notification.NotificationList.as_view()), + name='notification'), + + url(r'^import_users/', include([ + url(r'^$', user.ImportUsersView.as_view(), name='import_users'), + url(r'post_file/$', user.import_users_post_file, name='import_users_post_file'), + url(r'submit/$', user.import_users_submit, name='import_users_submit'), + url(r'sample/$', user.sample_import_users, name='import_users_sample') + ])), + + url(r'^ide/', include([ + url(r'^$', login_required(TitledTemplateView.as_view(template_name='ide.html', title=_('IDE'))), name='ide'), + url(r'api', ide.api, name='ide_api'), + ])), ] +favicon_paths = ['apple-touch-icon-180x180.png', 'apple-touch-icon-114x114.png', 'android-chrome-72x72.png', + 'apple-touch-icon-57x57.png', 'apple-touch-icon-72x72.png', 'apple-touch-icon.png', 'mstile-70x70.png', + 'android-chrome-36x36.png', 'apple-touch-icon-precomposed.png', 'apple-touch-icon-76x76.png', + 'apple-touch-icon-60x60.png', 'android-chrome-96x96.png', 'mstile-144x144.png', 'mstile-150x150.png', + 'safari-pinned-tab.svg', 'android-chrome-144x144.png', 'apple-touch-icon-152x152.png', + 'favicon-96x96.png', + 'favicon-32x32.png', 'favicon-16x16.png', 'android-chrome-192x192.png', 'android-chrome-48x48.png', + 'mstile-310x150.png', 'apple-touch-icon-144x144.png', 'browserconfig.xml', 'manifest.json', + 'apple-touch-icon-120x120.png', 'mstile-310x310.png', 'reload.png'] + for favicon in favicon_paths: - urlpatterns.append( - url(r"^%s$" % favicon, RedirectView.as_view(url=static("icons/" + favicon))) - ) + urlpatterns.append(url(r'^%s$' % favicon, RedirectView.as_view( + url=static('icons/' + favicon) + ))) -handler404 = "judge.views.error.error404" -handler403 = "judge.views.error.error403" -handler500 = "judge.views.error.error500" +handler404 = 'judge.views.error.error404' +handler403 = 'judge.views.error.error403' +handler500 = 'judge.views.error.error500' -if "impersonate" in settings.INSTALLED_APPS: - urlpatterns.append(url(r"^impersonate/", include("impersonate.urls"))) +if 'newsletter' in settings.INSTALLED_APPS: + urlpatterns.append(url(r'^newsletter/', include('newsletter.urls'))) +if 'impersonate' in settings.INSTALLED_APPS: + urlpatterns.append(url(r'^impersonate/', include('impersonate.urls'))) diff --git a/dmoj/wsgi.py b/dmoj/wsgi.py index 3cde2a4..6bec753 100644 --- a/dmoj/wsgi.py +++ b/dmoj/wsgi.py @@ -1,6 +1,5 @@ import os - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dmoj.settings") +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dmoj.settings') try: import MySQLdb # noqa: F401, imported for side effect @@ -9,8 +8,5 @@ except ImportError: pymysql.install_as_MySQLdb() -from django.core.wsgi import ( - get_wsgi_application, -) # noqa: E402, django must be imported here - +from django.core.wsgi import get_wsgi_application # noqa: E402, django must be imported here application = get_wsgi_application() diff --git a/dmoj/wsgi_async.py b/dmoj/wsgi_async.py index 4a727c0..69e105f 100644 --- a/dmoj/wsgi_async.py +++ b/dmoj/wsgi_async.py @@ -2,17 +2,13 @@ import os import gevent.monkey # noqa: I100, gevent must be imported here -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dmoj.settings") +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dmoj.settings') gevent.monkey.patch_all() # noinspection PyUnresolvedReferences import dmoj_install_pymysql # noqa: F401, I100, I202, imported for side effect -from django.core.wsgi import ( - get_wsgi_application, -) # noqa: E402, I100, I202, django must be imported here - +from django.core.wsgi import get_wsgi_application # noqa: E402, I100, I202, django must be imported here # noinspection PyUnresolvedReferences import django_2_2_pymysql_patch # noqa: I100, F401, I202, imported for side effect - application = get_wsgi_application() diff --git a/dmoj_bridge_async.py b/dmoj_bridge_async.py index f514d6b..dee4112 100644 --- a/dmoj_bridge_async.py +++ b/dmoj_bridge_async.py @@ -2,22 +2,19 @@ import os import gevent.monkey # noqa: I100, gevent must be imported here -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dmoj.settings") +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dmoj.settings') gevent.monkey.patch_all() # noinspection PyUnresolvedReferences import dmoj_install_pymysql # noqa: E402, F401, I100, I202, imported for side effect import django # noqa: E402, F401, I100, I202, django must be imported here - django.setup() # noinspection PyUnresolvedReferences import django_2_2_pymysql_patch # noqa: E402, I100, F401, I202, imported for side effect -from judge.bridge.daemon import ( - judge_daemon, -) # noqa: E402, I100, I202, django code must be imported here +from judge.bridge.daemon import judge_daemon # noqa: E402, I100, I202, django code must be imported here -if __name__ == "__main__": +if __name__ == '__main__': judge_daemon() diff --git a/dmoj_celery.py b/dmoj_celery.py index dc7996d..58ea344 100644 --- a/dmoj_celery.py +++ b/dmoj_celery.py @@ -6,7 +6,7 @@ except ImportError: import dmoj_install_pymysql # noqa: F401, imported for side effect # set the default Django settings module for the 'celery' program. -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dmoj.settings") +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dmoj.settings') # noinspection PyUnresolvedReferences import django_2_2_pymysql_patch # noqa: I100, F401, I202, imported for side effect diff --git a/dmoj_install_pymysql.py b/dmoj_install_pymysql.py index e1795f5..7adabd5 100644 --- a/dmoj_install_pymysql.py +++ b/dmoj_install_pymysql.py @@ -1,4 +1,4 @@ import pymysql pymysql.install_as_MySQLdb() -pymysql.version_info = (1, 4, 0, "final", 0) +pymysql.version_info = (1, 3, 13, "final", 0) diff --git a/judge/__init__.py b/judge/__init__.py index 8aefa9d..5c386cd 100644 --- a/judge/__init__.py +++ b/judge/__init__.py @@ -1 +1 @@ -default_app_config = "judge.apps.JudgeAppConfig" +default_app_config = 'judge.apps.JudgeAppConfig' diff --git a/judge/admin/__init__.py b/judge/admin/__init__.py index d303048..51a7173 100644 --- a/judge/admin/__init__.py +++ b/judge/admin/__init__.py @@ -1,62 +1,19 @@ from django.contrib import admin from django.contrib.admin.models import LogEntry -from django.contrib.auth.models import User from judge.admin.comments import CommentAdmin -from judge.admin.contest import ( - ContestAdmin, - ContestParticipationAdmin, - ContestTagAdmin, - ContestsSummaryAdmin, -) -from judge.admin.interface import ( - BlogPostAdmin, - LicenseAdmin, - LogEntryAdmin, - NavigationBarAdmin, -) +from judge.admin.contest import ContestAdmin, ContestParticipationAdmin, ContestTagAdmin +from judge.admin.interface import BlogPostAdmin, LicenseAdmin, LogEntryAdmin, NavigationBarAdmin from judge.admin.organization import OrganizationAdmin, OrganizationRequestAdmin -from judge.admin.problem import ProblemAdmin, ProblemPointsVoteAdmin -from judge.admin.profile import ProfileAdmin, UserAdmin +from judge.admin.problem import ProblemAdmin +from judge.admin.profile import ProfileAdmin from judge.admin.runtime import JudgeAdmin, LanguageAdmin from judge.admin.submission import SubmissionAdmin -from judge.admin.taxon import ( - ProblemGroupAdmin, - ProblemTypeAdmin, - OfficialContestCategoryAdmin, - OfficialContestLocationAdmin, -) +from judge.admin.taxon import ProblemGroupAdmin, ProblemTypeAdmin from judge.admin.ticket import TicketAdmin -from judge.admin.volunteer import VolunteerProblemVoteAdmin -from judge.admin.course import CourseAdmin -from judge.models import ( - BlogPost, - Comment, - CommentLock, - Contest, - ContestParticipation, - ContestTag, - Judge, - Language, - License, - MiscConfig, - NavigationBar, - Organization, - OrganizationRequest, - Problem, - ProblemGroup, - ProblemPointsVote, - ProblemType, - Profile, - Submission, - Ticket, - VolunteerProblemVote, - Course, - ContestsSummary, - OfficialContestCategory, - OfficialContestLocation, -) - +from judge.models import BlogPost, Comment, CommentLock, Contest, ContestParticipation, \ + ContestTag, Judge, Language, License, MiscConfig, NavigationBar, Organization, \ + OrganizationRequest, Problem, ProblemGroup, ProblemType, Profile, Submission, Ticket admin.site.register(BlogPost, BlogPostAdmin) admin.site.register(Comment, CommentAdmin) @@ -74,15 +31,7 @@ admin.site.register(Organization, OrganizationAdmin) admin.site.register(OrganizationRequest, OrganizationRequestAdmin) admin.site.register(Problem, ProblemAdmin) admin.site.register(ProblemGroup, ProblemGroupAdmin) -admin.site.register(ProblemPointsVote, ProblemPointsVoteAdmin) admin.site.register(ProblemType, ProblemTypeAdmin) admin.site.register(Profile, ProfileAdmin) admin.site.register(Submission, SubmissionAdmin) admin.site.register(Ticket, TicketAdmin) -admin.site.register(VolunteerProblemVote, VolunteerProblemVoteAdmin) -admin.site.register(Course, CourseAdmin) -admin.site.unregister(User) -admin.site.register(User, UserAdmin) -admin.site.register(ContestsSummary, ContestsSummaryAdmin) -admin.site.register(OfficialContestCategory, OfficialContestCategoryAdmin) -admin.site.register(OfficialContestLocation, OfficialContestLocationAdmin) diff --git a/judge/admin/comments.py b/judge/admin/comments.py index c33b683..be6780c 100644 --- a/judge/admin/comments.py +++ b/judge/admin/comments.py @@ -11,71 +11,52 @@ from judge.widgets import AdminHeavySelect2Widget, HeavyPreviewAdminPageDownWidg class CommentForm(ModelForm): class Meta: widgets = { - "author": AdminHeavySelect2Widget(data_view="profile_select2"), + 'author': AdminHeavySelect2Widget(data_view='profile_select2'), + 'parent': AdminHeavySelect2Widget(data_view='comment_select2'), } if HeavyPreviewAdminPageDownWidget is not None: - widgets["body"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("comment_preview") - ) + widgets['body'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('comment_preview')) class CommentAdmin(VersionAdmin): fieldsets = ( - ( - None, - { - "fields": ( - "author", - "parent", - "score", - "hidden", - "content_type", - "object_id", - ) - }, - ), - ("Content", {"fields": ("body",)}), + (None, {'fields': ('author', 'page', 'parent', 'score', 'hidden')}), + ('Content', {'fields': ('body',)}), ) - list_display = ["author", "linked_object", "time"] - search_fields = ["author__user__username", "body"] - readonly_fields = ["score", "parent"] - actions = ["hide_comment", "unhide_comment"] - list_filter = ["hidden"] + list_display = ['author', 'linked_page', 'time'] + search_fields = ['author__user__username', 'page', 'body'] + actions = ['hide_comment', 'unhide_comment'] + list_filter = ['hidden'] actions_on_top = True actions_on_bottom = True form = CommentForm - date_hierarchy = "time" + date_hierarchy = 'time' def get_queryset(self, request): - return Comment.objects.order_by("-time") + return Comment.objects.order_by('-time') def hide_comment(self, request, queryset): count = queryset.update(hidden=True) - self.message_user( - request, - ungettext( - "%d comment successfully hidden.", - "%d comments successfully hidden.", - count, - ) - % count, - ) - - hide_comment.short_description = _("Hide comments") + self.message_user(request, ungettext('%d comment successfully hidden.', + '%d comments successfully hidden.', + count) % count) + hide_comment.short_description = _('Hide comments') def unhide_comment(self, request, queryset): count = queryset.update(hidden=False) - self.message_user( - request, - ungettext( - "%d comment successfully unhidden.", - "%d comments successfully unhidden.", - count, - ) - % count, - ) + self.message_user(request, ungettext('%d comment successfully unhidden.', + '%d comments successfully unhidden.', + count) % count) + unhide_comment.short_description = _('Unhide comments') - unhide_comment.short_description = _("Unhide comments") + def linked_page(self, obj): + link = obj.link + if link is not None: + return format_html('{1}', link, obj.page) + else: + return format_html('{0}', obj.page) + linked_page.short_description = _('Associated page') + linked_page.admin_order_field = 'page' def save_model(self, request, obj, form, change): super(CommentAdmin, self).save_model(request, obj, form, change) diff --git a/judge/admin/contest.py b/judge/admin/contest.py index 062d014..8678281 100644 --- a/judge/admin/contest.py +++ b/judge/admin/contest.py @@ -3,7 +3,7 @@ from django.contrib import admin from django.core.exceptions import PermissionDenied from django.db import connection, transaction from django.db.models import Q, TextField -from django.forms import ModelForm, ModelMultipleChoiceField, TextInput +from django.forms import ModelForm, ModelMultipleChoiceField from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse, reverse_lazy @@ -11,28 +11,12 @@ from django.utils import timezone from django.utils.html import format_html from django.utils.translation import gettext_lazy as _, ungettext from reversion.admin import VersionAdmin -from reversion_compare.admin import CompareVersionAdmin from django_ace import AceWidget -from judge.models import ( - Contest, - ContestProblem, - ContestSubmission, - Profile, - Rating, - OfficialContest, -) +from judge.models import Contest, ContestProblem, ContestSubmission, Profile, Rating from judge.ratings import rate_contest -from judge.widgets import ( - AdminHeavySelect2MultipleWidget, - AdminHeavySelect2Widget, - AdminPagedownWidget, - AdminSelect2MultipleWidget, - AdminSelect2Widget, - HeavyPreviewAdminPageDownWidget, -) -from judge.views.contests import recalculate_contest_summary_result -from judge.utils.contest import maybe_trigger_contest_rescore +from judge.widgets import AdminHeavySelect2MultipleWidget, AdminHeavySelect2Widget, AdminPagedownWidget, \ + AdminSelect2MultipleWidget, AdminSelect2Widget, HeavyPreviewAdminPageDownWidget class AdminHeavySelect2Widget(AdminHeavySelect2Widget): @@ -43,394 +27,247 @@ class AdminHeavySelect2Widget(AdminHeavySelect2Widget): class ContestTagForm(ModelForm): contests = ModelMultipleChoiceField( - label=_("Included contests"), + label=_('Included contests'), queryset=Contest.objects.all(), required=False, - widget=AdminHeavySelect2MultipleWidget(data_view="contest_select2"), - ) + widget=AdminHeavySelect2MultipleWidget(data_view='contest_select2')) class ContestTagAdmin(admin.ModelAdmin): - fields = ("name", "color", "description", "contests") - list_display = ("name", "color") + fields = ('name', 'color', 'description', 'contests') + list_display = ('name', 'color') actions_on_top = True actions_on_bottom = True form = ContestTagForm if AdminPagedownWidget is not None: formfield_overrides = { - TextField: {"widget": AdminPagedownWidget}, + TextField: {'widget': AdminPagedownWidget}, } def save_model(self, request, obj, form, change): super(ContestTagAdmin, self).save_model(request, obj, form, change) - obj.contests.set(form.cleaned_data["contests"]) + obj.contests.set(form.cleaned_data['contests']) def get_form(self, request, obj=None, **kwargs): form = super(ContestTagAdmin, self).get_form(request, obj, **kwargs) if obj is not None: - form.base_fields["contests"].initial = obj.contests.all() + form.base_fields['contests'].initial = obj.contests.all() return form class ContestProblemInlineForm(ModelForm): class Meta: - widgets = { - "problem": AdminHeavySelect2Widget(data_view="problem_select2"), - "hidden_subtasks": TextInput(attrs={"size": "3"}), - "points": TextInput(attrs={"size": "1"}), - "order": TextInput(attrs={"size": "1"}), - } + widgets = {'problem': AdminHeavySelect2Widget(data_view='problem_select2')} class ContestProblemInline(admin.TabularInline): model = ContestProblem - verbose_name = _("Problem") - verbose_name_plural = "Problems" - fields = ( - "problem", - "points", - "partial", - "is_pretested", - "max_submissions", - "hidden_subtasks", - "show_testcases", - "order", - "rejudge_column", - ) - readonly_fields = ("rejudge_column",) + verbose_name = _('Problem') + verbose_name_plural = 'Problems' + fields = ('problem', 'points', 'partial', 'is_pretested', 'max_submissions', 'output_prefix_override', 'order', + 'rejudge_column') + readonly_fields = ('rejudge_column',) form = ContestProblemInlineForm def rejudge_column(self, obj): if obj.id is None: - return "" - return format_html( - 'Rejudge', - reverse("admin:judge_contest_rejudge", args=(obj.contest.id, obj.id)), - ) - - rejudge_column.short_description = "" + return '' + return format_html('Rejudge', + reverse('admin:judge_contest_rejudge', args=(obj.contest.id, obj.id))) + rejudge_column.short_description = '' class ContestForm(ModelForm): def __init__(self, *args, **kwargs): super(ContestForm, self).__init__(*args, **kwargs) - if "rate_exclude" in self.fields: + if 'rate_exclude' in self.fields: if self.instance and self.instance.id: - self.fields["rate_exclude"].queryset = Profile.objects.filter( - contest_history__contest=self.instance - ).distinct() + self.fields['rate_exclude'].queryset = \ + Profile.objects.filter(contest_history__contest=self.instance).distinct() else: - self.fields["rate_exclude"].queryset = Profile.objects.none() - self.fields["banned_users"].widget.can_add_related = False - self.fields["view_contest_scoreboard"].widget.can_add_related = False + self.fields['rate_exclude'].queryset = Profile.objects.none() + self.fields['banned_users'].widget.can_add_related = False + self.fields['view_contest_scoreboard'].widget.can_add_related = False def clean(self): cleaned_data = super(ContestForm, self).clean() - cleaned_data["banned_users"].filter( - current_contest__contest=self.instance - ).update(current_contest=None) + cleaned_data['banned_users'].filter(current_contest__contest=self.instance).update(current_contest=None) class Meta: widgets = { - "authors": AdminHeavySelect2MultipleWidget(data_view="profile_select2"), - "curators": AdminHeavySelect2MultipleWidget(data_view="profile_select2"), - "testers": AdminHeavySelect2MultipleWidget(data_view="profile_select2"), - "private_contestants": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "organizations": AdminHeavySelect2MultipleWidget( - data_view="organization_select2" - ), - "tags": AdminSelect2MultipleWidget, - "banned_users": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "view_contest_scoreboard": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), + 'authors': AdminHeavySelect2MultipleWidget(data_view='profile_select2'), + 'curators': AdminHeavySelect2MultipleWidget(data_view='profile_select2'), + 'testers': AdminHeavySelect2MultipleWidget(data_view='profile_select2'), + 'private_contestants': AdminHeavySelect2MultipleWidget(data_view='profile_select2', + attrs={'style': 'width: 100%'}), + 'organizations': AdminHeavySelect2MultipleWidget(data_view='organization_select2'), + 'tags': AdminSelect2MultipleWidget, + 'banned_users': AdminHeavySelect2MultipleWidget(data_view='profile_select2', + attrs={'style': 'width: 100%'}), + 'view_contest_scoreboard': AdminHeavySelect2MultipleWidget(data_view='profile_select2', + attrs={'style': 'width: 100%'}), } if HeavyPreviewAdminPageDownWidget is not None: - widgets["description"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("contest_preview") - ) + widgets['description'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('contest_preview')) -class OfficialContestInlineForm(ModelForm): - class Meta: - widgets = { - "category": AdminSelect2Widget, - "location": AdminSelect2Widget, - } - - -class OfficialContestInline(admin.StackedInline): - fields = ( - "category", - "year", - "location", - ) - model = OfficialContest - can_delete = True - form = OfficialContestInlineForm - extra = 0 - - -class ContestAdmin(CompareVersionAdmin): +class ContestAdmin(VersionAdmin): fieldsets = ( - (None, {"fields": ("key", "name", "authors", "curators", "testers")}), - ( - _("Settings"), - { - "fields": ( - "is_visible", - "use_clarifications", - "hide_problem_tags", - "public_scoreboard", - "scoreboard_visibility", - "run_pretests_only", - "points_precision", - "rate_limit", - ) - }, - ), - ( - _("Scheduling"), - {"fields": ("start_time", "end_time", "time_limit", "freeze_after")}, - ), - ( - _("Details"), - { - "fields": ( - "description", - "og_image", - "logo_override_image", - "tags", - "summary", - ) - }, - ), - ( - _("Format"), - {"fields": ("format_name", "format_config", "problem_label_script")}, - ), - ( - _("Rating"), - { - "fields": ( - "is_rated", - "rate_all", - "rating_floor", - "rating_ceiling", - "rate_exclude", - ) - }, - ), - ( - _("Access"), - { - "fields": ( - "access_code", - "private_contestants", - "organizations", - "view_contest_scoreboard", - ) - }, - ), - (_("Justice"), {"fields": ("banned_users",)}), + (None, {'fields': ('key', 'name', 'authors', 'curators', 'testers')}), + (_('Settings'), {'fields': ('is_visible', 'use_clarifications', 'hide_problem_tags', 'scoreboard_visibility', + 'run_pretests_only', 'points_precision')}), + (_('Scheduling'), {'fields': ('start_time', 'end_time', 'time_limit')}), + (_('Details'), {'fields': ('description', 'og_image', 'logo_override_image', 'tags', 'summary')}), + (_('Format'), {'fields': ('format_name', 'format_config', 'problem_label_script')}), + (_('Rating'), {'fields': ('is_rated', 'rate_all', 'rating_floor', 'rating_ceiling', 'rate_exclude')}), + (_('Access'), {'fields': ('access_code', 'is_private', 'private_contestants', 'is_organization_private', + 'organizations', 'view_contest_scoreboard')}), + (_('Justice'), {'fields': ('banned_users',)}), ) - list_display = ( - "key", - "name", - "is_visible", - "is_rated", - "start_time", - "end_time", - "time_limit", - "user_count", - ) - search_fields = ("key", "name") - inlines = [ContestProblemInline, OfficialContestInline] + list_display = ('key', 'name', 'is_visible', 'is_rated', 'start_time', 'end_time', 'time_limit', 'user_count') + search_fields = ('key', 'name') + inlines = [ContestProblemInline] actions_on_top = True actions_on_bottom = True form = ContestForm - change_list_template = "admin/judge/contest/change_list.html" - filter_horizontal = ["rate_exclude"] - date_hierarchy = "start_time" + change_list_template = 'admin/judge/contest/change_list.html' + filter_horizontal = ['rate_exclude'] + date_hierarchy = 'start_time' def get_actions(self, request): actions = super(ContestAdmin, self).get_actions(request) - if request.user.has_perm( - "judge.change_contest_visibility" - ) or request.user.has_perm("judge.create_private_contest"): - for action in ("make_visible", "make_hidden"): + if request.user.has_perm('judge.change_contest_visibility') or \ + request.user.has_perm('judge.create_private_contest'): + for action in ('make_visible', 'make_hidden'): actions[action] = self.get_action(action) return actions def get_queryset(self, request): queryset = Contest.objects.all() - if request.user.has_perm("judge.edit_all_contest"): + if request.user.has_perm('judge.edit_all_contest'): return queryset else: - return queryset.filter( - Q(authors=request.profile) | Q(curators=request.profile) - ).distinct() + return queryset.filter(Q(authors=request.profile) | Q(curators=request.profile)).distinct() def get_readonly_fields(self, request, obj=None): readonly = [] - if not request.user.has_perm("judge.contest_rating"): - readonly += ["is_rated", "rate_all", "rate_exclude"] - if not request.user.has_perm("judge.contest_access_code"): - readonly += ["access_code"] - if not request.user.has_perm("judge.create_private_contest"): - readonly += [ - "private_contestants", - "organizations", - ] - if not request.user.has_perm("judge.change_contest_visibility"): - readonly += ["is_visible"] - if not request.user.has_perm("judge.contest_problem_label"): - readonly += ["problem_label_script"] + if not request.user.has_perm('judge.contest_rating'): + readonly += ['is_rated', 'rate_all', 'rate_exclude'] + if not request.user.has_perm('judge.contest_access_code'): + readonly += ['access_code'] + if not request.user.has_perm('judge.create_private_contest'): + readonly += ['is_private', 'private_contestants', 'is_organization_private', 'organizations'] + if not request.user.has_perm('judge.change_contest_visibility'): + readonly += ['is_visible'] + if not request.user.has_perm('judge.contest_problem_label'): + readonly += ['problem_label_script'] return readonly def save_model(self, request, obj, form, change): # `is_visible` will not appear in `cleaned_data` if user cannot edit it - if form.cleaned_data.get("is_visible") and not request.user.has_perm( - "judge.change_contest_visibility" - ): - if ( - not len(form.cleaned_data["organizations"]) > 0 - and not len(form.cleaned_data["private_contestants"]) > 0 - ): + if form.cleaned_data.get('is_visible') and not request.user.has_perm('judge.change_contest_visibility'): + if not form.cleaned_data['is_private'] and not form.cleaned_data['is_organization_private']: raise PermissionDenied - if not request.user.has_perm("judge.create_private_contest"): + if not request.user.has_perm('judge.create_private_contest'): raise PermissionDenied super().save_model(request, obj, form, change) + # We need this flag because `save_related` deals with the inlines, but does not know if we have already rescored + self._rescored = False + if form.changed_data and any(f in form.changed_data for f in ('format_config', 'format_name')): + self._rescore(obj.key) + self._rescored = True + def save_related(self, request, form, formsets, change): super().save_related(request, form, formsets, change) # Only rescored if we did not already do so in `save_model` - formset_changed = False - if any(formset.has_changed() for formset in formsets): - formset_changed = True - - maybe_trigger_contest_rescore(form, form.instance, formset_changed) + if not self._rescored and any(formset.has_changed() for formset in formsets): + self._rescore(form.cleaned_data['key']) def has_change_permission(self, request, obj=None): - if not request.user.has_perm("judge.edit_own_contest"): + if not request.user.has_perm('judge.edit_own_contest'): return False if obj is None: return True return obj.is_editable_by(request.user) - def make_visible(self, request, queryset): - if not request.user.has_perm("judge.change_contest_visibility"): - queryset = queryset.filter( - Q(is_private=True) | Q(is_organization_private=True) - ) - count = queryset.update(is_visible=True) - self.message_user( - request, - ungettext( - "%d contest successfully marked as visible.", - "%d contests successfully marked as visible.", - count, - ) - % count, - ) + def _rescore(self, contest_key): + from judge.tasks import rescore_contest + transaction.on_commit(rescore_contest.s(contest_key).delay) - make_visible.short_description = _("Mark contests as visible") + def make_visible(self, request, queryset): + if not request.user.has_perm('judge.change_contest_visibility'): + queryset = queryset.filter(Q(is_private=True) | Q(is_organization_private=True)) + count = queryset.update(is_visible=True) + self.message_user(request, ungettext('%d contest successfully marked as visible.', + '%d contests successfully marked as visible.', + count) % count) + make_visible.short_description = _('Mark contests as visible') def make_hidden(self, request, queryset): - if not request.user.has_perm("judge.change_contest_visibility"): - queryset = queryset.filter( - Q(is_private=True) | Q(is_organization_private=True) - ) + if not request.user.has_perm('judge.change_contest_visibility'): + queryset = queryset.filter(Q(is_private=True) | Q(is_organization_private=True)) count = queryset.update(is_visible=True) - self.message_user( - request, - ungettext( - "%d contest successfully marked as hidden.", - "%d contests successfully marked as hidden.", - count, - ) - % count, - ) - - make_hidden.short_description = _("Mark contests as hidden") + self.message_user(request, ungettext('%d contest successfully marked as hidden.', + '%d contests successfully marked as hidden.', + count) % count) + make_hidden.short_description = _('Mark contests as hidden') def get_urls(self): return [ - url(r"^rate/all/$", self.rate_all_view, name="judge_contest_rate_all"), - url(r"^(\d+)/rate/$", self.rate_view, name="judge_contest_rate"), - url( - r"^(\d+)/judge/(\d+)/$", self.rejudge_view, name="judge_contest_rejudge" - ), + url(r'^rate/all/$', self.rate_all_view, name='judge_contest_rate_all'), + url(r'^(\d+)/rate/$', self.rate_view, name='judge_contest_rate'), + url(r'^(\d+)/judge/(\d+)/$', self.rejudge_view, name='judge_contest_rejudge'), ] + super(ContestAdmin, self).get_urls() def rejudge_view(self, request, contest_id, problem_id): - queryset = ContestSubmission.objects.filter( - problem_id=problem_id - ).select_related("submission") + queryset = ContestSubmission.objects.filter(problem_id=problem_id).select_related('submission') for model in queryset: model.submission.judge(rejudge=True) - self.message_user( - request, - ungettext( - "%d submission was successfully scheduled for rejudging.", - "%d submissions were successfully scheduled for rejudging.", - len(queryset), - ) - % len(queryset), - ) - return HttpResponseRedirect( - reverse("admin:judge_contest_change", args=(contest_id,)) - ) + self.message_user(request, ungettext('%d submission was successfully scheduled for rejudging.', + '%d submissions were successfully scheduled for rejudging.', + len(queryset)) % len(queryset)) + return HttpResponseRedirect(reverse('admin:judge_contest_change', args=(contest_id,))) def rate_all_view(self, request): - if not request.user.has_perm("judge.contest_rating"): + if not request.user.has_perm('judge.contest_rating'): raise PermissionDenied() with transaction.atomic(): with connection.cursor() as cursor: - cursor.execute("TRUNCATE TABLE `%s`" % Rating._meta.db_table) + cursor.execute('TRUNCATE TABLE `%s`' % Rating._meta.db_table) Profile.objects.update(rating=None) - for contest in Contest.objects.filter( - is_rated=True, end_time__lte=timezone.now() - ).order_by("end_time"): + for contest in Contest.objects.filter(is_rated=True, end_time__lte=timezone.now()).order_by('end_time'): rate_contest(contest) - return HttpResponseRedirect(reverse("admin:judge_contest_changelist")) + return HttpResponseRedirect(reverse('admin:judge_contest_changelist')) def rate_view(self, request, id): - if not request.user.has_perm("judge.contest_rating"): + if not request.user.has_perm('judge.contest_rating'): raise PermissionDenied() contest = get_object_or_404(Contest, id=id) if not contest.is_rated or not contest.ended: raise Http404() with transaction.atomic(): contest.rate() - return HttpResponseRedirect( - request.META.get("HTTP_REFERER", reverse("admin:judge_contest_changelist")) - ) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse('admin:judge_contest_changelist'))) def get_form(self, request, obj=None, **kwargs): form = super(ContestAdmin, self).get_form(request, obj, **kwargs) - if "problem_label_script" in form.base_fields: + if 'problem_label_script' in form.base_fields: # form.base_fields['problem_label_script'] does not exist when the user has only view permission # on the model. - form.base_fields["problem_label_script"].widget = AceWidget( - "lua", request.profile.ace_theme - ) - - perms = ("edit_own_contest", "edit_all_contest") - form.base_fields["curators"].queryset = Profile.objects.filter( - Q(user__is_superuser=True) - | Q(user__groups__permissions__codename__in=perms) - | Q(user__user_permissions__codename__in=perms), + form.base_fields['problem_label_script'].widget = AceWidget('lua', request.profile.ace_theme) + + perms = ('edit_own_contest', 'edit_all_contest') + form.base_fields['curators'].queryset = Profile.objects.filter( + Q(user__is_superuser=True) | + Q(user__groups__permissions__codename__in=perms) | + Q(user__user_permissions__codename__in=perms), ).distinct() return form @@ -438,48 +275,29 @@ class ContestAdmin(CompareVersionAdmin): class ContestParticipationForm(ModelForm): class Meta: widgets = { - "contest": AdminSelect2Widget(), - "user": AdminHeavySelect2Widget(data_view="profile_select2"), + 'contest': AdminSelect2Widget(), + 'user': AdminHeavySelect2Widget(data_view='profile_select2'), } class ContestParticipationAdmin(admin.ModelAdmin): - fields = ("contest", "user", "real_start", "virtual", "is_disqualified") - list_display = ( - "contest", - "username", - "show_virtual", - "real_start", - "score", - "cumtime", - "tiebreaker", - ) - actions = ["recalculate_results"] + fields = ('contest', 'user', 'real_start', 'virtual', 'is_disqualified') + list_display = ('contest', 'username', 'show_virtual', 'real_start', 'score', 'cumtime', 'tiebreaker') + actions = ['recalculate_results'] actions_on_bottom = actions_on_top = True - search_fields = ("contest__key", "contest__name", "user__user__username") + search_fields = ('contest__key', 'contest__name', 'user__user__username') form = ContestParticipationForm - date_hierarchy = "real_start" + date_hierarchy = 'real_start' def get_queryset(self, request): - return ( - super(ContestParticipationAdmin, self) - .get_queryset(request) - .only( - "contest__name", - "contest__format_name", - "contest__format_config", - "user__user__username", - "real_start", - "score", - "cumtime", - "tiebreaker", - "virtual", - ) + return super(ContestParticipationAdmin, self).get_queryset(request).only( + 'contest__name', 'contest__format_name', 'contest__format_config', + 'user__user__username', 'real_start', 'score', 'cumtime', 'tiebreaker', 'virtual', ) def save_model(self, request, obj, form, change): super().save_model(request, obj, form, change) - if form.changed_data and "is_disqualified" in form.changed_data: + if form.changed_data and 'is_disqualified' in form.changed_data: obj.set_disqualified(obj.is_disqualified) def recalculate_results(self, request, queryset): @@ -487,48 +305,17 @@ class ContestParticipationAdmin(admin.ModelAdmin): for participation in queryset: participation.recompute_results() count += 1 - self.message_user( - request, - ungettext( - "%d participation recalculated.", - "%d participations recalculated.", - count, - ) - % count, - ) - - recalculate_results.short_description = _("Recalculate results") + self.message_user(request, ungettext('%d participation recalculated.', + '%d participations recalculated.', + count) % count) + recalculate_results.short_description = _('Recalculate results') def username(self, obj): return obj.user.username - - username.short_description = _("username") - username.admin_order_field = "user__user__username" + username.short_description = _('username') + username.admin_order_field = 'user__user__username' def show_virtual(self, obj): - return obj.virtual or "-" - - show_virtual.short_description = _("virtual") - show_virtual.admin_order_field = "virtual" - - -class ContestsSummaryForm(ModelForm): - class Meta: - widgets = { - "contests": AdminHeavySelect2MultipleWidget( - data_view="contest_select2", attrs={"style": "width: 100%"} - ), - } - - -class ContestsSummaryAdmin(admin.ModelAdmin): - fields = ("key", "contests", "scores") - list_display = ("key",) - search_fields = ("key", "contests__key") - form = ContestsSummaryForm - - def save_model(self, request, obj, form, change): - super(ContestsSummaryAdmin, self).save_model(request, obj, form, change) - obj.refresh_from_db() - obj.results = recalculate_contest_summary_result(request, obj) - obj.save() + return obj.virtual or '-' + show_virtual.short_description = _('virtual') + show_virtual.admin_order_field = 'virtual' diff --git a/judge/admin/course.py b/judge/admin/course.py deleted file mode 100644 index ac8c0fd..0000000 --- a/judge/admin/course.py +++ /dev/null @@ -1,52 +0,0 @@ -from django.contrib import admin -from django.utils.html import format_html -from django.urls import reverse, reverse_lazy -from django.utils.translation import gettext, gettext_lazy as _, ungettext -from django.forms import ModelForm - -from judge.models import Course, CourseRole -from judge.widgets import AdminSelect2MultipleWidget -from judge.widgets import ( - AdminHeavySelect2MultipleWidget, - AdminHeavySelect2Widget, - HeavyPreviewAdminPageDownWidget, - AdminSelect2Widget, -) - - -class CourseRoleInlineForm(ModelForm): - class Meta: - widgets = { - "user": AdminHeavySelect2Widget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "role": AdminSelect2Widget, - } - - -class CourseRoleInline(admin.TabularInline): - model = CourseRole - extra = 1 - form = CourseRoleInlineForm - - -class CourseForm(ModelForm): - class Meta: - widgets = { - "organizations": AdminHeavySelect2MultipleWidget( - data_view="organization_select2" - ), - "about": HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("blog_preview") - ), - } - - -class CourseAdmin(admin.ModelAdmin): - prepopulated_fields = {"slug": ("name",)} - inlines = [ - CourseRoleInline, - ] - list_display = ("name", "is_public", "is_open") - search_fields = ("name",) - form = CourseForm diff --git a/judge/admin/interface.py b/judge/admin/interface.py index b377212..4691c8b 100644 --- a/judge/admin/interface.py +++ b/judge/admin/interface.py @@ -6,33 +6,26 @@ from django.utils.html import format_html from django.utils.translation import gettext_lazy as _ from mptt.admin import DraggableMPTTAdmin from reversion.admin import VersionAdmin -from reversion_compare.admin import CompareVersionAdmin - from judge.dblock import LockModel from judge.models import NavigationBar -from judge.widgets import ( - AdminHeavySelect2MultipleWidget, - AdminHeavySelect2Widget, - HeavyPreviewAdminPageDownWidget, -) +from judge.widgets import AdminHeavySelect2MultipleWidget, AdminHeavySelect2Widget, HeavyPreviewAdminPageDownWidget class NavigationBarAdmin(DraggableMPTTAdmin): - list_display = DraggableMPTTAdmin.list_display + ("key", "linked_path") - fields = ("key", "label", "path", "order", "regex", "parent") + list_display = DraggableMPTTAdmin.list_display + ('key', 'linked_path') + fields = ('key', 'label', 'path', 'order', 'regex', 'parent') list_editable = () # Bug in SortableModelAdmin: 500 without list_editable being set mptt_level_indent = 20 - sortable = "order" + sortable = 'order' def __init__(self, *args, **kwargs): super(NavigationBarAdmin, self).__init__(*args, **kwargs) self.__save_model_calls = 0 def linked_path(self, obj): - return format_html('{0}', obj.path) - - linked_path.short_description = _("link path") + return format_html(u'{0}', obj.path) + linked_path.short_description = _('link path') def save_model(self, request, obj, form, change): self.__save_model_calls += 1 @@ -41,9 +34,7 @@ class NavigationBarAdmin(DraggableMPTTAdmin): def changelist_view(self, request, extra_context=None): self.__save_model_calls = 0 with NavigationBar.objects.disable_mptt_updates(): - result = super(NavigationBarAdmin, self).changelist_view( - request, extra_context - ) + result = super(NavigationBarAdmin, self).changelist_view(request, extra_context) if self.__save_model_calls: with LockModel(write=(NavigationBar,)): NavigationBar.objects.rebuild() @@ -53,106 +44,74 @@ class NavigationBarAdmin(DraggableMPTTAdmin): class BlogPostForm(ModelForm): def __init__(self, *args, **kwargs): super(BlogPostForm, self).__init__(*args, **kwargs) - if "authors" in self.fields: - self.fields["authors"].widget.can_add_related = False + self.fields['authors'].widget.can_add_related = False class Meta: widgets = { - "authors": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "organizations": AdminHeavySelect2MultipleWidget( - data_view="organization_select2", attrs={"style": "width: 100%"} - ), + 'authors': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}), + 'organizations': AdminHeavySelect2MultipleWidget(data_view='organization_select2', + attrs={'style': 'width: 100%'}), } if HeavyPreviewAdminPageDownWidget is not None: - widgets["content"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("blog_preview") - ) - widgets["summary"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("blog_preview") - ) + widgets['content'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('blog_preview')) + widgets['summary'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('blog_preview')) -class BlogPostAdmin(CompareVersionAdmin): +class BlogPostAdmin(VersionAdmin): fieldsets = ( - ( - None, - { - "fields": ( - "title", - "slug", - "authors", - "visible", - "sticky", - "publish_on", - "is_organization_private", - "organizations", - ) - }, - ), - (_("Content"), {"fields": ("content", "og_image")}), - (_("Summary"), {"classes": ("collapse",), "fields": ("summary",)}), + (None, {'fields': ('title', 'slug', 'authors', 'visible', 'sticky', 'publish_on', + 'is_organization_private', 'organizations')}), + (_('Content'), {'fields': ('content', 'og_image')}), + (_('Summary'), {'classes': ('collapse',), 'fields': ('summary',)}), ) - prepopulated_fields = {"slug": ("title",)} - list_display = ("id", "title", "visible", "sticky", "publish_on") - list_display_links = ("id", "title") - ordering = ("-publish_on",) + prepopulated_fields = {'slug': ('title',)} + list_display = ('id', 'title', 'visible', 'sticky', 'publish_on') + list_display_links = ('id', 'title') + ordering = ('-publish_on',) form = BlogPostForm - date_hierarchy = "publish_on" + date_hierarchy = 'publish_on' def has_change_permission(self, request, obj=None): - return ( - request.user.has_perm("judge.edit_all_post") - or request.user.has_perm("judge.change_blogpost") - and (obj is None or obj.authors.filter(id=request.profile.id).exists()) - ) + return (request.user.has_perm('judge.edit_all_post') or + request.user.has_perm('judge.change_blogpost') and ( + obj is None or + obj.authors.filter(id=request.profile.id).exists())) class SolutionForm(ModelForm): def __init__(self, *args, **kwargs): super(SolutionForm, self).__init__(*args, **kwargs) - self.fields["authors"].widget.can_add_related = False + self.fields['authors'].widget.can_add_related = False class Meta: widgets = { - "authors": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "problem": AdminHeavySelect2Widget( - data_view="problem_select2", attrs={"style": "width: 250px"} - ), + 'authors': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}), + 'problem': AdminHeavySelect2Widget(data_view='problem_select2', attrs={'style': 'width: 250px'}), } if HeavyPreviewAdminPageDownWidget is not None: - widgets["content"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("solution_preview") - ) + widgets['content'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('solution_preview')) class LicenseForm(ModelForm): class Meta: if HeavyPreviewAdminPageDownWidget is not None: - widgets = { - "text": HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("license_preview") - ) - } + widgets = {'text': HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('license_preview'))} class LicenseAdmin(admin.ModelAdmin): - fields = ("key", "link", "name", "display", "icon", "text") - list_display = ("name", "key") + fields = ('key', 'link', 'name', 'display', 'icon', 'text') + list_display = ('name', 'key') form = LicenseForm class UserListFilter(admin.SimpleListFilter): - title = _("user") - parameter_name = "user" + title = _('user') + parameter_name = 'user' def lookups(self, request, model_admin): - return User.objects.filter(is_staff=True).values_list("id", "username") + return User.objects.filter(is_staff=True).values_list('id', 'username') def queryset(self, request, queryset): if self.value(): @@ -161,29 +120,10 @@ class UserListFilter(admin.SimpleListFilter): class LogEntryAdmin(admin.ModelAdmin): - readonly_fields = ( - "user", - "content_type", - "object_id", - "object_repr", - "action_flag", - "change_message", - ) - list_display = ( - "__str__", - "action_time", - "user", - "content_type", - "object_link", - "diff_link", - ) - search_fields = ( - "object_repr", - "change_message", - "user__username", - "content_type__model", - ) - list_filter = (UserListFilter, "content_type") + readonly_fields = ('user', 'content_type', 'object_id', 'object_repr', 'action_flag', 'change_message') + list_display = ('__str__', 'action_time', 'user', 'content_type', 'object_link') + search_fields = ('object_repr', 'change_message') + list_filter = (UserListFilter, 'content_type') list_display_links = None actions = None @@ -202,35 +142,13 @@ class LogEntryAdmin(admin.ModelAdmin): else: ct = obj.content_type try: - link = format_html( - '{0}', - obj.object_repr, - reverse( - "admin:%s_%s_change" % (ct.app_label, ct.model), - args=(obj.object_id,), - ), - ) + link = format_html('{0}', obj.object_repr, + reverse('admin:%s_%s_change' % (ct.app_label, ct.model), args=(obj.object_id,))) except NoReverseMatch: link = obj.object_repr return link - - object_link.admin_order_field = "object_repr" - object_link.short_description = _("object") - - def diff_link(self, obj): - if obj.is_deletion(): - return None - ct = obj.content_type - try: - url = reverse( - "admin:%s_%s_history" % (ct.app_label, ct.model), args=(obj.object_id,) - ) - link = format_html('{0}', _("Diff"), url) - except NoReverseMatch: - link = None - return link - - diff_link.short_description = _("diff") + object_link.admin_order_field = 'object_repr' + object_link.short_description = _('object') def queryset(self, request): - return super().queryset(request).prefetch_related("content_type") + return super().queryset(request).prefetch_related('content_type') diff --git a/judge/admin/organization.py b/judge/admin/organization.py index 4ac7708..f9d78d4 100644 --- a/judge/admin/organization.py +++ b/judge/admin/organization.py @@ -6,94 +6,61 @@ from django.utils.translation import gettext, gettext_lazy as _ from reversion.admin import VersionAdmin from judge.models import Organization -from judge.widgets import ( - AdminHeavySelect2MultipleWidget, - AdminHeavySelect2Widget, - HeavyPreviewAdminPageDownWidget, -) +from judge.widgets import AdminHeavySelect2MultipleWidget, AdminHeavySelect2Widget, HeavyPreviewAdminPageDownWidget class OrganizationForm(ModelForm): class Meta: widgets = { - "admins": AdminHeavySelect2MultipleWidget(data_view="profile_select2"), - "registrant": AdminHeavySelect2Widget(data_view="profile_select2"), + 'admins': AdminHeavySelect2MultipleWidget(data_view='profile_select2'), + 'registrant': AdminHeavySelect2Widget(data_view='profile_select2'), } if HeavyPreviewAdminPageDownWidget is not None: - widgets["about"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("organization_preview") - ) + widgets['about'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('organization_preview')) class OrganizationAdmin(VersionAdmin): - readonly_fields = ("creation_date",) - fields = ( - "name", - "slug", - "short_name", - "is_open", - "about", - "slots", - "registrant", - "creation_date", - "admins", - ) - list_display = ( - "name", - "short_name", - "is_open", - "creation_date", - "registrant", - "show_public", - ) - search_fields = ("name", "short_name", "registrant__user__username") - prepopulated_fields = {"slug": ("name",)} + readonly_fields = ('creation_date',) + fields = ('name', 'slug', 'short_name', 'is_open', 'about', 'logo_override_image', 'slots', 'registrant', + 'creation_date', 'admins') + list_display = ('name', 'short_name', 'is_open', 'slots', 'registrant', 'show_public') + prepopulated_fields = {'slug': ('name',)} actions_on_top = True actions_on_bottom = True form = OrganizationForm - ordering = ["-creation_date"] def show_public(self, obj): - return format_html( - '{1}', - obj.get_absolute_url(), - gettext("View on site"), - ) + return format_html('{1}', + obj.get_absolute_url(), gettext('View on site')) - show_public.short_description = "" + show_public.short_description = '' def get_readonly_fields(self, request, obj=None): fields = self.readonly_fields - if not request.user.has_perm("judge.organization_admin"): - return fields + ("registrant", "admins", "is_open", "slots") + if not request.user.has_perm('judge.organization_admin'): + return fields + ('registrant', 'admins', 'is_open', 'slots') return fields def get_queryset(self, request): queryset = Organization.objects.all() - if request.user.has_perm("judge.edit_all_organization"): + if request.user.has_perm('judge.edit_all_organization'): return queryset else: return queryset.filter(admins=request.profile.id) def has_change_permission(self, request, obj=None): - if not request.user.has_perm("judge.change_organization"): + if not request.user.has_perm('judge.change_organization'): return False - if request.user.has_perm("judge.edit_all_organization") or obj is None: + if request.user.has_perm('judge.edit_all_organization') or obj is None: return True return obj.admins.filter(id=request.profile.id).exists() - def save_related(self, request, form, formsets, change): - super().save_related(request, form, formsets, change) - obj = form.instance - obj.members.add(*obj.admins.all()) - class OrganizationRequestAdmin(admin.ModelAdmin): - list_display = ("username", "organization", "state", "time") - readonly_fields = ("user", "organization") + list_display = ('username', 'organization', 'state', 'time') + readonly_fields = ('user', 'organization') def username(self, obj): return obj.user.user.username - - username.short_description = _("username") - username.admin_order_field = "user__user__username" + username.short_description = _('username') + username.admin_order_field = 'user__user__username' diff --git a/judge/admin/problem.py b/judge/admin/problem.py index bf590db..164caa1 100644 --- a/judge/admin/problem.py +++ b/judge/admin/problem.py @@ -1,115 +1,54 @@ from operator import attrgetter from django import forms -from django.contrib import admin, messages -from django.db import transaction, IntegrityError -from django.db.models import Q, Avg, Count -from django.db.models.aggregates import StdDev -from django.forms import ModelForm, TextInput +from django.contrib import admin +from django.db import transaction +from django.db.models import Q +from django.forms import ModelForm from django.urls import reverse_lazy from django.utils.html import format_html from django.utils.translation import gettext, gettext_lazy as _, ungettext -from django_ace import AceWidget -from django.utils import timezone -from django.core.exceptions import ValidationError - from reversion.admin import VersionAdmin -from reversion_compare.admin import CompareVersionAdmin - -from judge.models import ( - LanguageLimit, - LanguageTemplate, - Problem, - ProblemTranslation, - Profile, - Solution, - Notification, -) -from judge.models.notification import make_notification -from judge.widgets import ( - AdminHeavySelect2MultipleWidget, - AdminSelect2MultipleWidget, - AdminSelect2Widget, - CheckboxSelectMultipleWithSelectAll, - HeavyPreviewAdminPageDownWidget, -) -from judge.utils.problems import user_editable_ids, user_tester_ids - -MEMORY_UNITS = (("KB", "KB"), ("MB", "MB")) +from judge.models import LanguageLimit, Problem, ProblemClarification, ProblemTranslation, Profile, Solution +from judge.widgets import AdminHeavySelect2MultipleWidget, AdminSelect2MultipleWidget, AdminSelect2Widget, \ + CheckboxSelectMultipleWithSelectAll, HeavyPreviewAdminPageDownWidget, HeavyPreviewPageDownWidget class ProblemForm(ModelForm): - change_message = forms.CharField( - max_length=256, label="Edit reason", required=False - ) - memory_unit = forms.ChoiceField(choices=MEMORY_UNITS) + change_message = forms.CharField(max_length=256, label='Edit reason', required=False) def __init__(self, *args, **kwargs): super(ProblemForm, self).__init__(*args, **kwargs) - self.fields["authors"].widget.can_add_related = False - self.fields["curators"].widget.can_add_related = False - self.fields["testers"].widget.can_add_related = False - self.fields["banned_users"].widget.can_add_related = False - self.fields["change_message"].widget.attrs.update( - { - "placeholder": gettext("Describe the changes you made (optional)"), - } - ) - - def clean_code(self): - code = self.cleaned_data.get("code") - if self.instance.pk: - return code - - if Problem.objects.filter(code=code).exists(): - raise ValidationError(_("A problem with this code already exists.")) - - return code - - def clean(self): - memory_unit = self.cleaned_data.get("memory_unit", "KB") - if memory_unit == "MB": - self.cleaned_data["memory_limit"] *= 1024 - date = self.cleaned_data.get("date") - if not date or date > timezone.now(): - self.cleaned_data["date"] = timezone.now() - return self.cleaned_data + self.fields['authors'].widget.can_add_related = False + self.fields['curators'].widget.can_add_related = False + self.fields['testers'].widget.can_add_related = False + self.fields['banned_users'].widget.can_add_related = False + self.fields['change_message'].widget.attrs.update({ + 'placeholder': gettext('Describe the changes you made (optional)'), + }) class Meta: widgets = { - "authors": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "curators": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "testers": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "banned_users": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "organizations": AdminHeavySelect2MultipleWidget( - data_view="organization_select2", attrs={"style": "width: 100%"} - ), - "types": AdminSelect2MultipleWidget, - "group": AdminSelect2Widget, - "memory_limit": TextInput(attrs={"size": "20"}), + 'authors': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}), + 'curators': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}), + 'testers': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}), + 'banned_users': AdminHeavySelect2MultipleWidget(data_view='profile_select2', + attrs={'style': 'width: 100%'}), + 'organizations': AdminHeavySelect2MultipleWidget(data_view='organization_select2', + attrs={'style': 'width: 100%'}), + 'types': AdminSelect2MultipleWidget, + 'group': AdminSelect2Widget, } if HeavyPreviewAdminPageDownWidget is not None: - widgets["description"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("problem_preview") - ) + widgets['description'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('problem_preview')) class ProblemCreatorListFilter(admin.SimpleListFilter): - title = parameter_name = "creator" + title = parameter_name = 'creator' def lookups(self, request, model_admin): - queryset = Profile.objects.exclude(authored_problems=None).values_list( - "user__username", flat=True - ) + queryset = Profile.objects.exclude(authored_problems=None).values_list('user__username', flat=True) return [(name, name) for name in queryset] def queryset(self, request, queryset): @@ -119,68 +58,46 @@ class ProblemCreatorListFilter(admin.SimpleListFilter): class LanguageLimitInlineForm(ModelForm): - memory_unit = forms.ChoiceField(choices=MEMORY_UNITS, label=_("Memory unit")) - class Meta: - widgets = { - "language": AdminSelect2Widget, - "memory_limit": TextInput(attrs={"size": "10"}), - } - - def clean(self): - if not self.cleaned_data.get("language"): - self.cleaned_data["DELETE"] = True - if ( - self.cleaned_data.get("memory_limit") - and self.cleaned_data.get("memory_unit") == "MB" - ): - self.cleaned_data["memory_limit"] *= 1024 - return self.cleaned_data + widgets = {'language': AdminSelect2Widget} class LanguageLimitInline(admin.TabularInline): model = LanguageLimit - fields = ("language", "time_limit", "memory_limit", "memory_unit") + fields = ('language', 'time_limit', 'memory_limit') form = LanguageLimitInlineForm - extra = 0 -class LanguageTemplateInlineForm(ModelForm): +class ProblemClarificationForm(ModelForm): class Meta: - widgets = { - "language": AdminSelect2Widget, - "source": AceWidget(width="600px", height="200px", toolbar=False), - } + if HeavyPreviewPageDownWidget is not None: + widgets = {'description': HeavyPreviewPageDownWidget(preview=reverse_lazy('comment_preview'))} -class LanguageTemplateInline(admin.TabularInline): - model = LanguageTemplate - fields = ("language", "source") - form = LanguageTemplateInlineForm +class ProblemClarificationInline(admin.StackedInline): + model = ProblemClarification + fields = ('description',) + form = ProblemClarificationForm extra = 0 class ProblemSolutionForm(ModelForm): def __init__(self, *args, **kwargs): super(ProblemSolutionForm, self).__init__(*args, **kwargs) - self.fields["authors"].widget.can_add_related = False + self.fields['authors'].widget.can_add_related = False class Meta: widgets = { - "authors": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), + 'authors': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}), } if HeavyPreviewAdminPageDownWidget is not None: - widgets["content"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("solution_preview") - ) + widgets['content'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('solution_preview')) class ProblemSolutionInline(admin.StackedInline): model = Solution - fields = ("is_public", "publish_on", "authors", "content") + fields = ('is_public', 'publish_on', 'authors', 'content') form = ProblemSolutionForm extra = 0 @@ -188,300 +105,134 @@ class ProblemSolutionInline(admin.StackedInline): class ProblemTranslationForm(ModelForm): class Meta: if HeavyPreviewAdminPageDownWidget is not None: - widgets = { - "description": HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("problem_preview") - ) - } + widgets = {'description': HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('problem_preview'))} class ProblemTranslationInline(admin.StackedInline): model = ProblemTranslation - fields = ("language", "name", "description") + fields = ('language', 'name', 'description') form = ProblemTranslationForm extra = 0 -class ProblemAdmin(CompareVersionAdmin): +class ProblemAdmin(VersionAdmin): fieldsets = ( - ( - None, - { - "fields": ( - "code", - "name", - "is_public", - "organizations", - "date", - "authors", - "curators", - "testers", - "description", - "pdf_description", - "license", - ), - }, - ), - ( - _("Social Media"), - {"classes": ("collapse",), "fields": ("og_image", "summary")}, - ), - (_("Taxonomy"), {"fields": ("types", "group")}), - (_("Points"), {"fields": (("points", "partial"), "short_circuit")}), - (_("Limits"), {"fields": ("time_limit", ("memory_limit", "memory_unit"))}), - (_("Language"), {"fields": ("allowed_languages",)}), - (_("Justice"), {"fields": ("banned_users",)}), - (_("History"), {"fields": ("change_message",)}), + (None, { + 'fields': ( + 'code', 'name', 'is_public', 'is_manually_managed', 'date', 'authors', 'curators', 'testers', + 'is_organization_private', 'organizations', 'description', 'license', + ), + }), + (_('Social Media'), {'classes': ('collapse',), 'fields': ('og_image', 'summary')}), + (_('Taxonomy'), {'fields': ('types', 'group')}), + (_('Points'), {'fields': (('points', 'partial'), 'short_circuit')}), + (_('Limits'), {'fields': ('time_limit', 'memory_limit')}), + (_('Language'), {'fields': ('allowed_languages',)}), + (_('Justice'), {'fields': ('banned_users',)}), + (_('History'), {'fields': ('change_message',)}), ) - list_display = [ - "code", - "name", - "show_authors", - "date", - "points", - "is_public", - "show_public", - ] - ordering = ["-date"] - search_fields = ( - "code", - "name", - "authors__user__username", - "curators__user__username", - ) - inlines = [ - LanguageLimitInline, - LanguageTemplateInline, - ProblemSolutionInline, - ProblemTranslationInline, - ] + list_display = ['code', 'name', 'show_authors', 'points', 'is_public', 'show_public'] + ordering = ['code'] + search_fields = ('code', 'name', 'authors__user__username', 'curators__user__username') + inlines = [LanguageLimitInline, ProblemClarificationInline, ProblemSolutionInline, ProblemTranslationInline] list_max_show_all = 1000 actions_on_top = True actions_on_bottom = True - list_filter = ("is_public", ProblemCreatorListFilter) + list_filter = ('is_public', ProblemCreatorListFilter) form = ProblemForm - date_hierarchy = "date" + date_hierarchy = 'date' def get_actions(self, request): actions = super(ProblemAdmin, self).get_actions(request) - if request.user.has_perm("judge.change_public_visibility"): - func, name, desc = self.get_action("make_public") + if request.user.has_perm('judge.change_public_visibility'): + func, name, desc = self.get_action('make_public') actions[name] = (func, name, desc) - func, name, desc = self.get_action("make_private") + func, name, desc = self.get_action('make_private') actions[name] = (func, name, desc) return actions def get_readonly_fields(self, request, obj=None): fields = self.readonly_fields - if not request.user.has_perm("judge.change_public_visibility"): - fields += ("is_public",) + if not request.user.has_perm('judge.change_public_visibility'): + fields += ('is_public',) + if not request.user.has_perm('judge.change_manually_managed'): + fields += ('is_manually_managed',) return fields def show_authors(self, obj): - return ", ".join(map(attrgetter("user.username"), obj.authors.all())) + return ', '.join(map(attrgetter('user.username'), obj.authors.all())) - show_authors.short_description = _("Authors") + show_authors.short_description = _('Authors') def show_public(self, obj): - return format_html( - '{0}', gettext("View on site"), obj.get_absolute_url() - ) + return format_html('{0}', gettext('View on site'), obj.get_absolute_url()) - show_public.short_description = "" + show_public.short_description = '' def _rescore(self, request, problem_id): from judge.tasks import rescore_problem - transaction.on_commit(rescore_problem.s(problem_id).delay) def make_public(self, request, queryset): count = queryset.update(is_public=True) - for problem_id in queryset.values_list("id", flat=True): + for problem_id in queryset.values_list('id', flat=True): self._rescore(request, problem_id) - self.message_user( - request, - ungettext( - "%d problem successfully marked as public.", - "%d problems successfully marked as public.", - count, - ) - % count, - ) + self.message_user(request, ungettext('%d problem successfully marked as public.', + '%d problems successfully marked as public.', + count) % count) - make_public.short_description = _("Mark problems as public") + make_public.short_description = _('Mark problems as public') def make_private(self, request, queryset): count = queryset.update(is_public=False) - for problem_id in queryset.values_list("id", flat=True): + for problem_id in queryset.values_list('id', flat=True): self._rescore(request, problem_id) - self.message_user( - request, - ungettext( - "%d problem successfully marked as private.", - "%d problems successfully marked as private.", - count, - ) - % count, - ) + self.message_user(request, ungettext('%d problem successfully marked as private.', + '%d problems successfully marked as private.', + count) % count) - make_private.short_description = _("Mark problems as private") + make_private.short_description = _('Mark problems as private') def get_queryset(self, request): - queryset = Problem.objects.prefetch_related("authors__user") - if request.user.has_perm("judge.edit_all_problem"): + queryset = Problem.objects.prefetch_related('authors__user') + if request.user.has_perm('judge.edit_all_problem'): return queryset access = Q() - if request.user.has_perm("judge.edit_public_problem"): + if request.user.has_perm('judge.edit_public_problem'): access |= Q(is_public=True) - if request.user.has_perm("judge.edit_own_problem"): - access |= Q(authors__id=request.profile.id) | Q( - curators__id=request.profile.id - ) + if request.user.has_perm('judge.edit_own_problem'): + access |= Q(authors__id=request.profile.id) | Q(curators__id=request.profile.id) return queryset.filter(access).distinct() if access else queryset.none() def has_change_permission(self, request, obj=None): - if request.user.has_perm("judge.edit_all_problem") or obj is None: + if request.user.has_perm('judge.edit_all_problem') or obj is None: return True - if request.user.has_perm("judge.edit_public_problem") and obj.is_public: + if request.user.has_perm('judge.edit_public_problem') and obj.is_public: return True - if not request.user.has_perm("judge.edit_own_problem"): + if not request.user.has_perm('judge.edit_own_problem'): return False return obj.is_editor(request.profile) def formfield_for_manytomany(self, db_field, request=None, **kwargs): - if db_field.name == "allowed_languages": - kwargs["widget"] = CheckboxSelectMultipleWithSelectAll() - return super(ProblemAdmin, self).formfield_for_manytomany( - db_field, request, **kwargs - ) + if db_field.name == 'allowed_languages': + kwargs['widget'] = CheckboxSelectMultipleWithSelectAll() + return super(ProblemAdmin, self).formfield_for_manytomany(db_field, request, **kwargs) def get_form(self, *args, **kwargs): form = super(ProblemAdmin, self).get_form(*args, **kwargs) - form.base_fields["authors"].queryset = Profile.objects.all() + form.base_fields['authors'].queryset = Profile.objects.all() return form def save_model(self, request, obj, form, change): - form.changed_data.remove("memory_unit") super().save_model(request, obj, form, change) - if form.changed_data and any( - f in form.changed_data for f in ("is_public", "points", "partial") - ): + if form.changed_data and any(f in form.changed_data for f in ('is_public', 'points', 'partial')): self._rescore(request, obj.id) - def save_related(self, request, form, formsets, change): - editors = set() - testers = set() - if "curators" in form.changed_data or "authors" in form.changed_data: - editors = set(form.instance.editor_ids) - if "testers" in form.changed_data: - testers = set(form.instance.tester_ids) - - super().save_related(request, form, formsets, change) - obj = form.instance - obj.curators.add(request.profile) - - if "curators" in form.changed_data or "authors" in form.changed_data: - del obj.editor_ids - editors = editors.union(set(obj.editor_ids)) - if "testers" in form.changed_data: - del obj.tester_ids - testers = testers.union(set(obj.tester_ids)) - - for editor in editors: - user_editable_ids.dirty(editor) - for tester in testers: - user_tester_ids.dirty(tester) - - # Create notification - if "is_public" in form.changed_data or "organizations" in form.changed_data: - users = set(obj.authors.all()) - users = users.union(users, set(obj.curators.all())) - orgs = [] - if obj.organizations.count() > 0: - for org in obj.organizations.all(): - users = users.union(users, set(org.admins.all())) - orgs.append(org.name) - else: - admins = Profile.objects.filter(user__is_superuser=True).all() - users = users.union(users, admins) - link = reverse_lazy("admin:judge_problem_change", args=(obj.id,)) - html = f'{obj.name}' - category = "Problem public: " + str(obj.is_public) - if orgs: - category += " (" + ", ".join(orgs) + ")" - make_notification(users, category, html, request.profile) - def construct_change_message(self, request, form, *args, **kwargs): - if form.cleaned_data.get("change_message"): - return form.cleaned_data["change_message"] - return super(ProblemAdmin, self).construct_change_message( - request, form, *args, **kwargs - ) - - -class ProblemPointsVoteAdmin(admin.ModelAdmin): - list_display = ( - "vote_points", - "voter", - "voter_rating", - "voter_point", - "problem_name", - "problem_code", - "problem_points", - ) - search_fields = ("voter__user__username", "problem__code", "problem__name") - readonly_fields = ( - "voter", - "problem", - "problem_code", - "problem_points", - "voter_rating", - "voter_point", - ) - - def has_change_permission(self, request, obj=None): - if obj is None: - return request.user.has_perm("judge.edit_own_problem") - return obj.problem.is_editable_by(request.user) - - def lookup_allowed(self, key, value): - return True - - def problem_code(self, obj): - return obj.problem.code - - problem_code.short_description = _("Problem code") - problem_code.admin_order_field = "problem__code" - - def problem_points(self, obj): - return obj.problem.points - - problem_points.short_description = _("Points") - problem_points.admin_order_field = "problem__points" - - def problem_name(self, obj): - return obj.problem.name - - problem_name.short_description = _("Problem name") - problem_name.admin_order_field = "problem__name" - - def voter_rating(self, obj): - return obj.voter.rating - - voter_rating.short_description = _("Voter rating") - voter_rating.admin_order_field = "voter__rating" - - def voter_point(self, obj): - return round(obj.voter.performance_points) - - voter_point.short_description = _("Voter point") - voter_point.admin_order_field = "voter__performance_points" - - def vote_points(self, obj): - return obj.points - - vote_points.short_description = _("Vote") + if form.cleaned_data.get('change_message'): + return form.cleaned_data['change_message'] + return super(ProblemAdmin, self).construct_change_message(request, form, *args, **kwargs) diff --git a/judge/admin/profile.py b/judge/admin/profile.py index 4c380c9..e5fbf46 100644 --- a/judge/admin/profile.py +++ b/judge/admin/profile.py @@ -1,58 +1,41 @@ from django.contrib import admin -from django.forms import ModelForm, CharField, TextInput +from django.forms import ModelForm from django.utils.html import format_html from django.utils.translation import gettext, gettext_lazy as _, ungettext -from django.contrib.auth.admin import UserAdmin as OldUserAdmin -from django.core.exceptions import ValidationError -from django.contrib.auth.forms import UserChangeForm - -from django_ace import AceWidget - -from judge.models import Profile, ProfileInfo -from judge.widgets import AdminPagedownWidget, AdminSelect2Widget - from reversion.admin import VersionAdmin -import re +from django_ace import AceWidget +from judge.models import Profile +from judge.widgets import AdminPagedownWidget, AdminSelect2Widget class ProfileForm(ModelForm): def __init__(self, *args, **kwargs): super(ProfileForm, self).__init__(*args, **kwargs) - if "current_contest" in self.base_fields: + if 'current_contest' in self.base_fields: # form.fields['current_contest'] does not exist when the user has only view permission on the model. - self.fields[ - "current_contest" - ].queryset = self.instance.contest_history.select_related("contest").only( - "contest__name", "user_id", "virtual" - ) - self.fields["current_contest"].label_from_instance = ( - lambda obj: "%s v%d" % (obj.contest.name, obj.virtual) - if obj.virtual - else obj.contest.name - ) + self.fields['current_contest'].queryset = self.instance.contest_history.select_related('contest') \ + .only('contest__name', 'user_id', 'virtual') + self.fields['current_contest'].label_from_instance = \ + lambda obj: '%s v%d' % (obj.contest.name, obj.virtual) if obj.virtual else obj.contest.name class Meta: widgets = { - "timezone": AdminSelect2Widget, - "language": AdminSelect2Widget, - "ace_theme": AdminSelect2Widget, - "current_contest": AdminSelect2Widget, + 'timezone': AdminSelect2Widget, + 'language': AdminSelect2Widget, + 'ace_theme': AdminSelect2Widget, + 'current_contest': AdminSelect2Widget, } if AdminPagedownWidget is not None: - widgets["about"] = AdminPagedownWidget + widgets['about'] = AdminPagedownWidget class TimezoneFilter(admin.SimpleListFilter): - title = _("timezone") - parameter_name = "timezone" + title = _('timezone') + parameter_name = 'timezone' def lookups(self, request, model_admin): - return ( - Profile.objects.values_list("timezone", "timezone") - .distinct() - .order_by("timezone") - ) + return Profile.objects.values_list('timezone', 'timezone').distinct().order_by('timezone') def queryset(self, request, queryset): if self.value() is None: @@ -60,168 +43,76 @@ class TimezoneFilter(admin.SimpleListFilter): return queryset.filter(timezone=self.value()) -class ProfileInfoInline(admin.StackedInline): - model = ProfileInfo - can_delete = False - verbose_name_plural = "profile info" - fk_name = "profile" - - class ProfileAdmin(VersionAdmin): - fields = ( - "user", - "display_rank", - "about", - "organizations", - "timezone", - "language", - "ace_theme", - "last_access", - "ip", - "mute", - "is_unlisted", - "notes", - "is_totp_enabled", - "current_contest", - ) - readonly_fields = ("user",) - list_display = ( - "admin_user_admin", - "email", - "is_totp_enabled", - "timezone_full", - "date_joined", - "last_access", - "ip", - "show_public", - ) - ordering = ("user__username",) - search_fields = ("user__username", "ip", "user__email") - list_filter = ("language", TimezoneFilter) - actions = ("recalculate_points",) + fields = ('user', 'display_rank', 'about', 'organizations', 'timezone', 'language', 'ace_theme', + 'math_engine', 'last_access', 'ip', 'mute', 'is_unlisted', 'notes', 'is_totp_enabled', 'user_script', + 'current_contest') + readonly_fields = ('user',) + list_display = ('admin_user_admin', 'email', 'is_totp_enabled', 'timezone_full', + 'date_joined', 'last_access', 'ip', 'show_public') + ordering = ('user__username',) + search_fields = ('user__username', 'ip', 'user__email') + list_filter = ('language', TimezoneFilter) + actions = ('recalculate_points',) actions_on_top = True actions_on_bottom = True form = ProfileForm - inlines = (ProfileInfoInline,) def get_queryset(self, request): - return super(ProfileAdmin, self).get_queryset(request).select_related("user") + return super(ProfileAdmin, self).get_queryset(request).select_related('user') def get_fields(self, request, obj=None): - if request.user.has_perm("judge.totp"): + if request.user.has_perm('judge.totp'): fields = list(self.fields) - fields.insert(fields.index("is_totp_enabled") + 1, "totp_key") + fields.insert(fields.index('is_totp_enabled') + 1, 'totp_key') return tuple(fields) else: return self.fields def get_readonly_fields(self, request, obj=None): fields = self.readonly_fields - if not request.user.has_perm("judge.totp"): - fields += ("is_totp_enabled",) + if not request.user.has_perm('judge.totp'): + fields += ('is_totp_enabled',) return fields def show_public(self, obj): - return format_html( - '{1}', - obj.get_absolute_url(), - gettext("View on site"), - ) - - show_public.short_description = "" + return format_html('{1}', + obj.get_absolute_url(), gettext('View on site')) + show_public.short_description = '' def admin_user_admin(self, obj): return obj.username - - admin_user_admin.admin_order_field = "user__username" - admin_user_admin.short_description = _("User") + admin_user_admin.admin_order_field = 'user__username' + admin_user_admin.short_description = _('User') def email(self, obj): - return obj.email - - email.admin_order_field = "user__email" - email.short_description = _("Email") + return obj.user.email + email.admin_order_field = 'user__email' + email.short_description = _('Email') def timezone_full(self, obj): return obj.timezone - - timezone_full.admin_order_field = "timezone" - timezone_full.short_description = _("Timezone") + timezone_full.admin_order_field = 'timezone' + timezone_full.short_description = _('Timezone') def date_joined(self, obj): return obj.user.date_joined - - date_joined.admin_order_field = "user__date_joined" - date_joined.short_description = _("date joined") + date_joined.admin_order_field = 'user__date_joined' + date_joined.short_description = _('date joined') def recalculate_points(self, request, queryset): count = 0 for profile in queryset: profile.calculate_points() count += 1 - self.message_user( - request, - ungettext( - "%d user have scores recalculated.", - "%d users have scores recalculated.", - count, - ) - % count, - ) + self.message_user(request, ungettext('%d user have scores recalculated.', + '%d users have scores recalculated.', + count) % count) + recalculate_points.short_description = _('Recalculate scores') - recalculate_points.short_description = _("Recalculate scores") - - -class UserForm(UserChangeForm): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields["username"].help_text = _( - "Username can only contain letters, digits, and underscores." - ) - - def clean_username(self): - username = self.cleaned_data.get("username") - if not re.match(r"^\w+$", username): - raise ValidationError( - _("Username can only contain letters, digits, and underscores.") - ) - return username - - -class UserAdmin(OldUserAdmin): - # Customize the fieldsets for adding and editing users - form = UserForm - fieldsets = ( - (None, {"fields": ("username", "password")}), - ("Personal Info", {"fields": ("first_name", "last_name", "email")}), - ( - "Permissions", - { - "fields": ( - "is_active", - "is_staff", - "is_superuser", - "groups", - "user_permissions", - ) - }, - ), - ("Important dates", {"fields": ("last_login", "date_joined")}), - ) - - readonly_fields = ("last_login", "date_joined") - - def get_readonly_fields(self, request, obj=None): - fields = self.readonly_fields - if not request.user.is_superuser: - fields += ( - "is_staff", - "is_active", - "is_superuser", - "groups", - "user_permissions", - ) - return fields - - def has_add_permission(self, request): - return False + def get_form(self, request, obj=None, **kwargs): + form = super(ProfileAdmin, self).get_form(request, obj, **kwargs) + if 'user_script' in form.base_fields: + # form.base_fields['user_script'] does not exist when the user has only view permission on the model. + form.base_fields['user_script'].widget = AceWidget('javascript', request.profile.ace_theme) + return form diff --git a/judge/admin/runtime.py b/judge/admin/runtime.py index 791083a..e9f44ec 100644 --- a/judge/admin/runtime.py +++ b/judge/admin/runtime.py @@ -16,63 +16,41 @@ from judge.widgets import AdminHeavySelect2MultipleWidget, AdminPagedownWidget class LanguageForm(ModelForm): problems = ModelMultipleChoiceField( - label=_("Disallowed problems"), + label=_('Disallowed problems'), queryset=Problem.objects.all(), required=False, - help_text=_("These problems are NOT allowed to be submitted in this language"), - widget=AdminHeavySelect2MultipleWidget(data_view="problem_select2"), - ) + help_text=_('These problems are NOT allowed to be submitted in this language'), + widget=AdminHeavySelect2MultipleWidget(data_view='problem_select2')) class Meta: if AdminPagedownWidget is not None: - widgets = {"description": AdminPagedownWidget} + widgets = {'description': AdminPagedownWidget} class LanguageAdmin(VersionAdmin): - fields = ( - "key", - "name", - "short_name", - "common_name", - "ace", - "pygments", - "info", - "description", - "template", - "problems", - ) - list_display = ("key", "name", "common_name", "info") + fields = ('key', 'name', 'short_name', 'common_name', 'ace', 'pygments', 'info', 'description', + 'template', 'problems') + list_display = ('key', 'name', 'common_name', 'info') form = LanguageForm def save_model(self, request, obj, form, change): super(LanguageAdmin, self).save_model(request, obj, form, change) - obj.problem_set.set( - Problem.objects.exclude(id__in=form.cleaned_data["problems"].values("id")) - ) + obj.problem_set.set(Problem.objects.exclude(id__in=form.cleaned_data['problems'].values('id'))) def get_form(self, request, obj=None, **kwargs): - self.form.base_fields["problems"].initial = ( - Problem.objects.exclude(id__in=obj.problem_set.values("id")).values_list( - "pk", flat=True - ) - if obj - else [] - ) + self.form.base_fields['problems'].initial = \ + Problem.objects.exclude(id__in=obj.problem_set.values('id')).values_list('pk', flat=True) if obj else [] form = super(LanguageAdmin, self).get_form(request, obj, **kwargs) if obj is not None: - form.base_fields["template"].widget = AceWidget( - obj.ace, request.profile.ace_theme - ) + form.base_fields['template'].widget = AceWidget(obj.ace, request.profile.ace_theme) return form class GenerateKeyTextInput(TextInput): def render(self, name, value, attrs=None, renderer=None): text = super(TextInput, self).render(name, value, attrs) - return mark_safe( - text - + format_html( - """\ + return mark_safe(text + format_html( + '''\ Regenerate -""", - name, - ) - ) +''', name)) class JudgeAdminForm(ModelForm): class Meta: - widgets = {"auth_key": GenerateKeyTextInput} + widgets = {'auth_key': GenerateKeyTextInput} if AdminPagedownWidget is not None: - widgets["description"] = AdminPagedownWidget + widgets['description'] = AdminPagedownWidget class JudgeAdmin(VersionAdmin): form = JudgeAdminForm - readonly_fields = ( - "created", - "online", - "start_time", - "ping", - "load", - "last_ip", - "runtimes", - "problems", - ) + readonly_fields = ('created', 'online', 'start_time', 'ping', 'load', 'last_ip', 'runtimes', 'problems') fieldsets = ( - (None, {"fields": ("name", "auth_key", "is_blocked")}), - (_("Description"), {"fields": ("description",)}), - ( - _("Information"), - {"fields": ("created", "online", "last_ip", "start_time", "ping", "load")}, - ), - (_("Capabilities"), {"fields": ("runtimes", "problems")}), + (None, {'fields': ('name', 'auth_key', 'is_blocked')}), + (_('Description'), {'fields': ('description',)}), + (_('Information'), {'fields': ('created', 'online', 'last_ip', 'start_time', 'ping', 'load')}), + (_('Capabilities'), {'fields': ('runtimes', 'problems')}), ) - list_display = ("name", "online", "start_time", "ping", "load", "last_ip") - ordering = ["-online", "name"] + list_display = ('name', 'online', 'start_time', 'ping', 'load', 'last_ip') + ordering = ['-online', 'name'] def get_urls(self): - return [ - url( - r"^(\d+)/disconnect/$", - self.disconnect_view, - name="judge_judge_disconnect", - ), - url( - r"^(\d+)/terminate/$", self.terminate_view, name="judge_judge_terminate" - ), - ] + super(JudgeAdmin, self).get_urls() + return ([url(r'^(\d+)/disconnect/$', self.disconnect_view, name='judge_judge_disconnect'), + url(r'^(\d+)/terminate/$', self.terminate_view, name='judge_judge_terminate')] + + super(JudgeAdmin, self).get_urls()) def disconnect_judge(self, id, force=False): judge = get_object_or_404(Judge, id=id) judge.disconnect(force=force) - return HttpResponseRedirect(reverse("admin:judge_judge_changelist")) + return HttpResponseRedirect(reverse('admin:judge_judge_changelist')) def disconnect_view(self, request, id): return self.disconnect_judge(id) @@ -149,7 +105,7 @@ class JudgeAdmin(VersionAdmin): def get_readonly_fields(self, request, obj=None): if obj is not None and obj.online: - return self.readonly_fields + ("name",) + return self.readonly_fields + ('name',) return self.readonly_fields def has_delete_permission(self, request, obj=None): @@ -160,5 +116,5 @@ class JudgeAdmin(VersionAdmin): if AdminPagedownWidget is not None: formfield_overrides = { - TextField: {"widget": AdminPagedownWidget}, + TextField: {'widget': AdminPagedownWidget}, } diff --git a/judge/admin/submission.py b/judge/admin/submission.py index 7218bb2..2689a76 100644 --- a/judge/admin/submission.py +++ b/judge/admin/submission.py @@ -13,365 +13,239 @@ from django.utils.html import format_html from django.utils.translation import gettext, gettext_lazy as _, pgettext, ungettext from django_ace import AceWidget -from judge.models import ( - ContestParticipation, - ContestProblem, - ContestSubmission, - Profile, - Submission, - SubmissionSource, - SubmissionTestCase, -) +from judge.models import ContestParticipation, ContestProblem, ContestSubmission, Profile, Submission, \ + SubmissionSource, SubmissionTestCase from judge.utils.raw_sql import use_straight_join class SubmissionStatusFilter(admin.SimpleListFilter): - parameter_name = title = "status" - __lookups = ( - ("None", _("None")), - ("NotDone", _("Not done")), - ("EX", _("Exceptional")), - ) + Submission.STATUS + parameter_name = title = 'status' + __lookups = (('None', _('None')), ('NotDone', _('Not done')), ('EX', _('Exceptional'))) + Submission.STATUS __handles = set(map(itemgetter(0), Submission.STATUS)) def lookups(self, request, model_admin): return self.__lookups def queryset(self, request, queryset): - if self.value() == "None": + if self.value() == 'None': return queryset.filter(status=None) - elif self.value() == "NotDone": - return queryset.exclude(status__in=["D", "IE", "CE", "AB"]) - elif self.value() == "EX": - return queryset.exclude(status__in=["D", "CE", "G", "AB"]) + elif self.value() == 'NotDone': + return queryset.exclude(status__in=['D', 'IE', 'CE', 'AB']) + elif self.value() == 'EX': + return queryset.exclude(status__in=['D', 'CE', 'G', 'AB']) elif self.value() in self.__handles: return queryset.filter(status=self.value()) class SubmissionResultFilter(admin.SimpleListFilter): - parameter_name = title = "result" - __lookups = (("None", _("None")), ("BAD", _("Unaccepted"))) + Submission.RESULT + parameter_name = title = 'result' + __lookups = (('None', _('None')), ('BAD', _('Unaccepted'))) + Submission.RESULT __handles = set(map(itemgetter(0), Submission.RESULT)) def lookups(self, request, model_admin): return self.__lookups def queryset(self, request, queryset): - if self.value() == "None": + if self.value() == 'None': return queryset.filter(result=None) - elif self.value() == "BAD": - return queryset.exclude(result="AC") + elif self.value() == 'BAD': + return queryset.exclude(result='AC') elif self.value() in self.__handles: return queryset.filter(result=self.value()) class SubmissionTestCaseInline(admin.TabularInline): - fields = ("case", "batch", "status", "time", "memory", "points", "total") - readonly_fields = ("case", "batch", "total") + fields = ('case', 'batch', 'status', 'time', 'memory', 'points', 'total') + readonly_fields = ('case', 'batch', 'total') model = SubmissionTestCase can_delete = False max_num = 0 class ContestSubmissionInline(admin.StackedInline): - fields = ("problem", "participation", "points") + fields = ('problem', 'participation', 'points') model = ContestSubmission def get_formset(self, request, obj=None, **kwargs): - kwargs["formfield_callback"] = partial( - self.formfield_for_dbfield, request=request, obj=obj - ) + kwargs['formfield_callback'] = partial(self.formfield_for_dbfield, request=request, obj=obj) return super(ContestSubmissionInline, self).get_formset(request, obj, **kwargs) def formfield_for_dbfield(self, db_field, **kwargs): - submission = kwargs.pop("obj", None) + submission = kwargs.pop('obj', None) label = None if submission: - if db_field.name == "participation": - kwargs["queryset"] = ContestParticipation.objects.filter( - user=submission.user, contest__problems=submission.problem - ).only("id", "contest__name") + if db_field.name == 'participation': + kwargs['queryset'] = ContestParticipation.objects.filter(user=submission.user, + contest__problems=submission.problem) \ + .only('id', 'contest__name') def label(obj): return obj.contest.name - - elif db_field.name == "problem": - kwargs["queryset"] = ContestProblem.objects.filter( - problem=submission.problem - ).only("id", "problem__name", "contest__name") + elif db_field.name == 'problem': + kwargs['queryset'] = ContestProblem.objects.filter(problem=submission.problem) \ + .only('id', 'problem__name', 'contest__name') def label(obj): - return pgettext("contest problem", "%(problem)s in %(contest)s") % { - "problem": obj.problem.name, - "contest": obj.contest.name, + return pgettext('contest problem', '%(problem)s in %(contest)s') % { + 'problem': obj.problem.name, 'contest': obj.contest.name, } - - field = super(ContestSubmissionInline, self).formfield_for_dbfield( - db_field, **kwargs - ) + field = super(ContestSubmissionInline, self).formfield_for_dbfield(db_field, **kwargs) if label is not None: field.label_from_instance = label return field class SubmissionSourceInline(admin.StackedInline): - fields = ("source",) + fields = ('source',) model = SubmissionSource can_delete = False extra = 0 def get_formset(self, request, obj=None, **kwargs): - kwargs.setdefault("widgets", {})["source"] = AceWidget( - mode=obj and obj.language.ace, theme=request.profile.ace_theme - ) + kwargs.setdefault('widgets', {})['source'] = AceWidget(mode=obj and obj.language.ace, + theme=request.profile.ace_theme) return super().get_formset(request, obj, **kwargs) class SubmissionAdmin(admin.ModelAdmin): - readonly_fields = ("user", "problem", "date", "judged_date") - fields = ( - "user", - "problem", - "date", - "judged_date", - "time", - "memory", - "points", - "language", - "status", - "result", - "case_points", - "case_total", - "judged_on", - "error", - ) - actions = ("judge", "recalculate_score") - list_display = ( - "id", - "problem_code", - "problem_name", - "user_column", - "execution_time", - "pretty_memory", - "points", - "language_column", - "status", - "result", - "judge_column", - ) - list_filter = ("language", SubmissionStatusFilter, SubmissionResultFilter) - search_fields = ("problem__code", "problem__name", "user__user__username") + readonly_fields = ('user', 'problem', 'date', 'judged_date') + fields = ('user', 'problem', 'date', 'judged_date', 'time', 'memory', 'points', 'language', 'status', 'result', + 'case_points', 'case_total', 'judged_on', 'error') + actions = ('judge', 'recalculate_score') + list_display = ('id', 'problem_code', 'problem_name', 'user_column', 'execution_time', 'pretty_memory', + 'points', 'language_column', 'status', 'result', 'judge_column') + list_filter = ('language', SubmissionStatusFilter, SubmissionResultFilter) + search_fields = ('problem__code', 'problem__name', 'user__user__username') actions_on_top = True actions_on_bottom = True - inlines = [ - SubmissionSourceInline, - SubmissionTestCaseInline, - ContestSubmissionInline, - ] + inlines = [SubmissionSourceInline, SubmissionTestCaseInline, ContestSubmissionInline] def get_queryset(self, request): - queryset = Submission.objects.select_related( - "problem", "user__user", "language" - ).only( - "problem__code", - "problem__name", - "user__user__username", - "language__name", - "time", - "memory", - "points", - "status", - "result", + queryset = Submission.objects.select_related('problem', 'user__user', 'language').only( + 'problem__code', 'problem__name', 'user__user__username', 'language__name', + 'time', 'memory', 'points', 'status', 'result', ) use_straight_join(queryset) - if not request.user.has_perm("judge.edit_all_problem"): + if not request.user.has_perm('judge.edit_all_problem'): id = request.profile.id - queryset = queryset.filter( - Q(problem__authors__id=id) | Q(problem__curators__id=id) - ).distinct() + queryset = queryset.filter(Q(problem__authors__id=id) | Q(problem__curators__id=id)).distinct() return queryset def has_add_permission(self, request): return False - def lookup_allowed(self, key, value): - return super(SubmissionAdmin, self).lookup_allowed(key, value) or key in ( - "problem__code", - ) + def has_change_permission(self, request, obj=None): + if not request.user.has_perm('judge.edit_own_problem'): + return False + if request.user.has_perm('judge.edit_all_problem') or obj is None: + return True + return obj.problem.is_editor(request.profile) - def save_model(self, request, obj, form, change): - super().save_model(request, obj, form, change) - if "case_points" in form.changed_data or "case_total" in form.changed_data: - obj.update_contest() + def lookup_allowed(self, key, value): + return super(SubmissionAdmin, self).lookup_allowed(key, value) or key in ('problem__code',) def judge(self, request, queryset): - if not request.user.has_perm( - "judge.rejudge_submission" - ) or not request.user.has_perm("judge.edit_own_problem"): - self.message_user( - request, - gettext("You do not have the permission to rejudge submissions."), - level=messages.ERROR, - ) + if not request.user.has_perm('judge.rejudge_submission') or not request.user.has_perm('judge.edit_own_problem'): + self.message_user(request, gettext('You do not have the permission to rejudge submissions.'), + level=messages.ERROR) return - queryset = queryset.order_by("id") - if ( - not request.user.has_perm("judge.rejudge_submission_lot") - and queryset.count() > settings.DMOJ_SUBMISSIONS_REJUDGE_LIMIT - ): - self.message_user( - request, - gettext( - "You do not have the permission to rejudge THAT many submissions." - ), - level=messages.ERROR, - ) + queryset = queryset.order_by('id') + if not request.user.has_perm('judge.rejudge_submission_lot') and \ + queryset.count() > settings.DMOJ_SUBMISSIONS_REJUDGE_LIMIT: + self.message_user(request, gettext('You do not have the permission to rejudge THAT many submissions.'), + level=messages.ERROR) return - if not request.user.has_perm("judge.edit_all_problem"): + if not request.user.has_perm('judge.edit_all_problem'): id = request.profile.id - queryset = queryset.filter( - Q(problem__authors__id=id) | Q(problem__curators__id=id) - ) + queryset = queryset.filter(Q(problem__authors__id=id) | Q(problem__curators__id=id)) judged = len(queryset) for model in queryset: model.judge(rejudge=True, batch_rejudge=True) - self.message_user( - request, - ungettext( - "%d submission was successfully scheduled for rejudging.", - "%d submissions were successfully scheduled for rejudging.", - judged, - ) - % judged, - ) - - judge.short_description = _("Rejudge the selected submissions") + self.message_user(request, ungettext('%d submission was successfully scheduled for rejudging.', + '%d submissions were successfully scheduled for rejudging.', + judged) % judged) + judge.short_description = _('Rejudge the selected submissions') def recalculate_score(self, request, queryset): - if not request.user.has_perm("judge.rejudge_submission"): - self.message_user( - request, - gettext("You do not have the permission to rejudge submissions."), - level=messages.ERROR, - ) + if not request.user.has_perm('judge.rejudge_submission'): + self.message_user(request, gettext('You do not have the permission to rejudge submissions.'), + level=messages.ERROR) return - submissions = list( - queryset.defer(None) - .select_related(None) - .select_related("problem") - .only( - "points", - "case_points", - "case_total", - "problem__partial", - "problem__points", - ) - ) + submissions = list(queryset.defer(None).select_related(None).select_related('problem') + .only('points', 'case_points', 'case_total', 'problem__partial', 'problem__points')) for submission in submissions: - submission.points = round( - submission.case_points - / submission.case_total - * submission.problem.points - if submission.case_total - else 0, - 1, - ) - if ( - not submission.problem.partial - and submission.points < submission.problem.points - ): + submission.points = round(submission.case_points / submission.case_total * submission.problem.points + if submission.case_total else 0, 1) + if not submission.problem.partial and submission.points < submission.problem.points: submission.points = 0 submission.save() submission.update_contest() - for profile in Profile.objects.filter( - id__in=queryset.values_list("user_id", flat=True).distinct() - ): + for profile in Profile.objects.filter(id__in=queryset.values_list('user_id', flat=True).distinct()): profile.calculate_points() - cache.delete("user_complete:%d" % profile.id) - cache.delete("user_attempted:%d" % profile.id) + cache.delete('user_complete:%d' % profile.id) + cache.delete('user_attempted:%d' % profile.id) for participation in ContestParticipation.objects.filter( - id__in=queryset.values_list("contest__participation_id") - ).prefetch_related("contest"): + id__in=queryset.values_list('contest__participation_id')).prefetch_related('contest'): participation.recompute_results() - self.message_user( - request, - ungettext( - "%d submission were successfully rescored.", - "%d submissions were successfully rescored.", - len(submissions), - ) - % len(submissions), - ) - - recalculate_score.short_description = _("Rescore the selected submissions") + self.message_user(request, ungettext('%d submission were successfully rescored.', + '%d submissions were successfully rescored.', + len(submissions)) % len(submissions)) + recalculate_score.short_description = _('Rescore the selected submissions') def problem_code(self, obj): return obj.problem.code - - problem_code.short_description = _("Problem code") - problem_code.admin_order_field = "problem__code" + problem_code.short_description = _('Problem code') + problem_code.admin_order_field = 'problem__code' def problem_name(self, obj): return obj.problem.name - - problem_name.short_description = _("Problem name") - problem_name.admin_order_field = "problem__name" + problem_name.short_description = _('Problem name') + problem_name.admin_order_field = 'problem__name' def user_column(self, obj): return obj.user.user.username - - user_column.admin_order_field = "user__user__username" - user_column.short_description = _("User") + user_column.admin_order_field = 'user__user__username' + user_column.short_description = _('User') def execution_time(self, obj): - return round(obj.time, 2) if obj.time is not None else "None" - - execution_time.short_description = _("Time") - execution_time.admin_order_field = "time" + return round(obj.time, 2) if obj.time is not None else 'None' + execution_time.short_description = _('Time') + execution_time.admin_order_field = 'time' def pretty_memory(self, obj): memory = obj.memory if memory is None: - return gettext("None") + return gettext('None') if memory < 1000: - return gettext("%d KB") % memory + return gettext('%d KB') % memory else: - return gettext("%.2f MB") % (memory / 1024) - - pretty_memory.admin_order_field = "memory" - pretty_memory.short_description = _("Memory") + return gettext('%.2f MB') % (memory / 1024) + pretty_memory.admin_order_field = 'memory' + pretty_memory.short_description = _('Memory') def language_column(self, obj): return obj.language.name - - language_column.admin_order_field = "language__name" - language_column.short_description = _("Language") + language_column.admin_order_field = 'language__name' + language_column.short_description = _('Language') def judge_column(self, obj): - return format_html( - '', - obj.id, - ) - - judge_column.short_description = "" + return format_html('', obj.id) + judge_column.short_description = '' def get_urls(self): return [ - url(r"^(\d+)/judge/$", self.judge_view, name="judge_submission_rejudge"), + url(r'^(\d+)/judge/$', self.judge_view, name='judge_submission_rejudge'), ] + super(SubmissionAdmin, self).get_urls() def judge_view(self, request, id): - if not request.user.has_perm( - "judge.rejudge_submission" - ) or not request.user.has_perm("judge.edit_own_problem"): + if not request.user.has_perm('judge.rejudge_submission') or not request.user.has_perm('judge.edit_own_problem'): raise PermissionDenied() submission = get_object_or_404(Submission, id=id) - if not request.user.has_perm( - "judge.edit_all_problem" - ) and not submission.problem.is_editor(request.profile): + if not request.user.has_perm('judge.edit_all_problem') and \ + not submission.problem.is_editor(request.profile): raise PermissionDenied() submission.judge(rejudge=True) - return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) diff --git a/judge/admin/taxon.py b/judge/admin/taxon.py index dfdc622..c707144 100644 --- a/judge/admin/taxon.py +++ b/judge/admin/taxon.py @@ -8,59 +8,45 @@ from judge.widgets import AdminHeavySelect2MultipleWidget class ProblemGroupForm(ModelForm): problems = ModelMultipleChoiceField( - label=_("Included problems"), + label=_('Included problems'), queryset=Problem.objects.all(), required=False, - help_text=_("These problems are included in this group of problems"), - widget=AdminHeavySelect2MultipleWidget(data_view="problem_select2"), - ) + help_text=_('These problems are included in this group of problems'), + widget=AdminHeavySelect2MultipleWidget(data_view='problem_select2')) class ProblemGroupAdmin(admin.ModelAdmin): - fields = ("name", "full_name", "problems") + fields = ('name', 'full_name', 'problems') form = ProblemGroupForm def save_model(self, request, obj, form, change): super(ProblemGroupAdmin, self).save_model(request, obj, form, change) - obj.problem_set.set(form.cleaned_data["problems"]) + obj.problem_set.set(form.cleaned_data['problems']) obj.save() def get_form(self, request, obj=None, **kwargs): - self.form.base_fields["problems"].initial = ( - [o.pk for o in obj.problem_set.all()] if obj else [] - ) + self.form.base_fields['problems'].initial = [o.pk for o in obj.problem_set.all()] if obj else [] return super(ProblemGroupAdmin, self).get_form(request, obj, **kwargs) class ProblemTypeForm(ModelForm): problems = ModelMultipleChoiceField( - label=_("Included problems"), + label=_('Included problems'), queryset=Problem.objects.all(), required=False, - help_text=_("These problems are included in this type of problems"), - widget=AdminHeavySelect2MultipleWidget(data_view="problem_select2"), - ) + help_text=_('These problems are included in this type of problems'), + widget=AdminHeavySelect2MultipleWidget(data_view='problem_select2')) class ProblemTypeAdmin(admin.ModelAdmin): - fields = ("name", "full_name", "problems") + fields = ('name', 'full_name', 'problems') form = ProblemTypeForm def save_model(self, request, obj, form, change): super(ProblemTypeAdmin, self).save_model(request, obj, form, change) - obj.problem_set.set(form.cleaned_data["problems"]) + obj.problem_set.set(form.cleaned_data['problems']) obj.save() def get_form(self, request, obj=None, **kwargs): - self.form.base_fields["problems"].initial = ( - [o.pk for o in obj.problem_set.all()] if obj else [] - ) + self.form.base_fields['problems'].initial = [o.pk for o in obj.problem_set.all()] if obj else [] return super(ProblemTypeAdmin, self).get_form(request, obj, **kwargs) - - -class OfficialContestCategoryAdmin(admin.ModelAdmin): - fields = ("name",) - - -class OfficialContestLocationAdmin(admin.ModelAdmin): - fields = ("name",) diff --git a/judge/admin/ticket.py b/judge/admin/ticket.py index f7495dd..a5c5a5e 100644 --- a/judge/admin/ticket.py +++ b/judge/admin/ticket.py @@ -4,56 +4,36 @@ from django.forms import ModelForm from django.urls import reverse_lazy from judge.models import TicketMessage -from judge.widgets import ( - AdminHeavySelect2MultipleWidget, - AdminHeavySelect2Widget, - HeavyPreviewAdminPageDownWidget, -) +from judge.widgets import AdminHeavySelect2MultipleWidget, AdminHeavySelect2Widget, HeavyPreviewAdminPageDownWidget class TicketMessageForm(ModelForm): class Meta: widgets = { - "user": AdminHeavySelect2Widget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), + 'user': AdminHeavySelect2Widget(data_view='profile_select2', attrs={'style': 'width: 100%'}), } if HeavyPreviewAdminPageDownWidget is not None: - widgets["body"] = HeavyPreviewAdminPageDownWidget( - preview=reverse_lazy("ticket_preview") - ) + widgets['body'] = HeavyPreviewAdminPageDownWidget(preview=reverse_lazy('ticket_preview')) class TicketMessageInline(StackedInline): model = TicketMessage form = TicketMessageForm - fields = ("user", "body") + fields = ('user', 'body') class TicketForm(ModelForm): class Meta: widgets = { - "user": AdminHeavySelect2Widget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), - "assignees": AdminHeavySelect2MultipleWidget( - data_view="profile_select2", attrs={"style": "width: 100%"} - ), + 'user': AdminHeavySelect2Widget(data_view='profile_select2', attrs={'style': 'width: 100%'}), + 'assignees': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}), } class TicketAdmin(ModelAdmin): - fields = ( - "title", - "time", - "user", - "assignees", - "content_type", - "object_id", - "notes", - ) - readonly_fields = ("time",) - list_display = ("title", "user", "time", "linked_item") + fields = ('title', 'time', 'user', 'assignees', 'content_type', 'object_id', 'notes') + readonly_fields = ('time',) + list_display = ('title', 'user', 'time', 'linked_item') inlines = [TicketMessageInline] form = TicketForm - date_hierarchy = "time" + date_hierarchy = 'time' diff --git a/judge/admin/volunteer.py b/judge/admin/volunteer.py deleted file mode 100644 index cadb090..0000000 --- a/judge/admin/volunteer.py +++ /dev/null @@ -1,67 +0,0 @@ -from operator import attrgetter - -from django.contrib import admin -from django.utils.html import format_html -from django.urls import reverse -from django.utils.translation import gettext, gettext_lazy as _, ungettext -from django.forms import ModelForm - -from judge.models import VolunteerProblemVote -from judge.widgets import AdminSelect2MultipleWidget - - -class VolunteerProblemVoteForm(ModelForm): - class Meta: - widgets = { - "types": AdminSelect2MultipleWidget, - } - - -class VolunteerProblemVoteAdmin(admin.ModelAdmin): - form = VolunteerProblemVoteForm - fields = ( - "voter", - "problem_link", - "time", - "thinking_points", - "knowledge_points", - "types", - "feedback", - ) - readonly_fields = ("time", "problem_link", "voter") - list_display = ( - "voter", - "problem_link", - "thinking_points", - "knowledge_points", - "show_types", - "feedback", - ) - search_fields = ( - "voter__user__username", - "problem__code", - "problem__name", - ) - date_hierarchy = "time" - - def problem_link(self, obj): - if self.request.user.is_superuser: - url = reverse("admin:judge_problem_change", args=(obj.problem.id,)) - else: - url = reverse("problem_detail", args=(obj.problem.code,)) - return format_html(f"{obj.problem}") - - problem_link.short_description = _("Problem") - problem_link.admin_order_field = "problem__code" - - def show_types(self, obj): - return ", ".join(map(attrgetter("name"), obj.types.all())) - - show_types.short_description = _("Types") - - def get_queryset(self, request): - self.request = request - if request.user.is_superuser: - return super().get_queryset(request) - queryset = VolunteerProblemVote.objects.prefetch_related("voter") - return queryset.filter(voter=request.profile).distinct() diff --git a/judge/apps.py b/judge/apps.py index 4df4c5a..96ec022 100644 --- a/judge/apps.py +++ b/judge/apps.py @@ -4,15 +4,15 @@ from django.utils.translation import gettext_lazy class JudgeAppConfig(AppConfig): - name = "judge" - verbose_name = gettext_lazy("Online Judge") + name = 'judge' + verbose_name = gettext_lazy('Online Judge') def ready(self): # WARNING: AS THIS IS NOT A FUNCTIONAL PROGRAMMING LANGUAGE, # OPERATIONS MAY HAVE SIDE EFFECTS. # DO NOT REMOVE THINKING THE IMPORT IS UNUSED. # noinspection PyUnresolvedReferences - from . import models, signals, jinja2 # noqa: F401, imported for side effects + from . import signals, jinja2 # noqa: F401, imported for side effects from django.contrib.flatpages.models import FlatPage from django.contrib.flatpages.admin import FlatPageAdmin diff --git a/judge/authentication.py b/judge/authentication.py deleted file mode 100644 index c7ae7d6..0000000 --- a/judge/authentication.py +++ /dev/null @@ -1,48 +0,0 @@ -from django.contrib.auth.backends import ModelBackend -from django.contrib.auth.models import User -from django.contrib.auth.forms import PasswordChangeForm -from django.contrib.auth.views import PasswordChangeView -from django.urls import reverse_lazy -from django.utils.translation import gettext_lazy as _ - - -class CustomModelBackend(ModelBackend): - def authenticate(self, request, username=None, password=None, **kwargs): - try: - # Check if the username is an email - user = User.objects.get(username=username) - except User.DoesNotExist: - # If the username is not an email, try authenticating with the username field - user = User.objects.filter(email=username).first() - - if user and user.check_password(password): - return user - - -class CustomPasswordChangeForm(PasswordChangeForm): - def __init__(self, *args, **kwargs): - super(CustomPasswordChangeForm, self).__init__(*args, **kwargs) - if not self.user.has_usable_password(): - self.fields.pop("old_password") - - def clean_old_password(self): - if "old_password" not in self.cleaned_data: - return - return super(CustomPasswordChangeForm, self).clean_old_password() - - def clean(self): - cleaned_data = super(CustomPasswordChangeForm, self).clean() - if "old_password" not in self.cleaned_data and not self.errors: - cleaned_data["old_password"] = "" - return cleaned_data - - -class CustomPasswordChangeView(PasswordChangeView): - form_class = CustomPasswordChangeForm - success_url = reverse_lazy("password_change_done") - template_name = "registration/password_change_form.html" - - def get_form_kwargs(self): - kwargs = super(CustomPasswordChangeView, self).get_form_kwargs() - kwargs["user"] = self.request.user - return kwargs diff --git a/judge/bridge/base_handler.py b/judge/bridge/base_handler.py index 0e5c067..c3fbd40 100644 --- a/judge/bridge/base_handler.py +++ b/judge/bridge/base_handler.py @@ -8,9 +8,9 @@ from netaddr import IPGlob, IPSet from judge.utils.unicode import utf8text -logger = logging.getLogger("judge.bridge") +logger = logging.getLogger('judge.bridge') -size_pack = struct.Struct("!I") +size_pack = struct.Struct('!I') assert size_pack.size == 4 MAX_ALLOWED_PACKET_SIZE = 8 * 1024 * 1024 @@ -20,7 +20,7 @@ def proxy_list(human_readable): globs = [] addrs = [] for item in human_readable: - if "*" in item or "-" in item: + if '*' in item or '-' in item: globs.append(IPGlob(item)) else: addrs.append(item) @@ -43,7 +43,7 @@ class RequestHandlerMeta(type): try: handler.handle() except BaseException: - logger.exception("Error in base packet handling") + logger.exception('Error in base packet handling') raise finally: handler.on_disconnect() @@ -70,12 +70,8 @@ class ZlibPacketHandler(metaclass=RequestHandlerMeta): def read_sized_packet(self, size, initial=None): if size > MAX_ALLOWED_PACKET_SIZE: - logger.log( - logging.WARNING if self._got_packet else logging.INFO, - "Disconnecting client due to too-large message size (%d bytes): %s", - size, - self.client_address, - ) + logger.log(logging.WARNING if self._got_packet else logging.INFO, + 'Disconnecting client due to too-large message size (%d bytes): %s', size, self.client_address) raise Disconnect() buffer = [] @@ -90,7 +86,7 @@ class ZlibPacketHandler(metaclass=RequestHandlerMeta): data = self.request.recv(remainder) remainder -= len(data) buffer.append(data) - self._on_packet(b"".join(buffer)) + self._on_packet(b''.join(buffer)) def parse_proxy_protocol(self, line): words = line.split() @@ -98,18 +94,18 @@ class ZlibPacketHandler(metaclass=RequestHandlerMeta): if len(words) < 2: raise Disconnect() - if words[1] == b"TCP4": + if words[1] == b'TCP4': if len(words) != 6: raise Disconnect() self.client_address = (utf8text(words[2]), utf8text(words[4])) self.server_address = (utf8text(words[3]), utf8text(words[5])) - elif words[1] == b"TCP6": + elif words[1] == b'TCP6': self.client_address = (utf8text(words[2]), utf8text(words[4]), 0, 0) self.server_address = (utf8text(words[3]), utf8text(words[5]), 0, 0) - elif words[1] != b"UNKNOWN": + elif words[1] != b'UNKNOWN': raise Disconnect() - def read_size(self, buffer=b""): + def read_size(self, buffer=b''): while len(buffer) < size_pack.size: recv = self.request.recv(size_pack.size - len(buffer)) if not recv: @@ -117,9 +113,9 @@ class ZlibPacketHandler(metaclass=RequestHandlerMeta): buffer += recv return size_pack.unpack(buffer)[0] - def read_proxy_header(self, buffer=b""): + def read_proxy_header(self, buffer=b''): # Max line length for PROXY protocol is 107, and we received 4 already. - while b"\r\n" not in buffer: + while b'\r\n' not in buffer: if len(buffer) > 107: raise Disconnect() data = self.request.recv(107) @@ -129,7 +125,7 @@ class ZlibPacketHandler(metaclass=RequestHandlerMeta): return buffer def _on_packet(self, data): - decompressed = zlib.decompress(data).decode("utf-8") + decompressed = zlib.decompress(data).decode('utf-8') self._got_packet = True self.on_packet(decompressed) @@ -145,17 +141,12 @@ class ZlibPacketHandler(metaclass=RequestHandlerMeta): def on_timeout(self): pass - def on_cleanup(self): - pass - def handle(self): try: tag = self.read_size() self._initial_tag = size_pack.pack(tag) - if self.client_address[0] in self.proxies and self._initial_tag == b"PROX": - proxy, _, remainder = self.read_proxy_header( - self._initial_tag - ).partition(b"\r\n") + if self.client_address[0] in self.proxies and self._initial_tag == b'PROX': + proxy, _, remainder = self.read_proxy_header(self._initial_tag).partition(b'\r\n') self.parse_proxy_protocol(proxy) while remainder: @@ -163,8 +154,8 @@ class ZlibPacketHandler(metaclass=RequestHandlerMeta): self.read_sized_packet(self.read_size(remainder)) break - size = size_pack.unpack(remainder[: size_pack.size])[0] - remainder = remainder[size_pack.size :] + size = size_pack.unpack(remainder[:size_pack.size])[0] + remainder = remainder[size_pack.size:] if len(remainder) <= size: self.read_sized_packet(size, remainder) break @@ -180,38 +171,25 @@ class ZlibPacketHandler(metaclass=RequestHandlerMeta): return except zlib.error: if self._got_packet: - logger.warning( - "Encountered zlib error during packet handling, disconnecting client: %s", - self.client_address, - exc_info=True, - ) + logger.warning('Encountered zlib error during packet handling, disconnecting client: %s', + self.client_address, exc_info=True) else: - logger.info( - "Potentially wrong protocol (zlib error): %s: %r", - self.client_address, - self._initial_tag, - exc_info=True, - ) + logger.info('Potentially wrong protocol (zlib error): %s: %r', self.client_address, self._initial_tag, + exc_info=True) except socket.timeout: if self._got_packet: - logger.info("Socket timed out: %s", self.client_address) + logger.info('Socket timed out: %s', self.client_address) self.on_timeout() else: - logger.info( - "Potentially wrong protocol: %s: %r", - self.client_address, - self._initial_tag, - ) + logger.info('Potentially wrong protocol: %s: %r', self.client_address, self._initial_tag) except socket.error as e: # When a gevent socket is shutdown, gevent cancels all waits, causing recv to raise cancel_wait_ex. - if e.__class__.__name__ == "cancel_wait_ex": + if e.__class__.__name__ == 'cancel_wait_ex': return raise - finally: - self.on_cleanup() def send(self, data): - compressed = zlib.compress(data.encode("utf-8")) + compressed = zlib.compress(data.encode('utf-8')) self.request.sendall(size_pack.pack(len(compressed)) + compressed) def close(self): diff --git a/judge/bridge/daemon.py b/judge/bridge/daemon.py index b9988ce..ce8a702 100644 --- a/judge/bridge/daemon.py +++ b/judge/bridge/daemon.py @@ -11,7 +11,7 @@ from judge.bridge.judge_list import JudgeList from judge.bridge.server import Server from judge.models import Judge, Submission -logger = logging.getLogger("judge.bridge") +logger = logging.getLogger('judge.bridge') def reset_judges(): @@ -20,17 +20,12 @@ def reset_judges(): def judge_daemon(): reset_judges() - Submission.objects.filter(status__in=Submission.IN_PROGRESS_GRADING_STATUS).update( - status="IE", result="IE", error=None - ) + Submission.objects.filter(status__in=Submission.IN_PROGRESS_GRADING_STATUS) \ + .update(status='IE', result='IE', error=None) judges = JudgeList() - judge_server = Server( - settings.BRIDGED_JUDGE_ADDRESS, partial(JudgeHandler, judges=judges) - ) - django_server = Server( - settings.BRIDGED_DJANGO_ADDRESS, partial(DjangoHandler, judges=judges) - ) + judge_server = Server(settings.BRIDGED_JUDGE_ADDRESS, partial(JudgeHandler, judges=judges)) + django_server = Server(settings.BRIDGED_DJANGO_ADDRESS, partial(DjangoHandler, judges=judges)) threading.Thread(target=django_server.serve_forever).start() threading.Thread(target=judge_server.serve_forever).start() @@ -38,7 +33,7 @@ def judge_daemon(): stop = threading.Event() def signal_handler(signum, _): - logger.info("Exiting due to %s", signal.Signals(signum).name) + logger.info('Exiting due to %s', signal.Signals(signum).name) stop.set() signal.signal(signal.SIGINT, signal_handler) diff --git a/judge/bridge/django_handler.py b/judge/bridge/django_handler.py index 8a40c3f..b284b68 100644 --- a/judge/bridge/django_handler.py +++ b/judge/bridge/django_handler.py @@ -2,12 +2,10 @@ import json import logging import struct -from django import db - from judge.bridge.base_handler import Disconnect, ZlibPacketHandler -logger = logging.getLogger("judge.bridge") -size_pack = struct.Struct("!I") +logger = logging.getLogger('judge.bridge') +size_pack = struct.Struct('!I') class DjangoHandler(ZlibPacketHandler): @@ -15,52 +13,47 @@ class DjangoHandler(ZlibPacketHandler): super().__init__(request, client_address, server) self.handlers = { - "submission-request": self.on_submission, - "terminate-submission": self.on_termination, - "disconnect-judge": self.on_disconnect_request, + 'submission-request': self.on_submission, + 'terminate-submission': self.on_termination, + 'disconnect-judge': self.on_disconnect_request, } self.judges = judges def send(self, data): - super().send(json.dumps(data, separators=(",", ":"))) + super().send(json.dumps(data, separators=(',', ':'))) def on_packet(self, packet): packet = json.loads(packet) try: - result = self.handlers.get(packet.get("name", None), self.on_malformed)( - packet - ) + result = self.handlers.get(packet.get('name', None), self.on_malformed)(packet) except Exception: - logger.exception("Error in packet handling (Django-facing)") - result = {"name": "bad-request"} + logger.exception('Error in packet handling (Django-facing)') + result = {'name': 'bad-request'} self.send(result) raise Disconnect() def on_submission(self, data): - id = data["submission-id"] - problem = data["problem-id"] - language = data["language"] - source = data["source"] - judge_id = data["judge-id"] - priority = data["priority"] + id = data['submission-id'] + problem = data['problem-id'] + language = data['language'] + source = data['source'] + judge_id = data['judge-id'] + priority = data['priority'] if not self.judges.check_priority(priority): - return {"name": "bad-request"} + return {'name': 'bad-request'} self.judges.judge(id, problem, language, source, judge_id, priority) - return {"name": "submission-received", "submission-id": id} + return {'name': 'submission-received', 'submission-id': id} def on_termination(self, data): - return { - "name": "submission-received", - "judge-aborted": self.judges.abort(data["submission-id"]), - } + return {'name': 'submission-received', 'judge-aborted': self.judges.abort(data['submission-id'])} def on_disconnect_request(self, data): - judge_id = data["judge-id"] - force = data["force"] + judge_id = data['judge-id'] + force = data['force'] self.judges.disconnect(judge_id, force=force) def on_malformed(self, packet): - logger.error("Malformed packet: %s", packet) + logger.error('Malformed packet: %s', packet) - def on_cleanup(self): - db.connection.close() + def on_close(self): + self._to_kill = False diff --git a/judge/bridge/echo_test_client.py b/judge/bridge/echo_test_client.py index 801d34e..8fec692 100644 --- a/judge/bridge/echo_test_client.py +++ b/judge/bridge/echo_test_client.py @@ -4,7 +4,7 @@ import struct import time import zlib -size_pack = struct.Struct("!I") +size_pack = struct.Struct('!I') def open_connection(): @@ -13,70 +13,69 @@ def open_connection(): def zlibify(data): - data = zlib.compress(data.encode("utf-8")) + data = zlib.compress(data.encode('utf-8')) return size_pack.pack(len(data)) + data def dezlibify(data, skip_head=True): if skip_head: - data = data[size_pack.size :] - return zlib.decompress(data).decode("utf-8") + data = data[size_pack.size:] + return zlib.decompress(data).decode('utf-8') def main(): global host, port import argparse - parser = argparse.ArgumentParser() - parser.add_argument("-l", "--host", default="localhost") - parser.add_argument("-p", "--port", default=9999, type=int) + parser.add_argument('-l', '--host', default='localhost') + parser.add_argument('-p', '--port', default=9999, type=int) args = parser.parse_args() host, port = args.host, args.port - print("Opening idle connection:", end=" ") + print('Opening idle connection:', end=' ') s1 = open_connection() - print("Success") - print("Opening hello world connection:", end=" ") + print('Success') + print('Opening hello world connection:', end=' ') s2 = open_connection() - print("Success") - print("Sending Hello, World!", end=" ") - s2.sendall(zlibify("Hello, World!")) - print("Success") - print("Testing blank connection:", end=" ") + print('Success') + print('Sending Hello, World!', end=' ') + s2.sendall(zlibify('Hello, World!')) + print('Success') + print('Testing blank connection:', end=' ') s3 = open_connection() s3.close() - print("Success") + print('Success') result = dezlibify(s2.recv(1024)) - assert result == "Hello, World!" + assert result == 'Hello, World!' print(result) s2.close() - print("Large random data test:", end=" ") + print('Large random data test:', end=' ') s4 = open_connection() - data = os.urandom(1000000).decode("iso-8859-1") - print("Generated", end=" ") + data = os.urandom(1000000).decode('iso-8859-1') + print('Generated', end=' ') s4.sendall(zlibify(data)) - print("Sent", end=" ") - result = b"" + print('Sent', end=' ') + result = b'' while len(result) < size_pack.size: result += s4.recv(1024) - size = size_pack.unpack(result[: size_pack.size])[0] - result = result[size_pack.size :] + size = size_pack.unpack(result[:size_pack.size])[0] + result = result[size_pack.size:] while len(result) < size: result += s4.recv(1024) - print("Received", end=" ") + print('Received', end=' ') assert dezlibify(result, False) == data - print("Success") + print('Success') s4.close() - print("Test malformed connection:", end=" ") + print('Test malformed connection:', end=' ') s5 = open_connection() - s5.sendall(data[:100000].encode("utf-8")) + s5.sendall(data[:100000].encode('utf-8')) s5.close() - print("Success") - print("Waiting for timeout to close idle connection:", end=" ") + print('Success') + print('Waiting for timeout to close idle connection:', end=' ') time.sleep(6) - print("Done") + print('Done') s1.close() -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/judge/bridge/echo_test_server.py b/judge/bridge/echo_test_server.py index 2d84e85..59e21fa 100644 --- a/judge/bridge/echo_test_server.py +++ b/judge/bridge/echo_test_server.py @@ -3,22 +3,19 @@ from judge.bridge.base_handler import ZlibPacketHandler class EchoPacketHandler(ZlibPacketHandler): def on_connect(self): - print("New client:", self.client_address) + print('New client:', self.client_address) self.timeout = 5 def on_timeout(self): - print("Inactive client:", self.client_address) + print('Inactive client:', self.client_address) def on_packet(self, data): self.timeout = None - print( - "Data from %s: %r" - % (self.client_address, data[:30] if len(data) > 30 else data) - ) + print('Data from %s: %r' % (self.client_address, data[:30] if len(data) > 30 else data)) self.send(data) def on_disconnect(self): - print("Closed client:", self.client_address) + print('Closed client:', self.client_address) def main(): @@ -26,9 +23,9 @@ def main(): from judge.bridge.server import Server parser = argparse.ArgumentParser() - parser.add_argument("-l", "--host", action="append") - parser.add_argument("-p", "--port", type=int, action="append") - parser.add_argument("-P", "--proxy", action="append") + parser.add_argument('-l', '--host', action='append') + parser.add_argument('-p', '--port', type=int, action='append') + parser.add_argument('-P', '--proxy', action='append') args = parser.parse_args() class Handler(EchoPacketHandler): @@ -38,5 +35,5 @@ def main(): server.serve_forever() -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/judge/bridge/judge_handler.py b/judge/bridge/judge_handler.py index bb80f5a..86f53b4 100644 --- a/judge/bridge/judge_handler.py +++ b/judge/bridge/judge_handler.py @@ -10,36 +10,25 @@ from django import db from django.conf import settings from django.utils import timezone from django.db.models import F -from django.core.cache import cache from judge import event_poster as event from judge.bridge.base_handler import ZlibPacketHandler, proxy_list -from judge.utils.problems import finished_submission -from judge.models import ( - Judge, - Language, - LanguageLimit, - Problem, - RuntimeVersion, - Submission, - SubmissionTestCase, -) -from judge.bridge.utils import VanishedSubmission -from judge.caching import cache_wrapper +from judge.caching import finished_submission +from judge.models import Judge, Language, LanguageLimit, Problem, RuntimeVersion, Submission, SubmissionTestCase -logger = logging.getLogger("judge.bridge") -json_log = logging.getLogger("judge.json.bridge") +logger = logging.getLogger('judge.bridge') +json_log = logging.getLogger('judge.json.bridge') UPDATE_RATE_LIMIT = 5 UPDATE_RATE_TIME = 0.5 -SubmissionData = namedtuple( - "SubmissionData", - "time memory short_circuit pretests_only contest_no attempt_no user_id", -) +SubmissionData = namedtuple('SubmissionData', 'time memory short_circuit pretests_only contest_no attempt_no user_id') def _ensure_connection(): - db.connection.close_if_unusable_or_obsolete() + try: + db.connection.cursor().execute('SELECT 1').fetchall() + except Exception: + db.connection.close() class JudgeHandler(ZlibPacketHandler): @@ -50,25 +39,25 @@ class JudgeHandler(ZlibPacketHandler): self.judges = judges self.handlers = { - "grading-begin": self.on_grading_begin, - "grading-end": self.on_grading_end, - "compile-error": self.on_compile_error, - "compile-message": self.on_compile_message, - "batch-begin": self.on_batch_begin, - "batch-end": self.on_batch_end, - "test-case-status": self.on_test_case, - "internal-error": self.on_internal_error, - "submission-terminated": self.on_submission_terminated, - "submission-acknowledged": self.on_submission_acknowledged, - "ping-response": self.on_ping_response, - "supported-problems": self.on_supported_problems, - "handshake": self.on_handshake, + 'grading-begin': self.on_grading_begin, + 'grading-end': self.on_grading_end, + 'compile-error': self.on_compile_error, + 'compile-message': self.on_compile_message, + 'batch-begin': self.on_batch_begin, + 'batch-end': self.on_batch_end, + 'test-case-status': self.on_test_case, + 'internal-error': self.on_internal_error, + 'submission-terminated': self.on_submission_terminated, + 'submission-acknowledged': self.on_submission_acknowledged, + 'ping-response': self.on_ping_response, + 'supported-problems': self.on_supported_problems, + 'handshake': self.on_handshake, } self._working = False - self._working_data = {} self._no_response_job = None + self._problems = [] self.executors = {} - self.problems = set() + self.problems = {} self.latency = None self.time_delta = None self.load = 1e100 @@ -89,103 +78,40 @@ class JudgeHandler(ZlibPacketHandler): def on_connect(self): self.timeout = 15 - logger.info("Judge connected from: %s", self.client_address) - json_log.info(self._make_json_log(action="connect")) + logger.info('Judge connected from: %s', self.client_address) + json_log.info(self._make_json_log(action='connect')) def on_disconnect(self): self._stop_ping.set() + if self._working: + logger.error('Judge %s disconnected while handling submission %s', self.name, self._working) self.judges.remove(self) if self.name is not None: self._disconnected() - logger.info( - "Judge disconnected from: %s with name %s", self.client_address, self.name - ) + logger.info('Judge disconnected from: %s with name %s', self.client_address, self.name) - json_log.info( - self._make_json_log(action="disconnect", info="judge disconnected") - ) + json_log.info(self._make_json_log(action='disconnect', info='judge disconnected')) if self._working: - self.judges.judge( - self._working, - self._working_data["problem"], - self._working_data["language"], - self._working_data["source"], - None, - 0, - ) + Submission.objects.filter(id=self._working).update(status='IE', result='IE', error='') + json_log.error(self._make_json_log(sub=self._working, action='close', info='IE due to shutdown on grading')) def _authenticate(self, id, key): try: - judge = Judge.objects.get(name=id) + judge = Judge.objects.get(name=id, is_blocked=False) except Judge.DoesNotExist: - if settings.BRIDGED_AUTO_CREATE_JUDGE: - judge = Judge() - judge.name = id - judge.auth_key = key - judge.save() - result = True - else: - result = False + result = False else: - if judge.is_blocked: - result = False - else: - result = hmac.compare_digest(judge.auth_key, key) + result = hmac.compare_digest(judge.auth_key, key) if not result: - json_log.warning( - self._make_json_log( - action="auth", judge=id, info="judge failed authentication" - ) - ) + json_log.warning(self._make_json_log(action='auth', judge=id, info='judge failed authentication')) return result - def _update_supported_problems(self, problem_packet): - # problem_packet is a dict {code: mtimes} from judge-server - self.problems = set(p for p, _ in problem_packet) - - def _update_judge_problems(self): - chunk_size = 500 - - target_problem_codes = self.problems - current_problems = _get_judge_problems(self.judge) - - updated = False - problems_to_add = list(target_problem_codes - current_problems) - problems_to_remove = list(current_problems - target_problem_codes) - - if problems_to_add: - for i in range(0, len(problems_to_add), chunk_size): - chunk = problems_to_add[i : i + chunk_size] - problem_ids = Problem.objects.filter(code__in=chunk).values_list( - "id", flat=True - ) - if not problem_ids: - continue - logger.info("%s: Add %d problems", self.name, len(problem_ids)) - self.judge.problems.add(*problem_ids) - updated = True - - if problems_to_remove: - for i in range(0, len(problems_to_remove), chunk_size): - chunk = problems_to_remove[i : i + chunk_size] - problem_ids = Problem.objects.filter(code__in=chunk).values_list( - "id", flat=True - ) - if not problem_ids: - continue - logger.info("%s: Remove %d problems", self.name, len(problem_ids)) - self.judge.problems.remove(*problem_ids) - updated = True - - if updated: - _get_judge_problems.dirty(self.judge) - def _connected(self): judge = self.judge = Judge.objects.get(name=self.name) judge.start_time = timezone.now() judge.online = True - self._update_judge_problems() + judge.problems.set(Problem.objects.filter(code__in=list(self.problems.keys()))) judge.runtimes.set(Language.objects.filter(key__in=list(self.executors.keys()))) # Delete now in case we somehow crashed and left some over from the last connection @@ -193,81 +119,56 @@ class JudgeHandler(ZlibPacketHandler): versions = [] for lang in judge.runtimes.all(): versions += [ - RuntimeVersion( - language=lang, - name=name, - version=".".join(map(str, version)), - priority=idx, - judge=judge, - ) + RuntimeVersion(language=lang, name=name, version='.'.join(map(str, version)), priority=idx, judge=judge) for idx, (name, version) in enumerate(self.executors[lang.key]) ] RuntimeVersion.objects.bulk_create(versions) judge.last_ip = self.client_address[0] judge.save() - self.judge_address = "[%s]:%s" % ( - self.client_address[0], - self.client_address[1], - ) - json_log.info( - self._make_json_log( - action="auth", - info="judge successfully authenticated", - executors=list(self.executors.keys()), - ) - ) + self.judge_address = '[%s]:%s' % (self.client_address[0], self.client_address[1]) + json_log.info(self._make_json_log(action='auth', info='judge successfully authenticated', + executors=list(self.executors.keys()))) def _disconnected(self): Judge.objects.filter(id=self.judge.id).update(online=False) RuntimeVersion.objects.filter(judge=self.judge).delete() - self.judge.problems.clear() - _get_judge_problems.dirty(self.judge) def _update_ping(self): try: - Judge.objects.filter(name=self.name).update( - ping=self.latency, load=self.load - ) + Judge.objects.filter(name=self.name).update(ping=self.latency, load=self.load) except Exception as e: # What can I do? I don't want to tie this to MySQL. - if ( - e.__class__.__name__ == "OperationalError" - and e.__module__ == "_mysql_exceptions" - and e.args[0] == 2006 - ): + if e.__class__.__name__ == 'OperationalError' and e.__module__ == '_mysql_exceptions' and e.args[0] == 2006: db.connection.close() def send(self, data): - super().send(json.dumps(data, separators=(",", ":"))) + super().send(json.dumps(data, separators=(',', ':'))) def on_handshake(self, packet): - if "id" not in packet or "key" not in packet: - logger.warning("Malformed handshake: %s", self.client_address) + if 'id' not in packet or 'key' not in packet: + logger.warning('Malformed handshake: %s', self.client_address) self.close() return - if not self._authenticate(packet["id"], packet["key"]): - logger.warning("Authentication failure: %s", self.client_address) + if not self._authenticate(packet['id'], packet['key']): + logger.warning('Authentication failure: %s', self.client_address) self.close() return self.timeout = 60 - self._update_supported_problems(packet["problems"]) - self.executors = packet["executors"] - self.name = packet["id"] + self._problems = packet['problems'] + self.problems = dict(self._problems) + self.executors = packet['executors'] + self.name = packet['id'] - self.send({"name": "handshake-success"}) - logger.info("Judge authenticated: %s (%s)", self.client_address, packet["id"]) + self.send({'name': 'handshake-success'}) + logger.info('Judge authenticated: %s (%s)', self.client_address, packet['id']) self.judges.register(self) threading.Thread(target=self._ping_thread).start() self._connected() def can_judge(self, problem, executor, judge_id=None): - return ( - problem in self.problems - and executor in self.executors - and (not judge_id or self.name == judge_id) - ) + return problem in self.problems and executor in self.executors and (not judge_id or self.name == judge_id) @property def working(self): @@ -277,60 +178,25 @@ class JudgeHandler(ZlibPacketHandler): _ensure_connection() try: - ( - pid, - time, - memory, - short_circuit, - lid, - is_pretested, - sub_date, - uid, - part_virtual, - part_id, - ) = ( - Submission.objects.filter(id=submission).values_list( - "problem__id", - "problem__time_limit", - "problem__memory_limit", - "problem__short_circuit", - "language__id", - "is_pretested", - "date", - "user__id", - "contest__participation__virtual", - "contest__participation__id", - ) - ).get() + pid, time, memory, short_circuit, lid, is_pretested, sub_date, uid, part_virtual, part_id = ( + Submission.objects.filter(id=submission) + .values_list('problem__id', 'problem__time_limit', 'problem__memory_limit', + 'problem__short_circuit', 'language__id', 'is_pretested', 'date', 'user__id', + 'contest__participation__virtual', 'contest__participation__id')).get() except Submission.DoesNotExist: - logger.error("Submission vanished: %s", submission) - json_log.error( - self._make_json_log( - sub=self._working, - action="request", - info="submission vanished when fetching info", - ) - ) + logger.error('Submission vanished: %s', submission) + json_log.error(self._make_json_log( + sub=self._working, action='request', + info='submission vanished when fetching info', + )) return - attempt_no = ( - Submission.objects.filter( - problem__id=pid, - contest__participation__id=part_id, - user__id=uid, - date__lt=sub_date, - ) - .exclude(status__in=("CE", "IE")) - .count() - + 1 - ) + attempt_no = Submission.objects.filter(problem__id=pid, contest__participation__id=part_id, user__id=uid, + date__lt=sub_date).exclude(status__in=('CE', 'IE')).count() + 1 try: - time, memory = ( - LanguageLimit.objects.filter(problem__id=pid, language__id=lid) - .values_list("time_limit", "memory_limit") - .get() - ) + time, memory = (LanguageLimit.objects.filter(problem__id=pid, language__id=lid) + .values_list('time_limit', 'memory_limit').get()) except LanguageLimit.DoesNotExist: pass @@ -349,190 +215,139 @@ class JudgeHandler(ZlibPacketHandler): # Yank the power out. self.close() else: - self.send({"name": "disconnect"}) + self.send({'name': 'disconnect'}) def submit(self, id, problem, language, source): data = self.get_related_submission_data(id) - if not data: - self._update_internal_error_submission(id, "Submission vanished") - raise VanishedSubmission() self._working = id - self._working_data = { - "problem": problem, - "language": language, - "source": source, - } self._no_response_job = threading.Timer(20, self._kill_if_no_response) - self.send( - { - "name": "submission-request", - "submission-id": id, - "problem-id": problem, - "language": language, - "source": source, - "time-limit": data.time, - "memory-limit": data.memory, - "short-circuit": data.short_circuit, - "meta": { - "pretests-only": data.pretests_only, - "in-contest": data.contest_no, - "attempt-no": data.attempt_no, - "user": data.user_id, - }, - } - ) + self.send({ + 'name': 'submission-request', + 'submission-id': id, + 'problem-id': problem, + 'language': language, + 'source': source, + 'time-limit': data.time, + 'memory-limit': data.memory, + 'short-circuit': data.short_circuit, + 'meta': { + 'pretests-only': data.pretests_only, + 'in-contest': data.contest_no, + 'attempt-no': data.attempt_no, + 'user': data.user_id, + }, + }) def _kill_if_no_response(self): - logger.error( - "Judge failed to acknowledge submission: %s: %s", self.name, self._working - ) + logger.error('Judge failed to acknowledge submission: %s: %s', self.name, self._working) self.close() def on_timeout(self): if self.name: - logger.warning("Judge seems dead: %s: %s", self.name, self._working) + logger.warning('Judge seems dead: %s: %s', self.name, self._working) def malformed_packet(self, exception): - logger.exception("Judge sent malformed packet: %s", self.name) + logger.exception('Judge sent malformed packet: %s', self.name) super(JudgeHandler, self).malformed_packet(exception) def on_submission_processing(self, packet): _ensure_connection() - id = packet["submission-id"] - if Submission.objects.filter(id=id).update(status="P", judged_on=self.judge): - event.post("sub_%s" % Submission.get_id_secret(id), {"type": "processing"}) - self._post_update_submission(id, "processing") - json_log.info(self._make_json_log(packet, action="processing")) + id = packet['submission-id'] + if Submission.objects.filter(id=id).update(status='P', judged_on=self.judge): + event.post('sub_%s' % Submission.get_id_secret(id), {'type': 'processing'}) + self._post_update_submission(id, 'processing') + json_log.info(self._make_json_log(packet, action='processing')) else: - logger.warning("Unknown submission: %s", id) - json_log.error( - self._make_json_log( - packet, action="processing", info="unknown submission" - ) - ) + logger.warning('Unknown submission: %s', id) + json_log.error(self._make_json_log(packet, action='processing', info='unknown submission')) def on_submission_wrong_acknowledge(self, packet, expected, got): - json_log.error( - self._make_json_log( - packet, action="processing", info="wrong-acknowledge", expected=expected - ) - ) - Submission.objects.filter(id=expected).update( - status="IE", result="IE", error=None - ) - Submission.objects.filter(id=got, status="QU").update( - status="IE", result="IE", error=None - ) + json_log.error(self._make_json_log(packet, action='processing', info='wrong-acknowledge', expected=expected)) + Submission.objects.filter(id=expected).update(status='IE', result='IE', error=None) + Submission.objects.filter(id=got, status='QU').update(status='IE', result='IE', error=None) def on_submission_acknowledged(self, packet): - if not packet.get("submission-id", None) == self._working: - logger.error( - "Wrong acknowledgement: %s: %s, expected: %s", - self.name, - packet.get("submission-id", None), - self._working, - ) - self.on_submission_wrong_acknowledge( - packet, self._working, packet.get("submission-id", None) - ) + if not packet.get('submission-id', None) == self._working: + logger.error('Wrong acknowledgement: %s: %s, expected: %s', self.name, packet.get('submission-id', None), + self._working) + self.on_submission_wrong_acknowledge(packet, self._working, packet.get('submission-id', None)) self.close() - logger.info("Submission acknowledged: %d", self._working) + logger.info('Submission acknowledged: %d', self._working) if self._no_response_job: self._no_response_job.cancel() self._no_response_job = None self.on_submission_processing(packet) def abort(self): - self.send({"name": "terminate-submission"}) + self.send({'name': 'terminate-submission'}) def get_current_submission(self): return self._working or None def ping(self): - self.send({"name": "ping", "when": time.time()}) + self.send({'name': 'ping', 'when': time.time()}) def on_packet(self, data): try: try: data = json.loads(data) - if "name" not in data: + if 'name' not in data: raise ValueError except ValueError: self.on_malformed(data) else: - handler = self.handlers.get(data["name"], self.on_malformed) + handler = self.handlers.get(data['name'], self.on_malformed) handler(data) except Exception: - logger.exception("Error in packet handling (Judge-side): %s", self.name) + logger.exception('Error in packet handling (Judge-side): %s', self.name) self._packet_exception() # You can't crash here because you aren't so sure about the judges # not being malicious or simply malforms. THIS IS A SERVER! def _packet_exception(self): - json_log.exception( - self._make_json_log(sub=self._working, info="packet processing exception") - ) + json_log.exception(self._make_json_log(sub=self._working, info='packet processing exception')) def _submission_is_batch(self, id): if not Submission.objects.filter(id=id).update(batch=True): - logger.warning("Unknown submission: %s", id) + logger.warning('Unknown submission: %s', id) def on_supported_problems(self, packet): - logger.info("%s: Updated problem list", self.name) - self._update_supported_problems(packet["problems"]) - + logger.info('%s: Updated problem list', self.name) + self._problems = packet['problems'] + self.problems = dict(self._problems) if not self.working: self.judges.update_problems(self) - self._update_judge_problems() - json_log.info( - self._make_json_log(action="update-problems", count=len(self.problems)) - ) + self.judge.problems.set(Problem.objects.filter(code__in=list(self.problems.keys()))) + json_log.info(self._make_json_log(action='update-problems', count=len(self.problems))) def on_grading_begin(self, packet): - logger.info("%s: Grading has begun on: %s", self.name, packet["submission-id"]) + logger.info('%s: Grading has begun on: %s', self.name, packet['submission-id']) self.batch_id = None - if Submission.objects.filter(id=packet["submission-id"]).update( - status="G", - is_pretested=packet["pretested"], - current_testcase=1, - points=0, - batch=False, - judged_date=timezone.now(), - ): - SubmissionTestCase.objects.filter( - submission_id=packet["submission-id"] - ).delete() - event.post( - "sub_%s" % Submission.get_id_secret(packet["submission-id"]), - {"type": "grading-begin"}, - ) - self._post_update_submission(packet["submission-id"], "grading-begin") - json_log.info(self._make_json_log(packet, action="grading-begin")) + if Submission.objects.filter(id=packet['submission-id']).update( + status='G', is_pretested=packet['pretested'], + current_testcase=1, points=0, + batch=False, judged_date=timezone.now()): + SubmissionTestCase.objects.filter(submission_id=packet['submission-id']).delete() + event.post('sub_%s' % Submission.get_id_secret(packet['submission-id']), {'type': 'grading-begin'}) + self._post_update_submission(packet['submission-id'], 'grading-begin') + json_log.info(self._make_json_log(packet, action='grading-begin')) else: - logger.warning("Unknown submission: %s", packet["submission-id"]) - json_log.error( - self._make_json_log( - packet, action="grading-begin", info="unknown submission" - ) - ) + logger.warning('Unknown submission: %s', packet['submission-id']) + json_log.error(self._make_json_log(packet, action='grading-begin', info='unknown submission')) def on_grading_end(self, packet): - logger.info("%s: Grading has ended on: %s", self.name, packet["submission-id"]) + logger.info('%s: Grading has ended on: %s', self.name, packet['submission-id']) self._free_self(packet) self.batch_id = None try: - submission = Submission.objects.get(id=packet["submission-id"]) + submission = Submission.objects.get(id=packet['submission-id']) except Submission.DoesNotExist: - logger.warning("Unknown submission: %s", packet["submission-id"]) - json_log.error( - self._make_json_log( - packet, action="grading-end", info="unknown submission" - ) - ) + logger.warning('Unknown submission: %s', packet['submission-id']) + json_log.error(self._make_json_log(packet, action='grading-end', info='unknown submission')) return time = 0 @@ -540,7 +355,7 @@ class JudgeHandler(ZlibPacketHandler): points = 0.0 total = 0 status = 0 - status_codes = ["SC", "AC", "WA", "MLE", "TLE", "IR", "RTE", "OLE"] + status_codes = ['SC', 'AC', 'WA', 'MLE', 'TLE', 'IR', 'RTE', 'OLE'] batches = {} # batch number: (points, total) for case in SubmissionTestCase.objects.filter(submission=submission): @@ -550,8 +365,8 @@ class JudgeHandler(ZlibPacketHandler): total += case.total else: if case.batch in batches: - batches[case.batch][0] += case.points - batches[case.batch][1] += case.total + batches[case.batch][0] = min(batches[case.batch][0], case.points) + batches[case.batch][1] = max(batches[case.batch][1], case.total) else: batches[case.batch] = [case.points, case.total] memory = max(memory, case.memory) @@ -563,8 +378,8 @@ class JudgeHandler(ZlibPacketHandler): points += batches[i][0] total += batches[i][1] - points = points - total = total + points = round(points, 1) + total = round(total, 1) submission.case_points = points submission.case_total = total @@ -573,29 +388,19 @@ class JudgeHandler(ZlibPacketHandler): if not problem.partial and sub_points != problem.points: sub_points = 0 - submission.status = "D" + submission.status = 'D' submission.time = time submission.memory = memory submission.points = sub_points submission.result = status_codes[status] submission.save() - json_log.info( - self._make_json_log( - packet, - action="grading-end", - time=time, - memory=memory, - points=sub_points, - total=problem.points, - result=submission.result, - case_points=points, - case_total=total, - user=submission.user_id, - problem=problem.code, - finish=True, - ) - ) + json_log.info(self._make_json_log( + packet, action='grading-end', time=time, memory=memory, + points=sub_points, total=problem.points, result=submission.result, + case_points=points, case_total=total, user=submission.user_id, + problem=problem.code, finish=True, + )) submission.user._updating_stats_only = True submission.user.calculate_points() @@ -605,257 +410,143 @@ class JudgeHandler(ZlibPacketHandler): finished_submission(submission) - event.post( - "sub_%s" % submission.id_secret, - { - "type": "grading-end", - "time": time, - "memory": memory, - "points": float(points), - "total": float(problem.points), - "result": submission.result, - }, - ) - if hasattr(submission, "contest"): + event.post('sub_%s' % submission.id_secret, { + 'type': 'grading-end', + 'time': time, + 'memory': memory, + 'points': float(points), + 'total': float(problem.points), + 'result': submission.result, + }) + if hasattr(submission, 'contest'): participation = submission.contest.participation - event.post("contest_%d" % participation.contest_id, {"type": "update"}) - self._post_update_submission(submission.id, "grading-end", done=True) + event.post('contest_%d' % participation.contest_id, {'type': 'update'}) + self._post_update_submission(submission.id, 'grading-end', done=True) def on_compile_error(self, packet): - logger.info( - "%s: Submission failed to compile: %s", self.name, packet["submission-id"] - ) + logger.info('%s: Submission failed to compile: %s', self.name, packet['submission-id']) self._free_self(packet) - if Submission.objects.filter(id=packet["submission-id"]).update( - status="CE", result="CE", error=packet["log"] - ): - event.post( - "sub_%s" % Submission.get_id_secret(packet["submission-id"]), - { - "type": "compile-error", - "log": packet["log"], - }, - ) - self._post_update_submission( - packet["submission-id"], "compile-error", done=True - ) - json_log.info( - self._make_json_log( - packet, - action="compile-error", - log=packet["log"], - finish=True, - result="CE", - ) - ) + if Submission.objects.filter(id=packet['submission-id']).update(status='CE', result='CE', error=packet['log']): + event.post('sub_%s' % Submission.get_id_secret(packet['submission-id']), { + 'type': 'compile-error', + 'log': packet['log'], + }) + self._post_update_submission(packet['submission-id'], 'compile-error', done=True) + json_log.info(self._make_json_log(packet, action='compile-error', log=packet['log'], + finish=True, result='CE')) else: - logger.warning("Unknown submission: %s", packet["submission-id"]) - json_log.error( - self._make_json_log( - packet, - action="compile-error", - info="unknown submission", - log=packet["log"], - finish=True, - result="CE", - ) - ) + logger.warning('Unknown submission: %s', packet['submission-id']) + json_log.error(self._make_json_log(packet, action='compile-error', info='unknown submission', + log=packet['log'], finish=True, result='CE')) def on_compile_message(self, packet): - logger.info( - "%s: Submission generated compiler messages: %s", - self.name, - packet["submission-id"], - ) + logger.info('%s: Submission generated compiler messages: %s', self.name, packet['submission-id']) - if Submission.objects.filter(id=packet["submission-id"]).update( - error=packet["log"] - ): - event.post( - "sub_%s" % Submission.get_id_secret(packet["submission-id"]), - {"type": "compile-message"}, - ) - json_log.info( - self._make_json_log(packet, action="compile-message", log=packet["log"]) - ) + if Submission.objects.filter(id=packet['submission-id']).update(error=packet['log']): + event.post('sub_%s' % Submission.get_id_secret(packet['submission-id']), {'type': 'compile-message'}) + json_log.info(self._make_json_log(packet, action='compile-message', log=packet['log'])) else: - logger.warning("Unknown submission: %s", packet["submission-id"]) - json_log.error( - self._make_json_log( - packet, - action="compile-message", - info="unknown submission", - log=packet["log"], - ) - ) + logger.warning('Unknown submission: %s', packet['submission-id']) + json_log.error(self._make_json_log(packet, action='compile-message', info='unknown submission', + log=packet['log'])) def on_internal_error(self, packet): try: - raise ValueError("\n\n" + packet["message"]) + raise ValueError('\n\n' + packet['message']) except ValueError: - logger.exception( - "Judge %s failed while handling submission %s", - self.name, - packet["submission-id"], - ) + logger.exception('Judge %s failed while handling submission %s', self.name, packet['submission-id']) self._free_self(packet) - id = packet["submission-id"] - self._update_internal_error_submission(id, packet["message"]) - - def _update_internal_error_submission(self, id, message): - if Submission.objects.filter(id=id).update( - status="IE", result="IE", error=message - ): - event.post( - "sub_%s" % Submission.get_id_secret(id), {"type": "internal-error"} - ) - self._post_update_submission(id, "internal-error", done=True) - json_log.info( - self._make_json_log( - sub=id, - action="internal-error", - message=message, - finish=True, - result="IE", - ) - ) + id = packet['submission-id'] + if Submission.objects.filter(id=id).update(status='IE', result='IE', error=packet['message']): + event.post('sub_%s' % Submission.get_id_secret(id), {'type': 'internal-error'}) + self._post_update_submission(id, 'internal-error', done=True) + json_log.info(self._make_json_log(packet, action='internal-error', message=packet['message'], + finish=True, result='IE')) else: - logger.warning("Unknown submission: %s", id) - json_log.error( - self._make_json_log( - sub=id, - action="internal-error", - info="unknown submission", - message=message, - finish=True, - result="IE", - ) - ) + logger.warning('Unknown submission: %s', id) + json_log.error(self._make_json_log(packet, action='internal-error', info='unknown submission', + message=packet['message'], finish=True, result='IE')) def on_submission_terminated(self, packet): - logger.info("%s: Submission aborted: %s", self.name, packet["submission-id"]) + logger.info('%s: Submission aborted: %s', self.name, packet['submission-id']) self._free_self(packet) - if Submission.objects.filter(id=packet["submission-id"]).update( - status="AB", result="AB" - ): - event.post( - "sub_%s" % Submission.get_id_secret(packet["submission-id"]), - {"type": "aborted-submission"}, - ) - self._post_update_submission( - packet["submission-id"], "terminated", done=True - ) - json_log.info( - self._make_json_log(packet, action="aborted", finish=True, result="AB") - ) + if Submission.objects.filter(id=packet['submission-id']).update(status='AB', result='AB'): + event.post('sub_%s' % Submission.get_id_secret(packet['submission-id']), {'type': 'aborted-submission'}) + self._post_update_submission(packet['submission-id'], 'terminated', done=True) + json_log.info(self._make_json_log(packet, action='aborted', finish=True, result='AB')) else: - logger.warning("Unknown submission: %s", packet["submission-id"]) - json_log.error( - self._make_json_log( - packet, - action="aborted", - info="unknown submission", - finish=True, - result="AB", - ) - ) + logger.warning('Unknown submission: %s', packet['submission-id']) + json_log.error(self._make_json_log(packet, action='aborted', info='unknown submission', + finish=True, result='AB')) def on_batch_begin(self, packet): - logger.info("%s: Batch began on: %s", self.name, packet["submission-id"]) + logger.info('%s: Batch began on: %s', self.name, packet['submission-id']) self.in_batch = True if self.batch_id is None: self.batch_id = 0 - self._submission_is_batch(packet["submission-id"]) + self._submission_is_batch(packet['submission-id']) self.batch_id += 1 - json_log.info( - self._make_json_log(packet, action="batch-begin", batch=self.batch_id) - ) + json_log.info(self._make_json_log(packet, action='batch-begin', batch=self.batch_id)) def on_batch_end(self, packet): self.in_batch = False - logger.info("%s: Batch ended on: %s", self.name, packet["submission-id"]) - json_log.info( - self._make_json_log(packet, action="batch-end", batch=self.batch_id) - ) + logger.info('%s: Batch ended on: %s', self.name, packet['submission-id']) + json_log.info(self._make_json_log(packet, action='batch-end', batch=self.batch_id)) - def on_test_case( - self, - packet, - max_feedback=SubmissionTestCase._meta.get_field("feedback").max_length, - ): - logger.info( - "%s: %d test case(s) completed on: %s", - self.name, - len(packet["cases"]), - packet["submission-id"], - ) + def on_test_case(self, packet, max_feedback=SubmissionTestCase._meta.get_field('feedback').max_length): + logger.info('%s: %d test case(s) completed on: %s', self.name, len(packet['cases']), packet['submission-id']) - id = packet["submission-id"] - updates = packet["cases"] - max_position = max(map(itemgetter("position"), updates)) - sum_points = sum(map(itemgetter("points"), updates)) + id = packet['submission-id'] + updates = packet['cases'] + max_position = max(map(itemgetter('position'), updates)) + sum_points = sum(map(itemgetter('points'), updates)) - if not Submission.objects.filter(id=id).update( - current_testcase=max_position + 1, points=F("points") + sum_points - ): - logger.warning("Unknown submission: %s", id) - json_log.error( - self._make_json_log( - packet, action="test-case", info="unknown submission" - ) - ) + + if not Submission.objects.filter(id=id).update(current_testcase=max_position + 1, points=F('points') + sum_points): + logger.warning('Unknown submission: %s', id) + json_log.error(self._make_json_log(packet, action='test-case', info='unknown submission')) return bulk_test_case_updates = [] for result in updates: - test_case = SubmissionTestCase(submission_id=id, case=result["position"]) - status = result["status"] + test_case = SubmissionTestCase(submission_id=id, case=result['position']) + status = result['status'] if status & 4: - test_case.status = "TLE" + test_case.status = 'TLE' elif status & 8: - test_case.status = "MLE" + test_case.status = 'MLE' elif status & 64: - test_case.status = "OLE" + test_case.status = 'OLE' elif status & 2: - test_case.status = "RTE" + test_case.status = 'RTE' elif status & 16: - test_case.status = "IR" + test_case.status = 'IR' elif status & 1: - test_case.status = "WA" + test_case.status = 'WA' elif status & 32: - test_case.status = "SC" + test_case.status = 'SC' else: - test_case.status = "AC" - test_case.time = result["time"] - test_case.memory = result["memory"] - test_case.points = result["points"] - test_case.total = result["total-points"] + test_case.status = 'AC' + test_case.time = result['time'] + test_case.memory = result['memory'] + test_case.points = result['points'] + test_case.total = result['total-points'] test_case.batch = self.batch_id if self.in_batch else None - test_case.feedback = (result.get("feedback") or "")[:max_feedback] - test_case.extended_feedback = result.get("extended-feedback") or "" - test_case.output = result["output"] + test_case.feedback = (result.get('feedback') or '')[:max_feedback] + test_case.extended_feedback = result.get('extended-feedback') or '' + test_case.output = result['output'] bulk_test_case_updates.append(test_case) - json_log.info( - self._make_json_log( - packet, - action="test-case", - case=test_case.case, - batch=test_case.batch, - time=test_case.time, - memory=test_case.memory, - feedback=test_case.feedback, - extended_feedback=test_case.extended_feedback, - output=test_case.output, - points=test_case.points, - total=test_case.total, - status=test_case.status, - ) - ) + json_log.info(self._make_json_log( + packet, action='test-case', case=test_case.case, batch=test_case.batch, + time=test_case.time, memory=test_case.memory, feedback=test_case.feedback, + extended_feedback=test_case.extended_feedback, output=test_case.output, + points=test_case.points, total=test_case.total, status=test_case.status, + )) do_post = True @@ -872,34 +563,29 @@ class JudgeHandler(ZlibPacketHandler): self.update_counter[id] = (1, time.monotonic()) if do_post: - event.post( - "sub_%s" % Submission.get_id_secret(id), - { - "type": "test-case", - "id": max_position, - }, - ) - self._post_update_submission(id, state="test-case") + event.post('sub_%s' % Submission.get_id_secret(id), { + 'type': 'test-case', + 'id': max_position, + }) + self._post_update_submission(id, state='test-case') SubmissionTestCase.objects.bulk_create(bulk_test_case_updates) def on_malformed(self, packet): - logger.error("%s: Malformed packet: %s", self.name, packet) - json_log.exception( - self._make_json_log(sub=self._working, info="malformed json packet") - ) + logger.error('%s: Malformed packet: %s', self.name, packet) + json_log.exception(self._make_json_log(sub=self._working, info='malformed json packet')) def on_ping_response(self, packet): end = time.time() - self._ping_average.append(end - packet["when"]) - self._time_delta.append((end + packet["when"]) / 2 - packet["time"]) + self._ping_average.append(end - packet['when']) + self._time_delta.append((end + packet['when']) / 2 - packet['time']) self.latency = sum(self._ping_average) / len(self._ping_average) self.time_delta = sum(self._time_delta) / len(self._time_delta) - self.load = packet["load"] + self.load = packet['load'] self._update_ping() def _free_self(self, packet): - self.judges.on_judge_free(self, packet["submission-id"]) + self.judges.on_judge_free(self, packet['submission-id']) def _ping_thread(self): try: @@ -908,19 +594,19 @@ class JudgeHandler(ZlibPacketHandler): if self._stop_ping.wait(10): break except Exception: - logger.exception("Ping error in %s", self.name) + logger.exception('Ping error in %s', self.name) self.close() raise def _make_json_log(self, packet=None, sub=None, **kwargs): data = { - "judge": self.name, - "address": self.judge_address, + 'judge': self.name, + 'address': self.judge_address, } if sub is None and packet is not None: - sub = packet.get("submission-id") + sub = packet.get('submission-id') if sub is not None: - data["submission"] = sub + data['submission'] = sub data.update(kwargs) return json.dumps(data) @@ -928,39 +614,17 @@ class JudgeHandler(ZlibPacketHandler): if self._submission_cache_id == id: data = self._submission_cache else: - self._submission_cache = data = ( - Submission.objects.filter(id=id) - .values( - "problem__is_public", - "contest_object__key", - "user_id", - "problem_id", - "status", - "language__key", - ) - .get() - ) + self._submission_cache = data = Submission.objects.filter(id=id).values( + 'problem__is_public', 'contest_object__key', + 'user_id', 'problem_id', 'status', 'language__key', + ).get() self._submission_cache_id = id - if data["problem__is_public"]: - event.post( - "submissions", - { - "type": "done-submission" if done else "update-submission", - "state": state, - "id": id, - "contest": data["contest_object__key"], - "user": data["user_id"], - "problem": data["problem_id"], - "status": data["status"], - "language": data["language__key"], - }, - ) - - def on_cleanup(self): - db.connection.close() - - -@cache_wrapper(prefix="gjp", timeout=3600) -def _get_judge_problems(judge): - return set(judge.problems.values_list("code", flat=True)) + if data['problem__is_public']: + event.post('submissions', { + 'type': 'done-submission' if done else 'update-submission', + 'state': state, 'id': id, + 'contest': data['contest_object__key'], + 'user': data['user_id'], 'problem': data['problem_id'], + 'status': data['status'], 'language': data['language__key'], + }) diff --git a/judge/bridge/judge_list.py b/judge/bridge/judge_list.py index bf2b54a..828bb83 100644 --- a/judge/bridge/judge_list.py +++ b/judge/bridge/judge_list.py @@ -3,16 +3,14 @@ from collections import namedtuple from operator import attrgetter from threading import RLock -from judge.bridge.utils import VanishedSubmission - try: from llist import dllist except ImportError: from pyllist import dllist -logger = logging.getLogger("judge.bridge") +logger = logging.getLogger('judge.bridge') -PriorityMarker = namedtuple("PriorityMarker", "priority") +PriorityMarker = namedtuple('PriorityMarker', 'priority') class JudgeList(object): @@ -20,9 +18,7 @@ class JudgeList(object): def __init__(self): self.queue = dllist() - self.priority = [ - self.queue.append(PriorityMarker(i)) for i in range(self.priorities) - ] + self.priority = [self.queue.append(PriorityMarker(i)) for i in range(self.priorities)] self.judges = set() self.node_map = {} self.submission_map = {} @@ -36,21 +32,11 @@ class JudgeList(object): id, problem, language, source, judge_id = node.value if judge.can_judge(problem, language, judge_id): self.submission_map[id] = judge - logger.info( - "Dispatched queued submission %d: %s", id, judge.name - ) + logger.info('Dispatched queued submission %d: %s', id, judge.name) try: judge.submit(id, problem, language, source) - except VanishedSubmission: - pass except Exception: - logger.exception( - "Failed to dispatch %d (%s, %s) to %s", - id, - problem, - language, - judge.name, - ) + logger.exception('Failed to dispatch %d (%s, %s) to %s', id, problem, language, judge.name) self.judges.remove(judge) return self.queue.remove(node) @@ -90,15 +76,14 @@ class JudgeList(object): def on_judge_free(self, judge, submission): with self.lock: - logger.info("Judge available after grading %d: %s", submission, judge.name) + logger.info('Judge available after grading %d: %s', submission, judge.name) del self.submission_map[submission] judge._working = False - judge._working_data = {} self._handle_free_judge(judge) def abort(self, submission): with self.lock: - logger.info("Abort request: %d", submission) + logger.info('Abort request: %d', submission) try: self.submission_map[submission].abort() return True @@ -123,33 +108,21 @@ class JudgeList(object): return candidates = [ - judge - for judge in self.judges - if not judge.working and judge.can_judge(problem, language, judge_id) + judge for judge in self.judges if not judge.working and judge.can_judge(problem, language, judge_id) ] if judge_id: - logger.info( - "Specified judge %s is%savailable", - judge_id, - " " if candidates else " not ", - ) + logger.info('Specified judge %s is%savailable', judge_id, ' ' if candidates else ' not ') else: - logger.info("Free judges: %d", len(candidates)) + logger.info('Free judges: %d', len(candidates)) if candidates: # Schedule the submission on the judge reporting least load. - judge = min(candidates, key=attrgetter("load")) - logger.info("Dispatched submission %d to: %s", id, judge.name) + judge = min(candidates, key=attrgetter('load')) + logger.info('Dispatched submission %d to: %s', id, judge.name) self.submission_map[id] = judge try: judge.submit(id, problem, language, source) except Exception: - logger.exception( - "Failed to dispatch %d (%s, %s) to %s", - id, - problem, - language, - judge.name, - ) + logger.exception('Failed to dispatch %d (%s, %s) to %s', id, problem, language, judge.name) self.judges.discard(judge) return self.judge(id, problem, language, source, judge_id, priority) else: @@ -157,4 +130,4 @@ class JudgeList(object): (id, problem, language, source, judge_id), self.priority[priority], ) - logger.info("Queued submission: %d", id) + logger.info('Queued submission: %d', id) diff --git a/judge/bridge/server.py b/judge/bridge/server.py index 4e67310..cc83f84 100644 --- a/judge/bridge/server.py +++ b/judge/bridge/server.py @@ -12,9 +12,7 @@ class Server: self._shutdown = threading.Event() def serve_forever(self): - threads = [ - threading.Thread(target=server.serve_forever) for server in self.servers - ] + threads = [threading.Thread(target=server.serve_forever) for server in self.servers] for thread in threads: thread.daemon = True thread.start() diff --git a/judge/bridge/utils.py b/judge/bridge/utils.py deleted file mode 100644 index dfb2ac9..0000000 --- a/judge/bridge/utils.py +++ /dev/null @@ -1,2 +0,0 @@ -class VanishedSubmission(Exception): - pass diff --git a/judge/caching.py b/judge/caching.py index f40adb1..7f0a687 100644 --- a/judge/caching.py +++ b/judge/caching.py @@ -1,117 +1,10 @@ -from inspect import signature -from django.core.cache import cache, caches -from django.db.models.query import QuerySet -from django.core.handlers.wsgi import WSGIRequest - -import hashlib - -from judge.logging import log_debug - -MAX_NUM_CHAR = 50 -NONE_RESULT = "__None__" +from django.core.cache import cache -def arg_to_str(arg): - if hasattr(arg, "id"): - return str(arg.id) - if isinstance(arg, list) or isinstance(arg, QuerySet): - return hashlib.sha1(str(list(arg)).encode()).hexdigest()[:MAX_NUM_CHAR] - if len(str(arg)) > MAX_NUM_CHAR: - return str(arg)[:MAX_NUM_CHAR] - return str(arg) - - -def filter_args(args_list): - return [x for x in args_list if not isinstance(x, WSGIRequest)] - - -l0_cache = caches["l0"] if "l0" in caches else None - - -def cache_wrapper(prefix, timeout=None, expected_type=None): - def get_key(func, *args, **kwargs): - args_list = list(args) - signature_args = list(signature(func).parameters.keys()) - args_list += [kwargs.get(k) for k in signature_args[len(args) :]] - args_list = filter_args(args_list) - args_list = [arg_to_str(i) for i in args_list] - key = prefix + ":" + ":".join(args_list) - key = key.replace(" ", "_") - return key - - def _get(key): - if not l0_cache: - return cache.get(key) - result = l0_cache.get(key) - if result is None: - result = cache.get(key) - return result - - def _set_l0(key, value): - if l0_cache: - l0_cache.set(key, value, 30) - - def _set(key, value, timeout): - _set_l0(key, value) - cache.set(key, value, timeout) - - def decorator(func): - def _validate_type(cache_key, result): - if expected_type and not isinstance(result, expected_type): - data = { - "function": f"{func.__module__}.{func.__qualname__}", - "result": str(result)[:30], - "expected_type": expected_type, - "type": type(result), - "key": cache_key, - } - log_debug("invalid_key", data) - return False - return True - - def wrapper(*args, **kwargs): - cache_key = get_key(func, *args, **kwargs) - result = _get(cache_key) - if result is not None and _validate_type(cache_key, result): - _set_l0(cache_key, result) - if type(result) == str and result == NONE_RESULT: - result = None - return result - result = func(*args, **kwargs) - if result is None: - cache_result = NONE_RESULT - else: - cache_result = result - _set(cache_key, cache_result, timeout) - return result - - def dirty(*args, **kwargs): - cache_key = get_key(func, *args, **kwargs) - cache.delete(cache_key) - if l0_cache: - l0_cache.delete(cache_key) - - def prefetch_multi(args_list): - keys = [] - for args in args_list: - keys.append(get_key(func, *args)) - results = cache.get_many(keys) - for key, result in results.items(): - if result is not None: - _set_l0(key, result) - - def dirty_multi(args_list): - keys = [] - for args in args_list: - keys.append(get_key(func, *args)) - cache.delete_many(keys) - if l0_cache: - l0_cache.delete_many(keys) - - wrapper.dirty = dirty - wrapper.prefetch_multi = prefetch_multi - wrapper.dirty_multi = dirty_multi - - return wrapper - - return decorator +def finished_submission(sub): + keys = ['user_complete:%d' % sub.user_id, 'user_attempted:%s' % sub.user_id] + if hasattr(sub, 'contest'): + participation = sub.contest.participation + keys += ['contest_complete:%d' % participation.id] + keys += ['contest_attempted:%d' % participation.id] + cache.delete_many(keys) diff --git a/judge/comments.py b/judge/comments.py new file mode 100644 index 0000000..7408e68 --- /dev/null +++ b/judge/comments.py @@ -0,0 +1,155 @@ +from django import forms +from django.conf import settings +from django.contrib.auth.decorators import login_required +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError +from django.db.models import Count +from django.db.models.expressions import F, Value +from django.db.models.functions import Coalesce +from django.forms import ModelForm +from django.http import HttpResponseForbidden, HttpResponseNotFound, HttpResponseRedirect +from django.urls import reverse_lazy +from django.utils.decorators import method_decorator +from django.utils.translation import gettext as _ +from django.views.generic import View +from django.views.generic.base import TemplateResponseMixin +from django.views.generic.detail import SingleObjectMixin +from reversion import revisions +from reversion.models import Revision, Version + +from judge.dblock import LockModel +from judge.models import Comment, CommentLock, CommentVote, Notification +from judge.utils.raw_sql import RawSQLColumn, unique_together_left_join +from judge.widgets import HeavyPreviewPageDownWidget +from judge.jinja2.reference import get_user_from_text + + + +def add_mention_notifications(comment): + user_referred = get_user_from_text(comment.body).exclude(id=comment.author.id) + for user in user_referred: + notification_ref = Notification(owner=user, + comment=comment, + category='Mention') + notification_ref.save() + +def del_mention_notifications(comment): + query = { + 'comment': comment, + 'category': 'Mention' + } + Notification.objects.filter(**query).delete() + + + +class CommentForm(ModelForm): + class Meta: + model = Comment + fields = ['body', 'parent'] + widgets = { + 'parent': forms.HiddenInput(), + } + + if HeavyPreviewPageDownWidget is not None: + widgets['body'] = HeavyPreviewPageDownWidget(preview=reverse_lazy('comment_preview'), + preview_timeout=1000, hide_preview_button=True) + + def __init__(self, request, *args, **kwargs): + self.request = request + super(CommentForm, self).__init__(*args, **kwargs) + self.fields['body'].widget.attrs.update({'placeholder': _('Comment body')}) + + def clean(self): + if self.request is not None and self.request.user.is_authenticated: + profile = self.request.profile + if profile.mute: + raise ValidationError(_('Your part is silent, little toad.')) + elif (not self.request.user.is_staff and + not profile.submission_set.filter(points=F('problem__points')).exists()): + raise ValidationError(_('You need to have solved at least one problem ' + 'before your voice can be heard.')) + return super(CommentForm, self).clean() + + +class CommentedDetailView(TemplateResponseMixin, SingleObjectMixin, View): + comment_page = None + + def get_comment_page(self): + if self.comment_page is None: + raise NotImplementedError() + return self.comment_page + + def is_comment_locked(self): + if self.request.user.has_perm('judge.override_comment_lock'): + return False + return (CommentLock.objects.filter(page=self.get_comment_page()).exists() + or (self.request.in_contest and self.request.participation.contest.use_clarifications)) + + @method_decorator(login_required) + def post(self, request, *args, **kwargs): + self.object = self.get_object() + page = self.get_comment_page() + + if self.is_comment_locked(): + return HttpResponseForbidden() + + parent = request.POST.get('parent') + if parent: + try: + parent = int(parent) + except ValueError: + return HttpResponseNotFound() + else: + if not Comment.objects.filter(hidden=False, id=parent, page=page).exists(): + return HttpResponseNotFound() + + form = CommentForm(request, request.POST) + if form.is_valid(): + comment = form.save(commit=False) + comment.author = request.profile + comment.page = page + + + with LockModel(write=(Comment, Revision, Version), read=(ContentType,)), revisions.create_revision(): + revisions.set_user(request.user) + revisions.set_comment(_('Posted comment')) + comment.save() + + # add notification for reply + if comment.parent and comment.parent.author != comment.author: + notification_rep = Notification(owner=comment.parent.author, + comment=comment, + category='Reply') + notification_rep.save() + + add_mention_notifications(comment) + + return HttpResponseRedirect(request.path) + + context = self.get_context_data(object=self.object, comment_form=form) + return self.render_to_response(context) + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + return self.render_to_response(self.get_context_data( + object=self.object, + comment_form=CommentForm(request, initial={'page': self.get_comment_page(), 'parent': None}), + )) + + def get_context_data(self, **kwargs): + context = super(CommentedDetailView, self).get_context_data(**kwargs) + queryset = Comment.objects.filter(hidden=False, page=self.get_comment_page()) + context['has_comments'] = queryset.exists() + context['comment_lock'] = self.is_comment_locked() + queryset = queryset.select_related('author__user').defer('author__about').annotate(revisions=Count('versions')) + + if self.request.user.is_authenticated: + queryset = queryset.annotate(vote_score=Coalesce(RawSQLColumn(CommentVote, 'score'), Value(0))) + profile = self.request.profile + unique_together_left_join(queryset, CommentVote, 'comment', 'voter', profile.id) + context['is_new_user'] = (not self.request.user.is_staff and + not profile.submission_set.filter(points=F('problem__points')).exists()) + context['comment_list'] = queryset + context['vote_hide_threshold'] = settings.DMOJ_COMMENT_VOTE_HIDE_THRESHOLD + return context + diff --git a/judge/contest_format/__init__.py b/judge/contest_format/__init__.py index 9f6d628..ee57ccf 100644 --- a/judge/contest_format/__init__.py +++ b/judge/contest_format/__init__.py @@ -3,6 +3,4 @@ from judge.contest_format.default import DefaultContestFormat from judge.contest_format.ecoo import ECOOContestFormat from judge.contest_format.icpc import ICPCContestFormat from judge.contest_format.ioi import IOIContestFormat -from judge.contest_format.new_ioi import NewIOIContestFormat -from judge.contest_format.ultimate import UltimateContestFormat from judge.contest_format.registry import choices, formats diff --git a/judge/contest_format/atcoder.py b/judge/contest_format/atcoder.py index 65ceb90..28b188d 100644 --- a/judge/contest_format/atcoder.py +++ b/judge/contest_format/atcoder.py @@ -10,18 +10,18 @@ from django.utils.translation import gettext_lazy from judge.contest_format.default import DefaultContestFormat from judge.contest_format.registry import register_contest_format -from judge.timezone import from_database_time, to_database_time +from judge.timezone import from_database_time from judge.utils.timedelta import nice_repr -@register_contest_format("atcoder") +@register_contest_format('atcoder') class AtCoderContestFormat(DefaultContestFormat): - name = gettext_lazy("AtCoder") - config_defaults = {"penalty": 5} - config_validators = {"penalty": lambda x: x >= 0} - """ + name = gettext_lazy('AtCoder') + config_defaults = {'penalty': 5} + config_validators = {'penalty': lambda x: x >= 0} + ''' penalty: Number of penalty minutes each incorrect submission adds. Defaults to 5. - """ + ''' @classmethod def validate(cls, config): @@ -29,9 +29,7 @@ class AtCoderContestFormat(DefaultContestFormat): return if not isinstance(config, dict): - raise ValidationError( - "AtCoder-styled contest expects no config or dict as config" - ) + raise ValidationError('AtCoder-styled contest expects no config or dict as config') for key, value in config.items(): if key not in cls.config_defaults: @@ -39,9 +37,7 @@ class AtCoderContestFormat(DefaultContestFormat): if not isinstance(value, type(cls.config_defaults[key])): raise ValidationError('invalid type for config key "%s"' % key) if not cls.config_validators[key](value): - raise ValidationError( - 'invalid value "%s" for config key "%s"' % (value, key) - ) + raise ValidationError('invalid value "%s" for config key "%s"' % (value, key)) def __init__(self, contest, config): self.config = self.config_defaults.copy() @@ -54,13 +50,8 @@ class AtCoderContestFormat(DefaultContestFormat): points = 0 format_data = {} - frozen_time = self.contest.end_time - if self.contest.freeze_after: - frozen_time = participation.start + self.contest.freeze_after - with connection.cursor() as cursor: - cursor.execute( - """ + cursor.execute(''' SELECT MAX(cs.points) as `score`, ( SELECT MIN(csub.date) FROM judge_contestsubmission ccs LEFT OUTER JOIN @@ -70,29 +61,22 @@ class AtCoderContestFormat(DefaultContestFormat): FROM judge_contestproblem cp INNER JOIN judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN judge_submission sub ON (sub.id = cs.submission_id) - WHERE sub.date < %s GROUP BY cp.id - """, - (participation.id, participation.id, to_database_time(frozen_time)), - ) + ''', (participation.id, participation.id)) for score, time, prob in cursor.fetchall(): time = from_database_time(time) dt = (time - participation.start).total_seconds() # Compute penalty - if self.config["penalty"]: + if self.config['penalty']: # An IE can have a submission result of `None` - subs = ( - participation.submissions.exclude( - submission__result__isnull=True - ) - .exclude(submission__result__in=["IE", "CE"]) - .filter(problem_id=prob) - ) + subs = participation.submissions.exclude(submission__result__isnull=True) \ + .exclude(submission__result__in=['IE', 'CE']) \ + .filter(problem_id=prob) if score: prev = subs.filter(submission__date__lte=time).count() - 1 - penalty += prev * self.config["penalty"] * 60 + penalty += prev * self.config['penalty'] * 60 else: # We should always display the penalty, even if the user has a score of 0 prev = subs.count() @@ -102,52 +86,29 @@ class AtCoderContestFormat(DefaultContestFormat): if score: cumtime = max(cumtime, dt) - format_data[str(prob)] = {"time": dt, "points": score, "penalty": prev} + format_data[str(prob)] = {'time': dt, 'points': score, 'penalty': prev} points += score - self.handle_frozen_state(participation, format_data) participation.cumtime = cumtime + penalty - participation.score = round(points, self.contest.points_precision) + participation.score = points participation.tiebreaker = 0 participation.format_data = format_data participation.save() - def display_user_problem(self, participation, contest_problem, show_final=False): + def display_user_problem(self, participation, contest_problem): format_data = (participation.format_data or {}).get(str(contest_problem.id)) if format_data: - penalty = ( - format_html( - ' ({penalty})', - penalty=floatformat(format_data["penalty"]), - ) - if format_data.get("penalty") - else "" - ) + penalty = format_html(' ({penalty})', + penalty=floatformat(format_data['penalty'])) if format_data['penalty'] else '' return format_html( - '{points}{penalty}
{time}
', - state=( - ( - "pretest-" - if self.contest.run_pretests_only - and contest_problem.is_pretested - else "" - ) - + self.best_solution_state( - format_data["points"], contest_problem.points - ) - + (" frozen" if format_data.get("frozen") else "") - ), - url=reverse( - "contest_user_submissions_ajax", - args=[ - self.contest.key, - participation.id, - contest_problem.problem.code, - ], - ), - points=floatformat(format_data["points"]), + '{points}{penalty}
{time}
', + state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') + + self.best_solution_state(format_data['points'], contest_problem.points)), + url=reverse('contest_user_submissions', + args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]), + points=floatformat(format_data['points']), penalty=penalty, - time=nice_repr(timedelta(seconds=format_data["time"]), "noday"), + time=nice_repr(timedelta(seconds=format_data['time']), 'noday'), ) else: return mark_safe('') diff --git a/judge/contest_format/base.py b/judge/contest_format/base.py index 3b47e59..d4e6e6f 100644 --- a/judge/contest_format/base.py +++ b/judge/contest_format/base.py @@ -1,5 +1,6 @@ from abc import ABCMeta, abstractmethod, abstractproperty -from django.db.models import Max + +from django.utils import six class abstractclassmethod(classmethod): @@ -10,9 +11,7 @@ class abstractclassmethod(classmethod): super(abstractclassmethod, self).__init__(callable) -class BaseContestFormat(metaclass=ABCMeta): - has_hidden_subtasks = False - +class BaseContestFormat(six.with_metaclass(ABCMeta)): @abstractmethod def __init__(self, contest, config): self.config = config @@ -50,7 +49,7 @@ class BaseContestFormat(metaclass=ABCMeta): raise NotImplementedError() @abstractmethod - def display_user_problem(self, participation, contest_problem, show_final): + def display_user_problem(self, participation, contest_problem): """ Returns the HTML fragment to show a user's performance on an individual problem. This is expected to use information from the format_data field instead of computing it from scratch. @@ -62,7 +61,7 @@ class BaseContestFormat(metaclass=ABCMeta): raise NotImplementedError() @abstractmethod - def display_participation_result(self, participation, show_final): + def display_participation_result(self, participation): """ Returns the HTML fragment to show a user's performance on the whole contest. This is expected to use information from the format_data field instead of computing it from scratch. @@ -94,30 +93,7 @@ class BaseContestFormat(metaclass=ABCMeta): @classmethod def best_solution_state(cls, points, total): if not points: - return "failed-score" + return 'failed-score' if points == total: - return "full-score" - return "partial-score" - - def handle_frozen_state(self, participation, format_data): - hidden_subtasks = {} - if hasattr(self, "get_hidden_subtasks"): - hidden_subtasks = self.get_hidden_subtasks() - - queryset = participation.submissions.values("problem_id").annotate( - time=Max("submission__date") - ) - for result in queryset: - problem = str(result["problem_id"]) - if not (self.contest.freeze_after or hidden_subtasks.get(problem)): - continue - if format_data.get(problem): - is_after_freeze = ( - self.contest.freeze_after - and result["time"] - >= self.contest.freeze_after + participation.start - ) - if is_after_freeze or hidden_subtasks.get(problem): - format_data[problem]["frozen"] = True - else: - format_data[problem] = {"time": 0, "points": 0, "frozen": True} + return 'full-score' + return 'partial-score' diff --git a/judge/contest_format/default.py b/judge/contest_format/default.py index 40fceb0..532102d 100644 --- a/judge/contest_format/default.py +++ b/judge/contest_format/default.py @@ -13,16 +13,14 @@ from judge.contest_format.registry import register_contest_format from judge.utils.timedelta import nice_repr -@register_contest_format("default") +@register_contest_format('default') class DefaultContestFormat(BaseContestFormat): - name = gettext_lazy("Default") + name = gettext_lazy('Default') @classmethod def validate(cls, config): if config is not None and (not isinstance(config, dict) or config): - raise ValidationError( - "default contest expects no config or empty dict as config" - ) + raise ValidationError('default contest expects no config or empty dict as config') def __init__(self, contest, config): super(DefaultContestFormat, self).__init__(contest, config) @@ -32,84 +30,49 @@ class DefaultContestFormat(BaseContestFormat): points = 0 format_data = {} - queryset = participation.submissions - - if self.contest.freeze_after: - queryset = queryset.filter( - submission__date__lt=participation.start + self.contest.freeze_after - ) - - queryset = queryset.values("problem_id").annotate( - time=Max("submission__date"), - points=Max("points"), - ) - - for result in queryset: - dt = (result["time"] - participation.start).total_seconds() - if result["points"]: + for result in participation.submissions.values('problem_id').annotate( + time=Max('submission__date'), points=Max('points'), + ): + dt = (result['time'] - participation.start).total_seconds() + if result['points']: cumtime += dt - format_data[str(result["problem_id"])] = { - "time": dt, - "points": result["points"], - } - points += result["points"] + format_data[str(result['problem_id'])] = {'time': dt, 'points': result['points']} + points += result['points'] - self.handle_frozen_state(participation, format_data) participation.cumtime = max(cumtime, 0) - participation.score = round(points, self.contest.points_precision) + participation.score = points participation.tiebreaker = 0 participation.format_data = format_data participation.save() - def display_user_problem(self, participation, contest_problem, show_final=False): + def display_user_problem(self, participation, contest_problem): format_data = (participation.format_data or {}).get(str(contest_problem.id)) if format_data: return format_html( - '{points}
{time}
', - state=( - ( - "pretest-" - if self.contest.run_pretests_only - and contest_problem.is_pretested - else "" - ) - + self.best_solution_state( - format_data["points"], contest_problem.points - ) - + (" frozen" if format_data.get("frozen") else "") - ), - url=reverse( - "contest_user_submissions_ajax", - args=[ - self.contest.key, - participation.id, - contest_problem.problem.code, - ], - ), - points=floatformat( - format_data["points"], -self.contest.points_precision - ), - time=nice_repr(timedelta(seconds=format_data["time"]), "noday"), + u'{points}
{time}
', + state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') + + self.best_solution_state(format_data['points'], contest_problem.points)), + url=reverse('contest_user_submissions', + args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]), + points=floatformat(format_data['points'], -self.contest.points_precision), + time=nice_repr(timedelta(seconds=format_data['time']), 'noday'), ) else: return mark_safe('') - def display_participation_result(self, participation, show_final=False): + def display_participation_result(self, participation): return format_html( - '{points}
{cumtime}
', + u'{points}
{cumtime}
', points=floatformat(participation.score, -self.contest.points_precision), - cumtime=nice_repr(timedelta(seconds=participation.cumtime), "noday"), + cumtime=nice_repr(timedelta(seconds=participation.cumtime), 'noday'), ) def get_problem_breakdown(self, participation, contest_problems): - return [ - (participation.format_data or {}).get(str(contest_problem.id)) - for contest_problem in contest_problems - ] + return [(participation.format_data or {}).get(str(contest_problem.id)) for contest_problem in contest_problems] def get_contest_problem_label_script(self): - return """ + return ''' function(n) return tostring(math.floor(n + 1)) end - """ + ''' \ No newline at end of file diff --git a/judge/contest_format/ecoo.py b/judge/contest_format/ecoo.py index c5c177d..1199826 100644 --- a/judge/contest_format/ecoo.py +++ b/judge/contest_format/ecoo.py @@ -10,25 +10,21 @@ from django.utils.translation import gettext_lazy from judge.contest_format.default import DefaultContestFormat from judge.contest_format.registry import register_contest_format -from judge.timezone import from_database_time, to_database_time +from judge.timezone import from_database_time from judge.utils.timedelta import nice_repr -@register_contest_format("ecoo") +@register_contest_format('ecoo') class ECOOContestFormat(DefaultContestFormat): - name = gettext_lazy("ECOO") - config_defaults = {"cumtime": False, "first_ac_bonus": 10, "time_bonus": 5} - config_validators = { - "cumtime": lambda x: True, - "first_ac_bonus": lambda x: x >= 0, - "time_bonus": lambda x: x >= 0, - } - """ + name = gettext_lazy('ECOO') + config_defaults = {'cumtime': False, 'first_ac_bonus': 10, 'time_bonus': 5} + config_validators = {'cumtime': lambda x: True, 'first_ac_bonus': lambda x: x >= 0, 'time_bonus': lambda x: x >= 0} + ''' cumtime: Specify True if cumulative time is to be used in breaking ties. Defaults to False. first_ac_bonus: The number of points to award if a solution gets AC on its first non-IE/CE run. Defaults to 10. time_bonus: Number of minutes to award an extra point for submitting before the contest end. Specify 0 to disable. Defaults to 5. - """ + ''' @classmethod def validate(cls, config): @@ -36,9 +32,7 @@ class ECOOContestFormat(DefaultContestFormat): return if not isinstance(config, dict): - raise ValidationError( - "ECOO-styled contest expects no config or dict as config" - ) + raise ValidationError('ECOO-styled contest expects no config or dict as config') for key, value in config.items(): if key not in cls.config_defaults: @@ -46,9 +40,7 @@ class ECOOContestFormat(DefaultContestFormat): if not isinstance(value, type(cls.config_defaults[key])): raise ValidationError('invalid type for config key "%s"' % key) if not cls.config_validators[key](value): - raise ValidationError( - 'invalid value "%s" for config key "%s"' % (value, key) - ) + raise ValidationError('invalid value "%s" for config key "%s"' % (value, key)) def __init__(self, contest, config): self.config = self.config_defaults.copy() @@ -60,13 +52,8 @@ class ECOOContestFormat(DefaultContestFormat): points = 0 format_data = {} - frozen_time = self.contest.end_time - if self.contest.freeze_after: - frozen_time = participation.start + self.contest.freeze_after - with connection.cursor() as cursor: - cursor.execute( - """ + cursor.execute(''' SELECT ( SELECT MAX(ccs.points) FROM judge_contestsubmission ccs LEFT OUTER JOIN @@ -81,92 +68,56 @@ class ECOOContestFormat(DefaultContestFormat): FROM judge_contestproblem cp INNER JOIN judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN judge_submission sub ON (sub.id = cs.submission_id) - WHERE sub.date < %s GROUP BY cp.id - """, - ( - participation.id, - participation.id, - participation.id, - to_database_time(frozen_time), - ), - ) + ''', (participation.id, participation.id, participation.id)) for score, time, prob, subs, max_score in cursor.fetchall(): time = from_database_time(time) dt = (time - participation.start).total_seconds() - if self.config["cumtime"]: + if self.config['cumtime']: cumtime += dt bonus = 0 if score > 0: # First AC bonus if subs == 1 and score == max_score: - bonus += self.config["first_ac_bonus"] + bonus += self.config['first_ac_bonus'] # Time bonus - if self.config["time_bonus"]: - bonus += ( - (participation.end_time - time).total_seconds() - // 60 - // self.config["time_bonus"] - ) + if self.config['time_bonus']: + bonus += (participation.end_time - time).total_seconds() // 60 // self.config['time_bonus'] points += bonus - format_data[str(prob)] = {"time": dt, "points": score, "bonus": bonus} + format_data[str(prob)] = {'time': dt, 'points': score, 'bonus': bonus} points += score - self.handle_frozen_state(participation, format_data) participation.cumtime = cumtime - participation.score = round(points, self.contest.points_precision) + participation.score = points participation.tiebreaker = 0 participation.format_data = format_data participation.save() - def display_user_problem(self, participation, contest_problem, show_final=False): + def display_user_problem(self, participation, contest_problem): format_data = (participation.format_data or {}).get(str(contest_problem.id)) if format_data: - bonus = ( - format_html( - " +{bonus}", bonus=floatformat(format_data["bonus"]) - ) - if format_data.get("bonus") - else "" - ) + bonus = format_html(' +{bonus}', + bonus=floatformat(format_data['bonus'])) if format_data['bonus'] else '' return format_html( - '{points}{bonus}
{time}
', - state=( - ( - "pretest-" - if self.contest.run_pretests_only - and contest_problem.is_pretested - else "" - ) - + self.best_solution_state( - format_data["points"], contest_problem.points - ) - + (" frozen" if format_data.get("frozen") else "") - ), - url=reverse( - "contest_user_submissions_ajax", - args=[ - self.contest.key, - participation.id, - contest_problem.problem.code, - ], - ), - points=floatformat(format_data["points"]), + '{points}{bonus}
{time}
', + state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') + + self.best_solution_state(format_data['points'], contest_problem.points)), + url=reverse('contest_user_submissions', + args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]), + points=floatformat(format_data['points']), bonus=bonus, - time=nice_repr(timedelta(seconds=format_data["time"]), "noday"), + time=nice_repr(timedelta(seconds=format_data['time']), 'noday'), ) else: - return mark_safe("") + return mark_safe('') - def display_participation_result(self, participation, show_final=False): + def display_participation_result(self, participation): return format_html( '{points}
{cumtime}
', points=floatformat(participation.score), - cumtime=nice_repr(timedelta(seconds=participation.cumtime), "noday") - if self.config["cumtime"] - else "", + cumtime=nice_repr(timedelta(seconds=participation.cumtime), 'noday') if self.config['cumtime'] else '', ) diff --git a/judge/contest_format/icpc.py b/judge/contest_format/icpc.py index bf0bfff..e858a6c 100644 --- a/judge/contest_format/icpc.py +++ b/judge/contest_format/icpc.py @@ -10,18 +10,18 @@ from django.utils.translation import gettext_lazy from judge.contest_format.default import DefaultContestFormat from judge.contest_format.registry import register_contest_format -from judge.timezone import from_database_time, to_database_time +from judge.timezone import from_database_time from judge.utils.timedelta import nice_repr -@register_contest_format("icpc") +@register_contest_format('icpc') class ICPCContestFormat(DefaultContestFormat): - name = gettext_lazy("ICPC") - config_defaults = {"penalty": 20} - config_validators = {"penalty": lambda x: x >= 0} - """ + name = gettext_lazy('ICPC') + config_defaults = {'penalty': 20} + config_validators = {'penalty': lambda x: x >= 0} + ''' penalty: Number of penalty minutes each incorrect submission adds. Defaults to 20. - """ + ''' @classmethod def validate(cls, config): @@ -29,9 +29,7 @@ class ICPCContestFormat(DefaultContestFormat): return if not isinstance(config, dict): - raise ValidationError( - "ICPC-styled contest expects no config or dict as config" - ) + raise ValidationError('ICPC-styled contest expects no config or dict as config') for key, value in config.items(): if key not in cls.config_defaults: @@ -39,9 +37,7 @@ class ICPCContestFormat(DefaultContestFormat): if not isinstance(value, type(cls.config_defaults[key])): raise ValidationError('invalid type for config key "%s"' % key) if not cls.config_validators[key](value): - raise ValidationError( - 'invalid value "%s" for config key "%s"' % (value, key) - ) + raise ValidationError('invalid value "%s" for config key "%s"' % (value, key)) def __init__(self, contest, config): self.config = self.config_defaults.copy() @@ -55,13 +51,8 @@ class ICPCContestFormat(DefaultContestFormat): score = 0 format_data = {} - frozen_time = self.contest.end_time - if self.contest.freeze_after: - frozen_time = participation.start + self.contest.freeze_after - with connection.cursor() as cursor: - cursor.execute( - """ + cursor.execute(''' SELECT MAX(cs.points) as `points`, ( SELECT MIN(csub.date) FROM judge_contestsubmission ccs LEFT OUTER JOIN @@ -71,29 +62,22 @@ class ICPCContestFormat(DefaultContestFormat): FROM judge_contestproblem cp INNER JOIN judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN judge_submission sub ON (sub.id = cs.submission_id) - WHERE sub.date < %s GROUP BY cp.id - """, - (participation.id, participation.id, to_database_time(frozen_time)), - ) + ''', (participation.id, participation.id)) for points, time, prob in cursor.fetchall(): time = from_database_time(time) dt = (time - participation.start).total_seconds() # Compute penalty - if self.config["penalty"]: + if self.config['penalty']: # An IE can have a submission result of `None` - subs = ( - participation.submissions.exclude( - submission__result__isnull=True - ) - .exclude(submission__result__in=["IE", "CE"]) - .filter(problem_id=prob) - ) + subs = participation.submissions.exclude(submission__result__isnull=True) \ + .exclude(submission__result__in=['IE', 'CE']) \ + .filter(problem_id=prob) if points: prev = subs.filter(submission__date__lte=time).count() - 1 - penalty += prev * self.config["penalty"] * 60 + penalty += prev * self.config['penalty'] * 60 else: # We should always display the penalty, even if the user has a score of 0 prev = subs.count() @@ -104,58 +88,35 @@ class ICPCContestFormat(DefaultContestFormat): cumtime += dt last = max(last, dt) - format_data[str(prob)] = {"time": dt, "points": points, "penalty": prev} + format_data[str(prob)] = {'time': dt, 'points': points, 'penalty': prev} score += points - self.handle_frozen_state(participation, format_data) participation.cumtime = max(0, cumtime + penalty) - participation.score = round(score, self.contest.points_precision) + participation.score = score participation.tiebreaker = last # field is sorted from least to greatest participation.format_data = format_data participation.save() - def display_user_problem(self, participation, contest_problem, show_final=False): + def display_user_problem(self, participation, contest_problem): format_data = (participation.format_data or {}).get(str(contest_problem.id)) if format_data: - penalty = ( - format_html( - ' +{penalty}', - penalty=floatformat(format_data["penalty"]), - ) - if format_data.get("penalty") - else "" - ) + penalty = format_html(' ({penalty})', + penalty=floatformat(format_data['penalty'])) if format_data['penalty'] else '' return format_html( - '{points}{penalty}
{time}
', - state=( - ( - "pretest-" - if self.contest.run_pretests_only - and contest_problem.is_pretested - else "" - ) - + self.best_solution_state( - format_data["points"], contest_problem.points - ) - + (" frozen" if format_data.get("frozen") else "") - ), - url=reverse( - "contest_user_submissions_ajax", - args=[ - self.contest.key, - participation.id, - contest_problem.problem.code, - ], - ), - points=floatformat(format_data["points"]), + '{points}{penalty}
{time}
', + state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') + + self.best_solution_state(format_data['points'], contest_problem.points)), + url=reverse('contest_user_submissions', + args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]), + points=floatformat(format_data['points']), penalty=penalty, - time=nice_repr(timedelta(seconds=format_data["time"]), "noday"), + time=nice_repr(timedelta(seconds=format_data['time']), 'noday'), ) else: - return mark_safe("") + return mark_safe('') def get_contest_problem_label_script(self): - return """ + return ''' function(n) n = n + 1 ret = "" @@ -165,4 +126,4 @@ class ICPCContestFormat(DefaultContestFormat): end return ret end - """ + ''' \ No newline at end of file diff --git a/judge/contest_format/ioi.py b/judge/contest_format/ioi.py index ce93252..dba337c 100644 --- a/judge/contest_format/ioi.py +++ b/judge/contest_format/ioi.py @@ -12,16 +12,15 @@ from judge.contest_format.default import DefaultContestFormat from judge.contest_format.registry import register_contest_format from judge.timezone import from_database_time from judge.utils.timedelta import nice_repr -from django.db.models import Min, OuterRef, Subquery -@register_contest_format("ioi") +@register_contest_format('ioi') class IOIContestFormat(DefaultContestFormat): - name = gettext_lazy("IOI") - config_defaults = {"cumtime": False} - """ + name = gettext_lazy('IOI') + config_defaults = {'cumtime': False} + ''' cumtime: Specify True if time penalties are to be computed. Defaults to False. - """ + ''' @classmethod def validate(cls, config): @@ -29,9 +28,7 @@ class IOIContestFormat(DefaultContestFormat): return if not isinstance(config, dict): - raise ValidationError( - "IOI-styled contest expects no config or dict as config" - ) + raise ValidationError('IOI-styled contest expects no config or dict as config') for key, value in config.items(): if key not in cls.config_defaults: @@ -46,97 +43,58 @@ class IOIContestFormat(DefaultContestFormat): def update_participation(self, participation): cumtime = 0 - score = 0 + points = 0 format_data = {} - queryset = participation.submissions - if self.contest.freeze_after: - queryset = queryset.filter( - submission__date__lt=participation.start + self.contest.freeze_after - ) + with connection.cursor() as cursor: + cursor.execute(''' + SELECT MAX(cs.points) as `score`, ( + SELECT MIN(csub.date) + FROM judge_contestsubmission ccs LEFT OUTER JOIN + judge_submission csub ON (csub.id = ccs.submission_id) + WHERE ccs.problem_id = cp.id AND ccs.participation_id = %s AND ccs.points = MAX(cs.points) + ) AS `time`, cp.id AS `prob` + FROM judge_contestproblem cp INNER JOIN + judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN + judge_submission sub ON (sub.id = cs.submission_id) + GROUP BY cp.id + ''', (participation.id, participation.id)) - queryset = ( - queryset.values("problem_id") - .filter( - points=Subquery( - queryset.filter(problem_id=OuterRef("problem_id")) - .order_by("-points") - .values("points")[:1] - ) - ) - .annotate(time=Min("submission__date")) - .values_list("problem_id", "time", "points") - ) + for score, time, prob in cursor.fetchall(): + if self.config['cumtime']: + dt = (from_database_time(time) - participation.start).total_seconds() + if score: + cumtime += dt + else: + dt = 0 - for problem_id, time, points in queryset: - if self.config["cumtime"]: - dt = (time - participation.start).total_seconds() - if points: - cumtime += dt - else: - dt = 0 + format_data[str(prob)] = {'time': dt, 'points': score} + points += score - format_data[str(problem_id)] = {"points": points, "time": dt} - score += points - - self.handle_frozen_state(participation, format_data) participation.cumtime = max(cumtime, 0) - participation.score = round(score, self.contest.points_precision) + participation.score = points participation.tiebreaker = 0 participation.format_data = format_data participation.save() - def display_user_problem(self, participation, contest_problem, show_final=False): - if show_final: - format_data = (participation.format_data_final or {}).get( - str(contest_problem.id) - ) - else: - format_data = (participation.format_data or {}).get(str(contest_problem.id)) + def display_user_problem(self, participation, contest_problem): + format_data = (participation.format_data or {}).get(str(contest_problem.id)) if format_data: return format_html( - '{points}
{time}
', - state=( - ( - "pretest-" - if self.contest.run_pretests_only - and contest_problem.is_pretested - else "" - ) - + self.best_solution_state( - format_data["points"], contest_problem.points - ) - + (" frozen" if format_data.get("frozen") else "") - ), - url=reverse( - "contest_user_submissions_ajax", - args=[ - self.contest.key, - participation.id, - contest_problem.problem.code, - ], - ), - points=floatformat( - format_data["points"], -self.contest.points_precision - ), - time=nice_repr(timedelta(seconds=format_data["time"]), "noday") - if self.config["cumtime"] - else "", + '{points}
{time}
', + state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') + + self.best_solution_state(format_data['points'], contest_problem.points)), + url=reverse('contest_user_submissions', + args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]), + points=floatformat(format_data['points']), + time=nice_repr(timedelta(seconds=format_data['time']), 'noday') if self.config['cumtime'] else '', ) else: return mark_safe('') - def display_participation_result(self, participation, show_final=False): - if show_final: - score = participation.score_final - cumtime = participation.cumtime_final - else: - score = participation.score - cumtime = participation.cumtime + def display_participation_result(self, participation): return format_html( '{points}
{cumtime}
', - points=floatformat(score, -self.contest.points_precision), - cumtime=nice_repr(timedelta(seconds=cumtime), "noday") - if self.config["cumtime"] - else "", + points=floatformat(participation.score), + cumtime=nice_repr(timedelta(seconds=participation.cumtime), 'noday') if self.config['cumtime'] else '', ) diff --git a/judge/contest_format/new_ioi.py b/judge/contest_format/new_ioi.py deleted file mode 100644 index 6e09353..0000000 --- a/judge/contest_format/new_ioi.py +++ /dev/null @@ -1,173 +0,0 @@ -from django.db import connection -from django.utils.translation import gettext as _, gettext_lazy - -from judge.contest_format.ioi import IOIContestFormat -from judge.contest_format.registry import register_contest_format -from judge.timezone import from_database_time, to_database_time - - -@register_contest_format("ioi16") -class NewIOIContestFormat(IOIContestFormat): - name = gettext_lazy("New IOI") - config_defaults = {"cumtime": False} - has_hidden_subtasks = True - """ - cumtime: Specify True if time penalties are to be computed. Defaults to False. - """ - - def get_hidden_subtasks(self): - queryset = self.contest.contest_problems.values_list("id", "hidden_subtasks") - res = {} - for problem_id, hidden_subtasks in queryset: - subtasks = set() - if hidden_subtasks: - hidden_subtasks = hidden_subtasks.split(",") - for i in hidden_subtasks: - try: - subtasks.add(int(i)) - except Exception as e: - pass - res[str(problem_id)] = subtasks - return res - - def get_results_by_subtask(self, participation, include_frozen=False): - frozen_time = self.contest.end_time - if self.contest.freeze_after and not include_frozen: - frozen_time = participation.start + self.contest.freeze_after - - with connection.cursor() as cursor: - cursor.execute( - """ - SELECT q.prob, - q.prob_points, - MIN(q.date) as `date`, - q.batch_points, - q.total_batch_points, - q.batch, - q.subid - FROM ( - SELECT cp.id as `prob`, - cp.points as `prob_points`, - sub.id as `subid`, - sub.date as `date`, - tc.points as `points`, - tc.batch as `batch`, - SUM(tc.points) as `batch_points`, - SUM(tc.total) as `total_batch_points` - FROM judge_contestproblem cp - INNER JOIN - judge_contestsubmission cs - ON (cs.problem_id = cp.id AND cs.participation_id = %s) - LEFT OUTER JOIN - judge_submission sub - ON (sub.id = cs.submission_id AND sub.status = 'D') - INNER JOIN judge_submissiontestcase tc - ON sub.id = tc.submission_id - WHERE sub.date < %s - GROUP BY cp.id, tc.batch, sub.id - ) q - INNER JOIN ( - SELECT prob, batch, MAX(r.batch_points) as max_batch_points - FROM ( - SELECT cp.id as `prob`, - tc.batch as `batch`, - SUM(tc.points) as `batch_points` - FROM judge_contestproblem cp - INNER JOIN - judge_contestsubmission cs - ON (cs.problem_id = cp.id AND cs.participation_id = %s) - LEFT OUTER JOIN - judge_submission sub - ON (sub.id = cs.submission_id AND sub.status = 'D') - INNER JOIN judge_submissiontestcase tc - ON sub.id = tc.submission_id - WHERE sub.date < %s - GROUP BY cp.id, tc.batch, sub.id - ) r - GROUP BY prob, batch - ) p - ON p.prob = q.prob AND (p.batch = q.batch OR p.batch is NULL AND q.batch is NULL) - WHERE p.max_batch_points = q.batch_points - GROUP BY q.prob, q.batch - """, - ( - participation.id, - to_database_time(frozen_time), - participation.id, - to_database_time(frozen_time), - ), - ) - - return cursor.fetchall() - - def update_participation(self, participation): - hidden_subtasks = self.get_hidden_subtasks() - - def calculate_format_data(participation, include_frozen): - format_data = {} - for ( - problem_id, - problem_points, - time, - subtask_points, - total_subtask_points, - subtask, - sub_id, - ) in self.get_results_by_subtask(participation, include_frozen): - problem_id = str(problem_id) - time = from_database_time(time) - if self.config["cumtime"]: - dt = (time - participation.start).total_seconds() - else: - dt = 0 - - if format_data.get(problem_id) is None: - format_data[problem_id] = { - "points": 0, - "time": 0, - "total_points": 0, - } - if ( - subtask not in hidden_subtasks.get(problem_id, set()) - or include_frozen - ): - format_data[problem_id]["points"] += subtask_points - format_data[problem_id]["total_points"] += total_subtask_points - format_data[problem_id]["time"] = max( - dt, format_data[problem_id]["time"] - ) - format_data[problem_id]["problem_points"] = problem_points - - return format_data - - def recalculate_results(format_data): - cumtime = 0 - score = 0 - for problem_data in format_data.values(): - if not problem_data["total_points"]: - continue - penalty = problem_data["time"] - problem_data["points"] = ( - problem_data["points"] - / problem_data["total_points"] - * problem_data["problem_points"] - ) - if self.config["cumtime"] and problem_data["points"]: - cumtime += penalty - score += problem_data["points"] - return score, cumtime - - format_data = calculate_format_data(participation, False) - score, cumtime = recalculate_results(format_data) - self.handle_frozen_state(participation, format_data) - participation.cumtime = max(cumtime, 0) - participation.score = round(score, self.contest.points_precision) - participation.tiebreaker = 0 - participation.format_data = format_data - - format_data_final = calculate_format_data(participation, True) - score_final, cumtime_final = recalculate_results(format_data_final) - participation.cumtime_final = max(cumtime_final, 0) - participation.score_final = round(score_final, self.contest.points_precision) - participation.format_data_final = format_data_final - participation.save() diff --git a/judge/contest_format/registry.py b/judge/contest_format/registry.py index ce5d39a..fba22c0 100644 --- a/judge/contest_format/registry.py +++ b/judge/contest_format/registry.py @@ -1,3 +1,5 @@ +from django.utils import six + formats = {} @@ -11,4 +13,4 @@ def register_contest_format(name): def choices(): - return [(key, value.name) for key, value in sorted(formats.items())] + return [(key, value.name) for key, value in sorted(six.iteritems(formats))] diff --git a/judge/contest_format/ultimate.py b/judge/contest_format/ultimate.py deleted file mode 100644 index 7960d02..0000000 --- a/judge/contest_format/ultimate.py +++ /dev/null @@ -1,55 +0,0 @@ -from django.utils.translation import gettext_lazy - -from judge.contest_format.ioi import IOIContestFormat -from judge.contest_format.registry import register_contest_format -from django.db.models import Min, OuterRef, Subquery - -# This contest format only counts last submission for each problem. - - -@register_contest_format("ultimate") -class UltimateContestFormat(IOIContestFormat): - name = gettext_lazy("Ultimate") - - def update_participation(self, participation): - cumtime = 0 - score = 0 - format_data = {} - - queryset = participation.submissions - if self.contest.freeze_after: - queryset = queryset.filter( - submission__date__lt=participation.start + self.contest.freeze_after - ) - - queryset = ( - queryset.values("problem_id") - .filter( - id=Subquery( - queryset.filter(problem_id=OuterRef("problem_id")) - .order_by("-id") - .values("id")[:1] - ) - ) - .values_list("problem_id", "submission__date", "points") - ) - - for problem_id, time, points in queryset: - if self.config["cumtime"]: - dt = (time - participation.start).total_seconds() - if points: - cumtime += dt - else: - dt = 0 - format_data[str(problem_id)] = { - "time": dt, - "points": points, - } - score += points - - self.handle_frozen_state(participation, format_data) - participation.cumtime = max(cumtime, 0) - participation.score = round(score, self.contest.points_precision) - participation.tiebreaker = 0 - participation.format_data = format_data - participation.save() diff --git a/judge/custom_translations.py b/judge/custom_translations.py deleted file mode 100644 index efe4939..0000000 --- a/judge/custom_translations.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.utils.translation import gettext_lazy as _, ngettext - - -def custom_trans(): - return [ - # Password reset - ngettext( - "This password is too short. It must contain at least %(min_length)d character.", - "This password is too short. It must contain at least %(min_length)d characters.", - 0, - ), - ngettext( - "Your password must contain at least %(min_length)d character.", - "Your password must contain at least %(min_length)d characters.", - 0, - ), - _("The two password fields didn’t match."), - _("Your password can’t be entirely numeric."), - # Navbar - _("Bug Report"), - _("Courses"), - ] diff --git a/judge/dblock.py b/judge/dblock.py index 5b7feab..d4d5184 100644 --- a/judge/dblock.py +++ b/judge/dblock.py @@ -5,21 +5,19 @@ from django.db import connection, transaction class LockModel(object): def __init__(self, write, read=()): - self.tables = ", ".join( - chain( - ("`%s` WRITE" % model._meta.db_table for model in write), - ("`%s` READ" % model._meta.db_table for model in read), - ) - ) + self.tables = ', '.join(chain( + ('`%s` WRITE' % model._meta.db_table for model in write), + ('`%s` READ' % model._meta.db_table for model in read), + )) self.cursor = connection.cursor() def __enter__(self): - self.cursor.execute("LOCK TABLES " + self.tables) + self.cursor.execute('LOCK TABLES ' + self.tables) def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: transaction.commit() else: transaction.rollback() - self.cursor.execute("UNLOCK TABLES") + self.cursor.execute('UNLOCK TABLES') self.cursor.close() diff --git a/judge/event_poster.py b/judge/event_poster.py index e7c57fd..29100bd 100644 --- a/judge/event_poster.py +++ b/judge/event_poster.py @@ -1,6 +1,6 @@ from django.conf import settings -__all__ = ["last", "post"] +__all__ = ['last', 'post'] if not settings.EVENT_DAEMON_USE: real = False @@ -10,12 +10,9 @@ if not settings.EVENT_DAEMON_USE: def last(): return 0 - -elif hasattr(settings, "EVENT_DAEMON_AMQP"): +elif hasattr(settings, 'EVENT_DAEMON_AMQP'): from .event_poster_amqp import last, post - real = True else: from .event_poster_ws import last, post - real = True diff --git a/judge/event_poster_amqp.py b/judge/event_poster_amqp.py index 24cec72..74f6331 100644 --- a/judge/event_poster_amqp.py +++ b/judge/event_poster_amqp.py @@ -6,7 +6,7 @@ import pika from django.conf import settings from pika.exceptions import AMQPError -__all__ = ["EventPoster", "post", "last"] +__all__ = ['EventPoster', 'post', 'last'] class EventPoster(object): @@ -15,19 +15,14 @@ class EventPoster(object): self._exchange = settings.EVENT_DAEMON_AMQP_EXCHANGE def _connect(self): - self._conn = pika.BlockingConnection( - pika.URLParameters(settings.EVENT_DAEMON_AMQP), - ) + self._conn = pika.BlockingConnection(pika.URLParameters(settings.EVENT_DAEMON_AMQP)) self._chan = self._conn.channel() def post(self, channel, message, tries=0): try: id = int(time() * 1000000) - self._chan.basic_publish( - self._exchange, - "#", - json.dumps({"id": id, "channel": channel, "message": message}), - ) + self._chan.basic_publish(self._exchange, '', + json.dumps({'id': id, 'channel': channel, 'message': message})) return id except AMQPError: if tries > 10: @@ -40,7 +35,7 @@ _local = threading.local() def _get_poster(): - if "poster" not in _local.__dict__: + if 'poster' not in _local.__dict__: _local.poster = EventPoster() return _local.poster diff --git a/judge/event_poster_ws.py b/judge/event_poster_ws.py index 8daddab..fba4052 100644 --- a/judge/event_poster_ws.py +++ b/judge/event_poster_ws.py @@ -5,7 +5,7 @@ import threading from django.conf import settings from websocket import WebSocketException, create_connection -__all__ = ["EventPostingError", "EventPoster", "post", "last"] +__all__ = ['EventPostingError', 'EventPoster', 'post', 'last'] _local = threading.local() @@ -20,23 +20,19 @@ class EventPoster(object): def _connect(self): self._conn = create_connection(settings.EVENT_DAEMON_POST) if settings.EVENT_DAEMON_KEY is not None: - self._conn.send( - json.dumps({"command": "auth", "key": settings.EVENT_DAEMON_KEY}) - ) + self._conn.send(json.dumps({'command': 'auth', 'key': settings.EVENT_DAEMON_KEY})) resp = json.loads(self._conn.recv()) - if resp["status"] == "error": - raise EventPostingError(resp["code"]) + if resp['status'] == 'error': + raise EventPostingError(resp['code']) def post(self, channel, message, tries=0): try: - self._conn.send( - json.dumps({"command": "post", "channel": channel, "message": message}) - ) + self._conn.send(json.dumps({'command': 'post', 'channel': channel, 'message': message})) resp = json.loads(self._conn.recv()) - if resp["status"] == "error": - raise EventPostingError(resp["code"]) + if resp['status'] == 'error': + raise EventPostingError(resp['code']) else: - return resp["id"] + return resp['id'] except WebSocketException: if tries > 10: raise @@ -47,10 +43,10 @@ class EventPoster(object): try: self._conn.send('{"command": "last-msg"}') resp = json.loads(self._conn.recv()) - if resp["status"] == "error": - raise EventPostingError(resp["code"]) + if resp['status'] == 'error': + raise EventPostingError(resp['code']) else: - return resp["id"] + return resp['id'] except WebSocketException: if tries > 10: raise @@ -59,7 +55,7 @@ class EventPoster(object): def _get_poster(): - if "poster" not in _local.__dict__: + if 'poster' not in _local.__dict__: _local.poster = EventPoster() return _local.poster diff --git a/judge/feed.py b/judge/feed.py new file mode 100644 index 0000000..6d8d825 --- /dev/null +++ b/judge/feed.py @@ -0,0 +1,110 @@ +from django.conf import settings +from django.contrib.auth.models import AnonymousUser +from django.contrib.syndication.views import Feed +from django.core.cache import cache +from django.utils import timezone +from django.utils.feedgenerator import Atom1Feed + +from judge.jinja2.markdown import markdown +from judge.models import BlogPost, Comment, Problem + +import re + + +# https://lsimons.wordpress.com/2011/03/17/stripping-illegal-characters-out-of-xml-in-python/ +def escape_xml_illegal_chars(val, replacement='?'): + _illegal_xml_chars_RE = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]') + return _illegal_xml_chars_RE.sub(replacement, val) + +class ProblemFeed(Feed): + title = 'Recently Added %s Problems' % settings.SITE_NAME + link = '/' + description = 'The latest problems added on the %s website' % settings.SITE_LONG_NAME + + def items(self): + return Problem.objects.filter(is_public=True, is_organization_private=False).defer('description')\ + .order_by('-date', '-id')[:25] + + def item_title(self, problem): + return problem.name + + def item_description(self, problem): + key = 'problem_feed:%d' % problem.id + desc = cache.get(key) + if desc is None: + desc = str(markdown(problem.description, 'problem'))[:500] + '...' + desc = escape_xml_illegal_chars(desc) + cache.set(key, desc, 86400) + return desc + + def item_pubdate(self, problem): + return problem.date + + item_updateddate = item_pubdate + + +class AtomProblemFeed(ProblemFeed): + feed_type = Atom1Feed + subtitle = ProblemFeed.description + + +class CommentFeed(Feed): + title = 'Latest %s Comments' % settings.SITE_NAME + link = '/' + description = 'The latest comments on the %s website' % settings.SITE_LONG_NAME + + def items(self): + return Comment.most_recent(AnonymousUser(), 25) + + def item_title(self, comment): + return '%s -> %s' % (comment.author.user.username, comment.page_title) + + def item_description(self, comment): + key = 'comment_feed:%d' % comment.id + desc = cache.get(key) + if desc is None: + desc = str(markdown(comment.body, 'comment')) + desc = escape_xml_illegal_chars(desc) + cache.set(key, desc, 86400) + return desc + + def item_pubdate(self, comment): + return comment.time + + item_updateddate = item_pubdate + + +class AtomCommentFeed(CommentFeed): + feed_type = Atom1Feed + subtitle = CommentFeed.description + + +class BlogFeed(Feed): + title = 'Latest %s Blog Posts' % settings.SITE_NAME + link = '/' + description = 'The latest blog posts from the %s' % settings.SITE_LONG_NAME + + def items(self): + return BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()).order_by('-sticky', '-publish_on') + + def item_title(self, post): + return post.title + + def item_description(self, post): + key = 'blog_feed:%d' % post.id + summary = cache.get(key) + if summary is None: + summary = str(markdown(post.summary or post.content, 'blog')) + summary = escape_xml_illegal_chars(summary) + cache.set(key, summary, 86400) + return summary + + def item_pubdate(self, post): + return post.publish_on + + item_updateddate = item_pubdate + + +class AtomBlogFeed(BlogFeed): + feed_type = Atom1Feed + subtitle = BlogFeed.description diff --git a/judge/fixtures/demo.json b/judge/fixtures/demo.json index b9fbea7..9d22a9f 100644 --- a/judge/fixtures/demo.json +++ b/judge/fixtures/demo.json @@ -8,6 +8,7 @@ "ip": "10.0.2.2", "language": 1, "last_access": "2017-12-02T08:57:10.093Z", + "math_engine": "auto", "mute": false, "organizations": [ 1 @@ -17,7 +18,8 @@ "problem_count": 0, "rating": null, "timezone": "America/Toronto", - "user": 1 + "user": 1, + "user_script": "" }, "model": "judge.profile", "pk": 1 @@ -145,8 +147,25 @@ }, { "fields": { - "domain": "localhost:8000", - "name": "LQDOJ" + "author": 1, + "body": "This is your first comment!", + "hidden": false, + "level": 0, + "lft": 1, + "page": "b:1", + "parent": null, + "rght": 2, + "score": 0, + "time": "2017-12-02T08:46:54.007Z", + "tree_id": 1 + }, + "model": "judge.comment", + "pk": 1 + }, + { + "fields": { + "domain": "localhost:8081", + "name": "DMOJ: Modern Online Judge" }, "model": "sites.site", "pk": 1 diff --git a/judge/forms.py b/judge/forms.py index 47a4b36..67ccd12 100644 --- a/judge/forms.py +++ b/judge/forms.py @@ -1,599 +1,164 @@ -import os -import secrets from operator import attrgetter -import pyotp -import time -import datetime +import pyotp from django import forms from django.conf import settings -from django.contrib.auth.models import User from django.contrib.auth.forms import AuthenticationForm -from django.core.exceptions import ValidationError, ObjectDoesNotExist +from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from django.db.models import Q -from django.forms import ( - CharField, - ChoiceField, - Form, - ModelForm, - formset_factory, - BaseModelFormSet, - FileField, -) -from django.urls import reverse_lazy, reverse +from django.forms import CharField, ChoiceField, Form, ModelForm +from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ -from django.utils import timezone from django_ace import AceWidget -from judge.models import ( - Contest, - Language, - TestFormatterModel, - Organization, - PrivateMessage, - Problem, - ProblemPointsVote, - Profile, - Submission, - BlogPost, - ContestProblem, - TestFormatterModel, - ProfileInfo, -) - -from judge.widgets import ( - HeavyPreviewPageDownWidget, - PagedownWidget, - Select2MultipleWidget, - Select2Widget, - HeavySelect2MultipleWidget, - HeavySelect2Widget, - Select2MultipleWidget, - DateTimePickerWidget, - ImageWidget, - DatePickerWidget, -) +from judge.models import Contest, Language, Organization, PrivateMessage, Problem, Profile, Submission +from judge.utils.subscription import newsletter_id +from judge.widgets import HeavyPreviewPageDownWidget, MathJaxPagedownWidget, PagedownWidget, Select2MultipleWidget, \ + Select2Widget -def fix_unicode(string, unsafe=tuple("\u202a\u202b\u202d\u202e")): - return ( - string + (sum(k in unsafe for k in string) - string.count("\u202c")) * "\u202c" - ) - - -class UserForm(ModelForm): - class Meta: - model = User - fields = [ - "first_name", - "last_name", - ] - - -class ProfileInfoForm(ModelForm): - class Meta: - model = ProfileInfo - fields = ["tshirt_size", "date_of_birth", "address"] - widgets = { - "tshirt_size": Select2Widget(attrs={"style": "width:100%"}), - "date_of_birth": DatePickerWidget, - "address": forms.TextInput(attrs={"style": "width:100%"}), - } +def fix_unicode(string, unsafe=tuple('\u202a\u202b\u202d\u202e')): + return string + (sum(k in unsafe for k in string) - string.count('\u202c')) * '\u202c' class ProfileForm(ModelForm): + if newsletter_id is not None: + newsletter = forms.BooleanField(label=_('Subscribe to contest updates'), initial=False, required=False) + test_site = forms.BooleanField(label=_('Enable experimental features'), initial=False, required=False) + class Meta: model = Profile - fields = [ - "about", - "timezone", - "language", - "ace_theme", - "profile_image", - "css_background", - ] + fields = ['about', 'organizations', 'timezone', 'language', 'ace_theme', 'user_script'] widgets = { - "timezone": Select2Widget(attrs={"style": "width:200px"}), - "language": Select2Widget(attrs={"style": "width:200px"}), - "ace_theme": Select2Widget(attrs={"style": "width:200px"}), - "profile_image": ImageWidget, - "css_background": forms.TextInput(), + 'user_script': AceWidget(theme='github'), + 'timezone': Select2Widget(attrs={'style': 'width:200px'}), + 'language': Select2Widget(attrs={'style': 'width:200px'}), + 'ace_theme': Select2Widget(attrs={'style': 'width:200px'}), } + has_math_config = bool(settings.MATHOID_URL) + if has_math_config: + fields.append('math_engine') + widgets['math_engine'] = Select2Widget(attrs={'style': 'width:200px'}) + if HeavyPreviewPageDownWidget is not None: - widgets["about"] = HeavyPreviewPageDownWidget( - preview=reverse_lazy("profile_preview"), - attrs={"style": "max-width:700px;min-width:700px;width:700px"}, + widgets['about'] = HeavyPreviewPageDownWidget( + preview=reverse_lazy('profile_preview'), + attrs={'style': 'max-width:700px;min-width:700px;width:700px'}, ) + def clean(self): + organizations = self.cleaned_data.get('organizations') or [] + max_orgs = settings.DMOJ_USER_MAX_ORGANIZATION_COUNT + + if sum(org.is_open for org in organizations) > max_orgs: + raise ValidationError( + _('You may not be part of more than {count} public organizations.').format(count=max_orgs)) + + return self.cleaned_data + def __init__(self, *args, **kwargs): - user = kwargs.pop("user", None) + user = kwargs.pop('user', None) super(ProfileForm, self).__init__(*args, **kwargs) - self.fields["profile_image"].required = False - - def clean_profile_image(self): - profile_image = self.cleaned_data.get("profile_image") - if profile_image: - if profile_image.size > 5 * 1024 * 1024: - raise ValidationError( - _("File size exceeds the maximum allowed limit of 5MB.") - ) - return profile_image - - -def file_size_validator(file): - limit = 10 * 1024 * 1024 - if file.size > limit: - raise ValidationError("File too large. Size should not exceed 10MB.") + if not user.has_perm('judge.edit_all_organization'): + self.fields['organizations'].queryset = Organization.objects.filter( + Q(is_open=True) | Q(id__in=user.profile.organizations.all()), + ) class ProblemSubmitForm(ModelForm): - source = CharField( - max_length=65536, widget=AceWidget(theme="twilight", no_ace_media=True) - ) + source = CharField(max_length=65536, widget=AceWidget(theme='twilight', no_ace_media=True)) judge = ChoiceField(choices=(), widget=forms.HiddenInput(), required=False) - source_file = FileField(required=False, validators=[file_size_validator]) - def __init__(self, *args, judge_choices=(), request=None, problem=None, **kwargs): + def __init__(self, *args, judge_choices=(), **kwargs): super(ProblemSubmitForm, self).__init__(*args, **kwargs) - self.source_file_name = None - self.request = request - self.problem = problem - self.fields["language"].empty_label = None - self.fields["language"].label_from_instance = attrgetter("display_name") - self.fields["language"].queryset = Language.objects.filter( - judges__online=True - ).distinct() + self.fields['language'].empty_label = None + self.fields['language'].label_from_instance = attrgetter('display_name') + self.fields['language'].queryset = Language.objects.filter(judges__online=True).distinct() if judge_choices: - self.fields["judge"].widget = Select2Widget( - attrs={"style": "width: 150px", "data-placeholder": _("Any judge")}, + self.fields['judge'].widget = Select2Widget( + attrs={'style': 'width: 150px', 'data-placeholder': _('Any judge')}, ) - self.fields["judge"].choices = judge_choices - - def allow_url_as_source(self): - key = self.cleaned_data["language"].key - filename = self.files["source_file"].name - if key == "OUTPUT" and self.problem.data_files.output_only: - return filename.endswith(".zip") - if key == "SCAT": - return filename.endswith(".sb3") - return False - - def clean(self): - if "source_file" in self.files: - if self.allow_url_as_source(): - filename = self.files["source_file"].name - now = datetime.datetime.now() - timestamp = str(int(time.mktime(now.timetuple()))) - self.source_file_name = ( - timestamp + secrets.token_hex(5) + "." + filename.split(".")[-1] - ) - filepath = os.path.join( - settings.DMOJ_SUBMISSION_ROOT, self.source_file_name - ) - with open(filepath, "wb+") as destination: - for chunk in self.files["source_file"].chunks(): - destination.write(chunk) - self.cleaned_data["source"] = self.request.build_absolute_uri( - reverse("submission_source_file", args=(self.source_file_name,)) - ) - del self.files["source_file"] - return self.cleaned_data + self.fields['judge'].choices = judge_choices class Meta: model = Submission - fields = ["language"] + fields = ['language'] class EditOrganizationForm(ModelForm): class Meta: model = Organization - fields = [ - "name", - "slug", - "short_name", - "about", - "organization_image", - "admins", - "is_open", - ] - widgets = { - "admins": Select2MultipleWidget(), - "organization_image": ImageWidget, - } + fields = ['about', 'logo_override_image', 'admins'] + widgets = {'admins': Select2MultipleWidget()} if HeavyPreviewPageDownWidget is not None: - widgets["about"] = HeavyPreviewPageDownWidget( - preview=reverse_lazy("organization_preview") - ) - - def __init__(self, *args, **kwargs): - super(EditOrganizationForm, self).__init__(*args, **kwargs) - self.fields["organization_image"].required = False - - def clean_organization_image(self): - organization_image = self.cleaned_data.get("organization_image") - if organization_image: - if organization_image.size > 5 * 1024 * 1024: - raise ValidationError( - _("File size exceeds the maximum allowed limit of 5MB.") - ) - return organization_image - - -class AddOrganizationForm(ModelForm): - class Meta: - model = Organization - fields = [ - "name", - "slug", - "short_name", - "about", - "organization_image", - "is_open", - ] - widgets = {} - if HeavyPreviewPageDownWidget is not None: - widgets["about"] = HeavyPreviewPageDownWidget( - preview=reverse_lazy("organization_preview") - ) - - def __init__(self, *args, **kwargs): - self.request = kwargs.pop("request", None) - super(AddOrganizationForm, self).__init__(*args, **kwargs) - self.fields["organization_image"].required = False - - def save(self, commit=True): - res = super(AddOrganizationForm, self).save(commit=False) - res.registrant = self.request.profile - if commit: - res.save() - return res - - -class AddOrganizationContestForm(ModelForm): - def __init__(self, *args, **kwargs): - self.request = kwargs.pop("request", None) - super(AddOrganizationContestForm, self).__init__(*args, **kwargs) - - def save(self, commit=True): - contest = super(AddOrganizationContestForm, self).save(commit=False) - old_save_m2m = self.save_m2m - - def save_m2m(): - for i, problem in enumerate(self.cleaned_data["problems"]): - contest_problem = ContestProblem( - contest=contest, problem=problem, points=100, order=i + 1 - ) - contest_problem.save() - contest.contest_problems.add(contest_problem) - old_save_m2m() - - self.save_m2m = save_m2m - contest.save() - self.save_m2m() - return contest - - class Meta: - model = Contest - fields = ( - "key", - "name", - "start_time", - "end_time", - "problems", - ) - widgets = { - "start_time": DateTimePickerWidget(), - "end_time": DateTimePickerWidget(), - "problems": HeavySelect2MultipleWidget(data_view="problem_select2"), - } - - -class EditOrganizationContestForm(ModelForm): - def __init__(self, *args, **kwargs): - self.org_id = kwargs.pop("org_id", 0) - super(EditOrganizationContestForm, self).__init__(*args, **kwargs) - for field in [ - "authors", - "curators", - "testers", - "private_contestants", - "banned_users", - "view_contest_scoreboard", - ]: - self.fields[field].widget.data_url = ( - self.fields[field].widget.get_url() + f"?org_id={self.org_id}" - ) - - class Meta: - model = Contest - fields = ( - "is_visible", - "key", - "name", - "start_time", - "end_time", - "format_name", - "authors", - "curators", - "testers", - "time_limit", - "freeze_after", - "use_clarifications", - "hide_problem_tags", - "public_scoreboard", - "scoreboard_visibility", - "points_precision", - "rate_limit", - "description", - "og_image", - "logo_override_image", - "summary", - "access_code", - "private_contestants", - "view_contest_scoreboard", - "banned_users", - ) - widgets = { - "authors": HeavySelect2MultipleWidget(data_view="profile_select2"), - "curators": HeavySelect2MultipleWidget(data_view="profile_select2"), - "testers": HeavySelect2MultipleWidget(data_view="profile_select2"), - "private_contestants": HeavySelect2MultipleWidget( - data_view="profile_select2" - ), - "banned_users": HeavySelect2MultipleWidget(data_view="profile_select2"), - "view_contest_scoreboard": HeavySelect2MultipleWidget( - data_view="profile_select2" - ), - "organizations": HeavySelect2MultipleWidget( - data_view="organization_select2" - ), - "tags": Select2MultipleWidget, - "description": HeavyPreviewPageDownWidget( - preview=reverse_lazy("contest_preview") - ), - "start_time": DateTimePickerWidget(), - "end_time": DateTimePickerWidget(), - "format_name": Select2Widget(), - "scoreboard_visibility": Select2Widget(), - } - - -class AddOrganizationMemberForm(ModelForm): - new_users = CharField( - max_length=65536, - widget=forms.Textarea, - help_text=_("Enter usernames separating by space"), - label=_("New users"), - ) - - def clean_new_users(self): - new_users = self.cleaned_data.get("new_users") or "" - usernames = new_users.split() - invalid_usernames = [] - valid_usernames = [] - - for username in usernames: - try: - valid_usernames.append(Profile.objects.get(user__username=username)) - except ObjectDoesNotExist: - invalid_usernames.append(username) - - if invalid_usernames: - raise ValidationError( - _("These usernames don't exist: {usernames}").format( - usernames=str(invalid_usernames) - ) - ) - return valid_usernames - - class Meta: - model = Organization - fields = () - - -class OrganizationBlogForm(ModelForm): - class Meta: - model = BlogPost - fields = ("title", "content", "publish_on") - widgets = { - "publish_on": forms.HiddenInput, - } - if HeavyPreviewPageDownWidget is not None: - widgets["content"] = HeavyPreviewPageDownWidget( - preview=reverse_lazy("organization_preview") - ) - - def __init__(self, *args, **kwargs): - super(OrganizationBlogForm, self).__init__(*args, **kwargs) - self.fields["publish_on"].required = False - self.fields["publish_on"].is_hidden = True - - def clean(self): - self.cleaned_data["publish_on"] = timezone.now() - return self.cleaned_data - - -class OrganizationAdminBlogForm(OrganizationBlogForm): - class Meta: - model = BlogPost - fields = ("visible", "sticky", "title", "content", "publish_on") - widgets = { - "publish_on": forms.HiddenInput, - } - if HeavyPreviewPageDownWidget is not None: - widgets["content"] = HeavyPreviewPageDownWidget( - preview=reverse_lazy("organization_preview") - ) + widgets['about'] = HeavyPreviewPageDownWidget(preview=reverse_lazy('organization_preview')) class NewMessageForm(ModelForm): class Meta: model = PrivateMessage - fields = ["title", "content"] + fields = ['title', 'content'] widgets = {} if PagedownWidget is not None: - widgets["content"] = PagedownWidget() + widgets['content'] = MathJaxPagedownWidget() class CustomAuthenticationForm(AuthenticationForm): def __init__(self, *args, **kwargs): super(CustomAuthenticationForm, self).__init__(*args, **kwargs) - self.fields["username"].widget.attrs.update( - {"placeholder": _("Username/Email")} - ) - self.fields["password"].widget.attrs.update({"placeholder": _("Password")}) + self.fields['username'].widget.attrs.update({'placeholder': _('Username')}) + self.fields['password'].widget.attrs.update({'placeholder': _('Password')}) - self.has_google_auth = self._has_social_auth("GOOGLE_OAUTH2") - self.has_facebook_auth = self._has_social_auth("FACEBOOK") - self.has_github_auth = self._has_social_auth("GITHUB_SECURE") + self.has_google_auth = self._has_social_auth('GOOGLE_OAUTH2') + self.has_facebook_auth = self._has_social_auth('FACEBOOK') + self.has_github_auth = self._has_social_auth('GITHUB_SECURE') def _has_social_auth(self, key): - return getattr(settings, "SOCIAL_AUTH_%s_KEY" % key, None) and getattr( - settings, "SOCIAL_AUTH_%s_SECRET" % key, None - ) + return (getattr(settings, 'SOCIAL_AUTH_%s_KEY' % key, None) and + getattr(settings, 'SOCIAL_AUTH_%s_SECRET' % key, None)) class NoAutoCompleteCharField(forms.CharField): def widget_attrs(self, widget): attrs = super(NoAutoCompleteCharField, self).widget_attrs(widget) - attrs["autocomplete"] = "off" + attrs['autocomplete'] = 'off' return attrs class TOTPForm(Form): TOLERANCE = settings.DMOJ_TOTP_TOLERANCE_HALF_MINUTES - totp_token = NoAutoCompleteCharField( - validators=[ - RegexValidator( - "^[0-9]{6}$", - _("Two Factor Authentication tokens must be 6 decimal digits."), - ), - ] - ) + totp_token = NoAutoCompleteCharField(validators=[ + RegexValidator('^[0-9]{6}$', _('Two Factor Authentication tokens must be 6 decimal digits.')), + ]) def __init__(self, *args, **kwargs): - self.totp_key = kwargs.pop("totp_key") + self.totp_key = kwargs.pop('totp_key') super(TOTPForm, self).__init__(*args, **kwargs) def clean_totp_token(self): - if not pyotp.TOTP(self.totp_key).verify( - self.cleaned_data["totp_token"], valid_window=self.TOLERANCE - ): - raise ValidationError(_("Invalid Two Factor Authentication token.")) + if not pyotp.TOTP(self.totp_key).verify(self.cleaned_data['totp_token'], valid_window=self.TOLERANCE): + raise ValidationError(_('Invalid Two Factor Authentication token.')) class ProblemCloneForm(Form): - code = CharField( - max_length=20, - validators=[ - RegexValidator("^[a-z0-9]+$", _("Problem code must be ^[a-z0-9]+$")) - ], - ) + code = CharField(max_length=20, validators=[RegexValidator('^[a-z0-9]+$', _('Problem code must be ^[a-z0-9]+$'))]) def clean_code(self): - code = self.cleaned_data["code"] + code = self.cleaned_data['code'] if Problem.objects.filter(code=code).exists(): - raise ValidationError(_("Problem with code already exists.")) + raise ValidationError(_('Problem with code already exists.')) return code class ContestCloneForm(Form): - key = CharField( - max_length=20, - validators=[RegexValidator("^[a-z0-9]+$", _("Contest id must be ^[a-z0-9]+$"))], - ) - organization = ChoiceField(choices=(), required=True) - - def __init__(self, *args, org_choices=(), profile=None, **kwargs): - super(ContestCloneForm, self).__init__(*args, **kwargs) - self.fields["organization"].widget = Select2Widget( - attrs={"style": "width: 100%", "data-placeholder": _("Group")}, - ) - self.fields["organization"].choices = org_choices - self.profile = profile + key = CharField(max_length=20, validators=[RegexValidator('^[a-z0-9]+$', _('Contest id must be ^[a-z0-9]+$'))]) def clean_key(self): - key = self.cleaned_data["key"] + key = self.cleaned_data['key'] if Contest.objects.filter(key=key).exists(): - raise ValidationError(_("Contest with key already exists.")) - return key - - def clean_organization(self): - organization_id = self.cleaned_data["organization"] - try: - organization = Organization.objects.get(id=organization_id) - except Exception: - raise ValidationError(_("Group doesn't exist.")) - if not organization.admins.filter(id=self.profile.id).exists(): - raise ValidationError(_("You don't have permission in this group.")) - return organization - - -class ProblemPointsVoteForm(ModelForm): - class Meta: - model = ProblemPointsVote - fields = ["points"] - - -class ContestProblemForm(ModelForm): - class Meta: - model = ContestProblem - fields = ( - "order", - "problem", - "points", - "partial", - "show_testcases", - "max_submissions", - ) - widgets = { - "problem": HeavySelect2Widget( - data_view="problem_select2", attrs={"style": "width: 100%"} - ), - } - - -class ContestProblemModelFormSet(BaseModelFormSet): - def is_valid(self): - valid = super().is_valid() - - if not valid: - return valid - - problems = set() - duplicates = [] - - for form in self.forms: - if form.cleaned_data and not form.cleaned_data.get("DELETE", False): - problem = form.cleaned_data.get("problem") - if problem in problems: - duplicates.append(problem) - else: - problems.add(problem) - - if duplicates: - for form in self.forms: - problem = form.cleaned_data.get("problem") - if problem in duplicates: - form.add_error("problem", _("This problem is duplicated.")) - return False - - return True - - -class ContestProblemFormSet( - formset_factory( - ContestProblemForm, formset=ContestProblemModelFormSet, extra=6, can_delete=True - ) -): - model = ContestProblem - - -class TestFormatterForm(ModelForm): - class Meta: - model = TestFormatterModel - fields = ["file"] + raise ValidationError(_('Contest with key already exists.')) + return key \ No newline at end of file diff --git a/judge/fulltext.py b/judge/fulltext.py index 209a87e..5b9f7d3 100644 --- a/judge/fulltext.py +++ b/judge/fulltext.py @@ -5,10 +5,10 @@ from django.db.models.query import QuerySet class SearchQuerySet(QuerySet): - DEFAULT = "" - BOOLEAN = " IN BOOLEAN MODE" - NATURAL_LANGUAGE = " IN NATURAL LANGUAGE MODE" - QUERY_EXPANSION = " WITH QUERY EXPANSION" + DEFAULT = '' + BOOLEAN = ' IN BOOLEAN MODE' + NATURAL_LANGUAGE = ' IN NATURAL LANGUAGE MODE' + QUERY_EXPANSION = ' WITH QUERY EXPANSION' def __init__(self, fields=None, **kwargs): super(SearchQuerySet, self).__init__(**kwargs) @@ -25,26 +25,20 @@ class SearchQuerySet(QuerySet): # Get the table name and column names from the model # in `table_name`.`column_name` style columns = [meta.get_field(name).column for name in self._search_fields] - full_names = [ - "%s.%s" - % ( - connection.ops.quote_name(meta.db_table), - connection.ops.quote_name(column), - ) - for column in columns - ] + full_names = ['%s.%s' % + (connection.ops.quote_name(meta.db_table), + connection.ops.quote_name(column)) + for column in columns] # Create the MATCH...AGAINST expressions - fulltext_columns = ", ".join(full_names) - match_expr = "MATCH(%s) AGAINST (%%s%s)" % (fulltext_columns, mode) + fulltext_columns = ', '.join(full_names) + match_expr = ('MATCH(%s) AGAINST (%%s%s)' % (fulltext_columns, mode)) # Add the extra SELECT and WHERE options - return self.extra( - select={"relevance": match_expr}, - select_params=[query], - where=[match_expr], - params=[query], - ) + return self.extra(select={'relevance': match_expr}, + select_params=[query], + where=[match_expr], + params=[query]) class SearchManager(models.Manager): diff --git a/judge/highlight_code.py b/judge/highlight_code.py index f5c91e3..308a58e 100644 --- a/judge/highlight_code.py +++ b/judge/highlight_code.py @@ -1,13 +1,38 @@ from django.utils.html import escape, mark_safe -from judge.markdown import markdown -__all__ = ["highlight_code"] +__all__ = ['highlight_code'] -def highlight_code(code, language, linenos=True, title=None): - linenos_option = 'linenums="1"' if linenos else "" - title_option = f'title="{title}"' if title else "" - options = f"{{.{language} {linenos_option} {title_option}}}" +def _make_pre_code(code): + return mark_safe('
' + escape(code) + '
') - value = f"```{options}\n{code}\n```\n" - return mark_safe(markdown(value)) + +def _wrap_code(inner): + yield 0, "" + for tup in inner: + yield tup + yield 0, "" + + +try: + import pygments + import pygments.lexers + import pygments.formatters.html + import pygments.util +except ImportError: + def highlight_code(code, language, cssclass=None): + return _make_pre_code(code) +else: + class HtmlCodeFormatter(pygments.formatters.HtmlFormatter): + def wrap(self, source, outfile): + return self._wrap_div(self._wrap_pre(_wrap_code(source))) + + def highlight_code(code, language, cssclass='codehilite', linenos=True): + try: + lexer = pygments.lexers.get_lexer_by_name(language) + except pygments.util.ClassNotFound: + return _make_pre_code(code) + + if linenos: + return mark_safe(pygments.highlight(code, lexer, HtmlCodeFormatter(cssclass=cssclass, linenos='table'))) + return mark_safe(pygments.highlight(code, lexer, HtmlCodeFormatter(cssclass=cssclass))) diff --git a/judge/jinja2/__init__.py b/judge/jinja2/__init__.py index e24ea8c..61ec4fd 100644 --- a/judge/jinja2/__init__.py +++ b/judge/jinja2/__init__.py @@ -8,33 +8,19 @@ from statici18n.templatetags.statici18n import inlinei18n from judge.highlight_code import highlight_code from judge.user_translations import gettext -from . import ( - camo, - chat, - datetime, - filesize, - gravatar, - language, - markdown, - rating, - reference, - render, - social, - spaceless, - timedelta, - comment, -) +from . import (camo, chat, datetime, filesize, gravatar, language, markdown, rating, reference, render, social, + spaceless, submission, timedelta) from . import registry -registry.function("str", str) -registry.filter("str", str) -registry.filter("json", json.dumps) -registry.filter("highlight", highlight_code) -registry.filter("urlquote", urlquote) -registry.filter("roundfloat", round) -registry.function("inlinei18n", inlinei18n) -registry.function("mptt_tree", get_cached_trees) -registry.function("user_trans", gettext) +registry.function('str', str) +registry.filter('str', str) +registry.filter('json', json.dumps) +registry.filter('highlight', highlight_code) +registry.filter('urlquote', urlquote) +registry.filter('roundfloat', round) +registry.function('inlinei18n', inlinei18n) +registry.function('mptt_tree', get_cached_trees) +registry.function('user_trans', gettext) @registry.function diff --git a/judge/jinja2/camo.py b/judge/jinja2/camo.py index 1baeeb2..a60da79 100644 --- a/judge/jinja2/camo.py +++ b/judge/jinja2/camo.py @@ -1,9 +1,8 @@ from judge.utils.camo import client as camo_client from . import registry - @registry.filter def camo(url): if camo_client is None: return url - return camo_client.rewrite_url(url) + return camo_client.rewrite_url(url) \ No newline at end of file diff --git a/judge/jinja2/chat.py b/judge/jinja2/chat.py index 564a748..07fec5b 100644 --- a/judge/jinja2/chat.py +++ b/judge/jinja2/chat.py @@ -1,7 +1,6 @@ from . import registry from chat_box.utils import encrypt_url - @registry.function def chat_param(request_profile, profile): - return encrypt_url(request_profile.id, profile.id) + return encrypt_url(request_profile.id, profile.id) \ No newline at end of file diff --git a/judge/jinja2/comment.py b/judge/jinja2/comment.py deleted file mode 100644 index 6baa365..0000000 --- a/judge/jinja2/comment.py +++ /dev/null @@ -1,12 +0,0 @@ -from . import registry - -from django.contrib.contenttypes.models import ContentType - -from judge.models.comment import get_visible_comment_count -from judge.caching import cache_wrapper - - -@registry.function -def comment_count(obj): - content_type = ContentType.objects.get_for_model(obj) - return get_visible_comment_count(content_type, obj.pk) diff --git a/judge/jinja2/datetime.py b/judge/jinja2/datetime.py index ce7cf32..76c6bfc 100644 --- a/judge/jinja2/datetime.py +++ b/judge/jinja2/datetime.py @@ -10,7 +10,7 @@ from . import registry def localtime_wrapper(func): @functools.wraps(func) def wrapper(datetime, *args, **kwargs): - if getattr(datetime, "convert_to_local_time", True): + if getattr(datetime, 'convert_to_local_time', True): datetime = localtime(datetime) return func(datetime, *args, **kwargs) @@ -22,6 +22,6 @@ registry.filter(localtime_wrapper(time)) @registry.function -@registry.render_with("widgets/relative-time.html") -def relative_time(time, format=_("N j, Y, g:i a"), rel=_("{time}"), abs=_("{time}")): - return {"time": time, "format": format, "rel_format": rel, "abs_format": abs} +@registry.render_with('widgets/relative-time.html') +def relative_time(time, format=_('N j, Y, g:i a'), rel=_('{time}'), abs=_('on {time}')): + return {'time': time, 'format': format, 'rel_format': rel, 'abs_format': abs} diff --git a/judge/jinja2/filesize.py b/judge/jinja2/filesize.py index 0cb0fef..7b27fde 100644 --- a/judge/jinja2/filesize.py +++ b/judge/jinja2/filesize.py @@ -13,28 +13,24 @@ def _format_size(bytes, callback): PB = 1 << 50 if bytes < KB: - return callback("", bytes) + return callback('', bytes) elif bytes < MB: - return callback("K", bytes / KB) + return callback('K', bytes / KB) elif bytes < GB: - return callback("M", bytes / MB) + return callback('M', bytes / MB) elif bytes < TB: - return callback("G", bytes / GB) + return callback('G', bytes / GB) elif bytes < PB: - return callback("T", bytes / TB) + return callback('T', bytes / TB) else: - return callback("P", bytes / PB) + return callback('P', bytes / PB) @registry.filter def kbdetailformat(bytes): - return avoid_wrapping( - _format_size( - bytes * 1024, lambda x, y: ["%d %sB", "%.2f %sB"][bool(x)] % (y, x) - ) - ) + return avoid_wrapping(_format_size(bytes * 1024, lambda x, y: ['%d %sB', '%.2f %sB'][bool(x)] % (y, x))) @registry.filter def kbsimpleformat(kb): - return _format_size(kb * 1024, lambda x, y: "%.0f%s" % (y, x or "B")) + return _format_size(kb * 1024, lambda x, y: '%.0f%s' % (y, x or 'B')) diff --git a/judge/jinja2/gravatar.py b/judge/jinja2/gravatar.py index 175992f..35b3fb8 100644 --- a/judge/jinja2/gravatar.py +++ b/judge/jinja2/gravatar.py @@ -9,23 +9,17 @@ from . import registry @registry.function -def gravatar(profile, size=80, default=None, profile_image=None, email=None): - if profile and not profile.is_muted: - if profile_image: - return profile_image - if profile and profile.profile_image_url: - return profile.profile_image_url - if profile: - email = email or profile.email +def gravatar(email, size=80, default=None): + if isinstance(email, Profile): if default is None: - default = profile.is_muted - gravatar_url = ( - "//www.gravatar.com/avatar/" - + hashlib.md5(utf8bytes(email.strip().lower())).hexdigest() - + "?" - ) - args = {"d": "identicon", "s": str(size)} + default = email.mute + email = email.user.email + elif isinstance(email, AbstractUser): + email = email.email + + gravatar_url = '//www.gravatar.com/avatar/' + hashlib.md5(utf8bytes(email.strip().lower())).hexdigest() + '?' + args = {'d': 'identicon', 's': str(size)} if default: - args["f"] = "y" + args['f'] = 'y' gravatar_url += urlencode(args) return gravatar_url diff --git a/judge/jinja2/language.py b/judge/jinja2/language.py index dda4456..344568a 100644 --- a/judge/jinja2/language.py +++ b/judge/jinja2/language.py @@ -3,7 +3,7 @@ from django.utils import translation from . import registry -@registry.function("language_info") +@registry.function('language_info') def get_language_info(language): # ``language`` is either a language code string or a sequence # with the language code as its first item @@ -13,6 +13,6 @@ def get_language_info(language): return translation.get_language_info(str(language)) -@registry.function("language_info_list") +@registry.function('language_info_list') def get_language_info_list(langs): return [get_language_info(lang) for lang in langs] diff --git a/judge/jinja2/markdown/__init__.py b/judge/jinja2/markdown/__init__.py index 18355d4..3cc5940 100644 --- a/judge/jinja2/markdown/__init__.py +++ b/judge/jinja2/markdown/__init__.py @@ -1,7 +1,142 @@ +import logging +import re +from html.parser import HTMLParser +from urllib.parse import urlparse + +import mistune +from django.conf import settings +from jinja2 import Markup +from lxml import html +from lxml.etree import ParserError, XMLSyntaxError + +from judge.highlight_code import highlight_code +from judge.jinja2.markdown.lazy_load import lazy_load as lazy_load_processor +from judge.jinja2.markdown.math import MathInlineGrammar, MathInlineLexer, MathRenderer +from judge.jinja2.markdown.spoiler import SpoilerInlineGrammar, SpoilerInlineLexer, SpoilerRenderer +from judge.utils.camo import client as camo_client +from judge.utils.texoid import TEXOID_ENABLED, TexoidRenderer from .. import registry -from judge.markdown import markdown as _markdown + +logger = logging.getLogger('judge.html') + +NOFOLLOW_WHITELIST = settings.NOFOLLOW_EXCLUDED + + +class CodeSafeInlineGrammar(mistune.InlineGrammar): + double_emphasis = re.compile(r'^\*{2}([\s\S]+?)()\*{2}(?!\*)') # **word** + emphasis = re.compile(r'^\*((?:\*\*|[^\*])+?)()\*(?!\*)') # *word* + + +class AwesomeInlineGrammar(MathInlineGrammar, SpoilerInlineGrammar, CodeSafeInlineGrammar): + pass + + +class AwesomeInlineLexer(MathInlineLexer, SpoilerInlineLexer, mistune.InlineLexer): + grammar_class = AwesomeInlineGrammar + + +class AwesomeRenderer(MathRenderer, SpoilerRenderer, mistune.Renderer): + def __init__(self, *args, **kwargs): + self.nofollow = kwargs.pop('nofollow', True) + self.texoid = TexoidRenderer() if kwargs.pop('texoid', False) else None + self.parser = HTMLParser() + super(AwesomeRenderer, self).__init__(*args, **kwargs) + + def _link_rel(self, href): + if href: + try: + url = urlparse(href) + except ValueError: + return ' rel="nofollow"' + else: + if url.netloc and url.netloc not in NOFOLLOW_WHITELIST: + return ' rel="nofollow"' + return '' + + def autolink(self, link, is_email=False): + text = link = mistune.escape(link) + if is_email: + link = 'mailto:%s' % link + return '%s' % (link, self._link_rel(link), text) + + def table(self, header, body): + return ( + '\n%s\n' + '\n%s\n
\n' + ) % (header, body) + + def link(self, link, title, text): + link = mistune.escape_link(link) + if not title: + return '%s' % (link, self._link_rel(link), text) + title = mistune.escape(title, quote=True) + return '%s' % (link, title, self._link_rel(link), text) + + def block_code(self, code, lang=None): + if not lang: + return '\n
%s
\n' % mistune.escape(code).rstrip() + return highlight_code(code, lang) + + def block_html(self, html): + if self.texoid and html.startswith('')] + latex = html[html.index('>') + 1:html.rindex('<')] + latex = self.parser.unescape(latex) + result = self.texoid.get_result(latex) + if not result: + return '
%s
' % mistune.escape(latex, smart_amp=False) + elif 'error' not in result: + img = ('''') % { + 'svg': result['svg'], 'png': result['png'], + 'width': result['meta']['width'], 'height': result['meta']['height'], + 'tail': ' /' if self.options.get('use_xhtml') else '', + } + style = ['max-width: 100%', + 'height: %s' % result['meta']['height'], + 'max-height: %s' % result['meta']['height'], + 'width: %s' % result['meta']['height']] + if 'inline' in attr: + tag = 'span' + else: + tag = 'div' + style += ['text-align: center'] + return '<%s style="%s">%s' % (tag, ';'.join(style), img, tag) + else: + return '
%s
' % mistune.escape(result['error'], smart_amp=False) + return super(AwesomeRenderer, self).block_html(html) + + def header(self, text, level, *args, **kwargs): + return super(AwesomeRenderer, self).header(text, level + 2, *args, **kwargs) @registry.filter -def markdown(value, lazy_load=False): - return _markdown(value, lazy_load) +def markdown(value, style, math_engine=None, lazy_load=False): + styles = settings.MARKDOWN_STYLES.get(style, settings.MARKDOWN_DEFAULT_STYLE) + escape = styles.get('safe_mode', True) + nofollow = styles.get('nofollow', True) + texoid = TEXOID_ENABLED and styles.get('texoid', False) + math = hasattr(settings, 'MATHOID_URL') and styles.get('math', False) + + post_processors = [] + if styles.get('use_camo', False) and camo_client is not None: + post_processors.append(camo_client.update_tree) + if lazy_load: + post_processors.append(lazy_load_processor) + + renderer = AwesomeRenderer(escape=escape, nofollow=nofollow, texoid=texoid, + math=math and math_engine is not None, math_engine=math_engine) + markdown = mistune.Markdown(renderer=renderer, inline=AwesomeInlineLexer, + parse_block_html=1, parse_inline_html=1) + result = markdown(value) + if post_processors: + try: + tree = html.fromstring(result, parser=html.HTMLParser(recover=True)) + except (XMLSyntaxError, ParserError) as e: + if result and (not isinstance(e, ParserError) or e.args[0] != 'Document is empty'): + logger.exception('Failed to parse HTML string') + tree = html.Element('div') + for processor in post_processors: + processor(tree) + result = html.tostring(tree, encoding='unicode') + return Markup(result) diff --git a/judge/jinja2/markdown/lazy_load.py b/judge/jinja2/markdown/lazy_load.py new file mode 100644 index 0000000..cf9849c --- /dev/null +++ b/judge/jinja2/markdown/lazy_load.py @@ -0,0 +1,20 @@ +from copy import deepcopy + +from django.templatetags.static import static +from lxml import html + + +def lazy_load(tree): + blank = static('blank.gif') + for img in tree.xpath('.//img'): + src = img.get('src', '') + if src.startswith('data') or '-math' in img.get('class', ''): + continue + noscript = html.Element('noscript') + copy = deepcopy(img) + copy.tail = '' + noscript.append(copy) + img.addprevious(noscript) + img.set('data-src', src) + img.set('src', blank) + img.set('class', img.get('class') + ' unveil' if img.get('class') else 'unveil') diff --git a/judge/jinja2/markdown/math.py b/judge/jinja2/markdown/math.py new file mode 100644 index 0000000..08883c5 --- /dev/null +++ b/judge/jinja2/markdown/math.py @@ -0,0 +1,67 @@ +import re + +import mistune + +from django.conf import settings + +from judge.utils.mathoid import MathoidMathParser + +mistune._pre_tags.append('latex') + + +class MathInlineGrammar(mistune.InlineGrammar): + block_math = re.compile(r'^\$\$(.*?)\$\$|^\\\[(.*?)\\\]', re.DOTALL) + math = re.compile(r'^~(.*?)~|^\\\((.*?)\\\)', re.DOTALL) + text = re.compile(r'^[\s\S]+?(?=[\\%s' % (tag, extra, text, tag) + else: + html = m.group(0) + return self.renderer.inline_html(html) + + +class MathRenderer(mistune.Renderer): + def __init__(self, *args, **kwargs): + if kwargs.pop('math', False) and settings.MATHOID_URL != False: + self.mathoid = MathoidMathParser(kwargs.pop('math_engine', None) or 'svg') + else: + self.mathoid = None + super(MathRenderer, self).__init__(*args, **kwargs) + + def block_math(self, math): + if self.mathoid is None or not math: + return r'\[%s\]' % mistune.escape(str(math)) + return self.mathoid.display_math(math) + + def math(self, math): + if self.mathoid is None or not math: + return r'\(%s\)' % mistune.escape(str(math)) + return self.mathoid.inline_math(math) \ No newline at end of file diff --git a/judge/jinja2/markdown/spoiler.py b/judge/jinja2/markdown/spoiler.py new file mode 100644 index 0000000..5df7950 --- /dev/null +++ b/judge/jinja2/markdown/spoiler.py @@ -0,0 +1,27 @@ +import re +import mistune + + +class SpoilerInlineGrammar(mistune.InlineGrammar): + spoiler = re.compile(r'^\|\|(.+?)\s+([\s\S]+?)\s*\|\|') + + +class SpoilerInlineLexer(mistune.InlineLexer): + grammar_class = SpoilerInlineGrammar + + def __init__(self, *args, **kwargs): + self.default_rules.insert(0, 'spoiler') + super(SpoilerInlineLexer, self).__init__(*args, **kwargs) + + def output_spoiler(self, m): + return self.renderer.spoiler(m.group(1), m.group(2)) + + +class SpoilerRenderer(mistune.Renderer): + def spoiler(self, summary, text): + return '''
+ + %s + +
%s
+
''' % (summary, text) \ No newline at end of file diff --git a/judge/jinja2/rating.py b/judge/jinja2/rating.py index 0144fbc..b531bb7 100644 --- a/judge/jinja2/rating.py +++ b/judge/jinja2/rating.py @@ -1,3 +1,5 @@ +from django.utils import six + from judge.ratings import rating_class, rating_name, rating_progress from . import registry @@ -6,28 +8,28 @@ def _get_rating_value(func, obj): if obj is None: return None - if isinstance(obj, int): + if isinstance(obj, six.integer_types): return func(obj) else: return func(obj.rating) -@registry.function("rating_class") +@registry.function('rating_class') def get_rating_class(obj): - return _get_rating_value(rating_class, obj) or "rate-none" + return _get_rating_value(rating_class, obj) or 'rate-none' -@registry.function(name="rating_name") +@registry.function(name='rating_name') def get_name(obj): - return _get_rating_value(rating_name, obj) or "Unrated" + return _get_rating_value(rating_name, obj) or 'Unrated' -@registry.function(name="rating_progress") +@registry.function(name='rating_progress') def get_progress(obj): return _get_rating_value(rating_progress, obj) or 0.0 @registry.function -@registry.render_with("user/rating.html") +@registry.render_with('user/rating.html') def rating_number(obj): - return {"rating": obj} + return {'rating': obj} diff --git a/judge/jinja2/reference.py b/judge/jinja2/reference.py index 274382e..184e109 100644 --- a/judge/jinja2/reference.py +++ b/judge/jinja2/reference.py @@ -13,17 +13,17 @@ from judge.models import Contest, Problem, Profile from judge.ratings import rating_class, rating_progress from . import registry -rereference = re.compile(r"\[(r?user):(\w+)\]") +rereference = re.compile(r'\[(r?user):(\w+)\]') def get_user(username, data): if not data: - element = Element("span") + element = Element('span') element.text = username return element - element = Element("span", {"class": Profile.get_user_css_class(*data)}) - link = Element("a", {"href": reverse("user_page", args=[username])}) + element = Element('span', {'class': Profile.get_user_css_class(*data)}) + link = Element('a', {'href': reverse('user_page', args=[username])}) link.text = username element.append(link) return element @@ -31,21 +31,17 @@ def get_user(username, data): def get_user_rating(username, data): if not data: - element = Element("span") + element = Element('span') element.text = username return element rating = data[1] - element = Element( - "a", {"class": "rate-group", "href": reverse("user_page", args=[username])} - ) + element = Element('a', {'class': 'rate-group', 'href': reverse('user_page', args=[username])}) if rating: rating_css = rating_class(rating) - rate_box = Element("span", {"class": "rate-box " + rating_css}) - rate_box.append( - Element("span", {"style": "height: %3.fem" % rating_progress(rating)}) - ) - user = Element("span", {"class": "rating " + rating_css}) + rate_box = Element('span', {'class': 'rate-box ' + rating_css}) + rate_box.append(Element('span', {'style': 'height: %3.fem' % rating_progress(rating)})) + user = Element('span', {'class': 'rating ' + rating_css}) user.text = username element.append(rate_box) element.append(user) @@ -55,24 +51,21 @@ def get_user_rating(username, data): def get_user_info(usernames): - return { - name: (rank, rating) - for name, rank, rating in Profile.objects.filter( - user__username__in=usernames - ).values_list("user__username", "display_rank", "rating") - } + return {name: (rank, rating) for name, rank, rating in + Profile.objects.filter(user__username__in=usernames) + .values_list('user__username', 'display_rank', 'rating')} def get_user_from_text(text): user_list = set() for i in rereference.finditer(text): - user_list.add(text[i.start() + 6 : i.end() - 1]) + user_list.add(text[i.start() + 6: i.end() - 1]) return Profile.objects.filter(user__username__in=user_list) reference_map = { - "user": (get_user, get_user_info), - "ruser": (get_user_rating, get_user_info), + 'user': (get_user, get_user_info), + 'ruser': (get_user_rating, get_user_info), } @@ -84,9 +77,9 @@ def process_reference(text): elements = [] for piece in rereference.finditer(text): if prev is None: - tail = text[last : piece.start()] + tail = text[last:piece.start()] else: - prev.append(text[last : piece.start()]) + prev.append(text[last:piece.start()]) prev = list(piece.groups()) elements.append(prev) last = piece.end() @@ -150,52 +143,52 @@ def item_title(item): return item.name elif isinstance(item, Contest): return item.name - return "" + return '' @registry.function -@registry.render_with("user/link.html") -def link_user(user, show_image=False): +@registry.render_with('user/link.html') +def link_user(user): if isinstance(user, Profile): - profile = user + user, profile = user.user, user elif isinstance(user, AbstractUser): profile = user.profile - elif isinstance(user, int): - profile = Profile(id=user) + elif type(user).__name__ == 'ContestRankingProfile': + user, profile = user.user, user else: - raise ValueError("Expected profile or user, got %s" % (type(user),)) - return {"profile": profile, "show_image": show_image} + raise ValueError('Expected profile or user, got %s' % (type(user),)) + return {'user': user, 'profile': profile} @registry.function -@registry.render_with("user/link-list.html") +@registry.render_with('user/link-list.html') def link_users(users): - return {"users": users} + return {'users': users} @registry.function -@registry.render_with("runtime-version-fragment.html") +@registry.render_with('runtime-version-fragment.html') def runtime_versions(versions): - return {"runtime_versions": versions} + return {'runtime_versions': versions} -@registry.filter(name="absolutify") +@registry.filter(name='absolutify') def absolute_links(text, url): tree = lxml_tree.fromstring(text) - for anchor in tree.xpath(".//a"): - href = anchor.get("href") + for anchor in tree.xpath('.//a'): + href = anchor.get('href') if href: - anchor.set("href", urljoin(url, href)) + anchor.set('href', urljoin(url, href)) return tree -@registry.function(name="urljoin") +@registry.function(name='urljoin') def join(first, second, *rest): if not rest: return urljoin(first, second) return urljoin(urljoin(first, second), *rest) -@registry.filter(name="ansi2html") +@registry.filter(name='ansi2html') def ansi2html(s): return mark_safe(Ansi2HTMLConverter(inline=True).convert(s, full=False)) diff --git a/judge/jinja2/registry.py b/judge/jinja2/registry.py index 21d3b85..da12166 100644 --- a/judge/jinja2/registry.py +++ b/judge/jinja2/registry.py @@ -5,7 +5,7 @@ tests = {} filters = {} extensions = [] -__all__ = ["render_with", "function", "filter", "test", "extension"] +__all__ = ['render_with', 'function', 'filter', 'test', 'extension'] def _store_function(store, func, name=None): @@ -16,7 +16,6 @@ def _store_function(store, func, name=None): def _register_function(store, name, func): if name is None and func is None: - def decorator(func): _store_function(store, func) return func @@ -27,7 +26,6 @@ def _register_function(store, name, func): _store_function(store, name) return name else: - def decorator(func): _store_function(store, func, name) return func diff --git a/judge/jinja2/render.py b/judge/jinja2/render.py index bea8c7c..778e26a 100644 --- a/judge/jinja2/render.py +++ b/judge/jinja2/render.py @@ -1,9 +1,5 @@ -from django.template import ( - Context, - Template as DjangoTemplate, - TemplateSyntaxError as DjangoTemplateSyntaxError, - VariableDoesNotExist, -) +from django.template import (Context, Template as DjangoTemplate, TemplateSyntaxError as DjangoTemplateSyntaxError, + VariableDoesNotExist) from . import registry @@ -28,4 +24,4 @@ def render_django(template, **context): try: return compile_template(template).render(Context(context)) except (VariableDoesNotExist, DjangoTemplateSyntaxError): - return "Error rendering: %r" % template + return 'Error rendering: %r' % template diff --git a/judge/jinja2/social.py b/judge/jinja2/social.py index f7ea1c5..9f82971 100644 --- a/judge/jinja2/social.py +++ b/judge/jinja2/social.py @@ -1,29 +1,13 @@ from django.template.loader import get_template from django.utils.safestring import mark_safe -from django_social_share.templatetags.social_share import ( - post_to_facebook_url, - post_to_gplus_url, - post_to_twitter_url, -) +from django_social_share.templatetags.social_share import post_to_facebook_url, post_to_gplus_url, post_to_twitter_url from . import registry SHARES = [ - ( - "post_to_twitter", - "django_social_share/templatetags/post_to_twitter.html", - post_to_twitter_url, - ), - ( - "post_to_facebook", - "django_social_share/templatetags/post_to_facebook.html", - post_to_facebook_url, - ), - ( - "post_to_gplus", - "django_social_share/templatetags/post_to_gplus.html", - post_to_gplus_url, - ), + ('post_to_twitter', 'django_social_share/templatetags/post_to_twitter.html', post_to_twitter_url), + ('post_to_facebook', 'django_social_share/templatetags/post_to_facebook.html', post_to_facebook_url), + ('post_to_gplus', 'django_social_share/templatetags/post_to_gplus.html', post_to_gplus_url), # For future versions: # ('post_to_linkedin', 'django_social_share/templatetags/post_to_linkedin.html', post_to_linkedin_url), # ('post_to_reddit', 'django_social_share/templatetags/post_to_reddit.html', post_to_reddit_url), @@ -33,7 +17,7 @@ SHARES = [ def make_func(name, template, url_func): def func(request, *args): link_text = args[-1] - context = {"request": request, "link_text": mark_safe(link_text)} + context = {'request': request, 'link_text': mark_safe(link_text)} context = url_func(context, *args[:-1]) return mark_safe(get_template(template).render(context)) @@ -47,10 +31,4 @@ for name, template, url_func in SHARES: @registry.function def recaptcha_init(language=None): - return get_template("snowpenguin/recaptcha/recaptcha_init.html").render( - { - "explicit": False, - "language": language, - "recaptcha_host": "https://google.com", - } - ) + return get_template('snowpenguin/recaptcha/recaptcha_init.html').render({'explicit': False, 'language': language}) diff --git a/judge/jinja2/spaceless.py b/judge/jinja2/spaceless.py index 01eb5d8..81c5186 100644 --- a/judge/jinja2/spaceless.py +++ b/judge/jinja2/spaceless.py @@ -1,8 +1,7 @@ import re -from jinja2 import nodes +from jinja2 import Markup, nodes from jinja2.ext import Extension -from markupsafe import Markup class SpacelessExtension(Extension): @@ -16,17 +15,15 @@ class SpacelessExtension(Extension): https://stackoverflow.com/a/23741298/1090657 """ - tags = {"spaceless"} + tags = {'spaceless'} def parse(self, parser): lineno = next(parser.stream).lineno - body = parser.parse_statements(["name:endspaceless"], drop_needle=True) + body = parser.parse_statements(['name:endspaceless'], drop_needle=True) return nodes.CallBlock( - self.call_method("_strip_spaces", [], [], None, None), - [], - [], - body, + self.call_method('_strip_spaces', [], [], None, None), + [], [], body, ).set_lineno(lineno) def _strip_spaces(self, caller=None): - return Markup(re.sub(r">\s+<", "><", caller().unescape().strip())) + return Markup(re.sub(r'>\s+<', '><', caller().unescape().strip())) diff --git a/judge/jinja2/submission.py b/judge/jinja2/submission.py new file mode 100644 index 0000000..f1d352e --- /dev/null +++ b/judge/jinja2/submission.py @@ -0,0 +1,21 @@ +from . import registry + + +@registry.function +def submission_layout(submission, profile_id, user, editable_problem_ids, completed_problem_ids): + problem_id = submission.problem_id + can_view = False + + if problem_id in editable_problem_ids: + can_view = True + + if profile_id == submission.user_id: + can_view = True + + if user.has_perm('judge.change_submission'): + can_view = True + + if submission.problem_id in completed_problem_ids: + can_view |= submission.problem.is_public or profile_id in submission.problem.tester_ids + + return can_view diff --git a/judge/jinja2/timedelta.py b/judge/jinja2/timedelta.py index 95acff6..2069610 100644 --- a/judge/jinja2/timedelta.py +++ b/judge/jinja2/timedelta.py @@ -5,14 +5,14 @@ from . import registry @registry.filter -def timedelta(value, display="long"): +def timedelta(value, display='long'): if value is None: return value return nice_repr(value, display) @registry.filter -def timestampdelta(value, display="long"): +def timestampdelta(value, display='long'): value = datetime.timedelta(seconds=value) return timedelta(value, display) @@ -23,8 +23,8 @@ def seconds(timedelta): @registry.filter -@registry.render_with("time-remaining-fragment.html") +@registry.render_with('time-remaining-fragment.html') def as_countdown(time): time_now = datetime.datetime.now(datetime.timezone.utc) initial = abs(time - time_now) - return {"countdown": time, "initial": initial} + return {'countdown': time, 'initial': initial} diff --git a/judge/judgeapi.py b/judge/judgeapi.py index 96a2153..57627b2 100644 --- a/judge/judgeapi.py +++ b/judge/judgeapi.py @@ -8,51 +8,43 @@ from django.conf import settings from judge import event_poster as event -logger = logging.getLogger("judge.judgeapi") -size_pack = struct.Struct("!I") +logger = logging.getLogger('judge.judgeapi') +size_pack = struct.Struct('!I') def _post_update_submission(submission, done=False): if submission.problem.is_public: - event.post( - "submissions", - { - "type": "done-submission" if done else "update-submission", - "id": submission.id, - "contest": submission.contest_key, - "user": submission.user_id, - "problem": submission.problem_id, - "status": submission.status, - "language": submission.language.key, - }, - ) + event.post('submissions', {'type': 'done-submission' if done else 'update-submission', + 'id': submission.id, + 'contest': submission.contest_key, + 'user': submission.user_id, 'problem': submission.problem_id, + 'status': submission.status, 'language': submission.language.key}) def judge_request(packet, reply=True): - sock = socket.create_connection( - settings.BRIDGED_DJANGO_CONNECT or settings.BRIDGED_DJANGO_ADDRESS[0] - ) + sock = socket.create_connection(settings.BRIDGED_DJANGO_CONNECT or + settings.BRIDGED_DJANGO_ADDRESS[0]) - output = json.dumps(packet, separators=(",", ":")) - output = zlib.compress(output.encode("utf-8")) - writer = sock.makefile("wb") + output = json.dumps(packet, separators=(',', ':')) + output = zlib.compress(output.encode('utf-8')) + writer = sock.makefile('wb') writer.write(size_pack.pack(len(output))) writer.write(output) writer.close() if reply: - reader = sock.makefile("rb", -1) + reader = sock.makefile('rb', -1) input = reader.read(size_pack.size) if not input: - raise ValueError("Judge did not respond") + raise ValueError('Judge did not respond') length = size_pack.unpack(input)[0] input = reader.read(length) if not input: - raise ValueError("Judge did not respond") + raise ValueError('Judge did not respond') reader.close() sock.close() - result = json.loads(zlib.decompress(input).decode("utf-8")) + result = json.loads(zlib.decompress(input).decode('utf-8')) return result @@ -64,23 +56,13 @@ def judge_submission(submission, rejudge=False, batch_rejudge=False, judge_id=No REJUDGE_PRIORITY = 2 BATCH_REJUDGE_PRIORITY = 3 - updates = { - "time": None, - "memory": None, - "points": None, - "result": None, - "error": None, - "was_rejudged": rejudge, - "status": "QU", - } + updates = {'time': None, 'memory': None, 'points': None, 'result': None, 'error': None, + 'was_rejudged': rejudge, 'status': 'QU'} try: # This is set proactively; it might get unset in judgecallback's on_grading_begin if the problem doesn't # actually have pretests stored on the judge. - updates["is_pretested"] = all( - ContestSubmission.objects.filter(submission=submission).values_list( - "problem__contest__run_pretests_only", "problem__is_pretested" - )[0] - ) + updates['is_pretested'] = all(ContestSubmission.objects.filter(submission=submission) + .values_list('problem__contest__run_pretests_only', 'problem__is_pretested')[0]) except IndexError: priority = DEFAULT_PRIORITY else: @@ -94,65 +76,43 @@ def judge_submission(submission, rejudge=False, batch_rejudge=False, judge_id=No # as that would prevent people from knowing a submission is being scheduled for rejudging. # It is worth noting that this mechanism does not prevent a new rejudge from being scheduled # while already queued, but that does not lead to data corruption. - if ( - not Submission.objects.filter(id=submission.id) - .exclude(status__in=("P", "G")) - .update(**updates) - ): + if not Submission.objects.filter(id=submission.id).exclude(status__in=('P', 'G')).update(**updates): return False SubmissionTestCase.objects.filter(submission_id=submission.id).delete() try: - response = judge_request( - { - "name": "submission-request", - "submission-id": submission.id, - "problem-id": submission.problem.code, - "language": submission.language.key, - "source": submission.source.source, - "judge-id": judge_id, - "priority": BATCH_REJUDGE_PRIORITY - if batch_rejudge - else REJUDGE_PRIORITY - if rejudge - else priority, - } - ) + response = judge_request({ + 'name': 'submission-request', + 'submission-id': submission.id, + 'problem-id': submission.problem.code, + 'language': submission.language.key, + 'source': submission.source.source, + 'judge-id': judge_id, + 'priority': BATCH_REJUDGE_PRIORITY if batch_rejudge else REJUDGE_PRIORITY if rejudge else priority, + }) except BaseException: - logger.exception("Failed to send request to judge") - Submission.objects.filter(id=submission.id).update(status="IE", result="IE") + logger.exception('Failed to send request to judge') + Submission.objects.filter(id=submission.id).update(status='IE', result='IE') success = False else: - if ( - response["name"] != "submission-received" - or response["submission-id"] != submission.id - ): - Submission.objects.filter(id=submission.id).update(status="IE", result="IE") + if response['name'] != 'submission-received' or response['submission-id'] != submission.id: + Submission.objects.filter(id=submission.id).update(status='IE', result='IE') _post_update_submission(submission) success = True return success def disconnect_judge(judge, force=False): - judge_request( - {"name": "disconnect-judge", "judge-id": judge.name, "force": force}, - reply=False, - ) + judge_request({'name': 'disconnect-judge', 'judge-id': judge.name, 'force': force}, reply=False) def abort_submission(submission): from .models import Submission - - response = judge_request( - {"name": "terminate-submission", "submission-id": submission.id} - ) + response = judge_request({'name': 'terminate-submission', 'submission-id': submission.id}) # This defaults to true, so that in the case the JudgeList fails to remove the submission from the queue, # and returns a bad-request, the submission is not falsely shown as "Aborted" when it will still be judged. - if not response.get("judge-aborted", True): - Submission.objects.filter(id=submission.id).update(status="AB", result="AB") - event.post( - "sub_%s" % Submission.get_id_secret(submission.id), - {"type": "aborted-submission"}, - ) - _post_update_submission(submission, done=True) + if not response.get('judge-aborted', True): + Submission.objects.filter(id=submission.id).update(status='AB', result='AB') + event.post('sub_%s' % Submission.get_id_secret(submission.id), {'type': 'aborted-submission'}) + _post_update_submission(submission, done=True) \ No newline at end of file diff --git a/judge/logging.py b/judge/logging.py deleted file mode 100644 index a1a25be..0000000 --- a/judge/logging.py +++ /dev/null @@ -1,12 +0,0 @@ -import logging - -error_log = logging.getLogger("judge.errors") -debug_log = logging.getLogger("judge.debug") - - -def log_exception(msg): - error_log.exception(msg) - - -def log_debug(category, data): - debug_log.info(f"{category}: {data}") diff --git a/judge/lxml_tree.py b/judge/lxml_tree.py index bb71840..014f7dc 100644 --- a/judge/lxml_tree.py +++ b/judge/lxml_tree.py @@ -4,7 +4,7 @@ from django.utils.safestring import SafeData, mark_safe from lxml import html from lxml.etree import ParserError, XMLSyntaxError -logger = logging.getLogger("judge.html") +logger = logging.getLogger('judge.html') class HTMLTreeString(SafeData): @@ -12,11 +12,9 @@ class HTMLTreeString(SafeData): try: self._tree = html.fromstring(str, parser=html.HTMLParser(recover=True)) except (XMLSyntaxError, ParserError) as e: - if str and ( - not isinstance(e, ParserError) or e.args[0] != "Document is empty" - ): - logger.exception("Failed to parse HTML string") - self._tree = html.Element("div") + if str and (not isinstance(e, ParserError) or e.args[0] != 'Document is empty'): + logger.exception('Failed to parse HTML string') + self._tree = html.Element('div') def __getattr__(self, attr): try: @@ -25,15 +23,15 @@ class HTMLTreeString(SafeData): return getattr(str(self), attr) def __setattr__(self, key, value): - if key[0] == "_": + if key[0] == '_': super(HTMLTreeString, self).__setattr__(key, value) setattr(self._tree, key, value) def __repr__(self): - return "" % str(self) + return '' % str(self) def __str__(self): - return mark_safe(html.tostring(self._tree, encoding="unicode")) + return mark_safe(html.tostring(self._tree, encoding='unicode')) def __radd__(self, other): return other + str(self) diff --git a/judge/management/commands/addjudge.py b/judge/management/commands/addjudge.py index f9ad589..b659364 100644 --- a/judge/management/commands/addjudge.py +++ b/judge/management/commands/addjudge.py @@ -4,14 +4,14 @@ from judge.models import Judge class Command(BaseCommand): - help = "create a judge" + help = 'create a judge' def add_arguments(self, parser): - parser.add_argument("name", help="the name of the judge") - parser.add_argument("auth_key", help="authentication key for the judge") + parser.add_argument('name', help='the name of the judge') + parser.add_argument('auth_key', help='authentication key for the judge') def handle(self, *args, **options): judge = Judge() - judge.name = options["name"] - judge.auth_key = options["auth_key"] + judge.name = options['name'] + judge.auth_key = options['auth_key'] judge.save() diff --git a/judge/management/commands/adduser.py b/judge/management/commands/adduser.py index 640625c..f214f4a 100644 --- a/judge/management/commands/adduser.py +++ b/judge/management/commands/adduser.py @@ -6,39 +6,27 @@ from judge.models import Language, Profile class Command(BaseCommand): - help = "creates a user" + help = 'creates a user' def add_arguments(self, parser): - parser.add_argument("name", help="username") - parser.add_argument("email", help="email, not necessary to be resolvable") - parser.add_argument("password", help="password for the user") - parser.add_argument( - "language", - nargs="?", - default=settings.DEFAULT_USER_LANGUAGE, - help="default language ID for user", - ) + parser.add_argument('name', help='username') + parser.add_argument('email', help='email, not necessary to be resolvable') + parser.add_argument('password', help='password for the user') + parser.add_argument('language', nargs='?', default=settings.DEFAULT_USER_LANGUAGE, + help='default language ID for user') - parser.add_argument( - "--superuser", - action="store_true", - default=False, - help="if specified, creates user with superuser privileges", - ) - parser.add_argument( - "--staff", - action="store_true", - default=False, - help="if specified, creates user with staff privileges", - ) + parser.add_argument('--superuser', action='store_true', default=False, + help="if specified, creates user with superuser privileges") + parser.add_argument('--staff', action='store_true', default=False, + help="if specified, creates user with staff privileges") def handle(self, *args, **options): - usr = User(username=options["name"], email=options["email"], is_active=True) - usr.set_password(options["password"]) - usr.is_superuser = options["superuser"] - usr.is_staff = options["staff"] + usr = User(username=options['name'], email=options['email'], is_active=True) + usr.set_password(options['password']) + usr.is_superuser = options['superuser'] + usr.is_staff = options['staff'] usr.save() profile = Profile(user=usr) - profile.language = Language.objects.get(key=options["language"]) + profile.language = Language.objects.get(key=options['language']) profile.save() diff --git a/judge/management/commands/camo.py b/judge/management/commands/camo.py index 774837a..aa65521 100644 --- a/judge/management/commands/camo.py +++ b/judge/management/commands/camo.py @@ -4,13 +4,13 @@ from judge.utils.camo import client as camo_client class Command(BaseCommand): - help = "obtains the camo url for the specified url" + help = 'obtains the camo url for the specified url' def add_arguments(self, parser): - parser.add_argument("url", help="url to use camo on") + parser.add_argument('url', help='url to use camo on') def handle(self, *args, **options): if camo_client is None: - raise CommandError("Camo not available") + raise CommandError('Camo not available') - print(camo_client.image_url(options["url"])) + print(camo_client.image_url(options['url'])) diff --git a/judge/management/commands/copy_language.py b/judge/management/commands/copy_language.py index 09120a6..233ccc7 100644 --- a/judge/management/commands/copy_language.py +++ b/judge/management/commands/copy_language.py @@ -4,30 +4,24 @@ from judge.models import Language, LanguageLimit class Command(BaseCommand): - help = "allows the problems that allow to be submitted in " + help = 'allows the problems that allow to be submitted in ' def add_arguments(self, parser): - parser.add_argument("source", help="language to copy from") - parser.add_argument("target", help="language to copy to") + parser.add_argument('source', help='language to copy from') + parser.add_argument('target', help='language to copy to') def handle(self, *args, **options): try: - source = Language.objects.get(key=options["source"]) + source = Language.objects.get(key=options['source']) except Language.DoesNotExist: - raise CommandError("Invalid source language: %s" % options["source"]) + raise CommandError('Invalid source language: %s' % options['source']) try: - target = Language.objects.get(key=options["target"]) + target = Language.objects.get(key=options['target']) except Language.DoesNotExist: - raise CommandError("Invalid target language: %s" % options["target"]) + raise CommandError('Invalid target language: %s' % options['target']) target.problem_set.set(source.problem_set.all()) - LanguageLimit.objects.bulk_create( - LanguageLimit( - problem=ll.problem, - language=target, - time_limit=ll.time_limit, - memory_limit=ll.memory_limit, - ) - for ll in LanguageLimit.objects.filter(language=source) - ) + LanguageLimit.objects.bulk_create(LanguageLimit(problem=ll.problem, language=target, time_limit=ll.time_limit, + memory_limit=ll.memory_limit) + for ll in LanguageLimit.objects.filter(language=source)) diff --git a/judge/management/commands/create_problem.py b/judge/management/commands/create_problem.py index 568103f..b095e08 100644 --- a/judge/management/commands/create_problem.py +++ b/judge/management/commands/create_problem.py @@ -4,20 +4,20 @@ from judge.models import Problem, ProblemGroup, ProblemType class Command(BaseCommand): - help = "create an empty problem" + help = 'create an empty problem' def add_arguments(self, parser): - parser.add_argument("code", help="problem code") - parser.add_argument("name", help="problem title") - parser.add_argument("body", help="problem description") - parser.add_argument("type", help="problem type") - parser.add_argument("group", help="problem group") + parser.add_argument('code', help='problem code') + parser.add_argument('name', help='problem title') + parser.add_argument('body', help='problem description') + parser.add_argument('type', help='problem type') + parser.add_argument('group', help='problem group') def handle(self, *args, **options): problem = Problem() - problem.code = options["code"] - problem.name = options["name"] - problem.description = options["body"] - problem.group = ProblemGroup.objects.get(name=options["group"]) - problem.types = [ProblemType.objects.get(name=options["type"])] + problem.code = options['code'] + problem.name = options['name'] + problem.description = options['body'] + problem.group = ProblemGroup.objects.get(name=options['group']) + problem.types = [ProblemType.objects.get(name=options['type'])] problem.save() diff --git a/judge/management/commands/generate_data.py b/judge/management/commands/generate_data.py deleted file mode 100644 index 4fcaa4e..0000000 --- a/judge/management/commands/generate_data.py +++ /dev/null @@ -1,59 +0,0 @@ -from django.core.management.base import BaseCommand -from judge.models import * -import csv -import os -from django.conf import settings -from django.db import connection - - -def gen_submissions(): - print("Generating submissions") - query = """ - SELECT user_id as uid, problem_id as pid from - (SELECT user_id, problem_id, max(date) as max_date - from judge_submission - group by user_id, problem_id) t - order by user_id, -max_date; - """ - with connection.cursor() as cursor: - cursor.execute(query) - headers = [i[0] for i in cursor.description] - with open( - os.path.join(settings.ML_DATA_PATH, "submissions.csv"), "w" - ) as csvfile: - f = csv.writer(csvfile) - f.writerow(headers) - for row in cursor.fetchall(): - f.writerow(row) - - -def gen_users(): - print("Generating users") - headers = ["uid", "username", "rating", "points"] - with open(os.path.join(settings.ML_DATA_PATH, "profiles.csv"), "w") as csvfile: - f = csv.writer(csvfile) - f.writerow(headers) - - for u in Profile.objects.all().iterator(): - f.writerow([u.id, u.username, u.rating, u.performance_points]) - - -def gen_problems(): - print("Generating problems") - headers = ["pid", "code", "name", "points", "url"] - with open(os.path.join(settings.ML_DATA_PATH, "problems.csv"), "w") as csvfile: - f = csv.writer(csvfile) - f.writerow(headers) - for p in Problem.objects.all().iterator(): - f.writerow( - [p.id, p.code, p.name, p.points, "lqdoj.edu.vn/problem/" + p.code] - ) - - -class Command(BaseCommand): - help = "generate data for ML" - - def handle(self, *args, **options): - gen_users() - gen_problems() - gen_submissions() diff --git a/judge/management/commands/makedmojmessages.py b/judge/management/commands/makedmojmessages.py index 5c2a9ba..55d3acc 100644 --- a/judge/management/commands/makedmojmessages.py +++ b/judge/management/commands/makedmojmessages.py @@ -5,69 +5,33 @@ import sys from django.conf import settings from django.core.management import CommandError -from django.core.management.commands.makemessages import ( - Command as MakeMessagesCommand, - check_programs, -) +from django.core.management.commands.makemessages import Command as MakeMessagesCommand, check_programs from judge.models import NavigationBar, ProblemType class Command(MakeMessagesCommand): def add_arguments(self, parser): - parser.add_argument( - "--locale", - "-l", - default=[], - dest="locale", - action="append", - help="Creates or updates the message files for the given locale(s) (e.g. pt_BR). " - "Can be used multiple times.", - ) - parser.add_argument( - "--exclude", - "-x", - default=[], - dest="exclude", - action="append", - help="Locales to exclude. Default is none. Can be used multiple times.", - ) - parser.add_argument( - "--all", - "-a", - action="store_true", - dest="all", - default=False, - help="Updates the message files for all existing locales.", - ) - parser.add_argument( - "--no-wrap", - action="store_true", - dest="no_wrap", - default=False, - help="Don't break long message lines into several lines.", - ) - parser.add_argument( - "--no-obsolete", - action="store_true", - dest="no_obsolete", - default=False, - help="Remove obsolete message strings.", - ) - parser.add_argument( - "--keep-pot", - action="store_true", - dest="keep_pot", - default=False, - help="Keep .pot file after making messages. Useful when debugging.", - ) + parser.add_argument('--locale', '-l', default=[], dest='locale', action='append', + help='Creates or updates the message files for the given locale(s) (e.g. pt_BR). ' + 'Can be used multiple times.') + parser.add_argument('--exclude', '-x', default=[], dest='exclude', action='append', + help='Locales to exclude. Default is none. Can be used multiple times.') + parser.add_argument('--all', '-a', action='store_true', dest='all', + default=False, help='Updates the message files for all existing locales.') + parser.add_argument('--no-wrap', action='store_true', dest='no_wrap', + default=False, help="Don't break long message lines into several lines.") + parser.add_argument('--no-obsolete', action='store_true', dest='no_obsolete', + default=False, help="Remove obsolete message strings.") + parser.add_argument('--keep-pot', action='store_true', dest='keep_pot', + default=False, help="Keep .pot file after making messages. Useful when debugging.") def handle(self, *args, **options): - locale = options.get("locale") - exclude = options.get("exclude") - self.domain = "dmoj-user" - self.verbosity = options.get("verbosity") - process_all = options.get("all") + locale = options.get('locale') + exclude = options.get('exclude') + self.domain = 'dmoj-user' + self.verbosity = options.get('verbosity') + process_all = options.get('all') # Need to ensure that the i18n framework is enabled if settings.configured: @@ -76,47 +40,43 @@ class Command(MakeMessagesCommand): settings.configure(USE_I18N=True) # Avoid messing with mutable class variables - if options.get("no_wrap"): - self.msgmerge_options = self.msgmerge_options[:] + ["--no-wrap"] - self.msguniq_options = self.msguniq_options[:] + ["--no-wrap"] - self.msgattrib_options = self.msgattrib_options[:] + ["--no-wrap"] - self.xgettext_options = self.xgettext_options[:] + ["--no-wrap"] - if options.get("no_location"): - self.msgmerge_options = self.msgmerge_options[:] + ["--no-location"] - self.msguniq_options = self.msguniq_options[:] + ["--no-location"] - self.msgattrib_options = self.msgattrib_options[:] + ["--no-location"] - self.xgettext_options = self.xgettext_options[:] + ["--no-location"] + if options.get('no_wrap'): + self.msgmerge_options = self.msgmerge_options[:] + ['--no-wrap'] + self.msguniq_options = self.msguniq_options[:] + ['--no-wrap'] + self.msgattrib_options = self.msgattrib_options[:] + ['--no-wrap'] + self.xgettext_options = self.xgettext_options[:] + ['--no-wrap'] + if options.get('no_location'): + self.msgmerge_options = self.msgmerge_options[:] + ['--no-location'] + self.msguniq_options = self.msguniq_options[:] + ['--no-location'] + self.msgattrib_options = self.msgattrib_options[:] + ['--no-location'] + self.xgettext_options = self.xgettext_options[:] + ['--no-location'] - self.no_obsolete = options.get("no_obsolete") - self.keep_pot = options.get("keep_pot") + self.no_obsolete = options.get('no_obsolete') + self.keep_pot = options.get('keep_pot') if locale is None and not exclude and not process_all: - raise CommandError( - "Type '%s help %s' for usage information." - % (os.path.basename(sys.argv[0]), sys.argv[1]) - ) + raise CommandError("Type '%s help %s' for usage information." % ( + os.path.basename(sys.argv[0]), sys.argv[1])) self.invoked_for_django = False self.locale_paths = [] self.default_locale_path = None - if os.path.isdir(os.path.join("conf", "locale")): - self.locale_paths = [os.path.abspath(os.path.join("conf", "locale"))] + if os.path.isdir(os.path.join('conf', 'locale')): + self.locale_paths = [os.path.abspath(os.path.join('conf', 'locale'))] self.default_locale_path = self.locale_paths[0] self.invoked_for_django = True else: self.locale_paths.extend(settings.LOCALE_PATHS) # Allow to run makemessages inside an app dir - if os.path.isdir("locale"): - self.locale_paths.append(os.path.abspath("locale")) + if os.path.isdir('locale'): + self.locale_paths.append(os.path.abspath('locale')) if self.locale_paths: self.default_locale_path = self.locale_paths[0] if not os.path.exists(self.default_locale_path): os.makedirs(self.default_locale_path) # Build locale list - locale_dirs = list( - filter(os.path.isdir, glob.glob("%s/*" % self.default_locale_path)) - ) + locale_dirs = list(filter(os.path.isdir, glob.glob('%s/*' % self.default_locale_path))) all_locales = list(map(os.path.basename, locale_dirs)) # Account for excluded locales @@ -127,9 +87,9 @@ class Command(MakeMessagesCommand): locales = set(locales) - set(exclude) if locales: - check_programs("msguniq", "msgmerge", "msgattrib") + check_programs('msguniq', 'msgmerge', 'msgattrib') - check_programs("xgettext") + check_programs('xgettext') try: potfiles = self.build_potfiles() @@ -148,41 +108,23 @@ class Command(MakeMessagesCommand): return [] def _emit_message(self, potfile, string): - potfile.write( - """ + potfile.write(''' msgid "%s" msgstr "" -""" - % string.replace("\\", r"\\") - .replace("\t", "\\t") - .replace("\n", "\\n") - .replace('"', '\\"') - ) +''' % string.replace('\\', r'\\').replace('\t', '\\t').replace('\n', '\\n').replace('"', '\\"')) def process_files(self, file_list): - with io.open( - os.path.join(self.default_locale_path, "dmoj-user.pot"), - "w", - encoding="utf-8", - ) as potfile: - potfile.write( - """ -msgid "" -msgstr "" - -"Content-Type: text/plain; charset=utf-8\\n" - """ - ) + with io.open(os.path.join(self.default_locale_path, 'dmoj-user.pot'), 'w', encoding='utf-8') as potfile: if self.verbosity > 1: - self.stdout.write("processing navigation bar") - for label in NavigationBar.objects.values_list("label", flat=True): + self.stdout.write('processing navigation bar') + for label in NavigationBar.objects.values_list('label', flat=True): if self.verbosity > 2: self.stdout.write('processing navigation item label "%s"\n' % label) self._emit_message(potfile, label) if self.verbosity > 1: - self.stdout.write("processing problem types") - for name in ProblemType.objects.values_list("full_name", flat=True): + self.stdout.write('processing problem types') + for name in ProblemType.objects.values_list('full_name', flat=True): if self.verbosity > 2: self.stdout.write('processing problem type name "%s"\n' % name) self._emit_message(potfile, name) diff --git a/judge/management/commands/render_pdf.py b/judge/management/commands/render_pdf.py index 87324b4..258081d 100644 --- a/judge/management/commands/render_pdf.py +++ b/judge/management/commands/render_pdf.py @@ -8,97 +8,52 @@ from django.template.loader import get_template from django.utils import translation from judge.models import Problem, ProblemTranslation -from judge.pdf_problems import ( - DefaultPdfMaker, - PhantomJSPdfMaker, - PuppeteerPDFRender, - SeleniumPDFRender, - SlimerJSPdfMaker, -) - +from judge.pdf_problems import DefaultPdfMaker, PhantomJSPdfMaker, PuppeteerPDFRender, SeleniumPDFRender, \ + SlimerJSPdfMaker class Command(BaseCommand): - help = "renders a PDF file of a problem" + help = 'renders a PDF file of a problem' def add_arguments(self, parser): - parser.add_argument("code", help="code of problem to render") - parser.add_argument( - "directory", nargs="?", help="directory to store temporaries" - ) - parser.add_argument( - "-l", - "--language", - default=settings.LANGUAGE_CODE, - help="language to render PDF in", - ) - parser.add_argument( - "-p", - "--phantomjs", - action="store_const", - const=PhantomJSPdfMaker, - default=DefaultPdfMaker, - dest="engine", - ) - parser.add_argument( - "-s", - "--slimerjs", - action="store_const", - const=SlimerJSPdfMaker, - dest="engine", - ) - parser.add_argument( - "-c", - "--chrome", - "--puppeteer", - action="store_const", - const=PuppeteerPDFRender, - dest="engine", - ) - parser.add_argument( - "-S", - "--selenium", - action="store_const", - const=SeleniumPDFRender, - dest="engine", - ) + parser.add_argument('code', help='code of problem to render') + parser.add_argument('directory', nargs='?', help='directory to store temporaries') + parser.add_argument('-l', '--language', default=settings.LANGUAGE_CODE, + help='language to render PDF in') + parser.add_argument('-p', '--phantomjs', action='store_const', const=PhantomJSPdfMaker, + default=DefaultPdfMaker, dest='engine') + parser.add_argument('-s', '--slimerjs', action='store_const', const=SlimerJSPdfMaker, dest='engine') + parser.add_argument('-c', '--chrome', '--puppeteer', action='store_const', + const=PuppeteerPDFRender, dest='engine') + parser.add_argument('-S', '--selenium', action='store_const', const=SeleniumPDFRender, dest='engine') def handle(self, *args, **options): try: - problem = Problem.objects.get(code=options["code"]) + problem = Problem.objects.get(code=options['code']) except Problem.DoesNotExist: - print("Bad problem code") + print('Bad problem code') return try: - trans = problem.translations.get(language=options["language"]) + trans = problem.translations.get(language=options['language']) except ProblemTranslation.DoesNotExist: trans = None - directory = options["directory"] - with options["engine"]( - directory, clean_up=directory is None - ) as maker, translation.override(options["language"]): + directory = options['directory'] + with options['engine'](directory, clean_up=directory is None) as maker, \ + translation.override(options['language']): problem_name = problem.name if trans is None else trans.name - maker.html = ( - get_template("problem/raw.html") - .render( - { - "problem": problem, - "problem_name": problem_name, - "description": problem.description - if trans is None - else trans.description, - "url": "", - } - ) - .replace('"//', '"https://') - .replace("'//", "'https://") - ) + maker.html = get_template('problem/raw.html').render({ + 'problem': problem, + 'problem_name': problem_name, + 'description': problem.description if trans is None else trans.description, + 'url': '', + 'math_engine': maker.math_engine, + }).replace('"//', '"https://').replace("'//", "'https://") maker.title = problem_name - for file in "style.css": + for file in ('style.css', 'pygment-github.css', 'mathjax_config.js'): maker.load(file, os.path.join(settings.DMOJ_RESOURCES, file)) maker.make(debug=True) if not maker.success: print(maker.log, file=sys.stderr) elif directory is None: - shutil.move(maker.pdffile, problem.code + ".pdf") + shutil.move(maker.pdffile, problem.code + '.pdf') diff --git a/judge/management/commands/runbridged.py b/judge/management/commands/runbridged.py index b968d63..c6f4536 100644 --- a/judge/management/commands/runbridged.py +++ b/judge/management/commands/runbridged.py @@ -5,4 +5,4 @@ from judge.bridge.daemon import judge_daemon class Command(BaseCommand): def handle(self, *args, **options): - judge_daemon() + judge_daemon() \ No newline at end of file diff --git a/judge/management/commands/runmoss.py b/judge/management/commands/runmoss.py index c8d026c..0558da3 100644 --- a/judge/management/commands/runmoss.py +++ b/judge/management/commands/runmoss.py @@ -6,50 +6,42 @@ from judge.models import Contest, ContestParticipation, Submission class Command(BaseCommand): - help = "Checks for duplicate code using MOSS" + help = 'Checks for duplicate code using MOSS' LANG_MAPPING = { - ("C++", MOSS_LANG_CC), - ("C", MOSS_LANG_C), - ("Java", MOSS_LANG_JAVA), - ("Python", MOSS_LANG_PYTHON), - ("Pascal", MOSS_LANG_PASCAL), + ('C++', MOSS_LANG_CC), + ('C', MOSS_LANG_C), + ('Java', MOSS_LANG_JAVA), + ('Python', MOSS_LANG_PYTHON), + ('Pascal', MOSS_LANG_PASCAL), } def add_arguments(self, parser): - parser.add_argument("contest", help="the id of the contest") + parser.add_argument('contest', help='the id of the contest') def handle(self, *args, **options): moss_api_key = settings.MOSS_API_KEY if moss_api_key is None: - print("No MOSS API Key supplied") + print('No MOSS API Key supplied') return - contest = options["contest"] + contest = options['contest'] - for problem in Contest.objects.get(key=contest).problems.order_by("code"): - print("========== %s / %s ==========" % (problem.code, problem.name)) + for problem in Contest.objects.get(key=contest).problems.order_by('code'): + print('========== %s / %s ==========' % (problem.code, problem.name)) for dmoj_lang, moss_lang in self.LANG_MAPPING: - print("%s: " % dmoj_lang, end=" ") + print("%s: " % dmoj_lang, end=' ') subs = Submission.objects.filter( - contest__participation__virtual__in=( - ContestParticipation.LIVE, - ContestParticipation.SPECTATE, - ), + contest__participation__virtual__in=(ContestParticipation.LIVE, ContestParticipation.SPECTATE), contest__participation__contest__key=contest, - result="AC", - problem__id=problem.id, + result='AC', problem__id=problem.id, language__common_name=dmoj_lang, - ).values_list("user__user__username", "source__source") + ).values_list('user__user__username', 'source__source') if not subs: - print("") + print('') continue - moss_call = MOSS( - moss_api_key, - language=moss_lang, - matching_file_limit=100, - comment="%s - %s" % (contest, problem.code), - ) + moss_call = MOSS(moss_api_key, language=moss_lang, matching_file_limit=100, + comment='%s - %s' % (contest, problem.code)) users = set() @@ -57,6 +49,6 @@ class Command(BaseCommand): if username in users: continue users.add(username) - moss_call.add_file_from_memory(username, source.encode("utf-8")) + moss_call.add_file_from_memory(username, source.encode('utf-8')) - print("(%d): %s" % (subs.count(), moss_call.process())) + print('(%d): %s' % (subs.count(), moss_call.process())) diff --git a/judge/markdown.py b/judge/markdown.py deleted file mode 100644 index 80eb21a..0000000 --- a/judge/markdown.py +++ /dev/null @@ -1,149 +0,0 @@ -import markdown as _markdown -import bleach -from django.utils.html import escape -from bs4 import BeautifulSoup -from pymdownx import superfences -from django.conf import settings -from urllib.parse import urlparse - -from judge.markdown_extensions import YouTubeExtension, EmoticonExtension - - -EXTENSIONS = [ - "pymdownx.arithmatex", - "pymdownx.magiclink", - "pymdownx.betterem", - "pymdownx.details", - "pymdownx.emoji", - "pymdownx.inlinehilite", - "pymdownx.superfences", - "pymdownx.highlight", - "pymdownx.tasklist", - "markdown.extensions.footnotes", - "markdown.extensions.attr_list", - "markdown.extensions.def_list", - "markdown.extensions.tables", - "markdown.extensions.admonition", - "nl2br", - "mdx_breakless_lists", - YouTubeExtension(), - EmoticonExtension(), -] - -EXTENSION_CONFIGS = { - "pymdownx.arithmatex": { - "generic": True, - }, - "pymdownx.superfences": { - "custom_fences": [ - { - "name": "sample", - "class": "no-border", - "format": superfences.fence_code_format, - } - ], - }, - "pymdownx.highlight": { - "auto_title": True, - "auto_title_map": { - "Text Only": "", - }, - }, -} - -ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + [ - "img", - "center", - "iframe", - "div", - "span", - "table", - "tr", - "td", - "th", - "tr", - "pre", - "code", - "p", - "hr", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "thead", - "tbody", - "sup", - "dl", - "dt", - "dd", - "br", - "details", - "summary", -] - -ALLOWED_ATTRS = [ - "src", - "width", - "height", - "href", - "class", - "open", - "title", - "frameborder", - "allow", - "allowfullscreen", - "loading", -] - - -def _wrap_img_iframe_with_lazy_load(soup): - for img in soup.findAll("img"): - if img.get("src"): - img["loading"] = "lazy" - for img in soup.findAll("iframe"): - if img.get("src"): - img["loading"] = "lazy" - return soup - - -def _wrap_images_with_featherlight(soup): - for img in soup.findAll("img"): - if img.get("src"): - link = soup.new_tag("a", href=img["src"], **{"data-featherlight": "image"}) - img.wrap(link) - return soup - - -def _open_external_links_in_new_tab(soup): - domain = settings.SITE_DOMAIN.lower() - for a in soup.findAll("a", href=True): - href = a["href"] - if href.startswith("http://") or href.startswith("https://"): - link_domain = urlparse(href).netloc.lower() - if link_domain != domain: - a["target"] = "_blank" - return soup - - -def markdown(value, lazy_load=False): - extensions = EXTENSIONS - html = _markdown.markdown( - value, extensions=extensions, extension_configs=EXTENSION_CONFIGS - ) - - html = bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS) - - if not html: - html = escape(value) - - soup = BeautifulSoup(html, features="html.parser") - if lazy_load: - soup = _wrap_img_iframe_with_lazy_load(soup) - - soup = _wrap_images_with_featherlight(soup) - soup = _open_external_links_in_new_tab(soup) - html = str(soup) - - return '
%s
' % html diff --git a/judge/markdown_extensions/__init__.py b/judge/markdown_extensions/__init__.py deleted file mode 100644 index bc405f3..0000000 --- a/judge/markdown_extensions/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .youtube import YouTubeExtension -from .emoticon import EmoticonExtension diff --git a/judge/markdown_extensions/emoticon.py b/judge/markdown_extensions/emoticon.py deleted file mode 100644 index 809b1d3..0000000 --- a/judge/markdown_extensions/emoticon.py +++ /dev/null @@ -1,112 +0,0 @@ -import markdown -from markdown.extensions import Extension -from markdown.inlinepatterns import InlineProcessor -import xml.etree.ElementTree as etree -import re - -EMOTICON_EMOJI_MAP = { - ":D": "\U0001F603", # Smiling Face with Open Mouth - ":)": "\U0001F642", # Slightly Smiling Face - ":-)": "\U0001F642", # Slightly Smiling Face with Nose - ":(": "\U0001F641", # Slightly Frowning Face - ":-(": "\U0001F641", # Slightly Frowning Face with Nose - ";)": "\U0001F609", # Winking Face - ";-)": "\U0001F609", # Winking Face with Nose - ":P": "\U0001F61B", # Face with Tongue - ":-P": "\U0001F61B", # Face with Tongue and Nose - ":p": "\U0001F61B", # Face with Tongue - ":-p": "\U0001F61B", # Face with Tongue and Nose - ";P": "\U0001F61C", # Winking Face with Tongue - ";-P": "\U0001F61C", # Winking Face with Tongue and Nose - ";p": "\U0001F61C", # Winking Face with Tongue - ";-p": "\U0001F61C", # Winking Face with Tongue and Nose - ":'(": "\U0001F622", # Crying Face - ":o": "\U0001F62E", # Face with Open Mouth - ":-o": "\U0001F62E", # Face with Open Mouth and Nose - ":O": "\U0001F62E", # Face with Open Mouth - ":-O": "\U0001F62E", # Face with Open Mouth and Nose - ":-0": "\U0001F62E", # Face with Open Mouth and Nose - ">:(": "\U0001F620", # Angry Face - ">:-(": "\U0001F620", # Angry Face with Nose - ">:)": "\U0001F608", # Smiling Face with Horns - ">:-)": "\U0001F608", # Smiling Face with Horns and Nose - "XD": "\U0001F606", # Grinning Squinting Face - "xD": "\U0001F606", # Grinning Squinting Face - "B)": "\U0001F60E", # Smiling Face with Sunglasses - "B-)": "\U0001F60E", # Smiling Face with Sunglasses and Nose - "O:)": "\U0001F607", # Smiling Face with Halo - "O:-)": "\U0001F607", # Smiling Face with Halo and Nose - "0:)": "\U0001F607", # Smiling Face with Halo - "0:-)": "\U0001F607", # Smiling Face with Halo and Nose - ">:P": "\U0001F92A", # Zany Face (sticking out tongue and winking) - ">:-P": "\U0001F92A", # Zany Face with Nose - ">:p": "\U0001F92A", # Zany Face (sticking out tongue and winking) - ">:-p": "\U0001F92A", # Zany Face with Nose - ":/": "\U0001F615", # Confused Face - ":-/": "\U0001F615", # Confused Face with Nose - ":\\": "\U0001F615", # Confused Face - ":-\\": "\U0001F615", # Confused Face with Nose - "3:)": "\U0001F608", # Smiling Face with Horns - "3:-)": "\U0001F608", # Smiling Face with Horns and Nose - "<3": "\u2764\uFE0F", # Red Heart - ":P": "\U0001F61D", # Face with Stuck-Out Tongue and Tightly-Closed Eyes - ":-/": "\U0001F615", # Confused Face - ":/": "\U0001F615", - ":\\": "\U0001F615", - ":-\\": "\U0001F615", - ":|": "\U0001F610", # Neutral Face - ":-|": "\U0001F610", - "8)": "\U0001F60E", # Smiling Face with Sunglasses - "8-)": "\U0001F60E", - "O:)": "\U0001F607", # Smiling Face with Halo - "O:-)": "\U0001F607", - ":3": "\U0001F60A", # Smiling Face with Smiling Eyes - "^.^": "\U0001F60A", - "-_-": "\U0001F611", # Expressionless Face - "T_T": "\U0001F62D", # Loudly Crying Face - "T.T": "\U0001F62D", - ">.<": "\U0001F623", # Persevering Face - "x_x": "\U0001F635", # Dizzy Face - "X_X": "\U0001F635", - ":]": "\U0001F600", # Grinning Face - ":[": "\U0001F641", # Slightly Frowning Face - "=]": "\U0001F600", - "=[": "\U0001F641", - "D:<": "\U0001F621", # Pouting Face - "D:": "\U0001F629", # Weary Face - "D=": "\U0001F6AB", # No Entry Sign (sometimes used to denote dismay or frustration) - ":'D": "\U0001F602", # Face with Tears of Joy - "D':": "\U0001F625", # Disappointed but Relieved Face - "D8": "\U0001F631", # Face Screaming in Fear - "-.-": "\U0001F644", # Face with Rolling Eyes - "-_-;": "\U0001F612", # Unamused -} - - -class EmoticonEmojiInlineProcessor(InlineProcessor): - def handleMatch(self, m, data): - emoticon = m.group(1) - emoji = EMOTICON_EMOJI_MAP.get(emoticon, "") - if emoji: - el = etree.Element("span") - el.text = markdown.util.AtomicString(emoji) - el.set("class", "big-emoji") - return el, m.start(0), m.end(0) - else: - return None, m.start(0), m.end(0) - - -class EmoticonExtension(Extension): - def extendMarkdown(self, md): - emoticon_pattern = ( - r"(?:(?<=\s)|^)" # Lookbehind for a whitespace character or the start of the string - r"(" + "|".join(map(re.escape, EMOTICON_EMOJI_MAP.keys())) + r")" - r"(?=\s|$)" # Lookahead for a whitespace character or the end of the string - ) - emoticon_processor = EmoticonEmojiInlineProcessor(emoticon_pattern, md) - md.inlinePatterns.register(emoticon_processor, "emoticon_to_emoji", 1) diff --git a/judge/markdown_extensions/youtube.py b/judge/markdown_extensions/youtube.py deleted file mode 100644 index 89a9fa8..0000000 --- a/judge/markdown_extensions/youtube.py +++ /dev/null @@ -1,36 +0,0 @@ -import markdown -from markdown.inlinepatterns import InlineProcessor -from markdown.extensions import Extension -import xml.etree.ElementTree as etree - -YOUTUBE_REGEX = ( - r"(https?://)?(www\.)?" "(youtube\.com/watch\?v=|youtu\.be/)" "([\w-]+)(&[\w=]*)?" -) - - -class YouTubeEmbedProcessor(InlineProcessor): - def handleMatch(self, m, data): - youtube_id = m.group(4) - if not youtube_id: - return None, None, None - - # Create an iframe element with the YouTube embed URL - iframe = etree.Element("iframe") - iframe.set("width", "100%") - iframe.set("height", "360") - iframe.set("src", f"https://www.youtube.com/embed/{youtube_id}") - iframe.set("frameborder", "0") - iframe.set("allowfullscreen", "true") - center = etree.Element("center") - center.append(iframe) - - # Return the iframe as the element to replace the match, along with the start and end indices - return center, m.start(0), m.end(0) - - -class YouTubeExtension(Extension): - def extendMarkdown(self, md): - # Create the YouTube link pattern - YOUTUBE_PATTERN = YouTubeEmbedProcessor(YOUTUBE_REGEX, md) - # Register the pattern to apply the YouTubeEmbedProcessor - md.inlinePatterns.register(YOUTUBE_PATTERN, "youtube", 175) diff --git a/judge/middleware.py b/judge/middleware.py index c08b2b9..b35e1a3 100644 --- a/judge/middleware.py +++ b/judge/middleware.py @@ -1,23 +1,7 @@ -import time -import logging -import random -import json -from datetime import datetime - from django.conf import settings -from django.http import HttpResponseRedirect, Http404 +from django.http import HttpResponseRedirect from django.urls import Resolver404, resolve, reverse from django.utils.http import urlquote -from django.contrib.sites.shortcuts import get_current_site -from django.core.exceptions import ObjectDoesNotExist -from django.utils.translation import gettext as _ - -from judge.models import Organization -from judge.utils.views import generic_message - - -USED_DOMAINS = ["www"] -URL_NAMES_BYPASS_SUBDOMAIN = ["submission_source_file"] class ShortCircuitMiddleware: @@ -26,13 +10,11 @@ class ShortCircuitMiddleware: def __call__(self, request): try: - callback, args, kwargs = resolve( - request.path_info, getattr(request, "urlconf", None) - ) + callback, args, kwargs = resolve(request.path_info, getattr(request, 'urlconf', None)) except Resolver404: callback, args, kwargs = None, None, None - if getattr(callback, "short_circuit_middleware", False): + if getattr(callback, 'short_circuit_middleware', False): return callback(request, *args, **kwargs) return self.get_response(request) @@ -44,16 +26,11 @@ class DMOJLoginMiddleware(object): def __call__(self, request): if request.user.is_authenticated: profile = request.profile = request.user.profile - login_2fa_path = reverse("login_2fa") - if ( - profile.is_totp_enabled - and not request.session.get("2fa_passed", False) - and request.path not in (login_2fa_path, reverse("auth_logout")) - and not request.path.startswith(settings.STATIC_URL) - ): - return HttpResponseRedirect( - login_2fa_path + "?next=" + urlquote(request.get_full_path()) - ) + login_2fa_path = reverse('login_2fa') + if (profile.is_totp_enabled and not request.session.get('2fa_passed', False) and + request.path not in (login_2fa_path, reverse('auth_logout')) and + not request.path.startswith(settings.STATIC_URL)): + return HttpResponseRedirect(login_2fa_path + '?next=' + urlquote(request.get_full_path())) else: request.profile = None return self.get_response(request) @@ -80,101 +57,9 @@ class ContestMiddleware(object): profile.update_contest() request.participation = profile.current_contest request.in_contest = request.participation is not None - request.contest_mode = request.session.get("contest_mode", True) + request.contest_mode = request.session.get('contest_mode', True) else: request.in_contest = False request.participation = None request.in_contest_mode = request.in_contest and request.contest_mode return self.get_response(request) - - -class DarkModeMiddleware(object): - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - if "darkmode" in request.GET: - return HttpResponseRedirect( - reverse("toggle_darkmode") + "?next=" + urlquote(request.path) - ) - return self.get_response(request) - - -class SubdomainMiddleware(object): - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - request.organization = None - if not settings.USE_SUBDOMAIN: - return self.get_response(request) - - domain = request.get_host() - site = get_current_site(request).domain - subdomain = domain[: len(domain) - len(site)].lower() - - if len(subdomain) <= 1: - return self.get_response(request) - - subdomain = subdomain[:-1] - - if ( - subdomain in USED_DOMAINS - or resolve(request.path).url_name in URL_NAMES_BYPASS_SUBDOMAIN - ): - return self.get_response(request) - - try: - organization = Organization.objects.get(slug=subdomain) - if request.profile and organization in request.profile.organizations.all(): - request.organization = organization - else: - if request.profile: - return generic_message( - request, - _("No permission"), - _("You need to join this group first"), - status=404, - ) - if not request.GET.get("next", None): - return HttpResponseRedirect( - reverse("auth_login") + "?next=" + urlquote(request.path) - ) - except ObjectDoesNotExist: - return generic_message( - request, - _("No such group"), - _("No such group"), - status=404, - ) - return self.get_response(request) - - -class SlowRequestMiddleware(object): - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - logger = logging.getLogger("judge.request_time") - logger_slow = logging.getLogger("judge.slow_request") - start_time = time.time() - response = self.get_response(request) - if response.status_code == 200: - try: - response_time = time.time() - start_time - url_name = resolve(request.path).url_name - message = { - "url_name": url_name, - "response_time": response_time * 1000, - "profile": request.user.username, - "date": datetime.now().strftime("%Y/%m/%d"), - "url": request.build_absolute_uri(), - "method": request.method, - } - if response_time > 9: - logger_slow.info(json.dumps(message)) - if random.random() < 0.1: - logger.info(json.dumps(message)) - except Exception: - pass - return response diff --git a/judge/migrations/0001_squashed_0084_contest_formats.py b/judge/migrations/0001_squashed_0084_contest_formats.py index ae4e6ae..22aa659 100644 --- a/judge/migrations/0001_squashed_0084_contest_formats.py +++ b/judge/migrations/0001_squashed_0084_contest_formats.py @@ -14,3275 +14,748 @@ import judge.utils.problem_data class Migration(migrations.Migration): - replaces = [ - ("judge", "0001_initial"), - ("judge", "0002_license"), - ("judge", "0003_license_key"), - ("judge", "0004_language_limit"), - ("judge", "0005_nav_path_len"), - ("judge", "0006_language_extension"), - ("judge", "0007_test_site_perm"), - ("judge", "0008_contestproblem_order"), - ("judge", "0009_solution_problem"), - ("judge", "0010_comment_page_index"), - ("judge", "0011_organization_is_open"), - ("judge", "0012_organization_perms"), - ("judge", "0013_private_contests"), - ("judge", "0014_multi_organization"), - ("judge", "0015_remove_single_organization"), - ("judge", "0016_organizationrequest"), - ("judge", "0017_edit_public_problem_perm"), - ("judge", "0018_django_1_9"), - ("judge", "0019_og_images"), - ("judge", "0020_profile_user_script"), - ("judge", "0021_output_prefix_override"), - ("judge", "0022_judge_last_ping"), - ("judge", "0023_contest_tag"), - ("judge", "0024_submission_judge"), - ("judge", "0025_submission_rejudge_flag"), - ("judge", "0026_change_public_visibility_perm"), - ("judge", "0027_bridge_revert"), - ("judge", "0028_judge_ip"), - ("judge", "0029_problem_translation"), - ("judge", "0030_remove_contest_profile"), - ("judge", "0031_judge_versions"), - ("judge", "0032_hide_problem_tags_in_contest"), - ("judge", "0033_proper_pretest_support"), - ("judge", "0034_submission_is_pretested"), - ("judge", "0035_contest_spectate_mode"), - ("judge", "0036_contest_participation_unique"), - ("judge", "0037_user_count_ac_rate_field"), - ("judge", "0038_profile_problem_count"), - ("judge", "0039_remove_contest_is_external"), - ("judge", "0040_profile_math_engine"), - ("judge", "0041_virtual_contest_participation"), - ("judge", "0042_remove_spectate_field"), - ("judge", "0043_contest_user_count"), - ("judge", "0044_organization_slots"), - ("judge", "0045_organization_access_code"), - ("judge", "0046_blogpost_authors"), - ("judge", "0047_site_managed_data"), - ("judge", "0048_site_managed_checkers"), - ("judge", "0049_contest_summary"), - ("judge", "0050_problem_tester_field"), - ("judge", "0051_was_rejudged_field"), - ("judge", "0052_switch_to_durationfield"), - ("judge", "0053_opengraph_problems"), - ("judge", "0054_tickets"), - ("judge", "0055_add_performance_points"), - ("judge", "0056_ticket_is_open"), - ("judge", "0057_blue_pretests"), - ("judge", "0058_problem_curator_field"), - ("judge", "0059_problem_is_manually_managed"), - ("judge", "0060_contest_clarifications"), - ("judge", "0061_language_template"), - ("judge", "0062_add_contest_submission_limit"), - ("judge", "0063_new_solutions"), - ("judge", "0064_unique_solution"), - ("judge", "0065_blogpost_perms"), - ("judge", "0066_submission_date_index"), - ("judge", "0067_contest_access_code"), - ("judge", "0068_hide_scoreboard"), - ("judge", "0069_judge_blocking"), - ("judge", "0070_organization_slug"), - ("judge", "0071_organization_private_problems"), - ("judge", "0072_contest_logo_override_image"), - ("judge", "0073_comment_lock"), - ("judge", "0074_totp"), - ("judge", "0075_organization_admin_reverse"), - ("judge", "0076_problem_statistics"), - ("judge", "0077_remove_organization_key"), - ("judge", "0078_add_user_notes"), - ("judge", "0079_remove_comment_title"), - ("judge", "0080_contest_banned_users"), - ("judge", "0081_unlisted_users"), - ("judge", "0082_remove_profile_name"), - ("judge", "0083_extended_feedback"), - ("judge", "0084_contest_formats"), - ] + replaces = [('judge', '0001_initial'), ('judge', '0002_license'), ('judge', '0003_license_key'), ('judge', '0004_language_limit'), ('judge', '0005_nav_path_len'), ('judge', '0006_language_extension'), ('judge', '0007_test_site_perm'), ('judge', '0008_contestproblem_order'), ('judge', '0009_solution_problem'), ('judge', '0010_comment_page_index'), ('judge', '0011_organization_is_open'), ('judge', '0012_organization_perms'), ('judge', '0013_private_contests'), ('judge', '0014_multi_organization'), ('judge', '0015_remove_single_organization'), ('judge', '0016_organizationrequest'), ('judge', '0017_edit_public_problem_perm'), ('judge', '0018_django_1_9'), ('judge', '0019_og_images'), ('judge', '0020_profile_user_script'), ('judge', '0021_output_prefix_override'), ('judge', '0022_judge_last_ping'), ('judge', '0023_contest_tag'), ('judge', '0024_submission_judge'), ('judge', '0025_submission_rejudge_flag'), ('judge', '0026_change_public_visibility_perm'), ('judge', '0027_bridge_revert'), ('judge', '0028_judge_ip'), ('judge', '0029_problem_translation'), ('judge', '0030_remove_contest_profile'), ('judge', '0031_judge_versions'), ('judge', '0032_hide_problem_tags_in_contest'), ('judge', '0033_proper_pretest_support'), ('judge', '0034_submission_is_pretested'), ('judge', '0035_contest_spectate_mode'), ('judge', '0036_contest_participation_unique'), ('judge', '0037_user_count_ac_rate_field'), ('judge', '0038_profile_problem_count'), ('judge', '0039_remove_contest_is_external'), ('judge', '0040_profile_math_engine'), ('judge', '0041_virtual_contest_participation'), ('judge', '0042_remove_spectate_field'), ('judge', '0043_contest_user_count'), ('judge', '0044_organization_slots'), ('judge', '0045_organization_access_code'), ('judge', '0046_blogpost_authors'), ('judge', '0047_site_managed_data'), ('judge', '0048_site_managed_checkers'), ('judge', '0049_contest_summary'), ('judge', '0050_problem_tester_field'), ('judge', '0051_was_rejudged_field'), ('judge', '0052_switch_to_durationfield'), ('judge', '0053_opengraph_problems'), ('judge', '0054_tickets'), ('judge', '0055_add_performance_points'), ('judge', '0056_ticket_is_open'), ('judge', '0057_blue_pretests'), ('judge', '0058_problem_curator_field'), ('judge', '0059_problem_is_manually_managed'), ('judge', '0060_contest_clarifications'), ('judge', '0061_language_template'), ('judge', '0062_add_contest_submission_limit'), ('judge', '0063_new_solutions'), ('judge', '0064_unique_solution'), ('judge', '0065_blogpost_perms'), ('judge', '0066_submission_date_index'), ('judge', '0067_contest_access_code'), ('judge', '0068_hide_scoreboard'), ('judge', '0069_judge_blocking'), ('judge', '0070_organization_slug'), ('judge', '0071_organization_private_problems'), ('judge', '0072_contest_logo_override_image'), ('judge', '0073_comment_lock'), ('judge', '0074_totp'), ('judge', '0075_organization_admin_reverse'), ('judge', '0076_problem_statistics'), ('judge', '0077_remove_organization_key'), ('judge', '0078_add_user_notes'), ('judge', '0079_remove_comment_title'), ('judge', '0080_contest_banned_users'), ('judge', '0081_unlisted_users'), ('judge', '0082_remove_profile_name'), ('judge', '0083_extended_feedback'), ('judge', '0084_contest_formats')] initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("contenttypes", "0002_remove_content_type_name"), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ migrations.CreateModel( - name="BlogPost", + name='BlogPost', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("title", models.CharField(max_length=100, verbose_name="post title")), - ("slug", models.SlugField(verbose_name="slug")), - ( - "visible", - models.BooleanField( - default=False, verbose_name="public visibility" - ), - ), - ("sticky", models.BooleanField(default=False, verbose_name="sticky")), - ("publish_on", models.DateTimeField(verbose_name="publish after")), - ("content", models.TextField(verbose_name="post content")), - ("summary", models.TextField(blank=True, verbose_name="post summary")), - ( - "og_image", - models.CharField( - blank=True, - default="", - max_length=150, - verbose_name="openGraph image", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100, verbose_name='post title')), + ('slug', models.SlugField(verbose_name='slug')), + ('visible', models.BooleanField(default=False, verbose_name='public visibility')), + ('sticky', models.BooleanField(default=False, verbose_name='sticky')), + ('publish_on', models.DateTimeField(verbose_name='publish after')), + ('content', models.TextField(verbose_name='post content')), + ('summary', models.TextField(blank=True, verbose_name='post summary')), + ('og_image', models.CharField(blank=True, default='', max_length=150, verbose_name='openGraph image')), ], options={ - "verbose_name_plural": "blog posts", - "permissions": (("edit_all_post", "Edit all posts"),), - "verbose_name": "blog post", + 'verbose_name_plural': 'blog posts', + 'permissions': (('edit_all_post', 'Edit all posts'),), + 'verbose_name': 'blog post', }, ), migrations.CreateModel( - name="Comment", + name='Comment', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "time", - models.DateTimeField(auto_now_add=True, verbose_name="posted time"), - ), - ( - "page", - models.CharField( - db_index=True, - max_length=30, - validators=[ - django.core.validators.RegexValidator( - "^[pcs]:[a-z0-9]+$|^b:\\d+$", - "Page code must be ^[pcs]:[a-z0-9]+$|^b:\\d+$", - ) - ], - verbose_name="associated page", - ), - ), - ("score", models.IntegerField(default=0, verbose_name="votes")), - ( - "body", - models.TextField(max_length=8192, verbose_name="body of comment"), - ), - ( - "hidden", - models.BooleanField(default=0, verbose_name="hide the comment"), - ), - ("lft", models.PositiveIntegerField(db_index=True, editable=False)), - ("rght", models.PositiveIntegerField(db_index=True, editable=False)), - ("tree_id", models.PositiveIntegerField(db_index=True, editable=False)), - ("level", models.PositiveIntegerField(db_index=True, editable=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('time', models.DateTimeField(auto_now_add=True, verbose_name='posted time')), + ('page', models.CharField(db_index=True, max_length=30, validators=[django.core.validators.RegexValidator('^[pcs]:[a-z0-9]+$|^b:\\d+$', 'Page code must be ^[pcs]:[a-z0-9]+$|^b:\\d+$')], verbose_name='associated page')), + ('score', models.IntegerField(default=0, verbose_name='votes')), + ('body', models.TextField(max_length=8192, verbose_name='body of comment')), + ('hidden', models.BooleanField(default=0, verbose_name='hide the comment')), + ('lft', models.PositiveIntegerField(db_index=True, editable=False)), + ('rght', models.PositiveIntegerField(db_index=True, editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(db_index=True, editable=False)), ], options={ - "verbose_name_plural": "comments", - "verbose_name": "comment", + 'verbose_name_plural': 'comments', + 'verbose_name': 'comment', }, ), migrations.CreateModel( - name="CommentLock", + name='CommentLock', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "page", - models.CharField( - db_index=True, - max_length=30, - validators=[ - django.core.validators.RegexValidator( - "^[pcs]:[a-z0-9]+$|^b:\\d+$", - "Page code must be ^[pcs]:[a-z0-9]+$|^b:\\d+$", - ) - ], - verbose_name="associated page", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('page', models.CharField(db_index=True, max_length=30, validators=[django.core.validators.RegexValidator('^[pcs]:[a-z0-9]+$|^b:\\d+$', 'Page code must be ^[pcs]:[a-z0-9]+$|^b:\\d+$')], verbose_name='associated page')), ], options={ - "permissions": (("override_comment_lock", "Override comment lock"),), + 'permissions': (('override_comment_lock', 'Override comment lock'),), }, ), migrations.CreateModel( - name="CommentVote", + name='CommentVote', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("score", models.IntegerField()), - ( - "comment", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="votes", - to="judge.Comment", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('score', models.IntegerField()), + ('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='votes', to='judge.Comment')), ], options={ - "verbose_name_plural": "comment votes", - "verbose_name": "comment vote", + 'verbose_name_plural': 'comment votes', + 'verbose_name': 'comment vote', }, ), migrations.CreateModel( - name="Contest", + name='Contest', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "key", - models.CharField( - max_length=20, - unique=True, - validators=[ - django.core.validators.RegexValidator( - "^[a-z0-9]+$", "Contest id must be ^[a-z0-9]+$" - ) - ], - verbose_name="contest id", - ), - ), - ( - "name", - models.CharField( - db_index=True, max_length=100, verbose_name="contest name" - ), - ), - ( - "description", - models.TextField(blank=True, verbose_name="description"), - ), - ( - "start_time", - models.DateTimeField(db_index=True, verbose_name="start time"), - ), - ( - "end_time", - models.DateTimeField(db_index=True, verbose_name="end time"), - ), - ( - "time_limit", - models.DurationField( - blank=True, null=True, verbose_name="time limit" - ), - ), - ( - "is_public", - models.BooleanField( - default=False, - help_text="Should be set even for organization-private contests, where it determines whether the contest is visible to members of the specified organizations.", - verbose_name="publicly visible", - ), - ), - ( - "is_rated", - models.BooleanField( - default=False, - help_text="Whether this contest can be rated.", - verbose_name="contest rated", - ), - ), - ( - "hide_scoreboard", - models.BooleanField( - default=False, - help_text="Whether the scoreboard should remain hidden for the duration of the contest.", - verbose_name="hide scoreboard", - ), - ), - ( - "use_clarifications", - models.BooleanField( - default=True, - help_text="Use clarification system instead of comments.", - verbose_name="no comments", - ), - ), - ( - "rate_all", - models.BooleanField( - default=False, - help_text="Rate all users who joined.", - verbose_name="rate all", - ), - ), - ( - "is_private", - models.BooleanField( - default=False, verbose_name="private to organizations" - ), - ), - ( - "hide_problem_tags", - models.BooleanField( - default=False, - help_text="Whether problem tags should be hidden by default.", - verbose_name="hide problem tags", - ), - ), - ( - "run_pretests_only", - models.BooleanField( - default=False, - help_text="Whether judges should grade pretests only, versus all testcases. Commonly set during a contest, then unset prior to rejudging user submissions when the contest ends.", - verbose_name="run pretests only", - ), - ), - ( - "og_image", - models.CharField( - blank=True, - default="", - max_length=150, - verbose_name="OpenGraph image", - ), - ), - ( - "logo_override_image", - models.CharField( - blank=True, - default="", - help_text="This image will replace the default site logo for users inside the contest.", - max_length=150, - verbose_name="Logo override image", - ), - ), - ( - "user_count", - models.IntegerField( - default=0, verbose_name="the amount of live participants" - ), - ), - ( - "summary", - models.TextField( - blank=True, - help_text="Plain-text, shown in meta description tag, e.g. for social media.", - verbose_name="contest summary", - ), - ), - ( - "access_code", - models.CharField( - blank=True, - default="", - help_text="An optional code to prompt contestants before they are allowed to join the contest. Leave it blank to disable.", - max_length=255, - verbose_name="access code", - ), - ), - ( - "format_name", - models.CharField( - choices=[("default", "Default")], - default="default", - help_text="The contest format module to use.", - max_length=32, - verbose_name="contest format", - ), - ), - ( - "format_config", - jsonfield.fields.JSONField( - blank=True, - help_text="A JSON object to serve as the configuration for the chosen contest format module. Leave empty to use None. Exact format depends on the contest format selected.", - null=True, - verbose_name="contest format configuration", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(max_length=20, unique=True, validators=[django.core.validators.RegexValidator('^[a-z0-9]+$', 'Contest id must be ^[a-z0-9]+$')], verbose_name='contest id')), + ('name', models.CharField(db_index=True, max_length=100, verbose_name='contest name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('start_time', models.DateTimeField(db_index=True, verbose_name='start time')), + ('end_time', models.DateTimeField(db_index=True, verbose_name='end time')), + ('time_limit', models.DurationField(blank=True, null=True, verbose_name='time limit')), + ('is_public', models.BooleanField(default=False, help_text='Should be set even for organization-private contests, where it determines whether the contest is visible to members of the specified organizations.', verbose_name='publicly visible')), + ('is_rated', models.BooleanField(default=False, help_text='Whether this contest can be rated.', verbose_name='contest rated')), + ('hide_scoreboard', models.BooleanField(default=False, help_text='Whether the scoreboard should remain hidden for the duration of the contest.', verbose_name='hide scoreboard')), + ('use_clarifications', models.BooleanField(default=True, help_text='Use clarification system instead of comments.', verbose_name='no comments')), + ('rate_all', models.BooleanField(default=False, help_text='Rate all users who joined.', verbose_name='rate all')), + ('is_private', models.BooleanField(default=False, verbose_name='private to organizations')), + ('hide_problem_tags', models.BooleanField(default=False, help_text='Whether problem tags should be hidden by default.', verbose_name='hide problem tags')), + ('run_pretests_only', models.BooleanField(default=False, help_text='Whether judges should grade pretests only, versus all testcases. Commonly set during a contest, then unset prior to rejudging user submissions when the contest ends.', verbose_name='run pretests only')), + ('og_image', models.CharField(blank=True, default='', max_length=150, verbose_name='OpenGraph image')), + ('logo_override_image', models.CharField(blank=True, default='', help_text='This image will replace the default site logo for users inside the contest.', max_length=150, verbose_name='Logo override image')), + ('user_count', models.IntegerField(default=0, verbose_name='the amount of live participants')), + ('summary', models.TextField(blank=True, help_text='Plain-text, shown in meta description tag, e.g. for social media.', verbose_name='contest summary')), + ('access_code', models.CharField(blank=True, default='', help_text='An optional code to prompt contestants before they are allowed to join the contest. Leave it blank to disable.', max_length=255, verbose_name='access code')), + ('format_name', models.CharField(choices=[('default', 'Default')], default='default', help_text='The contest format module to use.', max_length=32, verbose_name='contest format')), + ('format_config', jsonfield.fields.JSONField(blank=True, help_text='A JSON object to serve as the configuration for the chosen contest format module. Leave empty to use None. Exact format depends on the contest format selected.', null=True, verbose_name='contest format configuration')), ], options={ - "verbose_name_plural": "contests", - "permissions": ( - ("see_private_contest", "See private contests"), - ("edit_own_contest", "Edit own contests"), - ("edit_all_contest", "Edit all contests"), - ("contest_rating", "Rate contests"), - ("contest_access_code", "Contest access codes"), - ), - "verbose_name": "contest", + 'verbose_name_plural': 'contests', + 'permissions': (('see_private_contest', 'See private contests'), ('edit_own_contest', 'Edit own contests'), ('edit_all_contest', 'Edit all contests'), ('contest_rating', 'Rate contests'), ('contest_access_code', 'Contest access codes')), + 'verbose_name': 'contest', }, ), migrations.CreateModel( - name="ContestParticipation", + name='ContestParticipation', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "real_start", - models.DateTimeField( - db_column="start", - default=django.utils.timezone.now, - verbose_name="start time", - ), - ), - ( - "score", - models.IntegerField(db_index=True, default=0, verbose_name="score"), - ), - ( - "cumtime", - models.PositiveIntegerField( - default=0, verbose_name="cumulative time" - ), - ), - ( - "virtual", - models.IntegerField( - default=0, - help_text="0 means non-virtual, otherwise the n-th virtual participation", - verbose_name="virtual participation id", - ), - ), - ( - "format_data", - jsonfield.fields.JSONField( - blank=True, - null=True, - verbose_name="contest format specific data", - ), - ), - ( - "contest", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="users", - to="judge.Contest", - verbose_name="associated contest", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('real_start', models.DateTimeField(db_column='start', default=django.utils.timezone.now, verbose_name='start time')), + ('score', models.IntegerField(db_index=True, default=0, verbose_name='score')), + ('cumtime', models.PositiveIntegerField(default=0, verbose_name='cumulative time')), + ('virtual', models.IntegerField(default=0, help_text='0 means non-virtual, otherwise the n-th virtual participation', verbose_name='virtual participation id')), + ('format_data', jsonfield.fields.JSONField(blank=True, null=True, verbose_name='contest format specific data')), + ('contest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='users', to='judge.Contest', verbose_name='associated contest')), ], options={ - "verbose_name_plural": "contest participations", - "verbose_name": "contest participation", + 'verbose_name_plural': 'contest participations', + 'verbose_name': 'contest participation', }, ), migrations.CreateModel( - name="ContestProblem", + name='ContestProblem', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("points", models.IntegerField(verbose_name="points")), - ("partial", models.BooleanField(default=True, verbose_name="partial")), - ( - "is_pretested", - models.BooleanField(default=False, verbose_name="is pretested"), - ), - ( - "order", - models.PositiveIntegerField(db_index=True, verbose_name="order"), - ), - ( - "output_prefix_override", - models.IntegerField( - blank=True, - null=True, - verbose_name="output prefix length override", - ), - ), - ( - "max_submissions", - models.IntegerField( - default=0, - help_text="Maximum number of submissions for this problem, or 0 for no limit.", - validators=[ - django.core.validators.MinValueValidator( - 0, "Why include a problem you can't submit to?" - ) - ], - ), - ), - ( - "contest", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="contest_problems", - to="judge.Contest", - verbose_name="contest", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('points', models.IntegerField(verbose_name='points')), + ('partial', models.BooleanField(default=True, verbose_name='partial')), + ('is_pretested', models.BooleanField(default=False, verbose_name='is pretested')), + ('order', models.PositiveIntegerField(db_index=True, verbose_name='order')), + ('output_prefix_override', models.IntegerField(blank=True, null=True, verbose_name='output prefix length override')), + ('max_submissions', models.IntegerField(default=0, help_text='Maximum number of submissions for this problem, or 0 for no limit.', validators=[django.core.validators.MinValueValidator(0, "Why include a problem you can't submit to?")])), + ('contest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contest_problems', to='judge.Contest', verbose_name='contest')), ], options={ - "verbose_name_plural": "contest problems", - "verbose_name": "contest problem", + 'verbose_name_plural': 'contest problems', + 'verbose_name': 'contest problem', }, ), migrations.CreateModel( - name="ContestSubmission", + name='ContestSubmission', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("points", models.FloatField(default=0.0, verbose_name="points")), - ( - "is_pretest", - models.BooleanField( - default=False, - help_text="Whether this submission was ran only on pretests.", - verbose_name="is pretested", - ), - ), - ( - "participation", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="submissions", - related_query_name="submission", - to="judge.ContestParticipation", - verbose_name="participation", - ), - ), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="submissions", - related_query_name="submission", - to="judge.ContestProblem", - verbose_name="problem", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('points', models.FloatField(default=0.0, verbose_name='points')), + ('is_pretest', models.BooleanField(default=False, help_text='Whether this submission was ran only on pretests.', verbose_name='is pretested')), + ('participation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submissions', related_query_name='submission', to='judge.ContestParticipation', verbose_name='participation')), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submissions', related_query_name='submission', to='judge.ContestProblem', verbose_name='problem')), ], options={ - "verbose_name_plural": "contest submissions", - "verbose_name": "contest submission", + 'verbose_name_plural': 'contest submissions', + 'verbose_name': 'contest submission', }, ), migrations.CreateModel( - name="ContestTag", + name='ContestTag', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "name", - models.CharField( - max_length=20, - unique=True, - validators=[ - django.core.validators.RegexValidator( - "^[a-z-]+$", - message="Lowercase letters and hyphens only.", - ) - ], - verbose_name="tag name", - ), - ), - ( - "color", - models.CharField( - max_length=7, - validators=[ - django.core.validators.RegexValidator( - "^#(?:[A-Fa-f0-9]{3}){1,2}$", "Invalid colour." - ) - ], - verbose_name="tag colour", - ), - ), - ( - "description", - models.TextField(blank=True, verbose_name="tag description"), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, unique=True, validators=[django.core.validators.RegexValidator('^[a-z-]+$', message='Lowercase letters and hyphens only.')], verbose_name='tag name')), + ('color', models.CharField(max_length=7, validators=[django.core.validators.RegexValidator('^#(?:[A-Fa-f0-9]{3}){1,2}$', 'Invalid colour.')], verbose_name='tag colour')), + ('description', models.TextField(blank=True, verbose_name='tag description')), ], options={ - "verbose_name_plural": "contest tags", - "verbose_name": "contest tag", + 'verbose_name_plural': 'contest tags', + 'verbose_name': 'contest tag', }, ), migrations.CreateModel( - name="Judge", + name='Judge', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "name", - models.CharField( - help_text="Server name, hostname-style", - max_length=50, - unique=True, - ), - ), - ( - "created", - models.DateTimeField( - auto_now_add=True, verbose_name="time of creation" - ), - ), - ( - "auth_key", - models.CharField( - help_text="A key to authenticated this judge", - max_length=100, - verbose_name="authentication key", - ), - ), - ( - "is_blocked", - models.BooleanField( - default=False, - help_text="Whether this judge should be blocked from connecting, even if its key is correct.", - verbose_name="block judge", - ), - ), - ( - "online", - models.BooleanField( - default=False, verbose_name="judge online status" - ), - ), - ( - "start_time", - models.DateTimeField(null=True, verbose_name="judge start time"), - ), - ("ping", models.FloatField(null=True, verbose_name="response time")), - ( - "load", - models.FloatField( - help_text="Load for the last minute, divided by processors to be fair.", - null=True, - verbose_name="system load", - ), - ), - ( - "description", - models.TextField(blank=True, verbose_name="description"), - ), - ( - "last_ip", - models.GenericIPAddressField( - blank=True, null=True, verbose_name="Last connected IP" - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Server name, hostname-style', max_length=50, unique=True)), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='time of creation')), + ('auth_key', models.CharField(help_text='A key to authenticated this judge', max_length=100, verbose_name='authentication key')), + ('is_blocked', models.BooleanField(default=False, help_text='Whether this judge should be blocked from connecting, even if its key is correct.', verbose_name='block judge')), + ('online', models.BooleanField(default=False, verbose_name='judge online status')), + ('start_time', models.DateTimeField(null=True, verbose_name='judge start time')), + ('ping', models.FloatField(null=True, verbose_name='response time')), + ('load', models.FloatField(help_text='Load for the last minute, divided by processors to be fair.', null=True, verbose_name='system load')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('last_ip', models.GenericIPAddressField(blank=True, null=True, verbose_name='Last connected IP')), ], options={ - "ordering": ["name"], - "verbose_name_plural": "judges", - "verbose_name": "judge", + 'ordering': ['name'], + 'verbose_name_plural': 'judges', + 'verbose_name': 'judge', }, ), migrations.CreateModel( - name="Language", + name='Language', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "key", - models.CharField( - help_text="The identifier for this language; the same as its executor id for judges.", - max_length=6, - unique=True, - verbose_name="short identifier", - ), - ), - ( - "name", - models.CharField( - help_text='Longer name for the language, e.g. "Python 2" or "C++11".', - max_length=20, - verbose_name="long name", - ), - ), - ( - "short_name", - models.CharField( - blank=True, - help_text='More readable, but short, name to display publicly; e.g. "PY2" or "C++11". If left blank, it will default to the short identifier.', - max_length=10, - null=True, - verbose_name="short name", - ), - ), - ( - "common_name", - models.CharField( - help_text='Common name for the language. For example, the common name for C++03, C++11, and C++14 would be "C++"', - max_length=10, - verbose_name="common name", - ), - ), - ( - "ace", - models.CharField( - help_text='Language ID for Ace.js editor highlighting, appended to "mode-" to determine the Ace JavaScript file to use, e.g., "python".', - max_length=20, - verbose_name="ace mode name", - ), - ), - ( - "pygments", - models.CharField( - help_text="Language ID for Pygments highlighting in source windows.", - max_length=20, - verbose_name="pygments name", - ), - ), - ( - "template", - models.TextField( - blank=True, - help_text="Code template to display in submission editor.", - verbose_name="code template", - ), - ), - ( - "info", - models.CharField( - blank=True, - help_text="Do not set this unless you know what you're doing! It will override the usually more specific, judge-provided runtime info!", - max_length=50, - verbose_name="runtime info override", - ), - ), - ( - "description", - models.TextField( - blank=True, - help_text="Use field this to inform users of quirks with your environment, additional restrictions, etc.", - verbose_name="language description", - ), - ), - ( - "extension", - models.CharField( - help_text='The extension of source files, e.g., "py" or "cpp".', - max_length=10, - verbose_name="extension", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(help_text='The identifier for this language; the same as its executor id for judges.', max_length=6, unique=True, verbose_name='short identifier')), + ('name', models.CharField(help_text='Longer name for the language, e.g. "Python 2" or "C++11".', max_length=20, verbose_name='long name')), + ('short_name', models.CharField(blank=True, help_text='More readable, but short, name to display publicly; e.g. "PY2" or "C++11". If left blank, it will default to the short identifier.', max_length=10, null=True, verbose_name='short name')), + ('common_name', models.CharField(help_text='Common name for the language. For example, the common name for C++03, C++11, and C++14 would be "C++"', max_length=10, verbose_name='common name')), + ('ace', models.CharField(help_text='Language ID for Ace.js editor highlighting, appended to "mode-" to determine the Ace JavaScript file to use, e.g., "python".', max_length=20, verbose_name='ace mode name')), + ('pygments', models.CharField(help_text='Language ID for Pygments highlighting in source windows.', max_length=20, verbose_name='pygments name')), + ('template', models.TextField(blank=True, help_text='Code template to display in submission editor.', verbose_name='code template')), + ('info', models.CharField(blank=True, help_text="Do not set this unless you know what you're doing! It will override the usually more specific, judge-provided runtime info!", max_length=50, verbose_name='runtime info override')), + ('description', models.TextField(blank=True, help_text='Use field this to inform users of quirks with your environment, additional restrictions, etc.', verbose_name='language description')), + ('extension', models.CharField(help_text='The extension of source files, e.g., "py" or "cpp".', max_length=10, verbose_name='extension')), ], options={ - "ordering": ["key"], - "verbose_name_plural": "languages", - "verbose_name": "language", + 'ordering': ['key'], + 'verbose_name_plural': 'languages', + 'verbose_name': 'language', }, ), migrations.CreateModel( - name="LanguageLimit", + name='LanguageLimit', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("time_limit", models.FloatField(verbose_name="time limit")), - ("memory_limit", models.IntegerField(verbose_name="memory limit")), - ( - "language", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Language", - verbose_name="language", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('time_limit', models.FloatField(verbose_name='time limit')), + ('memory_limit', models.IntegerField(verbose_name='memory limit')), + ('language', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Language', verbose_name='language')), ], options={ - "verbose_name_plural": "language-specific resource limits", - "verbose_name": "language-specific resource limit", + 'verbose_name_plural': 'language-specific resource limits', + 'verbose_name': 'language-specific resource limit', }, ), migrations.CreateModel( - name="License", + name='License', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "key", - models.CharField( - max_length=20, - unique=True, - validators=[ - django.core.validators.RegexValidator( - "^[-\\w.]+$", "License key must be ^[-\\w.]+$" - ) - ], - verbose_name="key", - ), - ), - ("link", models.CharField(max_length=256, verbose_name="link")), - ("name", models.CharField(max_length=256, verbose_name="full name")), - ( - "display", - models.CharField( - blank=True, - help_text="Displayed on pages under this license", - max_length=256, - verbose_name="short name", - ), - ), - ( - "icon", - models.CharField( - blank=True, - help_text="URL to the icon", - max_length=256, - verbose_name="icon", - ), - ), - ("text", models.TextField(verbose_name="license text")), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(max_length=20, unique=True, validators=[django.core.validators.RegexValidator('^[-\\w.]+$', 'License key must be ^[-\\w.]+$')], verbose_name='key')), + ('link', models.CharField(max_length=256, verbose_name='link')), + ('name', models.CharField(max_length=256, verbose_name='full name')), + ('display', models.CharField(blank=True, help_text='Displayed on pages under this license', max_length=256, verbose_name='short name')), + ('icon', models.CharField(blank=True, help_text='URL to the icon', max_length=256, verbose_name='icon')), + ('text', models.TextField(verbose_name='license text')), ], options={ - "verbose_name_plural": "licenses", - "verbose_name": "license", + 'verbose_name_plural': 'licenses', + 'verbose_name': 'license', }, ), migrations.CreateModel( - name="MiscConfig", + name='MiscConfig', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("key", models.CharField(db_index=True, max_length=30)), - ("value", models.TextField(blank=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(db_index=True, max_length=30)), + ('value', models.TextField(blank=True)), ], options={ - "verbose_name_plural": "miscellaneous configuration", - "verbose_name": "configuration item", + 'verbose_name_plural': 'miscellaneous configuration', + 'verbose_name': 'configuration item', }, ), migrations.CreateModel( - name="NavigationBar", + name='NavigationBar', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "order", - models.PositiveIntegerField(db_index=True, verbose_name="order"), - ), - ( - "key", - models.CharField( - max_length=10, unique=True, verbose_name="identifier" - ), - ), - ("label", models.CharField(max_length=20, verbose_name="label")), - ("path", models.CharField(max_length=255, verbose_name="link path")), - ( - "regex", - models.TextField( - validators=[judge.models.interface.validate_regex], - verbose_name="highlight regex", - ), - ), - ("lft", models.PositiveIntegerField(db_index=True, editable=False)), - ("rght", models.PositiveIntegerField(db_index=True, editable=False)), - ("tree_id", models.PositiveIntegerField(db_index=True, editable=False)), - ("level", models.PositiveIntegerField(db_index=True, editable=False)), - ( - "parent", - mptt.fields.TreeForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="children", - to="judge.NavigationBar", - verbose_name="parent item", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.PositiveIntegerField(db_index=True, verbose_name='order')), + ('key', models.CharField(max_length=10, unique=True, verbose_name='identifier')), + ('label', models.CharField(max_length=20, verbose_name='label')), + ('path', models.CharField(max_length=255, verbose_name='link path')), + ('regex', models.TextField(validators=[judge.models.interface.validate_regex], verbose_name='highlight regex')), + ('lft', models.PositiveIntegerField(db_index=True, editable=False)), + ('rght', models.PositiveIntegerField(db_index=True, editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(db_index=True, editable=False)), + ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='judge.NavigationBar', verbose_name='parent item')), ], options={ - "verbose_name_plural": "navigation bar", - "verbose_name": "navigation item", + 'verbose_name_plural': 'navigation bar', + 'verbose_name': 'navigation item', }, ), migrations.CreateModel( - name="Organization", + name='Organization', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "name", - models.CharField(max_length=128, verbose_name="organization title"), - ), - ( - "slug", - models.SlugField( - help_text="Organization name shown in URL", - max_length=128, - verbose_name="organization slug", - ), - ), - ( - "short_name", - models.CharField( - help_text="Displayed beside user name during contests", - max_length=20, - verbose_name="short name", - ), - ), - ("about", models.TextField(verbose_name="organization description")), - ( - "creation_date", - models.DateTimeField( - auto_now_add=True, verbose_name="creation date" - ), - ), - ( - "is_open", - models.BooleanField( - default=True, - help_text="Allow joining organization", - verbose_name="is open organization?", - ), - ), - ( - "slots", - models.IntegerField( - blank=True, - help_text="Maximum amount of users in this organization, only applicable to private organizations", - null=True, - verbose_name="maximum size", - ), - ), - ( - "access_code", - models.CharField( - blank=True, - help_text="Student access code", - max_length=7, - null=True, - verbose_name="access code", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128, verbose_name='organization title')), + ('slug', models.SlugField(help_text='Organization name shown in URL', max_length=128, verbose_name='organization slug')), + ('short_name', models.CharField(help_text='Displayed beside user name during contests', max_length=20, verbose_name='short name')), + ('about', models.TextField(verbose_name='organization description')), + ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='creation date')), + ('is_open', models.BooleanField(default=True, help_text='Allow joining organization', verbose_name='is open organization?')), + ('slots', models.IntegerField(blank=True, help_text='Maximum amount of users in this organization, only applicable to private organizations', null=True, verbose_name='maximum size')), + ('access_code', models.CharField(blank=True, help_text='Student access code', max_length=7, null=True, verbose_name='access code')), ], options={ - "ordering": ["name"], - "verbose_name_plural": "organizations", - "permissions": ( - ("organization_admin", "Administer organizations"), - ("edit_all_organization", "Edit all organizations"), - ), - "verbose_name": "organization", + 'ordering': ['name'], + 'verbose_name_plural': 'organizations', + 'permissions': (('organization_admin', 'Administer organizations'), ('edit_all_organization', 'Edit all organizations')), + 'verbose_name': 'organization', }, ), migrations.CreateModel( - name="OrganizationRequest", + name='OrganizationRequest', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "time", - models.DateTimeField( - auto_now_add=True, verbose_name="request time" - ), - ), - ( - "state", - models.CharField( - choices=[ - ("P", "Pending"), - ("A", "Approved"), - ("R", "Rejected"), - ], - max_length=1, - verbose_name="state", - ), - ), - ("reason", models.TextField(verbose_name="reason")), - ( - "organization", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="requests", - to="judge.Organization", - verbose_name="organization", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('time', models.DateTimeField(auto_now_add=True, verbose_name='request time')), + ('state', models.CharField(choices=[('P', 'Pending'), ('A', 'Approved'), ('R', 'Rejected')], max_length=1, verbose_name='state')), + ('reason', models.TextField(verbose_name='reason')), + ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requests', to='judge.Organization', verbose_name='organization')), ], options={ - "verbose_name_plural": "organization join requests", - "verbose_name": "organization join request", + 'verbose_name_plural': 'organization join requests', + 'verbose_name': 'organization join request', }, ), migrations.CreateModel( - name="PrivateMessage", + name='PrivateMessage', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "title", - models.CharField(max_length=50, verbose_name="message title"), - ), - ("content", models.TextField(verbose_name="message body")), - ( - "timestamp", - models.DateTimeField( - auto_now_add=True, verbose_name="message timestamp" - ), - ), - ("read", models.BooleanField(default=False, verbose_name="read")), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=50, verbose_name='message title')), + ('content', models.TextField(verbose_name='message body')), + ('timestamp', models.DateTimeField(auto_now_add=True, verbose_name='message timestamp')), + ('read', models.BooleanField(default=False, verbose_name='read')), ], ), migrations.CreateModel( - name="PrivateMessageThread", + name='PrivateMessageThread', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "messages", - models.ManyToManyField( - to="judge.PrivateMessage", verbose_name="messages in the thread" - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('messages', models.ManyToManyField(to='judge.PrivateMessage', verbose_name='messages in the thread')), ], ), migrations.CreateModel( - name="Problem", + name='Problem', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "code", - models.CharField( - max_length=20, - unique=True, - validators=[ - django.core.validators.RegexValidator( - "^[a-z0-9]+$", "Problem code must be ^[a-z0-9]+$" - ) - ], - verbose_name="problem code", - ), - ), - ( - "name", - models.CharField( - db_index=True, max_length=100, verbose_name="problem name" - ), - ), - ("description", models.TextField(verbose_name="problem body")), - ( - "time_limit", - models.FloatField( - help_text="The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) are supported.", - verbose_name="time limit", - ), - ), - ( - "memory_limit", - models.IntegerField( - help_text="The memory limit for this problem, in kilobytes (e.g. 64mb = 65536 kilobytes).", - verbose_name="memory limit", - ), - ), - ("short_circuit", models.BooleanField(default=False)), - ("points", models.FloatField(verbose_name="points")), - ( - "partial", - models.BooleanField( - default=False, verbose_name="allows partial points" - ), - ), - ( - "is_public", - models.BooleanField( - db_index=True, default=False, verbose_name="publicly visible" - ), - ), - ( - "is_manually_managed", - models.BooleanField( - db_index=True, - default=False, - help_text="Whether judges should be allowed to manage data or not", - verbose_name="manually managed", - ), - ), - ( - "date", - models.DateTimeField( - blank=True, - db_index=True, - help_text="Doesn't have magic ability to auto-publish due to backward compatibility", - null=True, - verbose_name="date of publishing", - ), - ), - ( - "og_image", - models.CharField( - blank=True, max_length=150, verbose_name="OpenGraph image" - ), - ), - ( - "summary", - models.TextField( - blank=True, - help_text="Plain-text, shown in meta description tag, e.g. for social media.", - verbose_name="problem summary", - ), - ), - ( - "user_count", - models.IntegerField( - default=0, - help_text="The number of users who solved the problem.", - verbose_name="number of users", - ), - ), - ("ac_rate", models.FloatField(default=0, verbose_name="solve rate")), - ( - "is_organization_private", - models.BooleanField( - default=False, verbose_name="private to organizations" - ), - ), - ( - "allowed_languages", - models.ManyToManyField( - to="judge.Language", verbose_name="allowed languages" - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(max_length=20, unique=True, validators=[django.core.validators.RegexValidator('^[a-z0-9]+$', 'Problem code must be ^[a-z0-9]+$')], verbose_name='problem code')), + ('name', models.CharField(db_index=True, max_length=100, verbose_name='problem name')), + ('description', models.TextField(verbose_name='problem body')), + ('time_limit', models.FloatField(help_text='The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) are supported.', verbose_name='time limit')), + ('memory_limit', models.IntegerField(help_text='The memory limit for this problem, in kilobytes (e.g. 64mb = 65536 kilobytes).', verbose_name='memory limit')), + ('short_circuit', models.BooleanField(default=False)), + ('points', models.FloatField(verbose_name='points')), + ('partial', models.BooleanField(default=False, verbose_name='allows partial points')), + ('is_public', models.BooleanField(db_index=True, default=False, verbose_name='publicly visible')), + ('is_manually_managed', models.BooleanField(db_index=True, default=False, help_text='Whether judges should be allowed to manage data or not', verbose_name='manually managed')), + ('date', models.DateTimeField(blank=True, db_index=True, help_text="Doesn't have magic ability to auto-publish due to backward compatibility", null=True, verbose_name='date of publishing')), + ('og_image', models.CharField(blank=True, max_length=150, verbose_name='OpenGraph image')), + ('summary', models.TextField(blank=True, help_text='Plain-text, shown in meta description tag, e.g. for social media.', verbose_name='problem summary')), + ('user_count', models.IntegerField(default=0, help_text='The number of users who solved the problem.', verbose_name='number of users')), + ('ac_rate', models.FloatField(default=0, verbose_name='solve rate')), + ('is_organization_private', models.BooleanField(default=False, verbose_name='private to organizations')), + ('allowed_languages', models.ManyToManyField(to='judge.Language', verbose_name='allowed languages')), ], options={ - "verbose_name_plural": "problems", - "permissions": ( - ("see_private_problem", "See hidden problems"), - ("edit_own_problem", "Edit own problems"), - ("edit_all_problem", "Edit all problems"), - ("edit_public_problem", "Edit all public problems"), - ("clone_problem", "Clone problem"), - ("change_public_visibility", "Change is_public field"), - ("change_manually_managed", "Change is_manually_managed field"), - ("see_organization_problem", "See organization-private problems"), - ), - "verbose_name": "problem", + 'verbose_name_plural': 'problems', + 'permissions': (('see_private_problem', 'See hidden problems'), ('edit_own_problem', 'Edit own problems'), ('edit_all_problem', 'Edit all problems'), ('edit_public_problem', 'Edit all public problems'), ('clone_problem', 'Clone problem'), ('change_public_visibility', 'Change is_public field'), ('change_manually_managed', 'Change is_manually_managed field'), ('see_organization_problem', 'See organization-private problems')), + 'verbose_name': 'problem', }, ), migrations.CreateModel( - name="ProblemClarification", + name='ProblemClarification', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("description", models.TextField(verbose_name="clarification body")), - ( - "date", - models.DateTimeField( - auto_now_add=True, verbose_name="clarification timestamp" - ), - ), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Problem", - verbose_name="clarified problem", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('description', models.TextField(verbose_name='clarification body')), + ('date', models.DateTimeField(auto_now_add=True, verbose_name='clarification timestamp')), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Problem', verbose_name='clarified problem')), ], ), migrations.CreateModel( - name="ProblemData", + name='ProblemData', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "zipfile", - models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - verbose_name="data zip file", - ), - ), - ( - "generator", - models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - verbose_name="generator file", - ), - ), - ( - "output_prefix", - models.IntegerField( - blank=True, null=True, verbose_name="output prefix length" - ), - ), - ( - "output_limit", - models.IntegerField( - blank=True, null=True, verbose_name="output limit length" - ), - ), - ( - "feedback", - models.TextField( - blank=True, verbose_name="init.yml generation feedback" - ), - ), - ( - "checker", - models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ], - max_length=10, - verbose_name="checker", - ), - ), - ( - "checker_args", - models.TextField( - blank=True, - help_text="checker arguments as a JSON object", - verbose_name="checker arguments", - ), - ), - ( - "problem", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="data_files", - to="judge.Problem", - verbose_name="problem", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('zipfile', models.FileField(blank=True, null=True, storage=judge.utils.problem_data.ProblemDataStorage(), upload_to=judge.models.problem_data.problem_directory_file, verbose_name='data zip file')), + ('generator', models.FileField(blank=True, null=True, storage=judge.utils.problem_data.ProblemDataStorage(), upload_to=judge.models.problem_data.problem_directory_file, verbose_name='generator file')), + ('output_prefix', models.IntegerField(blank=True, null=True, verbose_name='output prefix length')), + ('output_limit', models.IntegerField(blank=True, null=True, verbose_name='output limit length')), + ('feedback', models.TextField(blank=True, verbose_name='init.yml generation feedback')), + ('checker', models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line')], max_length=10, verbose_name='checker')), + ('checker_args', models.TextField(blank=True, help_text='checker arguments as a JSON object', verbose_name='checker arguments')), + ('problem', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='data_files', to='judge.Problem', verbose_name='problem')), ], ), migrations.CreateModel( - name="ProblemGroup", + name='ProblemGroup', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "name", - models.CharField( - max_length=20, unique=True, verbose_name="problem group ID" - ), - ), - ( - "full_name", - models.CharField(max_length=100, verbose_name="problem group name"), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, unique=True, verbose_name='problem group ID')), + ('full_name', models.CharField(max_length=100, verbose_name='problem group name')), ], options={ - "ordering": ["full_name"], - "verbose_name_plural": "problem groups", - "verbose_name": "problem group", + 'ordering': ['full_name'], + 'verbose_name_plural': 'problem groups', + 'verbose_name': 'problem group', }, ), migrations.CreateModel( - name="ProblemTestCase", + name='ProblemTestCase', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("order", models.IntegerField(verbose_name="case position")), - ( - "type", - models.CharField( - choices=[ - ("C", "Normal case"), - ("S", "Batch start"), - ("E", "Batch end"), - ], - default="C", - max_length=1, - verbose_name="case type", - ), - ), - ( - "input_file", - models.CharField( - blank=True, max_length=100, verbose_name="input file name" - ), - ), - ( - "output_file", - models.CharField( - blank=True, max_length=100, verbose_name="output file name" - ), - ), - ( - "generator_args", - models.TextField(blank=True, verbose_name="generator arguments"), - ), - ( - "points", - models.IntegerField( - blank=True, null=True, verbose_name="point value" - ), - ), - ("is_pretest", models.BooleanField(verbose_name="case is pretest?")), - ( - "output_prefix", - models.IntegerField( - blank=True, null=True, verbose_name="output prefix length" - ), - ), - ( - "output_limit", - models.IntegerField( - blank=True, null=True, verbose_name="output limit length" - ), - ), - ( - "checker", - models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ], - max_length=10, - verbose_name="checker", - ), - ), - ( - "checker_args", - models.TextField( - blank=True, - help_text="checker arguments as a JSON object", - verbose_name="checker arguments", - ), - ), - ( - "dataset", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="cases", - to="judge.Problem", - verbose_name="problem data set", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.IntegerField(verbose_name='case position')), + ('type', models.CharField(choices=[('C', 'Normal case'), ('S', 'Batch start'), ('E', 'Batch end')], default='C', max_length=1, verbose_name='case type')), + ('input_file', models.CharField(blank=True, max_length=100, verbose_name='input file name')), + ('output_file', models.CharField(blank=True, max_length=100, verbose_name='output file name')), + ('generator_args', models.TextField(blank=True, verbose_name='generator arguments')), + ('points', models.IntegerField(blank=True, null=True, verbose_name='point value')), + ('is_pretest', models.BooleanField(verbose_name='case is pretest?')), + ('output_prefix', models.IntegerField(blank=True, null=True, verbose_name='output prefix length')), + ('output_limit', models.IntegerField(blank=True, null=True, verbose_name='output limit length')), + ('checker', models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line')], max_length=10, verbose_name='checker')), + ('checker_args', models.TextField(blank=True, help_text='checker arguments as a JSON object', verbose_name='checker arguments')), + ('dataset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cases', to='judge.Problem', verbose_name='problem data set')), ], ), migrations.CreateModel( - name="ProblemTranslation", + name='ProblemTranslation', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "language", - models.CharField( - choices=[ - ("de", "German"), - ("en", "English"), - ("es", "Spanish"), - ("fr", "French"), - ("hr", "Croatian"), - ("hu", "Hungarian"), - ("ko", "Korean"), - ("ro", "Romanian"), - ("ru", "Russian"), - ("sr-latn", "Serbian (Latin)"), - ("tr", "Turkish"), - ("vi", "Vietnamese"), - ("zh-hans", "Simplified Chinese"), - ], - max_length=7, - verbose_name="language", - ), - ), - ( - "name", - models.CharField( - db_index=True, max_length=100, verbose_name="translated name" - ), - ), - ( - "description", - models.TextField(verbose_name="translated description"), - ), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="translations", - to="judge.Problem", - verbose_name="problem", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(choices=[('de', 'German'), ('en', 'English'), ('es', 'Spanish'), ('fr', 'French'), ('hr', 'Croatian'), ('hu', 'Hungarian'), ('ko', 'Korean'), ('ro', 'Romanian'), ('ru', 'Russian'), ('sr-latn', 'Serbian (Latin)'), ('tr', 'Turkish'), ('vi', 'Vietnamese'), ('zh-hans', 'Simplified Chinese')], max_length=7, verbose_name='language')), + ('name', models.CharField(db_index=True, max_length=100, verbose_name='translated name')), + ('description', models.TextField(verbose_name='translated description')), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='judge.Problem', verbose_name='problem')), ], options={ - "verbose_name_plural": "problem translations", - "verbose_name": "problem translation", + 'verbose_name_plural': 'problem translations', + 'verbose_name': 'problem translation', }, ), migrations.CreateModel( - name="ProblemType", + name='ProblemType', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "name", - models.CharField( - max_length=20, unique=True, verbose_name="problem category ID" - ), - ), - ( - "full_name", - models.CharField( - max_length=100, verbose_name="problem category name" - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, unique=True, verbose_name='problem category ID')), + ('full_name', models.CharField(max_length=100, verbose_name='problem category name')), ], options={ - "ordering": ["full_name"], - "verbose_name_plural": "problem types", - "verbose_name": "problem type", + 'ordering': ['full_name'], + 'verbose_name_plural': 'problem types', + 'verbose_name': 'problem type', }, ), migrations.CreateModel( - name="Profile", + name='Profile', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "about", - models.TextField( - blank=True, null=True, verbose_name="self-description" - ), - ), - ( - "timezone", - models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ( - "America/Argentina/Catamarca", - "Argentina/Catamarca", - ), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ( - "America/Argentina/La_Rioja", - "Argentina/La_Rioja", - ), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ( - "America/Argentina/San_Juan", - "Argentina/San_Juan", - ), - ( - "America/Argentina/San_Luis", - "Argentina/San_Luis", - ), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ( - "America/Indiana/Indianapolis", - "Indiana/Indianapolis", - ), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ( - "America/Indiana/Petersburg", - "Indiana/Petersburg", - ), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ( - "America/Kentucky/Louisville", - "Kentucky/Louisville", - ), - ( - "America/Kentucky/Monticello", - "Kentucky/Monticello", - ), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ( - "America/North_Dakota/Beulah", - "North_Dakota/Beulah", - ), - ( - "America/North_Dakota/Center", - "North_Dakota/Center", - ), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="America/Toronto", - max_length=50, - verbose_name="location", - ), - ), - ("points", models.FloatField(db_index=True, default=0)), - ("performance_points", models.FloatField(db_index=True, default=0)), - ("problem_count", models.IntegerField(db_index=True, default=0)), - ( - "ace_theme", - models.CharField( - choices=[ - ("ambiance", "Ambiance"), - ("chaos", "Chaos"), - ("chrome", "Chrome"), - ("clouds", "Clouds"), - ("clouds_midnight", "Clouds Midnight"), - ("cobalt", "Cobalt"), - ("crimson_editor", "Crimson Editor"), - ("dawn", "Dawn"), - ("dreamweaver", "Dreamweaver"), - ("eclipse", "Eclipse"), - ("github", "Github"), - ("idle_fingers", "Idle Fingers"), - ("katzenmilch", "Katzenmilch"), - ("kr_theme", "KR Theme"), - ("kuroir", "Kuroir"), - ("merbivore", "Merbivore"), - ("merbivore_soft", "Merbivore Soft"), - ("mono_industrial", "Mono Industrial"), - ("monokai", "Monokai"), - ("pastel_on_dark", "Pastel on Dark"), - ("solarized_dark", "Solarized Dark"), - ("solarized_light", "Solarized Light"), - ("terminal", "Terminal"), - ("textmate", "Textmate"), - ("tomorrow", "Tomorrow"), - ("tomorrow_night", "Tomorrow Night"), - ("tomorrow_night_blue", "Tomorrow Night Blue"), - ("tomorrow_night_bright", "Tomorrow Night Bright"), - ("tomorrow_night_eighties", "Tomorrow Night Eighties"), - ("twilight", "Twilight"), - ("vibrant_ink", "Vibrant Ink"), - ("xcode", "XCode"), - ], - default="github", - max_length=30, - ), - ), - ( - "last_access", - models.DateTimeField( - default=django.utils.timezone.now, - verbose_name="last access time", - ), - ), - ( - "ip", - models.GenericIPAddressField( - blank=True, null=True, verbose_name="last IP" - ), - ), - ( - "display_rank", - models.CharField( - choices=[ - ("user", "Normal User"), - ("setter", "Problem Setter"), - ("admin", "Admin"), - ], - default="user", - max_length=10, - verbose_name="display rank", - ), - ), - ( - "mute", - models.BooleanField( - default=False, - help_text="Some users are at their best when silent.", - verbose_name="comment mute", - ), - ), - ( - "is_unlisted", - models.BooleanField( - default=False, - help_text="User will not be ranked.", - verbose_name="unlisted user", - ), - ), - ("rating", models.IntegerField(default=None, null=True)), - ( - "user_script", - models.TextField( - blank=True, - default="", - help_text="User-defined JavaScript for site customization.", - max_length=65536, - verbose_name="user script", - ), - ), - ( - "math_engine", - models.CharField( - choices=[ - ("tex", "Leave as LaTeX"), - ("svg", "SVG with PNG fallback"), - ("mml", "MathML only"), - ("jax", "MathJax with SVG/PNG fallback"), - ("auto", "Detect best quality"), - ], - default="auto", - help_text="the rendering engine used to render math", - max_length=4, - verbose_name="math engine", - ), - ), - ( - "is_totp_enabled", - models.BooleanField( - default=False, - help_text="check to enable TOTP-based two factor authentication", - verbose_name="2FA enabled", - ), - ), - ( - "totp_key", - judge.models.profile.EncryptedNullCharField( - blank=True, - help_text="32 character base32-encoded key for TOTP", - max_length=32, - null=True, - validators=[ - django.core.validators.RegexValidator( - "^$|^[A-Z2-7]{32}$", "TOTP key must be empty or base32" - ) - ], - verbose_name="TOTP key", - ), - ), - ( - "notes", - models.TextField( - blank=True, - help_text="Notes for administrators regarding this user.", - null=True, - verbose_name="internal notes", - ), - ), - ( - "current_contest", - models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to="judge.ContestParticipation", - verbose_name="current contest", - ), - ), - ( - "language", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Language", - verbose_name="preferred language", - ), - ), - ( - "organizations", - sortedm2m.fields.SortedManyToManyField( - blank=True, - help_text=None, - related_name="members", - related_query_name="member", - to="judge.Organization", - verbose_name="organization", - ), - ), - ( - "user", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - verbose_name="user associated", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('about', models.TextField(blank=True, null=True, verbose_name='self-description')), + ('timezone', models.CharField(choices=[('Africa', [('Africa/Abidjan', 'Abidjan'), ('Africa/Accra', 'Accra'), ('Africa/Addis_Ababa', 'Addis_Ababa'), ('Africa/Algiers', 'Algiers'), ('Africa/Asmara', 'Asmara'), ('Africa/Asmera', 'Asmera'), ('Africa/Bamako', 'Bamako'), ('Africa/Bangui', 'Bangui'), ('Africa/Banjul', 'Banjul'), ('Africa/Bissau', 'Bissau'), ('Africa/Blantyre', 'Blantyre'), ('Africa/Brazzaville', 'Brazzaville'), ('Africa/Bujumbura', 'Bujumbura'), ('Africa/Cairo', 'Cairo'), ('Africa/Casablanca', 'Casablanca'), ('Africa/Ceuta', 'Ceuta'), ('Africa/Conakry', 'Conakry'), ('Africa/Dakar', 'Dakar'), ('Africa/Dar_es_Salaam', 'Dar_es_Salaam'), ('Africa/Djibouti', 'Djibouti'), ('Africa/Douala', 'Douala'), ('Africa/El_Aaiun', 'El_Aaiun'), ('Africa/Freetown', 'Freetown'), ('Africa/Gaborone', 'Gaborone'), ('Africa/Harare', 'Harare'), ('Africa/Johannesburg', 'Johannesburg'), ('Africa/Juba', 'Juba'), ('Africa/Kampala', 'Kampala'), ('Africa/Khartoum', 'Khartoum'), ('Africa/Kigali', 'Kigali'), ('Africa/Kinshasa', 'Kinshasa'), ('Africa/Lagos', 'Lagos'), ('Africa/Libreville', 'Libreville'), ('Africa/Lome', 'Lome'), ('Africa/Luanda', 'Luanda'), ('Africa/Lubumbashi', 'Lubumbashi'), ('Africa/Lusaka', 'Lusaka'), ('Africa/Malabo', 'Malabo'), ('Africa/Maputo', 'Maputo'), ('Africa/Maseru', 'Maseru'), ('Africa/Mbabane', 'Mbabane'), ('Africa/Mogadishu', 'Mogadishu'), ('Africa/Monrovia', 'Monrovia'), ('Africa/Nairobi', 'Nairobi'), ('Africa/Ndjamena', 'Ndjamena'), ('Africa/Niamey', 'Niamey'), ('Africa/Nouakchott', 'Nouakchott'), ('Africa/Ouagadougou', 'Ouagadougou'), ('Africa/Porto-Novo', 'Porto-Novo'), ('Africa/Sao_Tome', 'Sao_Tome'), ('Africa/Timbuktu', 'Timbuktu'), ('Africa/Tripoli', 'Tripoli'), ('Africa/Tunis', 'Tunis'), ('Africa/Windhoek', 'Windhoek')]), ('America', [('America/Adak', 'Adak'), ('America/Anchorage', 'Anchorage'), ('America/Anguilla', 'Anguilla'), ('America/Antigua', 'Antigua'), ('America/Araguaina', 'Araguaina'), ('America/Argentina/Buenos_Aires', 'Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'Argentina/Cordoba'), ('America/Argentina/Jujuy', 'Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'Argentina/Salta'), ('America/Argentina/San_Juan', 'Argentina/San_Juan'), ('America/Argentina/San_Luis', 'Argentina/San_Luis'), ('America/Argentina/Tucuman', 'Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'Argentina/Ushuaia'), ('America/Aruba', 'Aruba'), ('America/Asuncion', 'Asuncion'), ('America/Atikokan', 'Atikokan'), ('America/Atka', 'Atka'), ('America/Bahia', 'Bahia'), ('America/Bahia_Banderas', 'Bahia_Banderas'), ('America/Barbados', 'Barbados'), ('America/Belem', 'Belem'), ('America/Belize', 'Belize'), ('America/Blanc-Sablon', 'Blanc-Sablon'), ('America/Boa_Vista', 'Boa_Vista'), ('America/Bogota', 'Bogota'), ('America/Boise', 'Boise'), ('America/Buenos_Aires', 'Buenos_Aires'), ('America/Cambridge_Bay', 'Cambridge_Bay'), ('America/Campo_Grande', 'Campo_Grande'), ('America/Cancun', 'Cancun'), ('America/Caracas', 'Caracas'), ('America/Catamarca', 'Catamarca'), ('America/Cayenne', 'Cayenne'), ('America/Cayman', 'Cayman'), ('America/Chicago', 'Chicago'), ('America/Chihuahua', 'Chihuahua'), ('America/Coral_Harbour', 'Coral_Harbour'), ('America/Cordoba', 'Cordoba'), ('America/Costa_Rica', 'Costa_Rica'), ('America/Creston', 'Creston'), ('America/Cuiaba', 'Cuiaba'), ('America/Curacao', 'Curacao'), ('America/Danmarkshavn', 'Danmarkshavn'), ('America/Dawson', 'Dawson'), ('America/Dawson_Creek', 'Dawson_Creek'), ('America/Denver', 'Denver'), ('America/Detroit', 'Detroit'), ('America/Dominica', 'Dominica'), ('America/Edmonton', 'Edmonton'), ('America/Eirunepe', 'Eirunepe'), ('America/El_Salvador', 'El_Salvador'), ('America/Ensenada', 'Ensenada'), ('America/Fort_Nelson', 'Fort_Nelson'), ('America/Fort_Wayne', 'Fort_Wayne'), ('America/Fortaleza', 'Fortaleza'), ('America/Glace_Bay', 'Glace_Bay'), ('America/Godthab', 'Godthab'), ('America/Goose_Bay', 'Goose_Bay'), ('America/Grand_Turk', 'Grand_Turk'), ('America/Grenada', 'Grenada'), ('America/Guadeloupe', 'Guadeloupe'), ('America/Guatemala', 'Guatemala'), ('America/Guayaquil', 'Guayaquil'), ('America/Guyana', 'Guyana'), ('America/Halifax', 'Halifax'), ('America/Havana', 'Havana'), ('America/Hermosillo', 'Hermosillo'), ('America/Indiana/Indianapolis', 'Indiana/Indianapolis'), ('America/Indiana/Knox', 'Indiana/Knox'), ('America/Indiana/Marengo', 'Indiana/Marengo'), ('America/Indiana/Petersburg', 'Indiana/Petersburg'), ('America/Indiana/Tell_City', 'Indiana/Tell_City'), ('America/Indiana/Vevay', 'Indiana/Vevay'), ('America/Indiana/Vincennes', 'Indiana/Vincennes'), ('America/Indiana/Winamac', 'Indiana/Winamac'), ('America/Indianapolis', 'Indianapolis'), ('America/Inuvik', 'Inuvik'), ('America/Iqaluit', 'Iqaluit'), ('America/Jamaica', 'Jamaica'), ('America/Jujuy', 'Jujuy'), ('America/Juneau', 'Juneau'), ('America/Kentucky/Louisville', 'Kentucky/Louisville'), ('America/Kentucky/Monticello', 'Kentucky/Monticello'), ('America/Knox_IN', 'Knox_IN'), ('America/Kralendijk', 'Kralendijk'), ('America/La_Paz', 'La_Paz'), ('America/Lima', 'Lima'), ('America/Los_Angeles', 'Los_Angeles'), ('America/Louisville', 'Louisville'), ('America/Lower_Princes', 'Lower_Princes'), ('America/Maceio', 'Maceio'), ('America/Managua', 'Managua'), ('America/Manaus', 'Manaus'), ('America/Marigot', 'Marigot'), ('America/Martinique', 'Martinique'), ('America/Matamoros', 'Matamoros'), ('America/Mazatlan', 'Mazatlan'), ('America/Mendoza', 'Mendoza'), ('America/Menominee', 'Menominee'), ('America/Merida', 'Merida'), ('America/Metlakatla', 'Metlakatla'), ('America/Mexico_City', 'Mexico_City'), ('America/Miquelon', 'Miquelon'), ('America/Moncton', 'Moncton'), ('America/Monterrey', 'Monterrey'), ('America/Montevideo', 'Montevideo'), ('America/Montreal', 'Montreal'), ('America/Montserrat', 'Montserrat'), ('America/Nassau', 'Nassau'), ('America/New_York', 'New_York'), ('America/Nipigon', 'Nipigon'), ('America/Nome', 'Nome'), ('America/Noronha', 'Noronha'), ('America/North_Dakota/Beulah', 'North_Dakota/Beulah'), ('America/North_Dakota/Center', 'North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'North_Dakota/New_Salem'), ('America/Ojinaga', 'Ojinaga'), ('America/Panama', 'Panama'), ('America/Pangnirtung', 'Pangnirtung'), ('America/Paramaribo', 'Paramaribo'), ('America/Phoenix', 'Phoenix'), ('America/Port-au-Prince', 'Port-au-Prince'), ('America/Port_of_Spain', 'Port_of_Spain'), ('America/Porto_Acre', 'Porto_Acre'), ('America/Porto_Velho', 'Porto_Velho'), ('America/Puerto_Rico', 'Puerto_Rico'), ('America/Punta_Arenas', 'Punta_Arenas'), ('America/Rainy_River', 'Rainy_River'), ('America/Rankin_Inlet', 'Rankin_Inlet'), ('America/Recife', 'Recife'), ('America/Regina', 'Regina'), ('America/Resolute', 'Resolute'), ('America/Rio_Branco', 'Rio_Branco'), ('America/Rosario', 'Rosario'), ('America/Santa_Isabel', 'Santa_Isabel'), ('America/Santarem', 'Santarem'), ('America/Santiago', 'Santiago'), ('America/Santo_Domingo', 'Santo_Domingo'), ('America/Sao_Paulo', 'Sao_Paulo'), ('America/Scoresbysund', 'Scoresbysund'), ('America/Shiprock', 'Shiprock'), ('America/Sitka', 'Sitka'), ('America/St_Barthelemy', 'St_Barthelemy'), ('America/St_Johns', 'St_Johns'), ('America/St_Kitts', 'St_Kitts'), ('America/St_Lucia', 'St_Lucia'), ('America/St_Thomas', 'St_Thomas'), ('America/St_Vincent', 'St_Vincent'), ('America/Swift_Current', 'Swift_Current'), ('America/Tegucigalpa', 'Tegucigalpa'), ('America/Thule', 'Thule'), ('America/Thunder_Bay', 'Thunder_Bay'), ('America/Tijuana', 'Tijuana'), ('America/Toronto', 'Toronto'), ('America/Tortola', 'Tortola'), ('America/Vancouver', 'Vancouver'), ('America/Virgin', 'Virgin'), ('America/Whitehorse', 'Whitehorse'), ('America/Winnipeg', 'Winnipeg'), ('America/Yakutat', 'Yakutat'), ('America/Yellowknife', 'Yellowknife')]), ('Antarctica', [('Antarctica/Casey', 'Casey'), ('Antarctica/Davis', 'Davis'), ('Antarctica/DumontDUrville', 'DumontDUrville'), ('Antarctica/Macquarie', 'Macquarie'), ('Antarctica/Mawson', 'Mawson'), ('Antarctica/McMurdo', 'McMurdo'), ('Antarctica/Palmer', 'Palmer'), ('Antarctica/Rothera', 'Rothera'), ('Antarctica/South_Pole', 'South_Pole'), ('Antarctica/Syowa', 'Syowa'), ('Antarctica/Troll', 'Troll'), ('Antarctica/Vostok', 'Vostok')]), ('Arctic', [('Arctic/Longyearbyen', 'Longyearbyen')]), ('Asia', [('Asia/Aden', 'Aden'), ('Asia/Almaty', 'Almaty'), ('Asia/Amman', 'Amman'), ('Asia/Anadyr', 'Anadyr'), ('Asia/Aqtau', 'Aqtau'), ('Asia/Aqtobe', 'Aqtobe'), ('Asia/Ashgabat', 'Ashgabat'), ('Asia/Ashkhabad', 'Ashkhabad'), ('Asia/Atyrau', 'Atyrau'), ('Asia/Baghdad', 'Baghdad'), ('Asia/Bahrain', 'Bahrain'), ('Asia/Baku', 'Baku'), ('Asia/Bangkok', 'Bangkok'), ('Asia/Barnaul', 'Barnaul'), ('Asia/Beirut', 'Beirut'), ('Asia/Bishkek', 'Bishkek'), ('Asia/Brunei', 'Brunei'), ('Asia/Calcutta', 'Calcutta'), ('Asia/Chita', 'Chita'), ('Asia/Choibalsan', 'Choibalsan'), ('Asia/Chongqing', 'Chongqing'), ('Asia/Chungking', 'Chungking'), ('Asia/Colombo', 'Colombo'), ('Asia/Dacca', 'Dacca'), ('Asia/Damascus', 'Damascus'), ('Asia/Dhaka', 'Dhaka'), ('Asia/Dili', 'Dili'), ('Asia/Dubai', 'Dubai'), ('Asia/Dushanbe', 'Dushanbe'), ('Asia/Famagusta', 'Famagusta'), ('Asia/Gaza', 'Gaza'), ('Asia/Harbin', 'Harbin'), ('Asia/Hebron', 'Hebron'), ('Asia/Ho_Chi_Minh', 'Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Hong_Kong'), ('Asia/Hovd', 'Hovd'), ('Asia/Irkutsk', 'Irkutsk'), ('Asia/Istanbul', 'Istanbul'), ('Asia/Jakarta', 'Jakarta'), ('Asia/Jayapura', 'Jayapura'), ('Asia/Jerusalem', 'Jerusalem'), ('Asia/Kabul', 'Kabul'), ('Asia/Kamchatka', 'Kamchatka'), ('Asia/Karachi', 'Karachi'), ('Asia/Kashgar', 'Kashgar'), ('Asia/Kathmandu', 'Kathmandu'), ('Asia/Katmandu', 'Katmandu'), ('Asia/Khandyga', 'Khandyga'), ('Asia/Kolkata', 'Kolkata'), ('Asia/Krasnoyarsk', 'Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Kuala_Lumpur'), ('Asia/Kuching', 'Kuching'), ('Asia/Kuwait', 'Kuwait'), ('Asia/Macao', 'Macao'), ('Asia/Macau', 'Macau'), ('Asia/Magadan', 'Magadan'), ('Asia/Makassar', 'Makassar'), ('Asia/Manila', 'Manila'), ('Asia/Muscat', 'Muscat'), ('Asia/Nicosia', 'Nicosia'), ('Asia/Novokuznetsk', 'Novokuznetsk'), ('Asia/Novosibirsk', 'Novosibirsk'), ('Asia/Omsk', 'Omsk'), ('Asia/Oral', 'Oral'), ('Asia/Phnom_Penh', 'Phnom_Penh'), ('Asia/Pontianak', 'Pontianak'), ('Asia/Pyongyang', 'Pyongyang'), ('Asia/Qatar', 'Qatar'), ('Asia/Qostanay', 'Qostanay'), ('Asia/Qyzylorda', 'Qyzylorda'), ('Asia/Rangoon', 'Rangoon'), ('Asia/Riyadh', 'Riyadh'), ('Asia/Saigon', 'Saigon'), ('Asia/Sakhalin', 'Sakhalin'), ('Asia/Samarkand', 'Samarkand'), ('Asia/Seoul', 'Seoul'), ('Asia/Shanghai', 'Shanghai'), ('Asia/Singapore', 'Singapore'), ('Asia/Srednekolymsk', 'Srednekolymsk'), ('Asia/Taipei', 'Taipei'), ('Asia/Tashkent', 'Tashkent'), ('Asia/Tbilisi', 'Tbilisi'), ('Asia/Tehran', 'Tehran'), ('Asia/Tel_Aviv', 'Tel_Aviv'), ('Asia/Thimbu', 'Thimbu'), ('Asia/Thimphu', 'Thimphu'), ('Asia/Tokyo', 'Tokyo'), ('Asia/Tomsk', 'Tomsk'), ('Asia/Ujung_Pandang', 'Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Ulaanbaatar'), ('Asia/Ulan_Bator', 'Ulan_Bator'), ('Asia/Urumqi', 'Urumqi'), ('Asia/Ust-Nera', 'Ust-Nera'), ('Asia/Vientiane', 'Vientiane'), ('Asia/Vladivostok', 'Vladivostok'), ('Asia/Yakutsk', 'Yakutsk'), ('Asia/Yangon', 'Yangon'), ('Asia/Yekaterinburg', 'Yekaterinburg'), ('Asia/Yerevan', 'Yerevan')]), ('Atlantic', [('Atlantic/Azores', 'Azores'), ('Atlantic/Bermuda', 'Bermuda'), ('Atlantic/Canary', 'Canary'), ('Atlantic/Cape_Verde', 'Cape_Verde'), ('Atlantic/Faeroe', 'Faeroe'), ('Atlantic/Faroe', 'Faroe'), ('Atlantic/Jan_Mayen', 'Jan_Mayen'), ('Atlantic/Madeira', 'Madeira'), ('Atlantic/Reykjavik', 'Reykjavik'), ('Atlantic/South_Georgia', 'South_Georgia'), ('Atlantic/St_Helena', 'St_Helena'), ('Atlantic/Stanley', 'Stanley')]), ('Australia', [('Australia/ACT', 'ACT'), ('Australia/Adelaide', 'Adelaide'), ('Australia/Brisbane', 'Brisbane'), ('Australia/Broken_Hill', 'Broken_Hill'), ('Australia/Canberra', 'Canberra'), ('Australia/Currie', 'Currie'), ('Australia/Darwin', 'Darwin'), ('Australia/Eucla', 'Eucla'), ('Australia/Hobart', 'Hobart'), ('Australia/LHI', 'LHI'), ('Australia/Lindeman', 'Lindeman'), ('Australia/Lord_Howe', 'Lord_Howe'), ('Australia/Melbourne', 'Melbourne'), ('Australia/NSW', 'NSW'), ('Australia/North', 'North'), ('Australia/Perth', 'Perth'), ('Australia/Queensland', 'Queensland'), ('Australia/South', 'South'), ('Australia/Sydney', 'Sydney'), ('Australia/Tasmania', 'Tasmania'), ('Australia/Victoria', 'Victoria'), ('Australia/West', 'West'), ('Australia/Yancowinna', 'Yancowinna')]), ('Brazil', [('Brazil/Acre', 'Acre'), ('Brazil/DeNoronha', 'DeNoronha'), ('Brazil/East', 'East'), ('Brazil/West', 'West')]), ('Canada', [('Canada/Atlantic', 'Atlantic'), ('Canada/Central', 'Central'), ('Canada/Eastern', 'Eastern'), ('Canada/Mountain', 'Mountain'), ('Canada/Newfoundland', 'Newfoundland'), ('Canada/Pacific', 'Pacific'), ('Canada/Saskatchewan', 'Saskatchewan'), ('Canada/Yukon', 'Yukon')]), ('Chile', [('Chile/Continental', 'Continental'), ('Chile/EasterIsland', 'EasterIsland')]), ('Etc', [('Etc/Greenwich', 'Greenwich'), ('Etc/UCT', 'UCT'), ('Etc/UTC', 'UTC'), ('Etc/Universal', 'Universal'), ('Etc/Zulu', 'Zulu')]), ('Europe', [('Europe/Amsterdam', 'Amsterdam'), ('Europe/Andorra', 'Andorra'), ('Europe/Astrakhan', 'Astrakhan'), ('Europe/Athens', 'Athens'), ('Europe/Belfast', 'Belfast'), ('Europe/Belgrade', 'Belgrade'), ('Europe/Berlin', 'Berlin'), ('Europe/Bratislava', 'Bratislava'), ('Europe/Brussels', 'Brussels'), ('Europe/Bucharest', 'Bucharest'), ('Europe/Budapest', 'Budapest'), ('Europe/Busingen', 'Busingen'), ('Europe/Chisinau', 'Chisinau'), ('Europe/Copenhagen', 'Copenhagen'), ('Europe/Dublin', 'Dublin'), ('Europe/Gibraltar', 'Gibraltar'), ('Europe/Guernsey', 'Guernsey'), ('Europe/Helsinki', 'Helsinki'), ('Europe/Isle_of_Man', 'Isle_of_Man'), ('Europe/Istanbul', 'Istanbul'), ('Europe/Jersey', 'Jersey'), ('Europe/Kaliningrad', 'Kaliningrad'), ('Europe/Kiev', 'Kiev'), ('Europe/Kirov', 'Kirov'), ('Europe/Lisbon', 'Lisbon'), ('Europe/Ljubljana', 'Ljubljana'), ('Europe/London', 'London'), ('Europe/Luxembourg', 'Luxembourg'), ('Europe/Madrid', 'Madrid'), ('Europe/Malta', 'Malta'), ('Europe/Mariehamn', 'Mariehamn'), ('Europe/Minsk', 'Minsk'), ('Europe/Monaco', 'Monaco'), ('Europe/Moscow', 'Moscow'), ('Europe/Nicosia', 'Nicosia'), ('Europe/Oslo', 'Oslo'), ('Europe/Paris', 'Paris'), ('Europe/Podgorica', 'Podgorica'), ('Europe/Prague', 'Prague'), ('Europe/Riga', 'Riga'), ('Europe/Rome', 'Rome'), ('Europe/Samara', 'Samara'), ('Europe/San_Marino', 'San_Marino'), ('Europe/Sarajevo', 'Sarajevo'), ('Europe/Saratov', 'Saratov'), ('Europe/Simferopol', 'Simferopol'), ('Europe/Skopje', 'Skopje'), ('Europe/Sofia', 'Sofia'), ('Europe/Stockholm', 'Stockholm'), ('Europe/Tallinn', 'Tallinn'), ('Europe/Tirane', 'Tirane'), ('Europe/Tiraspol', 'Tiraspol'), ('Europe/Ulyanovsk', 'Ulyanovsk'), ('Europe/Uzhgorod', 'Uzhgorod'), ('Europe/Vaduz', 'Vaduz'), ('Europe/Vatican', 'Vatican'), ('Europe/Vienna', 'Vienna'), ('Europe/Vilnius', 'Vilnius'), ('Europe/Volgograd', 'Volgograd'), ('Europe/Warsaw', 'Warsaw'), ('Europe/Zagreb', 'Zagreb'), ('Europe/Zaporozhye', 'Zaporozhye'), ('Europe/Zurich', 'Zurich')]), ('Indian', [('Indian/Antananarivo', 'Antananarivo'), ('Indian/Chagos', 'Chagos'), ('Indian/Christmas', 'Christmas'), ('Indian/Cocos', 'Cocos'), ('Indian/Comoro', 'Comoro'), ('Indian/Kerguelen', 'Kerguelen'), ('Indian/Mahe', 'Mahe'), ('Indian/Maldives', 'Maldives'), ('Indian/Mauritius', 'Mauritius'), ('Indian/Mayotte', 'Mayotte'), ('Indian/Reunion', 'Reunion')]), ('Mexico', [('Mexico/BajaNorte', 'BajaNorte'), ('Mexico/BajaSur', 'BajaSur'), ('Mexico/General', 'General')]), ('Other', [('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')]), ('Pacific', [('Pacific/Apia', 'Apia'), ('Pacific/Auckland', 'Auckland'), ('Pacific/Bougainville', 'Bougainville'), ('Pacific/Chatham', 'Chatham'), ('Pacific/Chuuk', 'Chuuk'), ('Pacific/Easter', 'Easter'), ('Pacific/Efate', 'Efate'), ('Pacific/Enderbury', 'Enderbury'), ('Pacific/Fakaofo', 'Fakaofo'), ('Pacific/Fiji', 'Fiji'), ('Pacific/Funafuti', 'Funafuti'), ('Pacific/Galapagos', 'Galapagos'), ('Pacific/Gambier', 'Gambier'), ('Pacific/Guadalcanal', 'Guadalcanal'), ('Pacific/Guam', 'Guam'), ('Pacific/Honolulu', 'Honolulu'), ('Pacific/Johnston', 'Johnston'), ('Pacific/Kiritimati', 'Kiritimati'), ('Pacific/Kosrae', 'Kosrae'), ('Pacific/Kwajalein', 'Kwajalein'), ('Pacific/Majuro', 'Majuro'), ('Pacific/Marquesas', 'Marquesas'), ('Pacific/Midway', 'Midway'), ('Pacific/Nauru', 'Nauru'), ('Pacific/Niue', 'Niue'), ('Pacific/Norfolk', 'Norfolk'), ('Pacific/Noumea', 'Noumea'), ('Pacific/Pago_Pago', 'Pago_Pago'), ('Pacific/Palau', 'Palau'), ('Pacific/Pitcairn', 'Pitcairn'), ('Pacific/Pohnpei', 'Pohnpei'), ('Pacific/Ponape', 'Ponape'), ('Pacific/Port_Moresby', 'Port_Moresby'), ('Pacific/Rarotonga', 'Rarotonga'), ('Pacific/Saipan', 'Saipan'), ('Pacific/Samoa', 'Samoa'), ('Pacific/Tahiti', 'Tahiti'), ('Pacific/Tarawa', 'Tarawa'), ('Pacific/Tongatapu', 'Tongatapu'), ('Pacific/Truk', 'Truk'), ('Pacific/Wake', 'Wake'), ('Pacific/Wallis', 'Wallis'), ('Pacific/Yap', 'Yap')]), ('US', [('US/Alaska', 'Alaska'), ('US/Aleutian', 'Aleutian'), ('US/Arizona', 'Arizona'), ('US/Central', 'Central'), ('US/East-Indiana', 'East-Indiana'), ('US/Eastern', 'Eastern'), ('US/Hawaii', 'Hawaii'), ('US/Indiana-Starke', 'Indiana-Starke'), ('US/Michigan', 'Michigan'), ('US/Mountain', 'Mountain'), ('US/Pacific', 'Pacific'), ('US/Samoa', 'Samoa')])], default='America/Toronto', max_length=50, verbose_name='location')), + ('points', models.FloatField(db_index=True, default=0)), + ('performance_points', models.FloatField(db_index=True, default=0)), + ('problem_count', models.IntegerField(db_index=True, default=0)), + ('ace_theme', models.CharField(choices=[('ambiance', 'Ambiance'), ('chaos', 'Chaos'), ('chrome', 'Chrome'), ('clouds', 'Clouds'), ('clouds_midnight', 'Clouds Midnight'), ('cobalt', 'Cobalt'), ('crimson_editor', 'Crimson Editor'), ('dawn', 'Dawn'), ('dreamweaver', 'Dreamweaver'), ('eclipse', 'Eclipse'), ('github', 'Github'), ('idle_fingers', 'Idle Fingers'), ('katzenmilch', 'Katzenmilch'), ('kr_theme', 'KR Theme'), ('kuroir', 'Kuroir'), ('merbivore', 'Merbivore'), ('merbivore_soft', 'Merbivore Soft'), ('mono_industrial', 'Mono Industrial'), ('monokai', 'Monokai'), ('pastel_on_dark', 'Pastel on Dark'), ('solarized_dark', 'Solarized Dark'), ('solarized_light', 'Solarized Light'), ('terminal', 'Terminal'), ('textmate', 'Textmate'), ('tomorrow', 'Tomorrow'), ('tomorrow_night', 'Tomorrow Night'), ('tomorrow_night_blue', 'Tomorrow Night Blue'), ('tomorrow_night_bright', 'Tomorrow Night Bright'), ('tomorrow_night_eighties', 'Tomorrow Night Eighties'), ('twilight', 'Twilight'), ('vibrant_ink', 'Vibrant Ink'), ('xcode', 'XCode')], default='github', max_length=30)), + ('last_access', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last access time')), + ('ip', models.GenericIPAddressField(blank=True, null=True, verbose_name='last IP')), + ('display_rank', models.CharField(choices=[('user', 'Normal User'), ('setter', 'Problem Setter'), ('admin', 'Admin')], default='user', max_length=10, verbose_name='display rank')), + ('mute', models.BooleanField(default=False, help_text='Some users are at their best when silent.', verbose_name='comment mute')), + ('is_unlisted', models.BooleanField(default=False, help_text='User will not be ranked.', verbose_name='unlisted user')), + ('rating', models.IntegerField(default=None, null=True)), + ('user_script', models.TextField(blank=True, default='', help_text='User-defined JavaScript for site customization.', max_length=65536, verbose_name='user script')), + ('math_engine', models.CharField(choices=[('tex', 'Leave as LaTeX'), ('svg', 'SVG with PNG fallback'), ('mml', 'MathML only'), ('jax', 'MathJax with SVG/PNG fallback'), ('auto', 'Detect best quality')], default='auto', help_text='the rendering engine used to render math', max_length=4, verbose_name='math engine')), + ('is_totp_enabled', models.BooleanField(default=False, help_text='check to enable TOTP-based two factor authentication', verbose_name='2FA enabled')), + ('totp_key', judge.models.profile.EncryptedNullCharField(blank=True, help_text='32 character base32-encoded key for TOTP', max_length=32, null=True, validators=[django.core.validators.RegexValidator('^$|^[A-Z2-7]{32}$', 'TOTP key must be empty or base32')], verbose_name='TOTP key')), + ('notes', models.TextField(blank=True, help_text='Notes for administrators regarding this user.', null=True, verbose_name='internal notes')), + ('current_contest', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='judge.ContestParticipation', verbose_name='current contest')), + ('language', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Language', verbose_name='preferred language')), + ('organizations', sortedm2m.fields.SortedManyToManyField(blank=True, help_text=None, related_name='members', related_query_name='member', to='judge.Organization', verbose_name='organization')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user associated')), ], options={ - "verbose_name_plural": "user profiles", - "permissions": ( - ("test_site", "Shows in-progress development stuff"), - ("totp", "Edit TOTP settings"), - ), - "verbose_name": "user profile", + 'verbose_name_plural': 'user profiles', + 'permissions': (('test_site', 'Shows in-progress development stuff'), ('totp', 'Edit TOTP settings')), + 'verbose_name': 'user profile', }, ), migrations.CreateModel( - name="Rating", + name='Rating', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("rank", models.IntegerField(verbose_name="rank")), - ("rating", models.IntegerField(verbose_name="rating")), - ("volatility", models.IntegerField(verbose_name="volatility")), - ( - "last_rated", - models.DateTimeField(db_index=True, verbose_name="last rated"), - ), - ( - "contest", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="ratings", - to="judge.Contest", - verbose_name="contest", - ), - ), - ( - "participation", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="rating", - to="judge.ContestParticipation", - verbose_name="participation", - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="ratings", - to="judge.Profile", - verbose_name="user", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rank', models.IntegerField(verbose_name='rank')), + ('rating', models.IntegerField(verbose_name='rating')), + ('volatility', models.IntegerField(verbose_name='volatility')), + ('last_rated', models.DateTimeField(db_index=True, verbose_name='last rated')), + ('contest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='judge.Contest', verbose_name='contest')), + ('participation', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='rating', to='judge.ContestParticipation', verbose_name='participation')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='judge.Profile', verbose_name='user')), ], options={ - "verbose_name_plural": "contest ratings", - "verbose_name": "contest rating", + 'verbose_name_plural': 'contest ratings', + 'verbose_name': 'contest rating', }, ), migrations.CreateModel( - name="RuntimeVersion", + name='RuntimeVersion', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=64, verbose_name="runtime name")), - ( - "version", - models.CharField( - blank=True, max_length=64, verbose_name="runtime version" - ), - ), - ( - "priority", - models.IntegerField( - default=0, verbose_name="order in which to display this runtime" - ), - ), - ( - "judge", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Judge", - verbose_name="judge on which this runtime exists", - ), - ), - ( - "language", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Language", - verbose_name="language to which this runtime belongs", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=64, verbose_name='runtime name')), + ('version', models.CharField(blank=True, max_length=64, verbose_name='runtime version')), + ('priority', models.IntegerField(default=0, verbose_name='order in which to display this runtime')), + ('judge', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Judge', verbose_name='judge on which this runtime exists')), + ('language', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Language', verbose_name='language to which this runtime belongs')), ], ), migrations.CreateModel( - name="Solution", + name='Solution', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "is_public", - models.BooleanField( - default=False, verbose_name="public visibility" - ), - ), - ("publish_on", models.DateTimeField(verbose_name="publish date")), - ("content", models.TextField(verbose_name="editorial content")), - ( - "authors", - models.ManyToManyField( - blank=True, to="judge.Profile", verbose_name="authors" - ), - ), - ( - "problem", - models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="solution", - to="judge.Problem", - verbose_name="associated problem", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_public', models.BooleanField(default=False, verbose_name='public visibility')), + ('publish_on', models.DateTimeField(verbose_name='publish date')), + ('content', models.TextField(verbose_name='editorial content')), + ('authors', models.ManyToManyField(blank=True, to='judge.Profile', verbose_name='authors')), + ('problem', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='solution', to='judge.Problem', verbose_name='associated problem')), ], options={ - "verbose_name_plural": "solutions", - "permissions": (("see_private_solution", "See hidden solutions"),), - "verbose_name": "solution", + 'verbose_name_plural': 'solutions', + 'permissions': (('see_private_solution', 'See hidden solutions'),), + 'verbose_name': 'solution', }, ), migrations.CreateModel( - name="Submission", + name='Submission', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "date", - models.DateTimeField( - auto_now_add=True, db_index=True, verbose_name="submission time" - ), - ), - ( - "time", - models.FloatField( - db_index=True, null=True, verbose_name="execution time" - ), - ), - ("memory", models.FloatField(null=True, verbose_name="memory usage")), - ( - "points", - models.FloatField( - db_index=True, null=True, verbose_name="points granted" - ), - ), - ( - "source", - models.TextField(max_length=65536, verbose_name="source code"), - ), - ( - "status", - models.CharField( - choices=[ - ("QU", "Queued"), - ("P", "Processing"), - ("G", "Grading"), - ("D", "Completed"), - ("IE", "Internal Error"), - ("CE", "Compile Error"), - ("AB", "Aborted"), - ], - db_index=True, - default="QU", - max_length=2, - verbose_name="status", - ), - ), - ( - "result", - models.CharField( - blank=True, - choices=[ - ("AC", "Accepted"), - ("WA", "Wrong Answer"), - ("TLE", "Time Limit Exceeded"), - ("MLE", "Memory Limit Exceeded"), - ("OLE", "Output Limit Exceeded"), - ("IR", "Invalid Return"), - ("RTE", "Runtime Error"), - ("CE", "Compile Error"), - ("IE", "Internal Error"), - ("SC", "Short circuit"), - ("AB", "Aborted"), - ], - db_index=True, - default=None, - max_length=3, - null=True, - verbose_name="result", - ), - ), - ( - "error", - models.TextField( - blank=True, null=True, verbose_name="compile errors" - ), - ), - ("current_testcase", models.IntegerField(default=0)), - ( - "batch", - models.BooleanField(default=False, verbose_name="batched cases"), - ), - ( - "case_points", - models.FloatField(default=0, verbose_name="test case points"), - ), - ( - "case_total", - models.FloatField(default=0, verbose_name="test case total points"), - ), - ( - "was_rejudged", - models.BooleanField( - default=False, verbose_name="was rejudged by admin" - ), - ), - ( - "is_pretested", - models.BooleanField( - default=False, verbose_name="was ran on pretests only" - ), - ), - ( - "judged_on", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to="judge.Judge", - verbose_name="judged on", - ), - ), - ( - "language", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Language", - verbose_name="submission language", - ), - ), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="judge.Problem" - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="judge.Profile" - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='submission time')), + ('time', models.FloatField(db_index=True, null=True, verbose_name='execution time')), + ('memory', models.FloatField(null=True, verbose_name='memory usage')), + ('points', models.FloatField(db_index=True, null=True, verbose_name='points granted')), + ('source', models.TextField(max_length=65536, verbose_name='source code')), + ('status', models.CharField(choices=[('QU', 'Queued'), ('P', 'Processing'), ('G', 'Grading'), ('D', 'Completed'), ('IE', 'Internal Error'), ('CE', 'Compile Error'), ('AB', 'Aborted')], db_index=True, default='QU', max_length=2, verbose_name='status')), + ('result', models.CharField(blank=True, choices=[('AC', 'Accepted'), ('WA', 'Wrong Answer'), ('TLE', 'Time Limit Exceeded'), ('MLE', 'Memory Limit Exceeded'), ('OLE', 'Output Limit Exceeded'), ('IR', 'Invalid Return'), ('RTE', 'Runtime Error'), ('CE', 'Compile Error'), ('IE', 'Internal Error'), ('SC', 'Short circuit'), ('AB', 'Aborted')], db_index=True, default=None, max_length=3, null=True, verbose_name='result')), + ('error', models.TextField(blank=True, null=True, verbose_name='compile errors')), + ('current_testcase', models.IntegerField(default=0)), + ('batch', models.BooleanField(default=False, verbose_name='batched cases')), + ('case_points', models.FloatField(default=0, verbose_name='test case points')), + ('case_total', models.FloatField(default=0, verbose_name='test case total points')), + ('was_rejudged', models.BooleanField(default=False, verbose_name='was rejudged by admin')), + ('is_pretested', models.BooleanField(default=False, verbose_name='was ran on pretests only')), + ('judged_on', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='judge.Judge', verbose_name='judged on')), + ('language', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Language', verbose_name='submission language')), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Problem')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Profile')), ], options={ - "verbose_name_plural": "submissions", - "permissions": ( - ("abort_any_submission", "Abort any submission"), - ("rejudge_submission", "Rejudge the submission"), - ("rejudge_submission_lot", "Rejudge a lot of submissions"), - ("spam_submission", "Submit without limit"), - ("view_all_submission", "View all submission"), - ("resubmit_other", "Resubmit others' submission"), - ), - "verbose_name": "submission", + 'verbose_name_plural': 'submissions', + 'permissions': (('abort_any_submission', 'Abort any submission'), ('rejudge_submission', 'Rejudge the submission'), ('rejudge_submission_lot', 'Rejudge a lot of submissions'), ('spam_submission', 'Submit without limit'), ('view_all_submission', 'View all submission'), ('resubmit_other', "Resubmit others' submission")), + 'verbose_name': 'submission', }, ), migrations.CreateModel( - name="SubmissionTestCase", + name='SubmissionTestCase', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("case", models.IntegerField(verbose_name="test case ID")), - ( - "status", - models.CharField( - choices=[ - ("AC", "Accepted"), - ("WA", "Wrong Answer"), - ("TLE", "Time Limit Exceeded"), - ("MLE", "Memory Limit Exceeded"), - ("OLE", "Output Limit Exceeded"), - ("IR", "Invalid Return"), - ("RTE", "Runtime Error"), - ("CE", "Compile Error"), - ("IE", "Internal Error"), - ("SC", "Short circuit"), - ("AB", "Aborted"), - ], - max_length=3, - verbose_name="status flag", - ), - ), - ("time", models.FloatField(null=True, verbose_name="execution time")), - ("memory", models.FloatField(null=True, verbose_name="memory usage")), - ("points", models.FloatField(null=True, verbose_name="points granted")), - ("total", models.FloatField(null=True, verbose_name="points possible")), - ("batch", models.IntegerField(null=True, verbose_name="batch number")), - ( - "feedback", - models.CharField( - blank=True, max_length=50, verbose_name="judging feedback" - ), - ), - ( - "extended_feedback", - models.TextField( - blank=True, verbose_name="extended judging feedback" - ), - ), - ("output", models.TextField(blank=True, verbose_name="program output")), - ( - "submission", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="test_cases", - to="judge.Submission", - verbose_name="associated submission", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('case', models.IntegerField(verbose_name='test case ID')), + ('status', models.CharField(choices=[('AC', 'Accepted'), ('WA', 'Wrong Answer'), ('TLE', 'Time Limit Exceeded'), ('MLE', 'Memory Limit Exceeded'), ('OLE', 'Output Limit Exceeded'), ('IR', 'Invalid Return'), ('RTE', 'Runtime Error'), ('CE', 'Compile Error'), ('IE', 'Internal Error'), ('SC', 'Short circuit'), ('AB', 'Aborted')], max_length=3, verbose_name='status flag')), + ('time', models.FloatField(null=True, verbose_name='execution time')), + ('memory', models.FloatField(null=True, verbose_name='memory usage')), + ('points', models.FloatField(null=True, verbose_name='points granted')), + ('total', models.FloatField(null=True, verbose_name='points possible')), + ('batch', models.IntegerField(null=True, verbose_name='batch number')), + ('feedback', models.CharField(blank=True, max_length=50, verbose_name='judging feedback')), + ('extended_feedback', models.TextField(blank=True, verbose_name='extended judging feedback')), + ('output', models.TextField(blank=True, verbose_name='program output')), + ('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='test_cases', to='judge.Submission', verbose_name='associated submission')), ], options={ - "verbose_name_plural": "submission test cases", - "verbose_name": "submission test case", + 'verbose_name_plural': 'submission test cases', + 'verbose_name': 'submission test case', }, ), migrations.CreateModel( - name="Ticket", + name='Ticket', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "title", - models.CharField(max_length=100, verbose_name="ticket title"), - ), - ( - "time", - models.DateTimeField( - auto_now_add=True, verbose_name="creation time" - ), - ), - ( - "notes", - models.TextField( - blank=True, - help_text="Staff notes for this issue to aid in processing.", - verbose_name="quick notes", - ), - ), - ( - "object_id", - models.PositiveIntegerField(verbose_name="linked item ID"), - ), - ( - "is_open", - models.BooleanField(default=True, verbose_name="is ticket open?"), - ), - ( - "assignees", - models.ManyToManyField( - related_name="assigned_tickets", - to="judge.Profile", - verbose_name="assignees", - ), - ), - ( - "content_type", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="contenttypes.ContentType", - verbose_name="linked item type", - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="tickets", - to="judge.Profile", - verbose_name="ticket creator", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100, verbose_name='ticket title')), + ('time', models.DateTimeField(auto_now_add=True, verbose_name='creation time')), + ('notes', models.TextField(blank=True, help_text='Staff notes for this issue to aid in processing.', verbose_name='quick notes')), + ('object_id', models.PositiveIntegerField(verbose_name='linked item ID')), + ('is_open', models.BooleanField(default=True, verbose_name='is ticket open?')), + ('assignees', models.ManyToManyField(related_name='assigned_tickets', to='judge.Profile', verbose_name='assignees')), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='linked item type')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tickets', to='judge.Profile', verbose_name='ticket creator')), ], ), migrations.CreateModel( - name="TicketMessage", + name='TicketMessage', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("body", models.TextField(verbose_name="message body")), - ( - "time", - models.DateTimeField( - auto_now_add=True, verbose_name="message time" - ), - ), - ( - "ticket", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="messages", - related_query_name="message", - to="judge.Ticket", - verbose_name="ticket", - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="ticket_messages", - to="judge.Profile", - verbose_name="poster", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('body', models.TextField(verbose_name='message body')), + ('time', models.DateTimeField(auto_now_add=True, verbose_name='message time')), + ('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', related_query_name='message', to='judge.Ticket', verbose_name='ticket')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticket_messages', to='judge.Profile', verbose_name='poster')), ], ), migrations.AddField( - model_name="problem", - name="authors", - field=models.ManyToManyField( - blank=True, - related_name="authored_problems", - to="judge.Profile", - verbose_name="creators", - ), + model_name='problem', + name='authors', + field=models.ManyToManyField(blank=True, related_name='authored_problems', to='judge.Profile', verbose_name='creators'), ), migrations.AddField( - model_name="problem", - name="banned_users", - field=models.ManyToManyField( - blank=True, - help_text="Bans the selected users from submitting to this problem.", - to="judge.Profile", - verbose_name="personae non gratae", - ), + model_name='problem', + name='banned_users', + field=models.ManyToManyField(blank=True, help_text='Bans the selected users from submitting to this problem.', to='judge.Profile', verbose_name='personae non gratae'), ), migrations.AddField( - model_name="problem", - name="curators", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to edit a problem, but not be publicly shown as an author.", - related_name="curated_problems", - to="judge.Profile", - verbose_name="curators", - ), + model_name='problem', + name='curators', + field=models.ManyToManyField(blank=True, help_text='These users will be able to edit a problem, but not be publicly shown as an author.', related_name='curated_problems', to='judge.Profile', verbose_name='curators'), ), migrations.AddField( - model_name="problem", - name="group", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.ProblemGroup", - verbose_name="problem group", - ), + model_name='problem', + name='group', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.ProblemGroup', verbose_name='problem group'), ), migrations.AddField( - model_name="problem", - name="license", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to="judge.License", - ), + model_name='problem', + name='license', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='judge.License'), ), migrations.AddField( - model_name="problem", - name="organizations", - field=models.ManyToManyField( - blank=True, - help_text="If private, only these organizations may see the problem.", - to="judge.Organization", - verbose_name="organizations", - ), + model_name='problem', + name='organizations', + field=models.ManyToManyField(blank=True, help_text='If private, only these organizations may see the problem.', to='judge.Organization', verbose_name='organizations'), ), migrations.AddField( - model_name="problem", - name="testers", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to view a private problem, but not edit it.", - related_name="tested_problems", - to="judge.Profile", - verbose_name="testers", - ), + model_name='problem', + name='testers', + field=models.ManyToManyField(blank=True, help_text='These users will be able to view a private problem, but not edit it.', related_name='tested_problems', to='judge.Profile', verbose_name='testers'), ), migrations.AddField( - model_name="problem", - name="types", - field=models.ManyToManyField( - to="judge.ProblemType", verbose_name="problem types" - ), + model_name='problem', + name='types', + field=models.ManyToManyField(to='judge.ProblemType', verbose_name='problem types'), ), migrations.AddField( - model_name="privatemessage", - name="sender", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="sent_messages", - to="judge.Profile", - verbose_name="sender", - ), + model_name='privatemessage', + name='sender', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_messages', to='judge.Profile', verbose_name='sender'), ), migrations.AddField( - model_name="privatemessage", - name="target", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="received_messages", - to="judge.Profile", - verbose_name="target", - ), + model_name='privatemessage', + name='target', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='received_messages', to='judge.Profile', verbose_name='target'), ), migrations.AddField( - model_name="organizationrequest", - name="user", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="requests", - to="judge.Profile", - verbose_name="user", - ), + model_name='organizationrequest', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requests', to='judge.Profile', verbose_name='user'), ), migrations.AddField( - model_name="organization", - name="admins", - field=models.ManyToManyField( - help_text="Those who can edit this organization", - related_name="admin_of", - to="judge.Profile", - verbose_name="administrators", - ), + model_name='organization', + name='admins', + field=models.ManyToManyField(help_text='Those who can edit this organization', related_name='admin_of', to='judge.Profile', verbose_name='administrators'), ), migrations.AddField( - model_name="organization", - name="registrant", - field=models.ForeignKey( - help_text="User who registered this organization", - on_delete=django.db.models.deletion.CASCADE, - related_name="registrant+", - to="judge.Profile", - verbose_name="registrant", - ), + model_name='organization', + name='registrant', + field=models.ForeignKey(help_text='User who registered this organization', on_delete=django.db.models.deletion.CASCADE, related_name='registrant+', to='judge.Profile', verbose_name='registrant'), ), migrations.AddField( - model_name="languagelimit", - name="problem", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="language_limits", - to="judge.Problem", - verbose_name="problem", - ), + model_name='languagelimit', + name='problem', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='language_limits', to='judge.Problem', verbose_name='problem'), ), migrations.AddField( - model_name="judge", - name="problems", - field=models.ManyToManyField( - related_name="judges", to="judge.Problem", verbose_name="problems" - ), + model_name='judge', + name='problems', + field=models.ManyToManyField(related_name='judges', to='judge.Problem', verbose_name='problems'), ), migrations.AddField( - model_name="judge", - name="runtimes", - field=models.ManyToManyField( - related_name="judges", to="judge.Language", verbose_name="judges" - ), + model_name='judge', + name='runtimes', + field=models.ManyToManyField(related_name='judges', to='judge.Language', verbose_name='judges'), ), migrations.AddField( - model_name="contestsubmission", - name="submission", - field=models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="contest", - to="judge.Submission", - verbose_name="submission", - ), + model_name='contestsubmission', + name='submission', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='contest', to='judge.Submission', verbose_name='submission'), ), migrations.AddField( - model_name="contestproblem", - name="problem", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="contests", - to="judge.Problem", - verbose_name="problem", - ), + model_name='contestproblem', + name='problem', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contests', to='judge.Problem', verbose_name='problem'), ), migrations.AddField( - model_name="contestparticipation", - name="user", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="contest_history", - to="judge.Profile", - verbose_name="user", - ), + model_name='contestparticipation', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contest_history', to='judge.Profile', verbose_name='user'), ), migrations.AddField( - model_name="contest", - name="banned_users", - field=models.ManyToManyField( - blank=True, - help_text="Bans the selected users from joining this contest.", - to="judge.Profile", - verbose_name="personae non gratae", - ), + model_name='contest', + name='banned_users', + field=models.ManyToManyField(blank=True, help_text='Bans the selected users from joining this contest.', to='judge.Profile', verbose_name='personae non gratae'), ), migrations.AddField( - model_name="contest", - name="organizations", - field=models.ManyToManyField( - blank=True, - help_text="If private, only these organizations may see the contest", - to="judge.Organization", - verbose_name="organizations", - ), + model_name='contest', + name='organizations', + field=models.ManyToManyField(blank=True, help_text='If private, only these organizations may see the contest', to='judge.Organization', verbose_name='organizations'), ), migrations.AddField( - model_name="contest", - name="organizers", - field=models.ManyToManyField( - help_text="These people will be able to edit the contest.", - related_name="_contest_organizers_+", - to="judge.Profile", - ), + model_name='contest', + name='organizers', + field=models.ManyToManyField(help_text='These people will be able to edit the contest.', related_name='_contest_organizers_+', to='judge.Profile'), ), migrations.AddField( - model_name="contest", - name="problems", - field=models.ManyToManyField( - through="judge.ContestProblem", - to="judge.Problem", - verbose_name="problems", - ), + model_name='contest', + name='problems', + field=models.ManyToManyField(through='judge.ContestProblem', to='judge.Problem', verbose_name='problems'), ), migrations.AddField( - model_name="contest", - name="rate_exclude", - field=models.ManyToManyField( - blank=True, - related_name="_contest_rate_exclude_+", - to="judge.Profile", - verbose_name="exclude from ratings", - ), + model_name='contest', + name='rate_exclude', + field=models.ManyToManyField(blank=True, related_name='_contest_rate_exclude_+', to='judge.Profile', verbose_name='exclude from ratings'), ), migrations.AddField( - model_name="contest", - name="tags", - field=models.ManyToManyField( - blank=True, - related_name="contests", - to="judge.ContestTag", - verbose_name="contest tags", - ), + model_name='contest', + name='tags', + field=models.ManyToManyField(blank=True, related_name='contests', to='judge.ContestTag', verbose_name='contest tags'), ), migrations.AddField( - model_name="commentvote", - name="voter", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="voted_comments", - to="judge.Profile", - ), + model_name='commentvote', + name='voter', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='voted_comments', to='judge.Profile'), ), migrations.AddField( - model_name="comment", - name="author", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Profile", - verbose_name="commenter", - ), + model_name='comment', + name='author', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Profile', verbose_name='commenter'), ), migrations.AddField( - model_name="comment", - name="parent", - field=mptt.fields.TreeForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="replies", - to="judge.Comment", - verbose_name="parent", - ), + model_name='comment', + name='parent', + field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='replies', to='judge.Comment', verbose_name='parent'), ), migrations.AddField( - model_name="blogpost", - name="authors", - field=models.ManyToManyField( - blank=True, to="judge.Profile", verbose_name="authors" - ), + model_name='blogpost', + name='authors', + field=models.ManyToManyField(blank=True, to='judge.Profile', verbose_name='authors'), ), migrations.AlterUniqueTogether( - name="rating", - unique_together=set([("user", "contest")]), + name='rating', + unique_together=set([('user', 'contest')]), ), migrations.AlterUniqueTogether( - name="problemtranslation", - unique_together=set([("problem", "language")]), + name='problemtranslation', + unique_together=set([('problem', 'language')]), ), migrations.AlterUniqueTogether( - name="languagelimit", - unique_together=set([("problem", "language")]), + name='languagelimit', + unique_together=set([('problem', 'language')]), ), migrations.AlterUniqueTogether( - name="contestproblem", - unique_together=set([("problem", "contest")]), + name='contestproblem', + unique_together=set([('problem', 'contest')]), ), migrations.AlterUniqueTogether( - name="contestparticipation", - unique_together=set([("contest", "user", "virtual")]), + name='contestparticipation', + unique_together=set([('contest', 'user', 'virtual')]), ), migrations.AlterUniqueTogether( - name="commentvote", - unique_together=set([("voter", "comment")]), + name='commentvote', + unique_together=set([('voter', 'comment')]), ), ] diff --git a/judge/migrations/0085_submission_source.py b/judge/migrations/0085_submission_source.py index 3f0dd18..d094efd 100644 --- a/judge/migrations/0085_submission_source.py +++ b/judge/migrations/0085_submission_source.py @@ -7,61 +7,33 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0084_contest_formats"), + ('judge', '0084_contest_formats'), ] operations = [ migrations.CreateModel( - name="SubmissionSource", + name='SubmissionSource', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "source", - models.TextField(max_length=65536, verbose_name="source code"), - ), - ( - "submission", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="link", - to="judge.Submission", - verbose_name="associated submission", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('source', models.TextField(max_length=65536, verbose_name='source code')), + ('submission', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='link', to='judge.Submission', verbose_name='associated submission')), ], ), migrations.RunSQL( - [ - """INSERT INTO judge_submissionsource (source, submission_id) - SELECT source, id AS 'submission_id' FROM judge_submission;""" - ], - [ - """UPDATE judge_submission sub + ['''INSERT INTO judge_submissionsource (source, submission_id) + SELECT source, id AS 'submission_id' FROM judge_submission;'''], + ['''UPDATE judge_submission sub INNER JOIN judge_submissionsource src ON sub.id = src.submission_id - SET sub.source = src.source;""" - ], + SET sub.source = src.source;'''], elidable=True, ), migrations.RemoveField( - model_name="submission", - name="source", + model_name='submission', + name='source', ), migrations.AlterField( - model_name="submissionsource", - name="submission", - field=models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="source", - to="judge.Submission", - verbose_name="associated submission", - ), + model_name='submissionsource', + name='submission', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='source', to='judge.Submission', verbose_name='associated submission'), ), ] diff --git a/judge/migrations/0086_rating_ceiling.py b/judge/migrations/0086_rating_ceiling.py index d529ac1..a544a21 100644 --- a/judge/migrations/0086_rating_ceiling.py +++ b/judge/migrations/0086_rating_ceiling.py @@ -6,28 +6,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0085_submission_source"), + ('judge', '0085_submission_source'), ] operations = [ migrations.AddField( - model_name="contest", - name="rating_ceiling", - field=models.IntegerField( - blank=True, - help_text="Rating ceiling for contest", - null=True, - verbose_name="rating ceiling", - ), + model_name='contest', + name='rating_ceiling', + field=models.IntegerField(blank=True, help_text='Rating ceiling for contest', null=True, verbose_name='rating ceiling'), ), migrations.AddField( - model_name="contest", - name="rating_floor", - field=models.IntegerField( - blank=True, - help_text="Rating floor for contest", - null=True, - verbose_name="rating floor", - ), + model_name='contest', + name='rating_floor', + field=models.IntegerField(blank=True, help_text='Rating floor for contest', null=True, verbose_name='rating floor'), ), ] diff --git a/judge/migrations/0087_problem_resource_limits.py b/judge/migrations/0087_problem_resource_limits.py index c9fd18f..e6da153 100644 --- a/judge/migrations/0087_problem_resource_limits.py +++ b/judge/migrations/0087_problem_resource_limits.py @@ -7,28 +7,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0086_rating_ceiling"), + ('judge', '0086_rating_ceiling'), ] operations = [ migrations.AlterField( - model_name="problem", - name="memory_limit", - field=models.PositiveIntegerField( - help_text="The memory limit for this problem, in kilobytes (e.g. 64mb = 65536 kilobytes).", - verbose_name="memory limit", - ), + model_name='problem', + name='memory_limit', + field=models.PositiveIntegerField(help_text='The memory limit for this problem, in kilobytes (e.g. 64mb = 65536 kilobytes).', verbose_name='memory limit'), ), migrations.AlterField( - model_name="problem", - name="time_limit", - field=models.FloatField( - help_text="The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) are supported.", - validators=[ - django.core.validators.MinValueValidator(0), - django.core.validators.MaxValueValidator(2000), - ], - verbose_name="time limit", - ), + model_name='problem', + name='time_limit', + field=models.FloatField(help_text='The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) are supported.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(2000)], verbose_name='time limit'), ), ] diff --git a/judge/migrations/0088_private_contests.py b/judge/migrations/0088_private_contests.py index 8a5b886..b3505b5 100644 --- a/judge/migrations/0088_private_contests.py +++ b/judge/migrations/0088_private_contests.py @@ -6,53 +6,32 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0087_problem_resource_limits"), + ('judge', '0087_problem_resource_limits'), ] operations = [ migrations.AlterModelOptions( - name="contest", - options={ - "permissions": ( - ("see_private_contest", "See private contests"), - ("edit_own_contest", "Edit own contests"), - ("edit_all_contest", "Edit all contests"), - ("contest_rating", "Rate contests"), - ("contest_access_code", "Contest access codes"), - ("create_private_contest", "Create private contests"), - ), - "verbose_name": "contest", - "verbose_name_plural": "contests", - }, + name='contest', + options={'permissions': (('see_private_contest', 'See private contests'), ('edit_own_contest', 'Edit own contests'), ('edit_all_contest', 'Edit all contests'), ('contest_rating', 'Rate contests'), ('contest_access_code', 'Contest access codes'), ('create_private_contest', 'Create private contests')), 'verbose_name': 'contest', 'verbose_name_plural': 'contests'}, ), migrations.RenameField( - model_name="contest", - old_name="is_public", - new_name="is_visible", + model_name='contest', + old_name='is_public', + new_name='is_visible', ), migrations.AddField( - model_name="contest", - name="is_organization_private", - field=models.BooleanField( - default=False, verbose_name="private to organizations" - ), + model_name='contest', + name='is_organization_private', + field=models.BooleanField(default=False, verbose_name='private to organizations'), ), migrations.AddField( - model_name="contest", - name="private_contestants", - field=models.ManyToManyField( - blank=True, - help_text="If private, only these users may see the contest", - related_name="_contest_private_contestants_+", - to="judge.Profile", - verbose_name="private contestants", - ), + model_name='contest', + name='private_contestants', + field=models.ManyToManyField(blank=True, help_text='If private, only these users may see the contest', related_name='_contest_private_contestants_+', to='judge.Profile', verbose_name='private contestants'), ), migrations.AlterField( - model_name="contest", - name="is_private", - field=models.BooleanField( - default=False, verbose_name="private to specific users" - ), + model_name='contest', + name='is_private', + field=models.BooleanField(default=False, verbose_name='private to specific users'), ), ] diff --git a/judge/migrations/0089_submission_to_contest.py b/judge/migrations/0089_submission_to_contest.py index f1cb283..5d464cb 100644 --- a/judge/migrations/0089_submission_to_contest.py +++ b/judge/migrations/0089_submission_to_contest.py @@ -7,31 +7,21 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0088_private_contests"), + ('judge', '0088_private_contests'), ] operations = [ migrations.AddField( - model_name="submission", - name="contest_object", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to="judge.Contest", - verbose_name="contest", - ), + model_name='submission', + name='contest_object', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='judge.Contest', verbose_name='contest'), ), - migrations.RunSQL( - """ + migrations.RunSQL(''' UPDATE `judge_submission` INNER JOIN `judge_contestsubmission` ON (`judge_submission`.`id` = `judge_contestsubmission`.`submission_id`) INNER JOIN `judge_contestparticipation` ON (`judge_contestsubmission`.`participation_id` = `judge_contestparticipation`.`id`) SET `judge_submission`.`contest_object_id` = `judge_contestparticipation`.`contest_id` - """, - migrations.RunSQL.noop, - ), + ''', migrations.RunSQL.noop), ] diff --git a/judge/migrations/0090_fix_contest_visibility.py b/judge/migrations/0090_fix_contest_visibility.py index 8e89949..10480fb 100644 --- a/judge/migrations/0090_fix_contest_visibility.py +++ b/judge/migrations/0090_fix_contest_visibility.py @@ -4,19 +4,16 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ("judge", "0089_submission_to_contest"), + ('judge', '0089_submission_to_contest'), ] operations = [ - migrations.RunSQL( - """ + migrations.RunSQL(''' UPDATE `judge_contest` SET `judge_contest`.`is_private` = 0, `judge_contest`.`is_organization_private` = 1 WHERE `judge_contest`.`is_private` = 1 - """, - """ + ''', ''' UPDATE `judge_contest` SET `judge_contest`.`is_private` = `judge_contest`.`is_organization_private` - """, - ), + '''), ] diff --git a/judge/migrations/0091_compiler_message_ansi2html.py b/judge/migrations/0091_compiler_message_ansi2html.py index 607ddee..7240f95 100644 --- a/judge/migrations/0091_compiler_message_ansi2html.py +++ b/judge/migrations/0091_compiler_message_ansi2html.py @@ -1,21 +1,21 @@ # -*- coding: utf-8 -*- import lxml.html as lh from django.db import migrations -from lxml_html_clean import clean_html +from lxml.html.clean import clean_html def strip_error_html(apps, schema_editor): - Submission = apps.get_model("judge", "Submission") + Submission = apps.get_model('judge', 'Submission') for sub in Submission.objects.filter(error__isnull=False).iterator(): if sub.error: sub.error = clean_html(lh.fromstring(sub.error)).text_content() - sub.save(update_fields=["error"]) + sub.save(update_fields=['error']) class Migration(migrations.Migration): dependencies = [ - ("judge", "0090_fix_contest_visibility"), + ('judge', '0090_fix_contest_visibility'), ] operations = [ diff --git a/judge/migrations/0092_contest_clone.py b/judge/migrations/0092_contest_clone.py index 0982ee8..0235513 100644 --- a/judge/migrations/0092_contest_clone.py +++ b/judge/migrations/0092_contest_clone.py @@ -6,24 +6,12 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ("judge", "0091_compiler_message_ansi2html"), + ('judge', '0091_compiler_message_ansi2html'), ] operations = [ migrations.AlterModelOptions( - name="contest", - options={ - "permissions": ( - ("see_private_contest", "See private contests"), - ("edit_own_contest", "Edit own contests"), - ("edit_all_contest", "Edit all contests"), - ("clone_contest", "Clone contest"), - ("contest_rating", "Rate contests"), - ("contest_access_code", "Contest access codes"), - ("create_private_contest", "Create private contests"), - ), - "verbose_name": "contest", - "verbose_name_plural": "contests", - }, + name='contest', + options={'permissions': (('see_private_contest', 'See private contests'), ('edit_own_contest', 'Edit own contests'), ('edit_all_contest', 'Edit all contests'), ('clone_contest', 'Clone contest'), ('contest_rating', 'Rate contests'), ('contest_access_code', 'Contest access codes'), ('create_private_contest', 'Create private contests')), 'verbose_name': 'contest', 'verbose_name_plural': 'contests'}, ), ] diff --git a/judge/migrations/0093_contest_moss.py b/judge/migrations/0093_contest_moss.py index 0dec938..10e2799 100644 --- a/judge/migrations/0093_contest_moss.py +++ b/judge/migrations/0093_contest_moss.py @@ -7,70 +7,39 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0092_contest_clone"), + ('judge', '0092_contest_clone'), ] operations = [ migrations.CreateModel( - name="ContestMoss", + name='ContestMoss', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("language", models.CharField(max_length=10)), - ("submission_count", models.PositiveIntegerField(default=0)), - ("url", models.URLField(blank=True, null=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(max_length=10)), + ('submission_count', models.PositiveIntegerField(default=0)), + ('url', models.URLField(blank=True, null=True)), ], options={ - "verbose_name": "contest moss result", - "verbose_name_plural": "contest moss results", + 'verbose_name': 'contest moss result', + 'verbose_name_plural': 'contest moss results', }, ), migrations.AlterModelOptions( - name="contest", - options={ - "permissions": ( - ("see_private_contest", "See private contests"), - ("edit_own_contest", "Edit own contests"), - ("edit_all_contest", "Edit all contests"), - ("clone_contest", "Clone contest"), - ("moss_contest", "MOSS contest"), - ("contest_rating", "Rate contests"), - ("contest_access_code", "Contest access codes"), - ("create_private_contest", "Create private contests"), - ), - "verbose_name": "contest", - "verbose_name_plural": "contests", - }, + name='contest', + options={'permissions': (('see_private_contest', 'See private contests'), ('edit_own_contest', 'Edit own contests'), ('edit_all_contest', 'Edit all contests'), ('clone_contest', 'Clone contest'), ('moss_contest', 'MOSS contest'), ('contest_rating', 'Rate contests'), ('contest_access_code', 'Contest access codes'), ('create_private_contest', 'Create private contests')), 'verbose_name': 'contest', 'verbose_name_plural': 'contests'}, ), migrations.AddField( - model_name="contestmoss", - name="contest", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="moss", - to="judge.Contest", - verbose_name="contest", - ), + model_name='contestmoss', + name='contest', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='moss', to='judge.Contest', verbose_name='contest'), ), migrations.AddField( - model_name="contestmoss", - name="problem", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="moss", - to="judge.Problem", - verbose_name="problem", - ), + model_name='contestmoss', + name='problem', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='moss', to='judge.Problem', verbose_name='problem'), ), migrations.AlterUniqueTogether( - name="contestmoss", - unique_together={("contest", "problem", "language")}, + name='contestmoss', + unique_together={('contest', 'problem', 'language')}, ), ] diff --git a/judge/migrations/0094_submissiontestcase_unique_together.py b/judge/migrations/0094_submissiontestcase_unique_together.py index c56cded..8341329 100644 --- a/judge/migrations/0094_submissiontestcase_unique_together.py +++ b/judge/migrations/0094_submissiontestcase_unique_together.py @@ -3,12 +3,12 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ("judge", "0093_contest_moss"), + ('judge', '0093_contest_moss'), ] operations = [ migrations.AlterUniqueTogether( - name="submissiontestcase", - unique_together={("submission", "case")}, + name='submissiontestcase', + unique_together={('submission', 'case')}, ), ] diff --git a/judge/migrations/0095_organization_logo_override.py b/judge/migrations/0095_organization_logo_override.py index 6db6499..b1d86eb 100644 --- a/judge/migrations/0095_organization_logo_override.py +++ b/judge/migrations/0095_organization_logo_override.py @@ -6,19 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0094_submissiontestcase_unique_together"), + ('judge', '0094_submissiontestcase_unique_together'), ] operations = [ migrations.AddField( - model_name="organization", - name="logo_override_image", - field=models.CharField( - blank=True, - default="", - help_text="This image will replace the default site logo for users viewing the organization.", - max_length=150, - verbose_name="Logo override image", - ), + model_name='organization', + name='logo_override_image', + field=models.CharField(blank=True, default='', help_text='This image will replace the default site logo for users viewing the organization.', max_length=150, verbose_name='Logo override image'), ), ] diff --git a/judge/migrations/0096_profile_language_set_default.py b/judge/migrations/0096_profile_language_set_default.py index 1468f78..a0a9453 100644 --- a/judge/migrations/0096_profile_language_set_default.py +++ b/judge/migrations/0096_profile_language_set_default.py @@ -7,26 +7,21 @@ import judge.models.runtime def create_python3(apps, schema_editor): - Language = apps.get_model("judge", "Language") - Language.objects.get_or_create(key="PY3", defaults={"name": "Python 3"})[0] + Language = apps.get_model('judge', 'Language') + Language.objects.get_or_create(key='PY3', defaults={'name': 'Python 3'})[0] class Migration(migrations.Migration): dependencies = [ - ("judge", "0095_organization_logo_override"), + ('judge', '0095_organization_logo_override'), ] operations = [ migrations.RunPython(create_python3, reverse_code=migrations.RunPython.noop), migrations.AlterField( - model_name="profile", - name="language", - field=models.ForeignKey( - default=judge.models.runtime.Language.get_default_language_pk, - on_delete=django.db.models.deletion.SET_DEFAULT, - to="judge.Language", - verbose_name="preferred language", - ), + model_name='profile', + name='language', + field=models.ForeignKey(default=judge.models.runtime.Language.get_default_language_pk, on_delete=django.db.models.deletion.SET_DEFAULT, to='judge.Language', verbose_name='preferred language'), ), ] diff --git a/judge/migrations/0097_participation_is_disqualified.py b/judge/migrations/0097_participation_is_disqualified.py index 3d3f367..537f83f 100644 --- a/judge/migrations/0097_participation_is_disqualified.py +++ b/judge/migrations/0097_participation_is_disqualified.py @@ -6,26 +6,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0096_profile_language_set_default"), + ('judge', '0096_profile_language_set_default'), ] operations = [ migrations.AddField( - model_name="contestparticipation", - name="is_disqualified", - field=models.BooleanField( - default=False, - help_text="Whether this participation is disqualified.", - verbose_name="is disqualified", - ), + model_name='contestparticipation', + name='is_disqualified', + field=models.BooleanField(default=False, help_text='Whether this participation is disqualified.', verbose_name='is disqualified'), ), migrations.AlterField( - model_name="contestparticipation", - name="virtual", - field=models.IntegerField( - default=0, - help_text="0 means non-virtual, otherwise the n-th virtual participation.", - verbose_name="virtual participation id", - ), + model_name='contestparticipation', + name='virtual', + field=models.IntegerField(default=0, help_text='0 means non-virtual, otherwise the n-th virtual participation.', verbose_name='virtual participation id'), ), ] diff --git a/judge/migrations/0098_auto_20200123_2136.py b/judge/migrations/0098_auto_20200123_2136.py index 3093cd3..333c67c 100644 --- a/judge/migrations/0098_auto_20200123_2136.py +++ b/judge/migrations/0098_auto_20200123_2136.py @@ -8,961 +8,148 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0097_participation_is_disqualified"), + ('judge', '0097_participation_is_disqualified'), ] operations = [ migrations.AlterField( - model_name="comment", - name="level", + model_name='comment', + name='level', field=models.PositiveIntegerField(editable=False), ), migrations.AlterField( - model_name="comment", - name="lft", + model_name='comment', + name='lft', field=models.PositiveIntegerField(editable=False), ), migrations.AlterField( - model_name="comment", - name="rght", + model_name='comment', + name='rght', field=models.PositiveIntegerField(editable=False), ), migrations.AlterField( - model_name="contest", - name="format_name", - field=models.CharField( - choices=[ - ("atcoder", "AtCoder"), - ("default", "Default"), - ("ecoo", "ECOO"), - ("ioi", "IOI"), - ], - default="default", - help_text="The contest format module to use.", - max_length=32, - verbose_name="contest format", - ), + model_name='contest', + name='format_name', + field=models.CharField(choices=[('atcoder', 'AtCoder'), ('default', 'Default'), ('ecoo', 'ECOO'), ('ioi', 'IOI')], default='default', help_text='The contest format module to use.', max_length=32, verbose_name='contest format'), ), migrations.AlterField( - model_name="judge", - name="auth_key", - field=models.CharField( - help_text="A key to authenticate this judge", - max_length=100, - verbose_name="authentication key", - ), + model_name='judge', + name='auth_key', + field=models.CharField(help_text='A key to authenticate this judge', max_length=100, verbose_name='authentication key'), ), migrations.AlterField( - model_name="language", - name="description", - field=models.TextField( - blank=True, - help_text="Use this field to inform users of quirks with your environment, additional restrictions, etc.", - verbose_name="language description", - ), + model_name='language', + name='description', + field=models.TextField(blank=True, help_text='Use this field to inform users of quirks with your environment, additional restrictions, etc.', verbose_name='language description'), ), migrations.AlterField( - model_name="languagelimit", - name="memory_limit", - field=models.IntegerField( - validators=[ - django.core.validators.MinValueValidator(0), - django.core.validators.MaxValueValidator(1048576), - ], - verbose_name="memory limit", - ), + model_name='languagelimit', + name='memory_limit', + field=models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1048576)], verbose_name='memory limit'), ), migrations.AlterField( - model_name="languagelimit", - name="time_limit", - field=models.FloatField( - validators=[ - django.core.validators.MinValueValidator(0), - django.core.validators.MaxValueValidator(60), - ], - verbose_name="time limit", - ), + model_name='languagelimit', + name='time_limit', + field=models.FloatField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(60)], verbose_name='time limit'), ), migrations.AlterField( - model_name="navigationbar", - name="level", + model_name='navigationbar', + name='level', field=models.PositiveIntegerField(editable=False), ), migrations.AlterField( - model_name="navigationbar", - name="lft", + model_name='navigationbar', + name='lft', field=models.PositiveIntegerField(editable=False), ), migrations.AlterField( - model_name="navigationbar", - name="rght", + model_name='navigationbar', + name='rght', field=models.PositiveIntegerField(editable=False), ), migrations.AlterField( - model_name="problem", - name="allowed_languages", - field=models.ManyToManyField( - help_text="List of allowed submission languages.", - to="judge.Language", - verbose_name="allowed languages", - ), + model_name='problem', + name='allowed_languages', + field=models.ManyToManyField(help_text='List of allowed submission languages.', to='judge.Language', verbose_name='allowed languages'), ), migrations.AlterField( - model_name="problem", - name="authors", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to edit the problem, and be listed as authors.", - related_name="authored_problems", - to="judge.Profile", - verbose_name="creators", - ), + model_name='problem', + name='authors', + field=models.ManyToManyField(blank=True, help_text='These users will be able to edit the problem, and be listed as authors.', related_name='authored_problems', to='judge.Profile', verbose_name='creators'), ), migrations.AlterField( - model_name="problem", - name="code", - field=models.CharField( - help_text="A short, unique code for the problem, used in the url after /problem/", - max_length=20, - unique=True, - validators=[ - django.core.validators.RegexValidator( - "^[a-z0-9]+$", "Problem code must be ^[a-z0-9]+$" - ) - ], - verbose_name="problem code", - ), + model_name='problem', + name='code', + field=models.CharField(help_text='A short, unique code for the problem, used in the url after /problem/', max_length=20, unique=True, validators=[django.core.validators.RegexValidator('^[a-z0-9]+$', 'Problem code must be ^[a-z0-9]+$')], verbose_name='problem code'), ), migrations.AlterField( - model_name="problem", - name="curators", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to edit the problem, but not be listed as authors.", - related_name="curated_problems", - to="judge.Profile", - verbose_name="curators", - ), + model_name='problem', + name='curators', + field=models.ManyToManyField(blank=True, help_text='These users will be able to edit the problem, but not be listed as authors.', related_name='curated_problems', to='judge.Profile', verbose_name='curators'), ), migrations.AlterField( - model_name="problem", - name="group", - field=models.ForeignKey( - help_text="The group of problem, shown under Category in the problem list.", - on_delete=django.db.models.deletion.CASCADE, - to="judge.ProblemGroup", - verbose_name="problem group", - ), + model_name='problem', + name='group', + field=models.ForeignKey(help_text='The group of problem, shown under Category in the problem list.', on_delete=django.db.models.deletion.CASCADE, to='judge.ProblemGroup', verbose_name='problem group'), ), migrations.AlterField( - model_name="problem", - name="is_manually_managed", - field=models.BooleanField( - db_index=True, - default=False, - help_text="Whether judges should be allowed to manage data or not.", - verbose_name="manually managed", - ), + model_name='problem', + name='is_manually_managed', + field=models.BooleanField(db_index=True, default=False, help_text='Whether judges should be allowed to manage data or not.', verbose_name='manually managed'), ), migrations.AlterField( - model_name="problem", - name="license", - field=models.ForeignKey( - blank=True, - help_text="The license under which this problem is published.", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to="judge.License", - ), + model_name='problem', + name='license', + field=models.ForeignKey(blank=True, help_text='The license under which this problem is published.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='judge.License'), ), migrations.AlterField( - model_name="problem", - name="memory_limit", - field=models.PositiveIntegerField( - help_text="The memory limit for this problem, in kilobytes (e.g. 64mb = 65536 kilobytes).", - validators=[ - django.core.validators.MinValueValidator(0), - django.core.validators.MaxValueValidator(1048576), - ], - verbose_name="memory limit", - ), + model_name='problem', + name='memory_limit', + field=models.PositiveIntegerField(help_text='The memory limit for this problem, in kilobytes (e.g. 64mb = 65536 kilobytes).', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1048576)], verbose_name='memory limit'), ), migrations.AlterField( - model_name="problem", - name="name", - field=models.CharField( - db_index=True, - help_text="The full name of the problem, as shown in the problem list.", - max_length=100, - verbose_name="problem name", - ), + model_name='problem', + name='name', + field=models.CharField(db_index=True, help_text='The full name of the problem, as shown in the problem list.', max_length=100, verbose_name='problem name'), ), migrations.AlterField( - model_name="problem", - name="points", - field=models.FloatField( - help_text="Points awarded for problem completion. Points are displayed with a 'p' suffix if partial.", - validators=[django.core.validators.MinValueValidator(0)], - verbose_name="points", - ), + model_name='problem', + name='points', + field=models.FloatField(help_text="Points awarded for problem completion. Points are displayed with a 'p' suffix if partial.", validators=[django.core.validators.MinValueValidator(0)], verbose_name='points'), ), migrations.AlterField( - model_name="problem", - name="testers", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to view the private problem, but not edit it.", - related_name="tested_problems", - to="judge.Profile", - verbose_name="testers", - ), + model_name='problem', + name='testers', + field=models.ManyToManyField(blank=True, help_text='These users will be able to view the private problem, but not edit it.', related_name='tested_problems', to='judge.Profile', verbose_name='testers'), ), migrations.AlterField( - model_name="problem", - name="time_limit", - field=models.FloatField( - help_text="The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) are supported.", - validators=[ - django.core.validators.MinValueValidator(0), - django.core.validators.MaxValueValidator(60), - ], - verbose_name="time limit", - ), + model_name='problem', + name='time_limit', + field=models.FloatField(help_text='The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) are supported.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(60)], verbose_name='time limit'), ), migrations.AlterField( - model_name="problem", - name="types", - field=models.ManyToManyField( - help_text="The type of problem, as shown on the problem's page.", - to="judge.ProblemType", - verbose_name="problem types", - ), + model_name='problem', + name='types', + field=models.ManyToManyField(help_text="The type of problem, as shown on the problem's page.", to='judge.ProblemType', verbose_name='problem types'), ), migrations.AlterField( - model_name="problemdata", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("checker.py", "Custom checker"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemdata', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('checker.py', 'Custom checker')], max_length=10, verbose_name='checker'), ), migrations.AlterField( - model_name="problemtestcase", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("checker.py", "Custom checker"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemtestcase', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('checker.py', 'Custom checker')], max_length=10, verbose_name='checker'), ), migrations.AlterField( - model_name="problemtranslation", - name="language", - field=models.CharField( - choices=[ - ("de", "German"), - ("en", "English"), - ("es", "Spanish"), - ("fr", "French"), - ("hr", "Croatian"), - ("hu", "Hungarian"), - ("ja", "Japanese"), - ("ko", "Korean"), - ("pt", "Brazilian Portuguese"), - ("ro", "Romanian"), - ("ru", "Russian"), - ("sr-latn", "Serbian (Latin)"), - ("tr", "Turkish"), - ("vi", "Vietnamese"), - ("zh-hans", "Simplified Chinese"), - ("zh-hant", "Traditional Chinese"), - ], - max_length=7, - verbose_name="language", - ), + model_name='problemtranslation', + name='language', + field=models.CharField(choices=[('de', 'German'), ('en', 'English'), ('es', 'Spanish'), ('fr', 'French'), ('hr', 'Croatian'), ('hu', 'Hungarian'), ('ja', 'Japanese'), ('ko', 'Korean'), ('pt', 'Brazilian Portuguese'), ('ro', 'Romanian'), ('ru', 'Russian'), ('sr-latn', 'Serbian (Latin)'), ('tr', 'Turkish'), ('vi', 'Vietnamese'), ('zh-hans', 'Simplified Chinese'), ('zh-hant', 'Traditional Chinese')], max_length=7, verbose_name='language'), ), migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="America/Mexico_City", - max_length=50, - verbose_name="location", - ), + model_name='profile', + name='timezone', + field=models.CharField(choices=[('Africa', [('Africa/Abidjan', 'Abidjan'), ('Africa/Accra', 'Accra'), ('Africa/Addis_Ababa', 'Addis_Ababa'), ('Africa/Algiers', 'Algiers'), ('Africa/Asmara', 'Asmara'), ('Africa/Asmera', 'Asmera'), ('Africa/Bamako', 'Bamako'), ('Africa/Bangui', 'Bangui'), ('Africa/Banjul', 'Banjul'), ('Africa/Bissau', 'Bissau'), ('Africa/Blantyre', 'Blantyre'), ('Africa/Brazzaville', 'Brazzaville'), ('Africa/Bujumbura', 'Bujumbura'), ('Africa/Cairo', 'Cairo'), ('Africa/Casablanca', 'Casablanca'), ('Africa/Ceuta', 'Ceuta'), ('Africa/Conakry', 'Conakry'), ('Africa/Dakar', 'Dakar'), ('Africa/Dar_es_Salaam', 'Dar_es_Salaam'), ('Africa/Djibouti', 'Djibouti'), ('Africa/Douala', 'Douala'), ('Africa/El_Aaiun', 'El_Aaiun'), ('Africa/Freetown', 'Freetown'), ('Africa/Gaborone', 'Gaborone'), ('Africa/Harare', 'Harare'), ('Africa/Johannesburg', 'Johannesburg'), ('Africa/Juba', 'Juba'), ('Africa/Kampala', 'Kampala'), ('Africa/Khartoum', 'Khartoum'), ('Africa/Kigali', 'Kigali'), ('Africa/Kinshasa', 'Kinshasa'), ('Africa/Lagos', 'Lagos'), ('Africa/Libreville', 'Libreville'), ('Africa/Lome', 'Lome'), ('Africa/Luanda', 'Luanda'), ('Africa/Lubumbashi', 'Lubumbashi'), ('Africa/Lusaka', 'Lusaka'), ('Africa/Malabo', 'Malabo'), ('Africa/Maputo', 'Maputo'), ('Africa/Maseru', 'Maseru'), ('Africa/Mbabane', 'Mbabane'), ('Africa/Mogadishu', 'Mogadishu'), ('Africa/Monrovia', 'Monrovia'), ('Africa/Nairobi', 'Nairobi'), ('Africa/Ndjamena', 'Ndjamena'), ('Africa/Niamey', 'Niamey'), ('Africa/Nouakchott', 'Nouakchott'), ('Africa/Ouagadougou', 'Ouagadougou'), ('Africa/Porto-Novo', 'Porto-Novo'), ('Africa/Sao_Tome', 'Sao_Tome'), ('Africa/Timbuktu', 'Timbuktu'), ('Africa/Tripoli', 'Tripoli'), ('Africa/Tunis', 'Tunis'), ('Africa/Windhoek', 'Windhoek')]), ('America', [('America/Adak', 'Adak'), ('America/Anchorage', 'Anchorage'), ('America/Anguilla', 'Anguilla'), ('America/Antigua', 'Antigua'), ('America/Araguaina', 'Araguaina'), ('America/Argentina/Buenos_Aires', 'Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'Argentina/Cordoba'), ('America/Argentina/Jujuy', 'Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'Argentina/Salta'), ('America/Argentina/San_Juan', 'Argentina/San_Juan'), ('America/Argentina/San_Luis', 'Argentina/San_Luis'), ('America/Argentina/Tucuman', 'Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'Argentina/Ushuaia'), ('America/Aruba', 'Aruba'), ('America/Asuncion', 'Asuncion'), ('America/Atikokan', 'Atikokan'), ('America/Atka', 'Atka'), ('America/Bahia', 'Bahia'), ('America/Bahia_Banderas', 'Bahia_Banderas'), ('America/Barbados', 'Barbados'), ('America/Belem', 'Belem'), ('America/Belize', 'Belize'), ('America/Blanc-Sablon', 'Blanc-Sablon'), ('America/Boa_Vista', 'Boa_Vista'), ('America/Bogota', 'Bogota'), ('America/Boise', 'Boise'), ('America/Buenos_Aires', 'Buenos_Aires'), ('America/Cambridge_Bay', 'Cambridge_Bay'), ('America/Campo_Grande', 'Campo_Grande'), ('America/Cancun', 'Cancun'), ('America/Caracas', 'Caracas'), ('America/Catamarca', 'Catamarca'), ('America/Cayenne', 'Cayenne'), ('America/Cayman', 'Cayman'), ('America/Chicago', 'Chicago'), ('America/Chihuahua', 'Chihuahua'), ('America/Coral_Harbour', 'Coral_Harbour'), ('America/Cordoba', 'Cordoba'), ('America/Costa_Rica', 'Costa_Rica'), ('America/Creston', 'Creston'), ('America/Cuiaba', 'Cuiaba'), ('America/Curacao', 'Curacao'), ('America/Danmarkshavn', 'Danmarkshavn'), ('America/Dawson', 'Dawson'), ('America/Dawson_Creek', 'Dawson_Creek'), ('America/Denver', 'Denver'), ('America/Detroit', 'Detroit'), ('America/Dominica', 'Dominica'), ('America/Edmonton', 'Edmonton'), ('America/Eirunepe', 'Eirunepe'), ('America/El_Salvador', 'El_Salvador'), ('America/Ensenada', 'Ensenada'), ('America/Fort_Nelson', 'Fort_Nelson'), ('America/Fort_Wayne', 'Fort_Wayne'), ('America/Fortaleza', 'Fortaleza'), ('America/Glace_Bay', 'Glace_Bay'), ('America/Godthab', 'Godthab'), ('America/Goose_Bay', 'Goose_Bay'), ('America/Grand_Turk', 'Grand_Turk'), ('America/Grenada', 'Grenada'), ('America/Guadeloupe', 'Guadeloupe'), ('America/Guatemala', 'Guatemala'), ('America/Guayaquil', 'Guayaquil'), ('America/Guyana', 'Guyana'), ('America/Halifax', 'Halifax'), ('America/Havana', 'Havana'), ('America/Hermosillo', 'Hermosillo'), ('America/Indiana/Indianapolis', 'Indiana/Indianapolis'), ('America/Indiana/Knox', 'Indiana/Knox'), ('America/Indiana/Marengo', 'Indiana/Marengo'), ('America/Indiana/Petersburg', 'Indiana/Petersburg'), ('America/Indiana/Tell_City', 'Indiana/Tell_City'), ('America/Indiana/Vevay', 'Indiana/Vevay'), ('America/Indiana/Vincennes', 'Indiana/Vincennes'), ('America/Indiana/Winamac', 'Indiana/Winamac'), ('America/Indianapolis', 'Indianapolis'), ('America/Inuvik', 'Inuvik'), ('America/Iqaluit', 'Iqaluit'), ('America/Jamaica', 'Jamaica'), ('America/Jujuy', 'Jujuy'), ('America/Juneau', 'Juneau'), ('America/Kentucky/Louisville', 'Kentucky/Louisville'), ('America/Kentucky/Monticello', 'Kentucky/Monticello'), ('America/Knox_IN', 'Knox_IN'), ('America/Kralendijk', 'Kralendijk'), ('America/La_Paz', 'La_Paz'), ('America/Lima', 'Lima'), ('America/Los_Angeles', 'Los_Angeles'), ('America/Louisville', 'Louisville'), ('America/Lower_Princes', 'Lower_Princes'), ('America/Maceio', 'Maceio'), ('America/Managua', 'Managua'), ('America/Manaus', 'Manaus'), ('America/Marigot', 'Marigot'), ('America/Martinique', 'Martinique'), ('America/Matamoros', 'Matamoros'), ('America/Mazatlan', 'Mazatlan'), ('America/Mendoza', 'Mendoza'), ('America/Menominee', 'Menominee'), ('America/Merida', 'Merida'), ('America/Metlakatla', 'Metlakatla'), ('America/Mexico_City', 'Mexico_City'), ('America/Miquelon', 'Miquelon'), ('America/Moncton', 'Moncton'), ('America/Monterrey', 'Monterrey'), ('America/Montevideo', 'Montevideo'), ('America/Montreal', 'Montreal'), ('America/Montserrat', 'Montserrat'), ('America/Nassau', 'Nassau'), ('America/New_York', 'New_York'), ('America/Nipigon', 'Nipigon'), ('America/Nome', 'Nome'), ('America/Noronha', 'Noronha'), ('America/North_Dakota/Beulah', 'North_Dakota/Beulah'), ('America/North_Dakota/Center', 'North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'North_Dakota/New_Salem'), ('America/Ojinaga', 'Ojinaga'), ('America/Panama', 'Panama'), ('America/Pangnirtung', 'Pangnirtung'), ('America/Paramaribo', 'Paramaribo'), ('America/Phoenix', 'Phoenix'), ('America/Port-au-Prince', 'Port-au-Prince'), ('America/Port_of_Spain', 'Port_of_Spain'), ('America/Porto_Acre', 'Porto_Acre'), ('America/Porto_Velho', 'Porto_Velho'), ('America/Puerto_Rico', 'Puerto_Rico'), ('America/Punta_Arenas', 'Punta_Arenas'), ('America/Rainy_River', 'Rainy_River'), ('America/Rankin_Inlet', 'Rankin_Inlet'), ('America/Recife', 'Recife'), ('America/Regina', 'Regina'), ('America/Resolute', 'Resolute'), ('America/Rio_Branco', 'Rio_Branco'), ('America/Rosario', 'Rosario'), ('America/Santa_Isabel', 'Santa_Isabel'), ('America/Santarem', 'Santarem'), ('America/Santiago', 'Santiago'), ('America/Santo_Domingo', 'Santo_Domingo'), ('America/Sao_Paulo', 'Sao_Paulo'), ('America/Scoresbysund', 'Scoresbysund'), ('America/Shiprock', 'Shiprock'), ('America/Sitka', 'Sitka'), ('America/St_Barthelemy', 'St_Barthelemy'), ('America/St_Johns', 'St_Johns'), ('America/St_Kitts', 'St_Kitts'), ('America/St_Lucia', 'St_Lucia'), ('America/St_Thomas', 'St_Thomas'), ('America/St_Vincent', 'St_Vincent'), ('America/Swift_Current', 'Swift_Current'), ('America/Tegucigalpa', 'Tegucigalpa'), ('America/Thule', 'Thule'), ('America/Thunder_Bay', 'Thunder_Bay'), ('America/Tijuana', 'Tijuana'), ('America/Toronto', 'Toronto'), ('America/Tortola', 'Tortola'), ('America/Vancouver', 'Vancouver'), ('America/Virgin', 'Virgin'), ('America/Whitehorse', 'Whitehorse'), ('America/Winnipeg', 'Winnipeg'), ('America/Yakutat', 'Yakutat'), ('America/Yellowknife', 'Yellowknife')]), ('Antarctica', [('Antarctica/Casey', 'Casey'), ('Antarctica/Davis', 'Davis'), ('Antarctica/DumontDUrville', 'DumontDUrville'), ('Antarctica/Macquarie', 'Macquarie'), ('Antarctica/Mawson', 'Mawson'), ('Antarctica/McMurdo', 'McMurdo'), ('Antarctica/Palmer', 'Palmer'), ('Antarctica/Rothera', 'Rothera'), ('Antarctica/South_Pole', 'South_Pole'), ('Antarctica/Syowa', 'Syowa'), ('Antarctica/Troll', 'Troll'), ('Antarctica/Vostok', 'Vostok')]), ('Arctic', [('Arctic/Longyearbyen', 'Longyearbyen')]), ('Asia', [('Asia/Aden', 'Aden'), ('Asia/Almaty', 'Almaty'), ('Asia/Amman', 'Amman'), ('Asia/Anadyr', 'Anadyr'), ('Asia/Aqtau', 'Aqtau'), ('Asia/Aqtobe', 'Aqtobe'), ('Asia/Ashgabat', 'Ashgabat'), ('Asia/Ashkhabad', 'Ashkhabad'), ('Asia/Atyrau', 'Atyrau'), ('Asia/Baghdad', 'Baghdad'), ('Asia/Bahrain', 'Bahrain'), ('Asia/Baku', 'Baku'), ('Asia/Bangkok', 'Bangkok'), ('Asia/Barnaul', 'Barnaul'), ('Asia/Beirut', 'Beirut'), ('Asia/Bishkek', 'Bishkek'), ('Asia/Brunei', 'Brunei'), ('Asia/Calcutta', 'Calcutta'), ('Asia/Chita', 'Chita'), ('Asia/Choibalsan', 'Choibalsan'), ('Asia/Chongqing', 'Chongqing'), ('Asia/Chungking', 'Chungking'), ('Asia/Colombo', 'Colombo'), ('Asia/Dacca', 'Dacca'), ('Asia/Damascus', 'Damascus'), ('Asia/Dhaka', 'Dhaka'), ('Asia/Dili', 'Dili'), ('Asia/Dubai', 'Dubai'), ('Asia/Dushanbe', 'Dushanbe'), ('Asia/Famagusta', 'Famagusta'), ('Asia/Gaza', 'Gaza'), ('Asia/Harbin', 'Harbin'), ('Asia/Hebron', 'Hebron'), ('Asia/Ho_Chi_Minh', 'Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Hong_Kong'), ('Asia/Hovd', 'Hovd'), ('Asia/Irkutsk', 'Irkutsk'), ('Asia/Istanbul', 'Istanbul'), ('Asia/Jakarta', 'Jakarta'), ('Asia/Jayapura', 'Jayapura'), ('Asia/Jerusalem', 'Jerusalem'), ('Asia/Kabul', 'Kabul'), ('Asia/Kamchatka', 'Kamchatka'), ('Asia/Karachi', 'Karachi'), ('Asia/Kashgar', 'Kashgar'), ('Asia/Kathmandu', 'Kathmandu'), ('Asia/Katmandu', 'Katmandu'), ('Asia/Khandyga', 'Khandyga'), ('Asia/Kolkata', 'Kolkata'), ('Asia/Krasnoyarsk', 'Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Kuala_Lumpur'), ('Asia/Kuching', 'Kuching'), ('Asia/Kuwait', 'Kuwait'), ('Asia/Macao', 'Macao'), ('Asia/Macau', 'Macau'), ('Asia/Magadan', 'Magadan'), ('Asia/Makassar', 'Makassar'), ('Asia/Manila', 'Manila'), ('Asia/Muscat', 'Muscat'), ('Asia/Nicosia', 'Nicosia'), ('Asia/Novokuznetsk', 'Novokuznetsk'), ('Asia/Novosibirsk', 'Novosibirsk'), ('Asia/Omsk', 'Omsk'), ('Asia/Oral', 'Oral'), ('Asia/Phnom_Penh', 'Phnom_Penh'), ('Asia/Pontianak', 'Pontianak'), ('Asia/Pyongyang', 'Pyongyang'), ('Asia/Qatar', 'Qatar'), ('Asia/Qostanay', 'Qostanay'), ('Asia/Qyzylorda', 'Qyzylorda'), ('Asia/Rangoon', 'Rangoon'), ('Asia/Riyadh', 'Riyadh'), ('Asia/Saigon', 'Saigon'), ('Asia/Sakhalin', 'Sakhalin'), ('Asia/Samarkand', 'Samarkand'), ('Asia/Seoul', 'Seoul'), ('Asia/Shanghai', 'Shanghai'), ('Asia/Singapore', 'Singapore'), ('Asia/Srednekolymsk', 'Srednekolymsk'), ('Asia/Taipei', 'Taipei'), ('Asia/Tashkent', 'Tashkent'), ('Asia/Tbilisi', 'Tbilisi'), ('Asia/Tehran', 'Tehran'), ('Asia/Tel_Aviv', 'Tel_Aviv'), ('Asia/Thimbu', 'Thimbu'), ('Asia/Thimphu', 'Thimphu'), ('Asia/Tokyo', 'Tokyo'), ('Asia/Tomsk', 'Tomsk'), ('Asia/Ujung_Pandang', 'Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Ulaanbaatar'), ('Asia/Ulan_Bator', 'Ulan_Bator'), ('Asia/Urumqi', 'Urumqi'), ('Asia/Ust-Nera', 'Ust-Nera'), ('Asia/Vientiane', 'Vientiane'), ('Asia/Vladivostok', 'Vladivostok'), ('Asia/Yakutsk', 'Yakutsk'), ('Asia/Yangon', 'Yangon'), ('Asia/Yekaterinburg', 'Yekaterinburg'), ('Asia/Yerevan', 'Yerevan')]), ('Atlantic', [('Atlantic/Azores', 'Azores'), ('Atlantic/Bermuda', 'Bermuda'), ('Atlantic/Canary', 'Canary'), ('Atlantic/Cape_Verde', 'Cape_Verde'), ('Atlantic/Faeroe', 'Faeroe'), ('Atlantic/Faroe', 'Faroe'), ('Atlantic/Jan_Mayen', 'Jan_Mayen'), ('Atlantic/Madeira', 'Madeira'), ('Atlantic/Reykjavik', 'Reykjavik'), ('Atlantic/South_Georgia', 'South_Georgia'), ('Atlantic/St_Helena', 'St_Helena'), ('Atlantic/Stanley', 'Stanley')]), ('Australia', [('Australia/ACT', 'ACT'), ('Australia/Adelaide', 'Adelaide'), ('Australia/Brisbane', 'Brisbane'), ('Australia/Broken_Hill', 'Broken_Hill'), ('Australia/Canberra', 'Canberra'), ('Australia/Currie', 'Currie'), ('Australia/Darwin', 'Darwin'), ('Australia/Eucla', 'Eucla'), ('Australia/Hobart', 'Hobart'), ('Australia/LHI', 'LHI'), ('Australia/Lindeman', 'Lindeman'), ('Australia/Lord_Howe', 'Lord_Howe'), ('Australia/Melbourne', 'Melbourne'), ('Australia/NSW', 'NSW'), ('Australia/North', 'North'), ('Australia/Perth', 'Perth'), ('Australia/Queensland', 'Queensland'), ('Australia/South', 'South'), ('Australia/Sydney', 'Sydney'), ('Australia/Tasmania', 'Tasmania'), ('Australia/Victoria', 'Victoria'), ('Australia/West', 'West'), ('Australia/Yancowinna', 'Yancowinna')]), ('Brazil', [('Brazil/Acre', 'Acre'), ('Brazil/DeNoronha', 'DeNoronha'), ('Brazil/East', 'East'), ('Brazil/West', 'West')]), ('Canada', [('Canada/Atlantic', 'Atlantic'), ('Canada/Central', 'Central'), ('Canada/Eastern', 'Eastern'), ('Canada/Mountain', 'Mountain'), ('Canada/Newfoundland', 'Newfoundland'), ('Canada/Pacific', 'Pacific'), ('Canada/Saskatchewan', 'Saskatchewan'), ('Canada/Yukon', 'Yukon')]), ('Chile', [('Chile/Continental', 'Continental'), ('Chile/EasterIsland', 'EasterIsland')]), ('Etc', [('Etc/Greenwich', 'Greenwich'), ('Etc/UCT', 'UCT'), ('Etc/UTC', 'UTC'), ('Etc/Universal', 'Universal'), ('Etc/Zulu', 'Zulu')]), ('Europe', [('Europe/Amsterdam', 'Amsterdam'), ('Europe/Andorra', 'Andorra'), ('Europe/Astrakhan', 'Astrakhan'), ('Europe/Athens', 'Athens'), ('Europe/Belfast', 'Belfast'), ('Europe/Belgrade', 'Belgrade'), ('Europe/Berlin', 'Berlin'), ('Europe/Bratislava', 'Bratislava'), ('Europe/Brussels', 'Brussels'), ('Europe/Bucharest', 'Bucharest'), ('Europe/Budapest', 'Budapest'), ('Europe/Busingen', 'Busingen'), ('Europe/Chisinau', 'Chisinau'), ('Europe/Copenhagen', 'Copenhagen'), ('Europe/Dublin', 'Dublin'), ('Europe/Gibraltar', 'Gibraltar'), ('Europe/Guernsey', 'Guernsey'), ('Europe/Helsinki', 'Helsinki'), ('Europe/Isle_of_Man', 'Isle_of_Man'), ('Europe/Istanbul', 'Istanbul'), ('Europe/Jersey', 'Jersey'), ('Europe/Kaliningrad', 'Kaliningrad'), ('Europe/Kiev', 'Kiev'), ('Europe/Kirov', 'Kirov'), ('Europe/Lisbon', 'Lisbon'), ('Europe/Ljubljana', 'Ljubljana'), ('Europe/London', 'London'), ('Europe/Luxembourg', 'Luxembourg'), ('Europe/Madrid', 'Madrid'), ('Europe/Malta', 'Malta'), ('Europe/Mariehamn', 'Mariehamn'), ('Europe/Minsk', 'Minsk'), ('Europe/Monaco', 'Monaco'), ('Europe/Moscow', 'Moscow'), ('Europe/Nicosia', 'Nicosia'), ('Europe/Oslo', 'Oslo'), ('Europe/Paris', 'Paris'), ('Europe/Podgorica', 'Podgorica'), ('Europe/Prague', 'Prague'), ('Europe/Riga', 'Riga'), ('Europe/Rome', 'Rome'), ('Europe/Samara', 'Samara'), ('Europe/San_Marino', 'San_Marino'), ('Europe/Sarajevo', 'Sarajevo'), ('Europe/Saratov', 'Saratov'), ('Europe/Simferopol', 'Simferopol'), ('Europe/Skopje', 'Skopje'), ('Europe/Sofia', 'Sofia'), ('Europe/Stockholm', 'Stockholm'), ('Europe/Tallinn', 'Tallinn'), ('Europe/Tirane', 'Tirane'), ('Europe/Tiraspol', 'Tiraspol'), ('Europe/Ulyanovsk', 'Ulyanovsk'), ('Europe/Uzhgorod', 'Uzhgorod'), ('Europe/Vaduz', 'Vaduz'), ('Europe/Vatican', 'Vatican'), ('Europe/Vienna', 'Vienna'), ('Europe/Vilnius', 'Vilnius'), ('Europe/Volgograd', 'Volgograd'), ('Europe/Warsaw', 'Warsaw'), ('Europe/Zagreb', 'Zagreb'), ('Europe/Zaporozhye', 'Zaporozhye'), ('Europe/Zurich', 'Zurich')]), ('Indian', [('Indian/Antananarivo', 'Antananarivo'), ('Indian/Chagos', 'Chagos'), ('Indian/Christmas', 'Christmas'), ('Indian/Cocos', 'Cocos'), ('Indian/Comoro', 'Comoro'), ('Indian/Kerguelen', 'Kerguelen'), ('Indian/Mahe', 'Mahe'), ('Indian/Maldives', 'Maldives'), ('Indian/Mauritius', 'Mauritius'), ('Indian/Mayotte', 'Mayotte'), ('Indian/Reunion', 'Reunion')]), ('Mexico', [('Mexico/BajaNorte', 'BajaNorte'), ('Mexico/BajaSur', 'BajaSur'), ('Mexico/General', 'General')]), ('Other', [('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')]), ('Pacific', [('Pacific/Apia', 'Apia'), ('Pacific/Auckland', 'Auckland'), ('Pacific/Bougainville', 'Bougainville'), ('Pacific/Chatham', 'Chatham'), ('Pacific/Chuuk', 'Chuuk'), ('Pacific/Easter', 'Easter'), ('Pacific/Efate', 'Efate'), ('Pacific/Enderbury', 'Enderbury'), ('Pacific/Fakaofo', 'Fakaofo'), ('Pacific/Fiji', 'Fiji'), ('Pacific/Funafuti', 'Funafuti'), ('Pacific/Galapagos', 'Galapagos'), ('Pacific/Gambier', 'Gambier'), ('Pacific/Guadalcanal', 'Guadalcanal'), ('Pacific/Guam', 'Guam'), ('Pacific/Honolulu', 'Honolulu'), ('Pacific/Johnston', 'Johnston'), ('Pacific/Kiritimati', 'Kiritimati'), ('Pacific/Kosrae', 'Kosrae'), ('Pacific/Kwajalein', 'Kwajalein'), ('Pacific/Majuro', 'Majuro'), ('Pacific/Marquesas', 'Marquesas'), ('Pacific/Midway', 'Midway'), ('Pacific/Nauru', 'Nauru'), ('Pacific/Niue', 'Niue'), ('Pacific/Norfolk', 'Norfolk'), ('Pacific/Noumea', 'Noumea'), ('Pacific/Pago_Pago', 'Pago_Pago'), ('Pacific/Palau', 'Palau'), ('Pacific/Pitcairn', 'Pitcairn'), ('Pacific/Pohnpei', 'Pohnpei'), ('Pacific/Ponape', 'Ponape'), ('Pacific/Port_Moresby', 'Port_Moresby'), ('Pacific/Rarotonga', 'Rarotonga'), ('Pacific/Saipan', 'Saipan'), ('Pacific/Samoa', 'Samoa'), ('Pacific/Tahiti', 'Tahiti'), ('Pacific/Tarawa', 'Tarawa'), ('Pacific/Tongatapu', 'Tongatapu'), ('Pacific/Truk', 'Truk'), ('Pacific/Wake', 'Wake'), ('Pacific/Wallis', 'Wallis'), ('Pacific/Yap', 'Yap')]), ('US', [('US/Alaska', 'Alaska'), ('US/Aleutian', 'Aleutian'), ('US/Arizona', 'Arizona'), ('US/Central', 'Central'), ('US/East-Indiana', 'East-Indiana'), ('US/Eastern', 'Eastern'), ('US/Hawaii', 'Hawaii'), ('US/Indiana-Starke', 'Indiana-Starke'), ('US/Michigan', 'Michigan'), ('US/Mountain', 'Mountain'), ('US/Pacific', 'Pacific'), ('US/Samoa', 'Samoa')])], default='America/Mexico_City', max_length=50, verbose_name='location'), ), ] diff --git a/judge/migrations/0099_custom_checker.py b/judge/migrations/0099_custom_checker.py index 24ab46b..b2552b7 100644 --- a/judge/migrations/0099_custom_checker.py +++ b/judge/migrations/0099_custom_checker.py @@ -8,59 +8,23 @@ import judge.utils.problem_data class Migration(migrations.Migration): dependencies = [ - ("judge", "0098_auto_20200123_2136"), + ('judge', '0098_auto_20200123_2136'), ] operations = [ migrations.AddField( - model_name="problemdata", - name="custom_checker", - field=models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - verbose_name="custom checker file", - ), + model_name='problemdata', + name='custom_checker', + field=models.FileField(blank=True, null=True, storage=judge.utils.problem_data.ProblemDataStorage(), upload_to=judge.models.problem_data.problem_directory_file, verbose_name='custom checker file'), ), migrations.AlterField( - model_name="problemdata", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemdata', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('custom', 'Custom checker')], max_length=10, verbose_name='checker'), ), migrations.AlterField( - model_name="problemtestcase", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemtestcase', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('custom', 'Custom checker')], max_length=10, verbose_name='checker'), ), ] diff --git a/judge/migrations/0100_auto_20200127_0059.py b/judge/migrations/0100_auto_20200127_0059.py index 5d1cd91..e070f7a 100644 --- a/judge/migrations/0100_auto_20200127_0059.py +++ b/judge/migrations/0100_auto_20200127_0059.py @@ -9,24 +9,13 @@ import judge.utils.problem_data class Migration(migrations.Migration): dependencies = [ - ("judge", "0099_custom_checker"), + ('judge', '0099_custom_checker'), ] operations = [ migrations.AlterField( - model_name="problemdata", - name="custom_checker", - field=models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - validators=[ - django.core.validators.FileExtensionValidator( - allowed_extensions=["py"] - ) - ], - verbose_name="custom checker file", - ), + model_name='problemdata', + name='custom_checker', + field=models.FileField(blank=True, null=True, storage=judge.utils.problem_data.ProblemDataStorage(), upload_to=judge.models.problem_data.problem_directory_file, validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['py'])], verbose_name='custom checker file'), ), ] diff --git a/judge/migrations/0101_custom_validator.py b/judge/migrations/0101_custom_validator.py index 8575ce1..842006c 100644 --- a/judge/migrations/0101_custom_validator.py +++ b/judge/migrations/0101_custom_validator.py @@ -9,727 +9,28 @@ import judge.utils.problem_data class Migration(migrations.Migration): dependencies = [ - ("judge", "0100_auto_20200127_0059"), + ('judge', '0100_auto_20200127_0059'), ] operations = [ migrations.AddField( - model_name="problemdata", - name="custom_valid", - field=models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - validators=[ - django.core.validators.FileExtensionValidator( - allowed_extensions=["cpp"] - ) - ], - verbose_name="custom validator file", - ), + model_name='problemdata', + name='custom_valid', + field=models.FileField(blank=True, null=True, storage=judge.utils.problem_data.ProblemDataStorage(), upload_to=judge.models.problem_data.problem_directory_file, validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['cpp'])], verbose_name='custom validator file'), ), migrations.AlterField( - model_name="problemdata", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker"), - ("custom_valid", "Custom Validator"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemdata', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('custom', 'Custom checker'), ('custom_valid', 'Custom Validator')], max_length=10, verbose_name='checker'), ), migrations.AlterField( - model_name="problemtestcase", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker"), - ("custom_valid", "Custom Validator"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemtestcase', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('custom', 'Custom checker'), ('custom_valid', 'Custom Validator')], max_length=10, verbose_name='checker'), ), migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), + model_name='profile', + name='timezone', + field=models.CharField(choices=[('Africa', [('Africa/Abidjan', 'Abidjan'), ('Africa/Accra', 'Accra'), ('Africa/Addis_Ababa', 'Addis_Ababa'), ('Africa/Algiers', 'Algiers'), ('Africa/Asmara', 'Asmara'), ('Africa/Asmera', 'Asmera'), ('Africa/Bamako', 'Bamako'), ('Africa/Bangui', 'Bangui'), ('Africa/Banjul', 'Banjul'), ('Africa/Bissau', 'Bissau'), ('Africa/Blantyre', 'Blantyre'), ('Africa/Brazzaville', 'Brazzaville'), ('Africa/Bujumbura', 'Bujumbura'), ('Africa/Cairo', 'Cairo'), ('Africa/Casablanca', 'Casablanca'), ('Africa/Ceuta', 'Ceuta'), ('Africa/Conakry', 'Conakry'), ('Africa/Dakar', 'Dakar'), ('Africa/Dar_es_Salaam', 'Dar_es_Salaam'), ('Africa/Djibouti', 'Djibouti'), ('Africa/Douala', 'Douala'), ('Africa/El_Aaiun', 'El_Aaiun'), ('Africa/Freetown', 'Freetown'), ('Africa/Gaborone', 'Gaborone'), ('Africa/Harare', 'Harare'), ('Africa/Johannesburg', 'Johannesburg'), ('Africa/Juba', 'Juba'), ('Africa/Kampala', 'Kampala'), ('Africa/Khartoum', 'Khartoum'), ('Africa/Kigali', 'Kigali'), ('Africa/Kinshasa', 'Kinshasa'), ('Africa/Lagos', 'Lagos'), ('Africa/Libreville', 'Libreville'), ('Africa/Lome', 'Lome'), ('Africa/Luanda', 'Luanda'), ('Africa/Lubumbashi', 'Lubumbashi'), ('Africa/Lusaka', 'Lusaka'), ('Africa/Malabo', 'Malabo'), ('Africa/Maputo', 'Maputo'), ('Africa/Maseru', 'Maseru'), ('Africa/Mbabane', 'Mbabane'), ('Africa/Mogadishu', 'Mogadishu'), ('Africa/Monrovia', 'Monrovia'), ('Africa/Nairobi', 'Nairobi'), ('Africa/Ndjamena', 'Ndjamena'), ('Africa/Niamey', 'Niamey'), ('Africa/Nouakchott', 'Nouakchott'), ('Africa/Ouagadougou', 'Ouagadougou'), ('Africa/Porto-Novo', 'Porto-Novo'), ('Africa/Sao_Tome', 'Sao_Tome'), ('Africa/Timbuktu', 'Timbuktu'), ('Africa/Tripoli', 'Tripoli'), ('Africa/Tunis', 'Tunis'), ('Africa/Windhoek', 'Windhoek')]), ('America', [('America/Adak', 'Adak'), ('America/Anchorage', 'Anchorage'), ('America/Anguilla', 'Anguilla'), ('America/Antigua', 'Antigua'), ('America/Araguaina', 'Araguaina'), ('America/Argentina/Buenos_Aires', 'Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'Argentina/Cordoba'), ('America/Argentina/Jujuy', 'Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'Argentina/Salta'), ('America/Argentina/San_Juan', 'Argentina/San_Juan'), ('America/Argentina/San_Luis', 'Argentina/San_Luis'), ('America/Argentina/Tucuman', 'Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'Argentina/Ushuaia'), ('America/Aruba', 'Aruba'), ('America/Asuncion', 'Asuncion'), ('America/Atikokan', 'Atikokan'), ('America/Atka', 'Atka'), ('America/Bahia', 'Bahia'), ('America/Bahia_Banderas', 'Bahia_Banderas'), ('America/Barbados', 'Barbados'), ('America/Belem', 'Belem'), ('America/Belize', 'Belize'), ('America/Blanc-Sablon', 'Blanc-Sablon'), ('America/Boa_Vista', 'Boa_Vista'), ('America/Bogota', 'Bogota'), ('America/Boise', 'Boise'), ('America/Buenos_Aires', 'Buenos_Aires'), ('America/Cambridge_Bay', 'Cambridge_Bay'), ('America/Campo_Grande', 'Campo_Grande'), ('America/Cancun', 'Cancun'), ('America/Caracas', 'Caracas'), ('America/Catamarca', 'Catamarca'), ('America/Cayenne', 'Cayenne'), ('America/Cayman', 'Cayman'), ('America/Chicago', 'Chicago'), ('America/Chihuahua', 'Chihuahua'), ('America/Coral_Harbour', 'Coral_Harbour'), ('America/Cordoba', 'Cordoba'), ('America/Costa_Rica', 'Costa_Rica'), ('America/Creston', 'Creston'), ('America/Cuiaba', 'Cuiaba'), ('America/Curacao', 'Curacao'), ('America/Danmarkshavn', 'Danmarkshavn'), ('America/Dawson', 'Dawson'), ('America/Dawson_Creek', 'Dawson_Creek'), ('America/Denver', 'Denver'), ('America/Detroit', 'Detroit'), ('America/Dominica', 'Dominica'), ('America/Edmonton', 'Edmonton'), ('America/Eirunepe', 'Eirunepe'), ('America/El_Salvador', 'El_Salvador'), ('America/Ensenada', 'Ensenada'), ('America/Fort_Nelson', 'Fort_Nelson'), ('America/Fort_Wayne', 'Fort_Wayne'), ('America/Fortaleza', 'Fortaleza'), ('America/Glace_Bay', 'Glace_Bay'), ('America/Godthab', 'Godthab'), ('America/Goose_Bay', 'Goose_Bay'), ('America/Grand_Turk', 'Grand_Turk'), ('America/Grenada', 'Grenada'), ('America/Guadeloupe', 'Guadeloupe'), ('America/Guatemala', 'Guatemala'), ('America/Guayaquil', 'Guayaquil'), ('America/Guyana', 'Guyana'), ('America/Halifax', 'Halifax'), ('America/Havana', 'Havana'), ('America/Hermosillo', 'Hermosillo'), ('America/Indiana/Indianapolis', 'Indiana/Indianapolis'), ('America/Indiana/Knox', 'Indiana/Knox'), ('America/Indiana/Marengo', 'Indiana/Marengo'), ('America/Indiana/Petersburg', 'Indiana/Petersburg'), ('America/Indiana/Tell_City', 'Indiana/Tell_City'), ('America/Indiana/Vevay', 'Indiana/Vevay'), ('America/Indiana/Vincennes', 'Indiana/Vincennes'), ('America/Indiana/Winamac', 'Indiana/Winamac'), ('America/Indianapolis', 'Indianapolis'), ('America/Inuvik', 'Inuvik'), ('America/Iqaluit', 'Iqaluit'), ('America/Jamaica', 'Jamaica'), ('America/Jujuy', 'Jujuy'), ('America/Juneau', 'Juneau'), ('America/Kentucky/Louisville', 'Kentucky/Louisville'), ('America/Kentucky/Monticello', 'Kentucky/Monticello'), ('America/Knox_IN', 'Knox_IN'), ('America/Kralendijk', 'Kralendijk'), ('America/La_Paz', 'La_Paz'), ('America/Lima', 'Lima'), ('America/Los_Angeles', 'Los_Angeles'), ('America/Louisville', 'Louisville'), ('America/Lower_Princes', 'Lower_Princes'), ('America/Maceio', 'Maceio'), ('America/Managua', 'Managua'), ('America/Manaus', 'Manaus'), ('America/Marigot', 'Marigot'), ('America/Martinique', 'Martinique'), ('America/Matamoros', 'Matamoros'), ('America/Mazatlan', 'Mazatlan'), ('America/Mendoza', 'Mendoza'), ('America/Menominee', 'Menominee'), ('America/Merida', 'Merida'), ('America/Metlakatla', 'Metlakatla'), ('America/Mexico_City', 'Mexico_City'), ('America/Miquelon', 'Miquelon'), ('America/Moncton', 'Moncton'), ('America/Monterrey', 'Monterrey'), ('America/Montevideo', 'Montevideo'), ('America/Montreal', 'Montreal'), ('America/Montserrat', 'Montserrat'), ('America/Nassau', 'Nassau'), ('America/New_York', 'New_York'), ('America/Nipigon', 'Nipigon'), ('America/Nome', 'Nome'), ('America/Noronha', 'Noronha'), ('America/North_Dakota/Beulah', 'North_Dakota/Beulah'), ('America/North_Dakota/Center', 'North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'North_Dakota/New_Salem'), ('America/Ojinaga', 'Ojinaga'), ('America/Panama', 'Panama'), ('America/Pangnirtung', 'Pangnirtung'), ('America/Paramaribo', 'Paramaribo'), ('America/Phoenix', 'Phoenix'), ('America/Port-au-Prince', 'Port-au-Prince'), ('America/Port_of_Spain', 'Port_of_Spain'), ('America/Porto_Acre', 'Porto_Acre'), ('America/Porto_Velho', 'Porto_Velho'), ('America/Puerto_Rico', 'Puerto_Rico'), ('America/Punta_Arenas', 'Punta_Arenas'), ('America/Rainy_River', 'Rainy_River'), ('America/Rankin_Inlet', 'Rankin_Inlet'), ('America/Recife', 'Recife'), ('America/Regina', 'Regina'), ('America/Resolute', 'Resolute'), ('America/Rio_Branco', 'Rio_Branco'), ('America/Rosario', 'Rosario'), ('America/Santa_Isabel', 'Santa_Isabel'), ('America/Santarem', 'Santarem'), ('America/Santiago', 'Santiago'), ('America/Santo_Domingo', 'Santo_Domingo'), ('America/Sao_Paulo', 'Sao_Paulo'), ('America/Scoresbysund', 'Scoresbysund'), ('America/Shiprock', 'Shiprock'), ('America/Sitka', 'Sitka'), ('America/St_Barthelemy', 'St_Barthelemy'), ('America/St_Johns', 'St_Johns'), ('America/St_Kitts', 'St_Kitts'), ('America/St_Lucia', 'St_Lucia'), ('America/St_Thomas', 'St_Thomas'), ('America/St_Vincent', 'St_Vincent'), ('America/Swift_Current', 'Swift_Current'), ('America/Tegucigalpa', 'Tegucigalpa'), ('America/Thule', 'Thule'), ('America/Thunder_Bay', 'Thunder_Bay'), ('America/Tijuana', 'Tijuana'), ('America/Toronto', 'Toronto'), ('America/Tortola', 'Tortola'), ('America/Vancouver', 'Vancouver'), ('America/Virgin', 'Virgin'), ('America/Whitehorse', 'Whitehorse'), ('America/Winnipeg', 'Winnipeg'), ('America/Yakutat', 'Yakutat'), ('America/Yellowknife', 'Yellowknife')]), ('Antarctica', [('Antarctica/Casey', 'Casey'), ('Antarctica/Davis', 'Davis'), ('Antarctica/DumontDUrville', 'DumontDUrville'), ('Antarctica/Macquarie', 'Macquarie'), ('Antarctica/Mawson', 'Mawson'), ('Antarctica/McMurdo', 'McMurdo'), ('Antarctica/Palmer', 'Palmer'), ('Antarctica/Rothera', 'Rothera'), ('Antarctica/South_Pole', 'South_Pole'), ('Antarctica/Syowa', 'Syowa'), ('Antarctica/Troll', 'Troll'), ('Antarctica/Vostok', 'Vostok')]), ('Arctic', [('Arctic/Longyearbyen', 'Longyearbyen')]), ('Asia', [('Asia/Aden', 'Aden'), ('Asia/Almaty', 'Almaty'), ('Asia/Amman', 'Amman'), ('Asia/Anadyr', 'Anadyr'), ('Asia/Aqtau', 'Aqtau'), ('Asia/Aqtobe', 'Aqtobe'), ('Asia/Ashgabat', 'Ashgabat'), ('Asia/Ashkhabad', 'Ashkhabad'), ('Asia/Atyrau', 'Atyrau'), ('Asia/Baghdad', 'Baghdad'), ('Asia/Bahrain', 'Bahrain'), ('Asia/Baku', 'Baku'), ('Asia/Bangkok', 'Bangkok'), ('Asia/Barnaul', 'Barnaul'), ('Asia/Beirut', 'Beirut'), ('Asia/Bishkek', 'Bishkek'), ('Asia/Brunei', 'Brunei'), ('Asia/Calcutta', 'Calcutta'), ('Asia/Chita', 'Chita'), ('Asia/Choibalsan', 'Choibalsan'), ('Asia/Chongqing', 'Chongqing'), ('Asia/Chungking', 'Chungking'), ('Asia/Colombo', 'Colombo'), ('Asia/Dacca', 'Dacca'), ('Asia/Damascus', 'Damascus'), ('Asia/Dhaka', 'Dhaka'), ('Asia/Dili', 'Dili'), ('Asia/Dubai', 'Dubai'), ('Asia/Dushanbe', 'Dushanbe'), ('Asia/Famagusta', 'Famagusta'), ('Asia/Gaza', 'Gaza'), ('Asia/Harbin', 'Harbin'), ('Asia/Hebron', 'Hebron'), ('Asia/Ho_Chi_Minh', 'Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Hong_Kong'), ('Asia/Hovd', 'Hovd'), ('Asia/Irkutsk', 'Irkutsk'), ('Asia/Istanbul', 'Istanbul'), ('Asia/Jakarta', 'Jakarta'), ('Asia/Jayapura', 'Jayapura'), ('Asia/Jerusalem', 'Jerusalem'), ('Asia/Kabul', 'Kabul'), ('Asia/Kamchatka', 'Kamchatka'), ('Asia/Karachi', 'Karachi'), ('Asia/Kashgar', 'Kashgar'), ('Asia/Kathmandu', 'Kathmandu'), ('Asia/Katmandu', 'Katmandu'), ('Asia/Khandyga', 'Khandyga'), ('Asia/Kolkata', 'Kolkata'), ('Asia/Krasnoyarsk', 'Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Kuala_Lumpur'), ('Asia/Kuching', 'Kuching'), ('Asia/Kuwait', 'Kuwait'), ('Asia/Macao', 'Macao'), ('Asia/Macau', 'Macau'), ('Asia/Magadan', 'Magadan'), ('Asia/Makassar', 'Makassar'), ('Asia/Manila', 'Manila'), ('Asia/Muscat', 'Muscat'), ('Asia/Nicosia', 'Nicosia'), ('Asia/Novokuznetsk', 'Novokuznetsk'), ('Asia/Novosibirsk', 'Novosibirsk'), ('Asia/Omsk', 'Omsk'), ('Asia/Oral', 'Oral'), ('Asia/Phnom_Penh', 'Phnom_Penh'), ('Asia/Pontianak', 'Pontianak'), ('Asia/Pyongyang', 'Pyongyang'), ('Asia/Qatar', 'Qatar'), ('Asia/Qostanay', 'Qostanay'), ('Asia/Qyzylorda', 'Qyzylorda'), ('Asia/Rangoon', 'Rangoon'), ('Asia/Riyadh', 'Riyadh'), ('Asia/Saigon', 'Saigon'), ('Asia/Sakhalin', 'Sakhalin'), ('Asia/Samarkand', 'Samarkand'), ('Asia/Seoul', 'Seoul'), ('Asia/Shanghai', 'Shanghai'), ('Asia/Singapore', 'Singapore'), ('Asia/Srednekolymsk', 'Srednekolymsk'), ('Asia/Taipei', 'Taipei'), ('Asia/Tashkent', 'Tashkent'), ('Asia/Tbilisi', 'Tbilisi'), ('Asia/Tehran', 'Tehran'), ('Asia/Tel_Aviv', 'Tel_Aviv'), ('Asia/Thimbu', 'Thimbu'), ('Asia/Thimphu', 'Thimphu'), ('Asia/Tokyo', 'Tokyo'), ('Asia/Tomsk', 'Tomsk'), ('Asia/Ujung_Pandang', 'Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Ulaanbaatar'), ('Asia/Ulan_Bator', 'Ulan_Bator'), ('Asia/Urumqi', 'Urumqi'), ('Asia/Ust-Nera', 'Ust-Nera'), ('Asia/Vientiane', 'Vientiane'), ('Asia/Vladivostok', 'Vladivostok'), ('Asia/Yakutsk', 'Yakutsk'), ('Asia/Yangon', 'Yangon'), ('Asia/Yekaterinburg', 'Yekaterinburg'), ('Asia/Yerevan', 'Yerevan')]), ('Atlantic', [('Atlantic/Azores', 'Azores'), ('Atlantic/Bermuda', 'Bermuda'), ('Atlantic/Canary', 'Canary'), ('Atlantic/Cape_Verde', 'Cape_Verde'), ('Atlantic/Faeroe', 'Faeroe'), ('Atlantic/Faroe', 'Faroe'), ('Atlantic/Jan_Mayen', 'Jan_Mayen'), ('Atlantic/Madeira', 'Madeira'), ('Atlantic/Reykjavik', 'Reykjavik'), ('Atlantic/South_Georgia', 'South_Georgia'), ('Atlantic/St_Helena', 'St_Helena'), ('Atlantic/Stanley', 'Stanley')]), ('Australia', [('Australia/ACT', 'ACT'), ('Australia/Adelaide', 'Adelaide'), ('Australia/Brisbane', 'Brisbane'), ('Australia/Broken_Hill', 'Broken_Hill'), ('Australia/Canberra', 'Canberra'), ('Australia/Currie', 'Currie'), ('Australia/Darwin', 'Darwin'), ('Australia/Eucla', 'Eucla'), ('Australia/Hobart', 'Hobart'), ('Australia/LHI', 'LHI'), ('Australia/Lindeman', 'Lindeman'), ('Australia/Lord_Howe', 'Lord_Howe'), ('Australia/Melbourne', 'Melbourne'), ('Australia/NSW', 'NSW'), ('Australia/North', 'North'), ('Australia/Perth', 'Perth'), ('Australia/Queensland', 'Queensland'), ('Australia/South', 'South'), ('Australia/Sydney', 'Sydney'), ('Australia/Tasmania', 'Tasmania'), ('Australia/Victoria', 'Victoria'), ('Australia/West', 'West'), ('Australia/Yancowinna', 'Yancowinna')]), ('Brazil', [('Brazil/Acre', 'Acre'), ('Brazil/DeNoronha', 'DeNoronha'), ('Brazil/East', 'East'), ('Brazil/West', 'West')]), ('Canada', [('Canada/Atlantic', 'Atlantic'), ('Canada/Central', 'Central'), ('Canada/Eastern', 'Eastern'), ('Canada/Mountain', 'Mountain'), ('Canada/Newfoundland', 'Newfoundland'), ('Canada/Pacific', 'Pacific'), ('Canada/Saskatchewan', 'Saskatchewan'), ('Canada/Yukon', 'Yukon')]), ('Chile', [('Chile/Continental', 'Continental'), ('Chile/EasterIsland', 'EasterIsland')]), ('Etc', [('Etc/Greenwich', 'Greenwich'), ('Etc/UCT', 'UCT'), ('Etc/UTC', 'UTC'), ('Etc/Universal', 'Universal'), ('Etc/Zulu', 'Zulu')]), ('Europe', [('Europe/Amsterdam', 'Amsterdam'), ('Europe/Andorra', 'Andorra'), ('Europe/Astrakhan', 'Astrakhan'), ('Europe/Athens', 'Athens'), ('Europe/Belfast', 'Belfast'), ('Europe/Belgrade', 'Belgrade'), ('Europe/Berlin', 'Berlin'), ('Europe/Bratislava', 'Bratislava'), ('Europe/Brussels', 'Brussels'), ('Europe/Bucharest', 'Bucharest'), ('Europe/Budapest', 'Budapest'), ('Europe/Busingen', 'Busingen'), ('Europe/Chisinau', 'Chisinau'), ('Europe/Copenhagen', 'Copenhagen'), ('Europe/Dublin', 'Dublin'), ('Europe/Gibraltar', 'Gibraltar'), ('Europe/Guernsey', 'Guernsey'), ('Europe/Helsinki', 'Helsinki'), ('Europe/Isle_of_Man', 'Isle_of_Man'), ('Europe/Istanbul', 'Istanbul'), ('Europe/Jersey', 'Jersey'), ('Europe/Kaliningrad', 'Kaliningrad'), ('Europe/Kiev', 'Kiev'), ('Europe/Kirov', 'Kirov'), ('Europe/Lisbon', 'Lisbon'), ('Europe/Ljubljana', 'Ljubljana'), ('Europe/London', 'London'), ('Europe/Luxembourg', 'Luxembourg'), ('Europe/Madrid', 'Madrid'), ('Europe/Malta', 'Malta'), ('Europe/Mariehamn', 'Mariehamn'), ('Europe/Minsk', 'Minsk'), ('Europe/Monaco', 'Monaco'), ('Europe/Moscow', 'Moscow'), ('Europe/Nicosia', 'Nicosia'), ('Europe/Oslo', 'Oslo'), ('Europe/Paris', 'Paris'), ('Europe/Podgorica', 'Podgorica'), ('Europe/Prague', 'Prague'), ('Europe/Riga', 'Riga'), ('Europe/Rome', 'Rome'), ('Europe/Samara', 'Samara'), ('Europe/San_Marino', 'San_Marino'), ('Europe/Sarajevo', 'Sarajevo'), ('Europe/Saratov', 'Saratov'), ('Europe/Simferopol', 'Simferopol'), ('Europe/Skopje', 'Skopje'), ('Europe/Sofia', 'Sofia'), ('Europe/Stockholm', 'Stockholm'), ('Europe/Tallinn', 'Tallinn'), ('Europe/Tirane', 'Tirane'), ('Europe/Tiraspol', 'Tiraspol'), ('Europe/Ulyanovsk', 'Ulyanovsk'), ('Europe/Uzhgorod', 'Uzhgorod'), ('Europe/Vaduz', 'Vaduz'), ('Europe/Vatican', 'Vatican'), ('Europe/Vienna', 'Vienna'), ('Europe/Vilnius', 'Vilnius'), ('Europe/Volgograd', 'Volgograd'), ('Europe/Warsaw', 'Warsaw'), ('Europe/Zagreb', 'Zagreb'), ('Europe/Zaporozhye', 'Zaporozhye'), ('Europe/Zurich', 'Zurich')]), ('Indian', [('Indian/Antananarivo', 'Antananarivo'), ('Indian/Chagos', 'Chagos'), ('Indian/Christmas', 'Christmas'), ('Indian/Cocos', 'Cocos'), ('Indian/Comoro', 'Comoro'), ('Indian/Kerguelen', 'Kerguelen'), ('Indian/Mahe', 'Mahe'), ('Indian/Maldives', 'Maldives'), ('Indian/Mauritius', 'Mauritius'), ('Indian/Mayotte', 'Mayotte'), ('Indian/Reunion', 'Reunion')]), ('Mexico', [('Mexico/BajaNorte', 'BajaNorte'), ('Mexico/BajaSur', 'BajaSur'), ('Mexico/General', 'General')]), ('Other', [('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')]), ('Pacific', [('Pacific/Apia', 'Apia'), ('Pacific/Auckland', 'Auckland'), ('Pacific/Bougainville', 'Bougainville'), ('Pacific/Chatham', 'Chatham'), ('Pacific/Chuuk', 'Chuuk'), ('Pacific/Easter', 'Easter'), ('Pacific/Efate', 'Efate'), ('Pacific/Enderbury', 'Enderbury'), ('Pacific/Fakaofo', 'Fakaofo'), ('Pacific/Fiji', 'Fiji'), ('Pacific/Funafuti', 'Funafuti'), ('Pacific/Galapagos', 'Galapagos'), ('Pacific/Gambier', 'Gambier'), ('Pacific/Guadalcanal', 'Guadalcanal'), ('Pacific/Guam', 'Guam'), ('Pacific/Honolulu', 'Honolulu'), ('Pacific/Johnston', 'Johnston'), ('Pacific/Kiritimati', 'Kiritimati'), ('Pacific/Kosrae', 'Kosrae'), ('Pacific/Kwajalein', 'Kwajalein'), ('Pacific/Majuro', 'Majuro'), ('Pacific/Marquesas', 'Marquesas'), ('Pacific/Midway', 'Midway'), ('Pacific/Nauru', 'Nauru'), ('Pacific/Niue', 'Niue'), ('Pacific/Norfolk', 'Norfolk'), ('Pacific/Noumea', 'Noumea'), ('Pacific/Pago_Pago', 'Pago_Pago'), ('Pacific/Palau', 'Palau'), ('Pacific/Pitcairn', 'Pitcairn'), ('Pacific/Pohnpei', 'Pohnpei'), ('Pacific/Ponape', 'Ponape'), ('Pacific/Port_Moresby', 'Port_Moresby'), ('Pacific/Rarotonga', 'Rarotonga'), ('Pacific/Saipan', 'Saipan'), ('Pacific/Samoa', 'Samoa'), ('Pacific/Tahiti', 'Tahiti'), ('Pacific/Tarawa', 'Tarawa'), ('Pacific/Tongatapu', 'Tongatapu'), ('Pacific/Truk', 'Truk'), ('Pacific/Wake', 'Wake'), ('Pacific/Wallis', 'Wallis'), ('Pacific/Yap', 'Yap')]), ('US', [('US/Alaska', 'Alaska'), ('US/Aleutian', 'Aleutian'), ('US/Arizona', 'Arizona'), ('US/Central', 'Central'), ('US/East-Indiana', 'East-Indiana'), ('US/Eastern', 'Eastern'), ('US/Hawaii', 'Hawaii'), ('US/Indiana-Starke', 'Indiana-Starke'), ('US/Michigan', 'Michigan'), ('US/Mountain', 'Mountain'), ('US/Pacific', 'Pacific'), ('US/Samoa', 'Samoa')])], default='Asia/Ho_Chi_Minh', max_length=50, verbose_name='location'), ), ] diff --git a/judge/migrations/0102_fix_custom_validator.py b/judge/migrations/0102_fix_custom_validator.py index e6bcd4a..a0dccce 100644 --- a/judge/migrations/0102_fix_custom_validator.py +++ b/judge/migrations/0102_fix_custom_validator.py @@ -6,50 +6,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0101_custom_validator"), + ('judge', '0101_custom_validator'), ] operations = [ migrations.AlterField( - model_name="problemdata", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker"), - ("customval", "Custom Validator"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemdata', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('custom', 'Custom checker'), ('customval', 'Custom Validator')], max_length=10, verbose_name='checker'), ), migrations.AlterField( - model_name="problemtestcase", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker"), - ("customval", "Custom Validator"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemtestcase', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('custom', 'Custom checker'), ('customval', 'Custom Validator')], max_length=10, verbose_name='checker'), ), ] diff --git a/judge/migrations/0103_fix_custom_validator.py b/judge/migrations/0103_fix_custom_validator.py index d354303..2720bb8 100644 --- a/judge/migrations/0103_fix_custom_validator.py +++ b/judge/migrations/0103_fix_custom_validator.py @@ -6,13 +6,13 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ("judge", "0102_fix_custom_validator"), + ('judge', '0102_fix_custom_validator'), ] operations = [ migrations.RenameField( - model_name="problemdata", - old_name="custom_valid", - new_name="custom_validator", + model_name='problemdata', + old_name='custom_valid', + new_name='custom_validator', ), ] diff --git a/judge/migrations/0104_auto_20200410_1313.py b/judge/migrations/0104_auto_20200410_1313.py index ccba53d..bbefe21 100644 --- a/judge/migrations/0104_auto_20200410_1313.py +++ b/judge/migrations/0104_auto_20200410_1313.py @@ -6,57 +6,23 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0103_fix_custom_validator"), + ('judge', '0103_fix_custom_validator'), ] operations = [ migrations.AlterField( - model_name="contestproblem", - name="output_prefix_override", - field=models.IntegerField( - blank=True, default=0, null=True, verbose_name="visible testcases" - ), + model_name='contestproblem', + name='output_prefix_override', + field=models.IntegerField(blank=True, default=0, null=True, verbose_name='visible testcases'), ), migrations.AlterField( - model_name="problemdata", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker (PY)"), - ("customval", "Custom validator (CPP)"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemdata', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('custom', 'Custom checker (PY)'), ('customval', 'Custom validator (CPP)')], max_length=10, verbose_name='checker'), ), migrations.AlterField( - model_name="problemtestcase", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker (PY)"), - ("customval", "Custom validator (CPP)"), - ], - max_length=10, - verbose_name="checker", - ), + model_name='problemtestcase', + name='checker', + field=models.CharField(blank=True, choices=[('standard', 'Standard'), ('floats', 'Floats'), ('floatsabs', 'Floats (absolute)'), ('floatsrel', 'Floats (relative)'), ('rstripped', 'Non-trailing spaces'), ('sorted', 'Unordered'), ('identical', 'Byte identical'), ('linecount', 'Line-by-line'), ('custom', 'Custom checker (PY)'), ('customval', 'Custom validator (CPP)')], max_length=10, verbose_name='checker'), ), ] diff --git a/judge/migrations/0105_auto_20200523_0756.py b/judge/migrations/0105_auto_20200523_0756.py index 553c24f..79ccb4e 100644 --- a/judge/migrations/0105_auto_20200523_0756.py +++ b/judge/migrations/0105_auto_20200523_0756.py @@ -6,681 +6,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0104_auto_20200410_1313"), + ('judge', '0104_auto_20200410_1313'), ] operations = [ migrations.AlterField( - model_name="contestproblem", - name="output_prefix_override", - field=models.IntegerField( - blank=True, - default=0, - help_text="0 to not show testcases, 1 to show", - null=True, - verbose_name="visible testcases", - ), + model_name='contestproblem', + name='output_prefix_override', + field=models.IntegerField(blank=True, default=0, help_text='0 to not show testcases, 1 to show', null=True, verbose_name='visible testcases'), ), migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Nuuk", "Nuuk"), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), + model_name='profile', + name='timezone', + field=models.CharField(choices=[('Africa', [('Africa/Abidjan', 'Abidjan'), ('Africa/Accra', 'Accra'), ('Africa/Addis_Ababa', 'Addis_Ababa'), ('Africa/Algiers', 'Algiers'), ('Africa/Asmara', 'Asmara'), ('Africa/Asmera', 'Asmera'), ('Africa/Bamako', 'Bamako'), ('Africa/Bangui', 'Bangui'), ('Africa/Banjul', 'Banjul'), ('Africa/Bissau', 'Bissau'), ('Africa/Blantyre', 'Blantyre'), ('Africa/Brazzaville', 'Brazzaville'), ('Africa/Bujumbura', 'Bujumbura'), ('Africa/Cairo', 'Cairo'), ('Africa/Casablanca', 'Casablanca'), ('Africa/Ceuta', 'Ceuta'), ('Africa/Conakry', 'Conakry'), ('Africa/Dakar', 'Dakar'), ('Africa/Dar_es_Salaam', 'Dar_es_Salaam'), ('Africa/Djibouti', 'Djibouti'), ('Africa/Douala', 'Douala'), ('Africa/El_Aaiun', 'El_Aaiun'), ('Africa/Freetown', 'Freetown'), ('Africa/Gaborone', 'Gaborone'), ('Africa/Harare', 'Harare'), ('Africa/Johannesburg', 'Johannesburg'), ('Africa/Juba', 'Juba'), ('Africa/Kampala', 'Kampala'), ('Africa/Khartoum', 'Khartoum'), ('Africa/Kigali', 'Kigali'), ('Africa/Kinshasa', 'Kinshasa'), ('Africa/Lagos', 'Lagos'), ('Africa/Libreville', 'Libreville'), ('Africa/Lome', 'Lome'), ('Africa/Luanda', 'Luanda'), ('Africa/Lubumbashi', 'Lubumbashi'), ('Africa/Lusaka', 'Lusaka'), ('Africa/Malabo', 'Malabo'), ('Africa/Maputo', 'Maputo'), ('Africa/Maseru', 'Maseru'), ('Africa/Mbabane', 'Mbabane'), ('Africa/Mogadishu', 'Mogadishu'), ('Africa/Monrovia', 'Monrovia'), ('Africa/Nairobi', 'Nairobi'), ('Africa/Ndjamena', 'Ndjamena'), ('Africa/Niamey', 'Niamey'), ('Africa/Nouakchott', 'Nouakchott'), ('Africa/Ouagadougou', 'Ouagadougou'), ('Africa/Porto-Novo', 'Porto-Novo'), ('Africa/Sao_Tome', 'Sao_Tome'), ('Africa/Timbuktu', 'Timbuktu'), ('Africa/Tripoli', 'Tripoli'), ('Africa/Tunis', 'Tunis'), ('Africa/Windhoek', 'Windhoek')]), ('America', [('America/Adak', 'Adak'), ('America/Anchorage', 'Anchorage'), ('America/Anguilla', 'Anguilla'), ('America/Antigua', 'Antigua'), ('America/Araguaina', 'Araguaina'), ('America/Argentina/Buenos_Aires', 'Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'Argentina/Cordoba'), ('America/Argentina/Jujuy', 'Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'Argentina/Salta'), ('America/Argentina/San_Juan', 'Argentina/San_Juan'), ('America/Argentina/San_Luis', 'Argentina/San_Luis'), ('America/Argentina/Tucuman', 'Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'Argentina/Ushuaia'), ('America/Aruba', 'Aruba'), ('America/Asuncion', 'Asuncion'), ('America/Atikokan', 'Atikokan'), ('America/Atka', 'Atka'), ('America/Bahia', 'Bahia'), ('America/Bahia_Banderas', 'Bahia_Banderas'), ('America/Barbados', 'Barbados'), ('America/Belem', 'Belem'), ('America/Belize', 'Belize'), ('America/Blanc-Sablon', 'Blanc-Sablon'), ('America/Boa_Vista', 'Boa_Vista'), ('America/Bogota', 'Bogota'), ('America/Boise', 'Boise'), ('America/Buenos_Aires', 'Buenos_Aires'), ('America/Cambridge_Bay', 'Cambridge_Bay'), ('America/Campo_Grande', 'Campo_Grande'), ('America/Cancun', 'Cancun'), ('America/Caracas', 'Caracas'), ('America/Catamarca', 'Catamarca'), ('America/Cayenne', 'Cayenne'), ('America/Cayman', 'Cayman'), ('America/Chicago', 'Chicago'), ('America/Chihuahua', 'Chihuahua'), ('America/Coral_Harbour', 'Coral_Harbour'), ('America/Cordoba', 'Cordoba'), ('America/Costa_Rica', 'Costa_Rica'), ('America/Creston', 'Creston'), ('America/Cuiaba', 'Cuiaba'), ('America/Curacao', 'Curacao'), ('America/Danmarkshavn', 'Danmarkshavn'), ('America/Dawson', 'Dawson'), ('America/Dawson_Creek', 'Dawson_Creek'), ('America/Denver', 'Denver'), ('America/Detroit', 'Detroit'), ('America/Dominica', 'Dominica'), ('America/Edmonton', 'Edmonton'), ('America/Eirunepe', 'Eirunepe'), ('America/El_Salvador', 'El_Salvador'), ('America/Ensenada', 'Ensenada'), ('America/Fort_Nelson', 'Fort_Nelson'), ('America/Fort_Wayne', 'Fort_Wayne'), ('America/Fortaleza', 'Fortaleza'), ('America/Glace_Bay', 'Glace_Bay'), ('America/Godthab', 'Godthab'), ('America/Goose_Bay', 'Goose_Bay'), ('America/Grand_Turk', 'Grand_Turk'), ('America/Grenada', 'Grenada'), ('America/Guadeloupe', 'Guadeloupe'), ('America/Guatemala', 'Guatemala'), ('America/Guayaquil', 'Guayaquil'), ('America/Guyana', 'Guyana'), ('America/Halifax', 'Halifax'), ('America/Havana', 'Havana'), ('America/Hermosillo', 'Hermosillo'), ('America/Indiana/Indianapolis', 'Indiana/Indianapolis'), ('America/Indiana/Knox', 'Indiana/Knox'), ('America/Indiana/Marengo', 'Indiana/Marengo'), ('America/Indiana/Petersburg', 'Indiana/Petersburg'), ('America/Indiana/Tell_City', 'Indiana/Tell_City'), ('America/Indiana/Vevay', 'Indiana/Vevay'), ('America/Indiana/Vincennes', 'Indiana/Vincennes'), ('America/Indiana/Winamac', 'Indiana/Winamac'), ('America/Indianapolis', 'Indianapolis'), ('America/Inuvik', 'Inuvik'), ('America/Iqaluit', 'Iqaluit'), ('America/Jamaica', 'Jamaica'), ('America/Jujuy', 'Jujuy'), ('America/Juneau', 'Juneau'), ('America/Kentucky/Louisville', 'Kentucky/Louisville'), ('America/Kentucky/Monticello', 'Kentucky/Monticello'), ('America/Knox_IN', 'Knox_IN'), ('America/Kralendijk', 'Kralendijk'), ('America/La_Paz', 'La_Paz'), ('America/Lima', 'Lima'), ('America/Los_Angeles', 'Los_Angeles'), ('America/Louisville', 'Louisville'), ('America/Lower_Princes', 'Lower_Princes'), ('America/Maceio', 'Maceio'), ('America/Managua', 'Managua'), ('America/Manaus', 'Manaus'), ('America/Marigot', 'Marigot'), ('America/Martinique', 'Martinique'), ('America/Matamoros', 'Matamoros'), ('America/Mazatlan', 'Mazatlan'), ('America/Mendoza', 'Mendoza'), ('America/Menominee', 'Menominee'), ('America/Merida', 'Merida'), ('America/Metlakatla', 'Metlakatla'), ('America/Mexico_City', 'Mexico_City'), ('America/Miquelon', 'Miquelon'), ('America/Moncton', 'Moncton'), ('America/Monterrey', 'Monterrey'), ('America/Montevideo', 'Montevideo'), ('America/Montreal', 'Montreal'), ('America/Montserrat', 'Montserrat'), ('America/Nassau', 'Nassau'), ('America/New_York', 'New_York'), ('America/Nipigon', 'Nipigon'), ('America/Nome', 'Nome'), ('America/Noronha', 'Noronha'), ('America/North_Dakota/Beulah', 'North_Dakota/Beulah'), ('America/North_Dakota/Center', 'North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'North_Dakota/New_Salem'), ('America/Nuuk', 'Nuuk'), ('America/Ojinaga', 'Ojinaga'), ('America/Panama', 'Panama'), ('America/Pangnirtung', 'Pangnirtung'), ('America/Paramaribo', 'Paramaribo'), ('America/Phoenix', 'Phoenix'), ('America/Port-au-Prince', 'Port-au-Prince'), ('America/Port_of_Spain', 'Port_of_Spain'), ('America/Porto_Acre', 'Porto_Acre'), ('America/Porto_Velho', 'Porto_Velho'), ('America/Puerto_Rico', 'Puerto_Rico'), ('America/Punta_Arenas', 'Punta_Arenas'), ('America/Rainy_River', 'Rainy_River'), ('America/Rankin_Inlet', 'Rankin_Inlet'), ('America/Recife', 'Recife'), ('America/Regina', 'Regina'), ('America/Resolute', 'Resolute'), ('America/Rio_Branco', 'Rio_Branco'), ('America/Rosario', 'Rosario'), ('America/Santa_Isabel', 'Santa_Isabel'), ('America/Santarem', 'Santarem'), ('America/Santiago', 'Santiago'), ('America/Santo_Domingo', 'Santo_Domingo'), ('America/Sao_Paulo', 'Sao_Paulo'), ('America/Scoresbysund', 'Scoresbysund'), ('America/Shiprock', 'Shiprock'), ('America/Sitka', 'Sitka'), ('America/St_Barthelemy', 'St_Barthelemy'), ('America/St_Johns', 'St_Johns'), ('America/St_Kitts', 'St_Kitts'), ('America/St_Lucia', 'St_Lucia'), ('America/St_Thomas', 'St_Thomas'), ('America/St_Vincent', 'St_Vincent'), ('America/Swift_Current', 'Swift_Current'), ('America/Tegucigalpa', 'Tegucigalpa'), ('America/Thule', 'Thule'), ('America/Thunder_Bay', 'Thunder_Bay'), ('America/Tijuana', 'Tijuana'), ('America/Toronto', 'Toronto'), ('America/Tortola', 'Tortola'), ('America/Vancouver', 'Vancouver'), ('America/Virgin', 'Virgin'), ('America/Whitehorse', 'Whitehorse'), ('America/Winnipeg', 'Winnipeg'), ('America/Yakutat', 'Yakutat'), ('America/Yellowknife', 'Yellowknife')]), ('Antarctica', [('Antarctica/Casey', 'Casey'), ('Antarctica/Davis', 'Davis'), ('Antarctica/DumontDUrville', 'DumontDUrville'), ('Antarctica/Macquarie', 'Macquarie'), ('Antarctica/Mawson', 'Mawson'), ('Antarctica/McMurdo', 'McMurdo'), ('Antarctica/Palmer', 'Palmer'), ('Antarctica/Rothera', 'Rothera'), ('Antarctica/South_Pole', 'South_Pole'), ('Antarctica/Syowa', 'Syowa'), ('Antarctica/Troll', 'Troll'), ('Antarctica/Vostok', 'Vostok')]), ('Arctic', [('Arctic/Longyearbyen', 'Longyearbyen')]), ('Asia', [('Asia/Aden', 'Aden'), ('Asia/Almaty', 'Almaty'), ('Asia/Amman', 'Amman'), ('Asia/Anadyr', 'Anadyr'), ('Asia/Aqtau', 'Aqtau'), ('Asia/Aqtobe', 'Aqtobe'), ('Asia/Ashgabat', 'Ashgabat'), ('Asia/Ashkhabad', 'Ashkhabad'), ('Asia/Atyrau', 'Atyrau'), ('Asia/Baghdad', 'Baghdad'), ('Asia/Bahrain', 'Bahrain'), ('Asia/Baku', 'Baku'), ('Asia/Bangkok', 'Bangkok'), ('Asia/Barnaul', 'Barnaul'), ('Asia/Beirut', 'Beirut'), ('Asia/Bishkek', 'Bishkek'), ('Asia/Brunei', 'Brunei'), ('Asia/Calcutta', 'Calcutta'), ('Asia/Chita', 'Chita'), ('Asia/Choibalsan', 'Choibalsan'), ('Asia/Chongqing', 'Chongqing'), ('Asia/Chungking', 'Chungking'), ('Asia/Colombo', 'Colombo'), ('Asia/Dacca', 'Dacca'), ('Asia/Damascus', 'Damascus'), ('Asia/Dhaka', 'Dhaka'), ('Asia/Dili', 'Dili'), ('Asia/Dubai', 'Dubai'), ('Asia/Dushanbe', 'Dushanbe'), ('Asia/Famagusta', 'Famagusta'), ('Asia/Gaza', 'Gaza'), ('Asia/Harbin', 'Harbin'), ('Asia/Hebron', 'Hebron'), ('Asia/Ho_Chi_Minh', 'Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Hong_Kong'), ('Asia/Hovd', 'Hovd'), ('Asia/Irkutsk', 'Irkutsk'), ('Asia/Istanbul', 'Istanbul'), ('Asia/Jakarta', 'Jakarta'), ('Asia/Jayapura', 'Jayapura'), ('Asia/Jerusalem', 'Jerusalem'), ('Asia/Kabul', 'Kabul'), ('Asia/Kamchatka', 'Kamchatka'), ('Asia/Karachi', 'Karachi'), ('Asia/Kashgar', 'Kashgar'), ('Asia/Kathmandu', 'Kathmandu'), ('Asia/Katmandu', 'Katmandu'), ('Asia/Khandyga', 'Khandyga'), ('Asia/Kolkata', 'Kolkata'), ('Asia/Krasnoyarsk', 'Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Kuala_Lumpur'), ('Asia/Kuching', 'Kuching'), ('Asia/Kuwait', 'Kuwait'), ('Asia/Macao', 'Macao'), ('Asia/Macau', 'Macau'), ('Asia/Magadan', 'Magadan'), ('Asia/Makassar', 'Makassar'), ('Asia/Manila', 'Manila'), ('Asia/Muscat', 'Muscat'), ('Asia/Nicosia', 'Nicosia'), ('Asia/Novokuznetsk', 'Novokuznetsk'), ('Asia/Novosibirsk', 'Novosibirsk'), ('Asia/Omsk', 'Omsk'), ('Asia/Oral', 'Oral'), ('Asia/Phnom_Penh', 'Phnom_Penh'), ('Asia/Pontianak', 'Pontianak'), ('Asia/Pyongyang', 'Pyongyang'), ('Asia/Qatar', 'Qatar'), ('Asia/Qostanay', 'Qostanay'), ('Asia/Qyzylorda', 'Qyzylorda'), ('Asia/Rangoon', 'Rangoon'), ('Asia/Riyadh', 'Riyadh'), ('Asia/Saigon', 'Saigon'), ('Asia/Sakhalin', 'Sakhalin'), ('Asia/Samarkand', 'Samarkand'), ('Asia/Seoul', 'Seoul'), ('Asia/Shanghai', 'Shanghai'), ('Asia/Singapore', 'Singapore'), ('Asia/Srednekolymsk', 'Srednekolymsk'), ('Asia/Taipei', 'Taipei'), ('Asia/Tashkent', 'Tashkent'), ('Asia/Tbilisi', 'Tbilisi'), ('Asia/Tehran', 'Tehran'), ('Asia/Tel_Aviv', 'Tel_Aviv'), ('Asia/Thimbu', 'Thimbu'), ('Asia/Thimphu', 'Thimphu'), ('Asia/Tokyo', 'Tokyo'), ('Asia/Tomsk', 'Tomsk'), ('Asia/Ujung_Pandang', 'Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Ulaanbaatar'), ('Asia/Ulan_Bator', 'Ulan_Bator'), ('Asia/Urumqi', 'Urumqi'), ('Asia/Ust-Nera', 'Ust-Nera'), ('Asia/Vientiane', 'Vientiane'), ('Asia/Vladivostok', 'Vladivostok'), ('Asia/Yakutsk', 'Yakutsk'), ('Asia/Yangon', 'Yangon'), ('Asia/Yekaterinburg', 'Yekaterinburg'), ('Asia/Yerevan', 'Yerevan')]), ('Atlantic', [('Atlantic/Azores', 'Azores'), ('Atlantic/Bermuda', 'Bermuda'), ('Atlantic/Canary', 'Canary'), ('Atlantic/Cape_Verde', 'Cape_Verde'), ('Atlantic/Faeroe', 'Faeroe'), ('Atlantic/Faroe', 'Faroe'), ('Atlantic/Jan_Mayen', 'Jan_Mayen'), ('Atlantic/Madeira', 'Madeira'), ('Atlantic/Reykjavik', 'Reykjavik'), ('Atlantic/South_Georgia', 'South_Georgia'), ('Atlantic/St_Helena', 'St_Helena'), ('Atlantic/Stanley', 'Stanley')]), ('Australia', [('Australia/ACT', 'ACT'), ('Australia/Adelaide', 'Adelaide'), ('Australia/Brisbane', 'Brisbane'), ('Australia/Broken_Hill', 'Broken_Hill'), ('Australia/Canberra', 'Canberra'), ('Australia/Currie', 'Currie'), ('Australia/Darwin', 'Darwin'), ('Australia/Eucla', 'Eucla'), ('Australia/Hobart', 'Hobart'), ('Australia/LHI', 'LHI'), ('Australia/Lindeman', 'Lindeman'), ('Australia/Lord_Howe', 'Lord_Howe'), ('Australia/Melbourne', 'Melbourne'), ('Australia/NSW', 'NSW'), ('Australia/North', 'North'), ('Australia/Perth', 'Perth'), ('Australia/Queensland', 'Queensland'), ('Australia/South', 'South'), ('Australia/Sydney', 'Sydney'), ('Australia/Tasmania', 'Tasmania'), ('Australia/Victoria', 'Victoria'), ('Australia/West', 'West'), ('Australia/Yancowinna', 'Yancowinna')]), ('Brazil', [('Brazil/Acre', 'Acre'), ('Brazil/DeNoronha', 'DeNoronha'), ('Brazil/East', 'East'), ('Brazil/West', 'West')]), ('Canada', [('Canada/Atlantic', 'Atlantic'), ('Canada/Central', 'Central'), ('Canada/Eastern', 'Eastern'), ('Canada/Mountain', 'Mountain'), ('Canada/Newfoundland', 'Newfoundland'), ('Canada/Pacific', 'Pacific'), ('Canada/Saskatchewan', 'Saskatchewan'), ('Canada/Yukon', 'Yukon')]), ('Chile', [('Chile/Continental', 'Continental'), ('Chile/EasterIsland', 'EasterIsland')]), ('Etc', [('Etc/Greenwich', 'Greenwich'), ('Etc/UCT', 'UCT'), ('Etc/UTC', 'UTC'), ('Etc/Universal', 'Universal'), ('Etc/Zulu', 'Zulu')]), ('Europe', [('Europe/Amsterdam', 'Amsterdam'), ('Europe/Andorra', 'Andorra'), ('Europe/Astrakhan', 'Astrakhan'), ('Europe/Athens', 'Athens'), ('Europe/Belfast', 'Belfast'), ('Europe/Belgrade', 'Belgrade'), ('Europe/Berlin', 'Berlin'), ('Europe/Bratislava', 'Bratislava'), ('Europe/Brussels', 'Brussels'), ('Europe/Bucharest', 'Bucharest'), ('Europe/Budapest', 'Budapest'), ('Europe/Busingen', 'Busingen'), ('Europe/Chisinau', 'Chisinau'), ('Europe/Copenhagen', 'Copenhagen'), ('Europe/Dublin', 'Dublin'), ('Europe/Gibraltar', 'Gibraltar'), ('Europe/Guernsey', 'Guernsey'), ('Europe/Helsinki', 'Helsinki'), ('Europe/Isle_of_Man', 'Isle_of_Man'), ('Europe/Istanbul', 'Istanbul'), ('Europe/Jersey', 'Jersey'), ('Europe/Kaliningrad', 'Kaliningrad'), ('Europe/Kiev', 'Kiev'), ('Europe/Kirov', 'Kirov'), ('Europe/Lisbon', 'Lisbon'), ('Europe/Ljubljana', 'Ljubljana'), ('Europe/London', 'London'), ('Europe/Luxembourg', 'Luxembourg'), ('Europe/Madrid', 'Madrid'), ('Europe/Malta', 'Malta'), ('Europe/Mariehamn', 'Mariehamn'), ('Europe/Minsk', 'Minsk'), ('Europe/Monaco', 'Monaco'), ('Europe/Moscow', 'Moscow'), ('Europe/Nicosia', 'Nicosia'), ('Europe/Oslo', 'Oslo'), ('Europe/Paris', 'Paris'), ('Europe/Podgorica', 'Podgorica'), ('Europe/Prague', 'Prague'), ('Europe/Riga', 'Riga'), ('Europe/Rome', 'Rome'), ('Europe/Samara', 'Samara'), ('Europe/San_Marino', 'San_Marino'), ('Europe/Sarajevo', 'Sarajevo'), ('Europe/Saratov', 'Saratov'), ('Europe/Simferopol', 'Simferopol'), ('Europe/Skopje', 'Skopje'), ('Europe/Sofia', 'Sofia'), ('Europe/Stockholm', 'Stockholm'), ('Europe/Tallinn', 'Tallinn'), ('Europe/Tirane', 'Tirane'), ('Europe/Tiraspol', 'Tiraspol'), ('Europe/Ulyanovsk', 'Ulyanovsk'), ('Europe/Uzhgorod', 'Uzhgorod'), ('Europe/Vaduz', 'Vaduz'), ('Europe/Vatican', 'Vatican'), ('Europe/Vienna', 'Vienna'), ('Europe/Vilnius', 'Vilnius'), ('Europe/Volgograd', 'Volgograd'), ('Europe/Warsaw', 'Warsaw'), ('Europe/Zagreb', 'Zagreb'), ('Europe/Zaporozhye', 'Zaporozhye'), ('Europe/Zurich', 'Zurich')]), ('Indian', [('Indian/Antananarivo', 'Antananarivo'), ('Indian/Chagos', 'Chagos'), ('Indian/Christmas', 'Christmas'), ('Indian/Cocos', 'Cocos'), ('Indian/Comoro', 'Comoro'), ('Indian/Kerguelen', 'Kerguelen'), ('Indian/Mahe', 'Mahe'), ('Indian/Maldives', 'Maldives'), ('Indian/Mauritius', 'Mauritius'), ('Indian/Mayotte', 'Mayotte'), ('Indian/Reunion', 'Reunion')]), ('Mexico', [('Mexico/BajaNorte', 'BajaNorte'), ('Mexico/BajaSur', 'BajaSur'), ('Mexico/General', 'General')]), ('Other', [('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')]), ('Pacific', [('Pacific/Apia', 'Apia'), ('Pacific/Auckland', 'Auckland'), ('Pacific/Bougainville', 'Bougainville'), ('Pacific/Chatham', 'Chatham'), ('Pacific/Chuuk', 'Chuuk'), ('Pacific/Easter', 'Easter'), ('Pacific/Efate', 'Efate'), ('Pacific/Enderbury', 'Enderbury'), ('Pacific/Fakaofo', 'Fakaofo'), ('Pacific/Fiji', 'Fiji'), ('Pacific/Funafuti', 'Funafuti'), ('Pacific/Galapagos', 'Galapagos'), ('Pacific/Gambier', 'Gambier'), ('Pacific/Guadalcanal', 'Guadalcanal'), ('Pacific/Guam', 'Guam'), ('Pacific/Honolulu', 'Honolulu'), ('Pacific/Johnston', 'Johnston'), ('Pacific/Kiritimati', 'Kiritimati'), ('Pacific/Kosrae', 'Kosrae'), ('Pacific/Kwajalein', 'Kwajalein'), ('Pacific/Majuro', 'Majuro'), ('Pacific/Marquesas', 'Marquesas'), ('Pacific/Midway', 'Midway'), ('Pacific/Nauru', 'Nauru'), ('Pacific/Niue', 'Niue'), ('Pacific/Norfolk', 'Norfolk'), ('Pacific/Noumea', 'Noumea'), ('Pacific/Pago_Pago', 'Pago_Pago'), ('Pacific/Palau', 'Palau'), ('Pacific/Pitcairn', 'Pitcairn'), ('Pacific/Pohnpei', 'Pohnpei'), ('Pacific/Ponape', 'Ponape'), ('Pacific/Port_Moresby', 'Port_Moresby'), ('Pacific/Rarotonga', 'Rarotonga'), ('Pacific/Saipan', 'Saipan'), ('Pacific/Samoa', 'Samoa'), ('Pacific/Tahiti', 'Tahiti'), ('Pacific/Tarawa', 'Tarawa'), ('Pacific/Tongatapu', 'Tongatapu'), ('Pacific/Truk', 'Truk'), ('Pacific/Wake', 'Wake'), ('Pacific/Wallis', 'Wallis'), ('Pacific/Yap', 'Yap')]), ('US', [('US/Alaska', 'Alaska'), ('US/Aleutian', 'Aleutian'), ('US/Arizona', 'Arizona'), ('US/Central', 'Central'), ('US/East-Indiana', 'East-Indiana'), ('US/Eastern', 'Eastern'), ('US/Hawaii', 'Hawaii'), ('US/Indiana-Starke', 'Indiana-Starke'), ('US/Michigan', 'Michigan'), ('US/Mountain', 'Mountain'), ('US/Pacific', 'Pacific'), ('US/Samoa', 'Samoa')])], default='Asia/Ho_Chi_Minh', max_length=50, verbose_name='location'), ), ] diff --git a/judge/migrations/0106_friend.py b/judge/migrations/0106_friend.py index 1235013..e820242 100644 --- a/judge/migrations/0106_friend.py +++ b/judge/migrations/0106_friend.py @@ -7,31 +7,16 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ("judge", "0105_auto_20200523_0756"), + ('judge', '0105_auto_20200523_0756'), ] operations = [ migrations.CreateModel( - name="Friend", + name='Friend', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "current_user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="following_users", - to="judge.Profile", - ), - ), - ("users", models.ManyToManyField(to="judge.Profile")), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('current_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='following_users', to='judge.Profile')), + ('users', models.ManyToManyField(to='judge.Profile')), ], ), ] diff --git a/judge/migrations/0107_notification.py b/judge/migrations/0107_notification.py index 9539711..d8857d6 100644 --- a/judge/migrations/0107_notification.py +++ b/judge/migrations/0107_notification.py @@ -7,45 +7,19 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ("judge", "0106_friend"), + ('judge', '0106_friend'), ] operations = [ migrations.CreateModel( - name="Notification", + name='Notification', fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "time", - models.DateTimeField(auto_now_add=True, verbose_name="posted time"), - ), - ("read", models.BooleanField(default=False, verbose_name="read")), - ("category", models.CharField(max_length=10, verbose_name="category")), - ( - "comment", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Comment", - verbose_name="comment", - ), - ), - ( - "owner", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="notifications", - to="judge.Profile", - verbose_name="owner", - ), - ), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('time', models.DateTimeField(auto_now_add=True, verbose_name='posted time')), + ('read', models.BooleanField(default=False, verbose_name='read')), + ('category', models.CharField(max_length=10, verbose_name='category')), + ('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='judge.Comment', verbose_name='comment')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to='judge.Profile', verbose_name='owner')), ], ), ] diff --git a/judge/migrations/0108_submission_judged_date.py b/judge/migrations/0108_submission_judged_date.py index e58ca6b..5794ace 100644 --- a/judge/migrations/0108_submission_judged_date.py +++ b/judge/migrations/0108_submission_judged_date.py @@ -6,15 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0107_notification"), + ('judge', '0107_notification'), ] operations = [ migrations.AddField( - model_name="submission", - name="judged_date", - field=models.DateTimeField( - default=None, null=True, verbose_name="submission judge time" - ), + model_name='submission', + name='judged_date', + field=models.DateTimeField(default=None, null=True, verbose_name='submission judge time'), ), ] diff --git a/judge/migrations/0109_auto_20201017_1151.py b/judge/migrations/0109_auto_20201017_1151.py index 3e94df0..9bce543 100644 --- a/judge/migrations/0109_auto_20201017_1151.py +++ b/judge/migrations/0109_auto_20201017_1151.py @@ -7,27 +7,18 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ("judge", "0108_submission_judged_date"), + ('judge', '0108_submission_judged_date'), ] operations = [ migrations.AddField( - model_name="notification", - name="html_link", - field=models.TextField( - default="", - max_length=1000, - verbose_name="html link to comments, used for non-comments", - ), + model_name='notification', + name='html_link', + field=models.TextField(default='', max_length=1000, verbose_name='html link to comments, used for non-comments'), ), migrations.AlterField( - model_name="notification", - name="comment", - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="judge.Comment", - verbose_name="comment", - ), + model_name='notification', + name='comment', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='judge.Comment', verbose_name='comment'), ), ] diff --git a/judge/migrations/0110_notification_author.py b/judge/migrations/0110_notification_author.py index 367c96a..e0dec3e 100644 --- a/judge/migrations/0110_notification_author.py +++ b/judge/migrations/0110_notification_author.py @@ -7,18 +7,13 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ("judge", "0109_auto_20201017_1151"), + ('judge', '0109_auto_20201017_1151'), ] operations = [ migrations.AddField( - model_name="notification", - name="author", - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="judge.Profile", - verbose_name="who trigger, used for non-comment", - ), + model_name='notification', + name='author', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='judge.Profile', verbose_name='who trigger, used for non-comment'), ), ] diff --git a/judge/migrations/0111_contest_decimal_points.py b/judge/migrations/0111_contest_decimal_points.py index b83e3b5..4dc3968 100644 --- a/judge/migrations/0111_contest_decimal_points.py +++ b/judge/migrations/0111_contest_decimal_points.py @@ -7,26 +7,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0110_notification_author"), + ('judge', '0110_notification_author'), ] operations = [ migrations.AddField( - model_name="contest", - name="points_precision", - field=models.IntegerField( - default=2, - help_text="Number of digits to round points to.", - validators=[ - django.core.validators.MinValueValidator(0), - django.core.validators.MaxValueValidator(10), - ], - verbose_name="precision points", - ), + model_name='contest', + name='points_precision', + field=models.IntegerField(default=2, help_text='Number of digits to round points to.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)], verbose_name='precision points'), ), migrations.AlterField( - model_name="contestparticipation", - name="score", - field=models.FloatField(db_index=True, default=0, verbose_name="score"), + model_name='contestparticipation', + name='score', + field=models.FloatField(db_index=True, default=0, verbose_name='score'), ), ] diff --git a/judge/migrations/0112_contest_view_contest_scoreboard.py b/judge/migrations/0112_contest_view_contest_scoreboard.py index 00e2da4..9a0dab2 100644 --- a/judge/migrations/0112_contest_view_contest_scoreboard.py +++ b/judge/migrations/0112_contest_view_contest_scoreboard.py @@ -6,19 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0111_contest_decimal_points"), + ('judge', '0111_contest_decimal_points'), ] operations = [ migrations.AddField( - model_name="contest", - name="view_contest_scoreboard", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to view the scoreboard.", - related_name="view_contest_scoreboard", - to="judge.Profile", - verbose_name="view contest scoreboard", - ), + model_name='contest', + name='view_contest_scoreboard', + field=models.ManyToManyField(blank=True, help_text='These users will be able to view the scoreboard.', related_name='view_contest_scoreboard', to='judge.Profile', verbose_name='view contest scoreboard'), ), ] diff --git a/judge/migrations/0113_auto_20201228_0911.py b/judge/migrations/0113_auto_20201228_0911.py index 36d7dda..f08350a 100644 --- a/judge/migrations/0113_auto_20201228_0911.py +++ b/judge/migrations/0113_auto_20201228_0911.py @@ -6,26 +6,12 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ("judge", "0112_contest_view_contest_scoreboard"), + ('judge', '0112_contest_view_contest_scoreboard'), ] operations = [ migrations.AlterModelOptions( - name="contest", - options={ - "permissions": ( - ("see_private_contest", "See private contests"), - ("edit_own_contest", "Edit own contests"), - ("edit_all_contest", "Edit all contests"), - ("clone_contest", "Clone contest"), - ("moss_contest", "MOSS contest"), - ("contest_rating", "Rate contests"), - ("contest_access_code", "Contest access codes"), - ("create_private_contest", "Create private contests"), - ("change_contest_visibility", "Change contest visibility"), - ), - "verbose_name": "contest", - "verbose_name_plural": "contests", - }, + name='contest', + options={'permissions': (('see_private_contest', 'See private contests'), ('edit_own_contest', 'Edit own contests'), ('edit_all_contest', 'Edit all contests'), ('clone_contest', 'Clone contest'), ('moss_contest', 'MOSS contest'), ('contest_rating', 'Rate contests'), ('contest_access_code', 'Contest access codes'), ('create_private_contest', 'Create private contests'), ('change_contest_visibility', 'Change contest visibility')), 'verbose_name': 'contest', 'verbose_name_plural': 'contests'}, ), ] diff --git a/judge/migrations/0114_auto_20201228_1041.py b/judge/migrations/0114_auto_20201228_1041.py index 609c5d4..3103b3c 100644 --- a/judge/migrations/0114_auto_20201228_1041.py +++ b/judge/migrations/0114_auto_20201228_1041.py @@ -6,25 +6,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0113_auto_20201228_0911"), + ('judge', '0113_auto_20201228_0911'), ] operations = [ migrations.AddField( - model_name="blogpost", - name="is_organization_private", - field=models.BooleanField( - default=False, verbose_name="private to organizations" - ), + model_name='blogpost', + name='is_organization_private', + field=models.BooleanField(default=False, verbose_name='private to organizations'), ), migrations.AddField( - model_name="blogpost", - name="organizations", - field=models.ManyToManyField( - blank=True, - help_text="If private, only these organizations may see the blog post.", - to="judge.Organization", - verbose_name="organizations", - ), + model_name='blogpost', + name='organizations', + field=models.ManyToManyField(blank=True, help_text='If private, only these organizations may see the blog post.', to='judge.Organization', verbose_name='organizations'), ), ] diff --git a/judge/migrations/0115_auto_20210525_0222.py b/judge/migrations/0115_auto_20210525_0222.py index 52490c2..91312de 100644 --- a/judge/migrations/0115_auto_20210525_0222.py +++ b/judge/migrations/0115_auto_20210525_0222.py @@ -2,109 +2,62 @@ from django.db import migrations, models - def hide_scoreboard_eq_true(apps, schema_editor): - Contest = apps.get_model("judge", "Contest") - Contest.objects.filter(hide_scoreboard=True).update(scoreboard_visibility="C") + Contest = apps.get_model('judge', 'Contest') + Contest.objects.filter(hide_scoreboard=True).update(scoreboard_visibility='C') def scoreboard_visibility_eq_contest(apps, schema_editor): - Contest = apps.get_model("judge", "Contest") - Contest.objects.filter(scoreboard_visibility__in=("C", "P")).update( - hide_scoreboard=True - ) - + Contest = apps.get_model('judge', 'Contest') + Contest.objects.filter(scoreboard_visibility__in=('C', 'P')).update(hide_scoreboard=True) class Migration(migrations.Migration): dependencies = [ - ("judge", "0114_auto_20201228_1041"), + ('judge', '0114_auto_20201228_1041'), ] operations = [ migrations.AlterModelOptions( - name="contest", - options={ - "permissions": ( - ("see_private_contest", "See private contests"), - ("edit_own_contest", "Edit own contests"), - ("edit_all_contest", "Edit all contests"), - ("clone_contest", "Clone contest"), - ("moss_contest", "MOSS contest"), - ("contest_rating", "Rate contests"), - ("contest_access_code", "Contest access codes"), - ("create_private_contest", "Create private contests"), - ("change_contest_visibility", "Change contest visibility"), - ("contest_problem_label", "Edit contest problem label script"), - ), - "verbose_name": "contest", - "verbose_name_plural": "contests", - }, + name='contest', + options={'permissions': (('see_private_contest', 'See private contests'), ('edit_own_contest', 'Edit own contests'), ('edit_all_contest', 'Edit all contests'), ('clone_contest', 'Clone contest'), ('moss_contest', 'MOSS contest'), ('contest_rating', 'Rate contests'), ('contest_access_code', 'Contest access codes'), ('create_private_contest', 'Create private contests'), ('change_contest_visibility', 'Change contest visibility'), ('contest_problem_label', 'Edit contest problem label script')), 'verbose_name': 'contest', 'verbose_name_plural': 'contests'}, ), migrations.RemoveField( - model_name="contest", - name="hide_scoreboard", + model_name='contest', + name='hide_scoreboard', ), migrations.RemoveField( - model_name="contest", - name="organizers", + model_name='contest', + name='organizers', ), migrations.AddField( - model_name="contest", - name="authors", - field=models.ManyToManyField( - help_text="These users will be able to edit the contest.", - related_name="_contest_authors_+", - to="judge.Profile", - ), + model_name='contest', + name='authors', + field=models.ManyToManyField(help_text='These users will be able to edit the contest.', related_name='_contest_authors_+', to='judge.Profile'), ), migrations.AddField( - model_name="contest", - name="curators", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to edit the contest, but will not be listed as authors.", - related_name="_contest_curators_+", - to="judge.Profile", - ), + model_name='contest', + name='curators', + field=models.ManyToManyField(blank=True, help_text='These users will be able to edit the contest, but will not be listed as authors.', related_name='_contest_curators_+', to='judge.Profile'), ), migrations.AddField( - model_name="contest", - name="problem_label_script", - field=models.TextField( - blank=True, - help_text="A custom Lua function to generate problem labels. Requires a single function with an integer parameter, the zero-indexed contest problem index, and returns a string, the label.", - verbose_name="contest problem label script", - ), + model_name='contest', + name='problem_label_script', + field=models.TextField(blank=True, help_text='A custom Lua function to generate problem labels. Requires a single function with an integer parameter, the zero-indexed contest problem index, and returns a string, the label.', verbose_name='contest problem label script'), ), migrations.AddField( - model_name="contest", - name="scoreboard_visibility", - field=models.CharField( - choices=[ - ("V", "Visible"), - ("C", "Hidden for duration of contest"), - ("P", "Hidden for duration of participation"), - ], - default="V", - help_text="Scoreboard visibility through the duration of the contest", - max_length=1, - verbose_name="scoreboard visibility", - ), + model_name='contest', + name='scoreboard_visibility', + field=models.CharField(choices=[('V', 'Visible'), ('C', 'Hidden for duration of contest'), ('P', 'Hidden for duration of participation')], default='V', help_text='Scoreboard visibility through the duration of the contest', max_length=1, verbose_name='scoreboard visibility'), ), migrations.AddField( - model_name="contest", - name="testers", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to view the contest, but not edit it.", - related_name="_contest_testers_+", - to="judge.Profile", - ), + model_name='contest', + name='testers', + field=models.ManyToManyField(blank=True, help_text='These users will be able to view the contest, but not edit it.', related_name='_contest_testers_+', to='judge.Profile'), ), migrations.AddField( - model_name="contestparticipation", - name="tiebreaker", - field=models.FloatField(default=0.0, verbose_name="tie-breaking field"), + model_name='contestparticipation', + name='tiebreaker', + field=models.FloatField(default=0.0, verbose_name='tie-breaking field'), ), ] diff --git a/judge/migrations/0116_auto_20211011_0645.py b/judge/migrations/0116_auto_20211011_0645.py index 79eaa8d..c067a59 100644 --- a/judge/migrations/0116_auto_20211011_0645.py +++ b/judge/migrations/0116_auto_20211011_0645.py @@ -6,25 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0115_auto_20210525_0222"), + ('judge', '0115_auto_20210525_0222'), ] operations = [ migrations.AlterField( - model_name="contest", - name="format_name", - field=models.CharField( - choices=[ - ("atcoder", "AtCoder"), - ("default", "Default"), - ("ecoo", "ECOO"), - ("icpc", "ICPC"), - ("ioi", "IOI"), - ], - default="default", - help_text="The contest format module to use.", - max_length=32, - verbose_name="contest format", - ), + model_name='contest', + name='format_name', + field=models.CharField(choices=[('atcoder', 'AtCoder'), ('default', 'Default'), ('ecoo', 'ECOO'), ('icpc', 'ICPC'), ('ioi', 'IOI')], default='default', help_text='The contest format module to use.', max_length=32, verbose_name='contest format'), ), ] diff --git a/judge/migrations/0117_auto_20211209_0612.py b/judge/migrations/0117_auto_20211209_0612.py index 88c170f..dd3879a 100644 --- a/judge/migrations/0117_auto_20211209_0612.py +++ b/judge/migrations/0117_auto_20211209_0612.py @@ -6,671 +6,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("judge", "0116_auto_20211011_0645"), + ('judge', '0116_auto_20211011_0645'), ] operations = [ migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Nuuk", "Nuuk"), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kanton", "Kanton"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), + model_name='profile', + name='timezone', + field=models.CharField(choices=[('Africa', [('Africa/Abidjan', 'Abidjan'), ('Africa/Accra', 'Accra'), ('Africa/Addis_Ababa', 'Addis_Ababa'), ('Africa/Algiers', 'Algiers'), ('Africa/Asmara', 'Asmara'), ('Africa/Asmera', 'Asmera'), ('Africa/Bamako', 'Bamako'), ('Africa/Bangui', 'Bangui'), ('Africa/Banjul', 'Banjul'), ('Africa/Bissau', 'Bissau'), ('Africa/Blantyre', 'Blantyre'), ('Africa/Brazzaville', 'Brazzaville'), ('Africa/Bujumbura', 'Bujumbura'), ('Africa/Cairo', 'Cairo'), ('Africa/Casablanca', 'Casablanca'), ('Africa/Ceuta', 'Ceuta'), ('Africa/Conakry', 'Conakry'), ('Africa/Dakar', 'Dakar'), ('Africa/Dar_es_Salaam', 'Dar_es_Salaam'), ('Africa/Djibouti', 'Djibouti'), ('Africa/Douala', 'Douala'), ('Africa/El_Aaiun', 'El_Aaiun'), ('Africa/Freetown', 'Freetown'), ('Africa/Gaborone', 'Gaborone'), ('Africa/Harare', 'Harare'), ('Africa/Johannesburg', 'Johannesburg'), ('Africa/Juba', 'Juba'), ('Africa/Kampala', 'Kampala'), ('Africa/Khartoum', 'Khartoum'), ('Africa/Kigali', 'Kigali'), ('Africa/Kinshasa', 'Kinshasa'), ('Africa/Lagos', 'Lagos'), ('Africa/Libreville', 'Libreville'), ('Africa/Lome', 'Lome'), ('Africa/Luanda', 'Luanda'), ('Africa/Lubumbashi', 'Lubumbashi'), ('Africa/Lusaka', 'Lusaka'), ('Africa/Malabo', 'Malabo'), ('Africa/Maputo', 'Maputo'), ('Africa/Maseru', 'Maseru'), ('Africa/Mbabane', 'Mbabane'), ('Africa/Mogadishu', 'Mogadishu'), ('Africa/Monrovia', 'Monrovia'), ('Africa/Nairobi', 'Nairobi'), ('Africa/Ndjamena', 'Ndjamena'), ('Africa/Niamey', 'Niamey'), ('Africa/Nouakchott', 'Nouakchott'), ('Africa/Ouagadougou', 'Ouagadougou'), ('Africa/Porto-Novo', 'Porto-Novo'), ('Africa/Sao_Tome', 'Sao_Tome'), ('Africa/Timbuktu', 'Timbuktu'), ('Africa/Tripoli', 'Tripoli'), ('Africa/Tunis', 'Tunis'), ('Africa/Windhoek', 'Windhoek')]), ('America', [('America/Adak', 'Adak'), ('America/Anchorage', 'Anchorage'), ('America/Anguilla', 'Anguilla'), ('America/Antigua', 'Antigua'), ('America/Araguaina', 'Araguaina'), ('America/Argentina/Buenos_Aires', 'Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'Argentina/Cordoba'), ('America/Argentina/Jujuy', 'Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'Argentina/Salta'), ('America/Argentina/San_Juan', 'Argentina/San_Juan'), ('America/Argentina/San_Luis', 'Argentina/San_Luis'), ('America/Argentina/Tucuman', 'Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'Argentina/Ushuaia'), ('America/Aruba', 'Aruba'), ('America/Asuncion', 'Asuncion'), ('America/Atikokan', 'Atikokan'), ('America/Atka', 'Atka'), ('America/Bahia', 'Bahia'), ('America/Bahia_Banderas', 'Bahia_Banderas'), ('America/Barbados', 'Barbados'), ('America/Belem', 'Belem'), ('America/Belize', 'Belize'), ('America/Blanc-Sablon', 'Blanc-Sablon'), ('America/Boa_Vista', 'Boa_Vista'), ('America/Bogota', 'Bogota'), ('America/Boise', 'Boise'), ('America/Buenos_Aires', 'Buenos_Aires'), ('America/Cambridge_Bay', 'Cambridge_Bay'), ('America/Campo_Grande', 'Campo_Grande'), ('America/Cancun', 'Cancun'), ('America/Caracas', 'Caracas'), ('America/Catamarca', 'Catamarca'), ('America/Cayenne', 'Cayenne'), ('America/Cayman', 'Cayman'), ('America/Chicago', 'Chicago'), ('America/Chihuahua', 'Chihuahua'), ('America/Coral_Harbour', 'Coral_Harbour'), ('America/Cordoba', 'Cordoba'), ('America/Costa_Rica', 'Costa_Rica'), ('America/Creston', 'Creston'), ('America/Cuiaba', 'Cuiaba'), ('America/Curacao', 'Curacao'), ('America/Danmarkshavn', 'Danmarkshavn'), ('America/Dawson', 'Dawson'), ('America/Dawson_Creek', 'Dawson_Creek'), ('America/Denver', 'Denver'), ('America/Detroit', 'Detroit'), ('America/Dominica', 'Dominica'), ('America/Edmonton', 'Edmonton'), ('America/Eirunepe', 'Eirunepe'), ('America/El_Salvador', 'El_Salvador'), ('America/Ensenada', 'Ensenada'), ('America/Fort_Nelson', 'Fort_Nelson'), ('America/Fort_Wayne', 'Fort_Wayne'), ('America/Fortaleza', 'Fortaleza'), ('America/Glace_Bay', 'Glace_Bay'), ('America/Godthab', 'Godthab'), ('America/Goose_Bay', 'Goose_Bay'), ('America/Grand_Turk', 'Grand_Turk'), ('America/Grenada', 'Grenada'), ('America/Guadeloupe', 'Guadeloupe'), ('America/Guatemala', 'Guatemala'), ('America/Guayaquil', 'Guayaquil'), ('America/Guyana', 'Guyana'), ('America/Halifax', 'Halifax'), ('America/Havana', 'Havana'), ('America/Hermosillo', 'Hermosillo'), ('America/Indiana/Indianapolis', 'Indiana/Indianapolis'), ('America/Indiana/Knox', 'Indiana/Knox'), ('America/Indiana/Marengo', 'Indiana/Marengo'), ('America/Indiana/Petersburg', 'Indiana/Petersburg'), ('America/Indiana/Tell_City', 'Indiana/Tell_City'), ('America/Indiana/Vevay', 'Indiana/Vevay'), ('America/Indiana/Vincennes', 'Indiana/Vincennes'), ('America/Indiana/Winamac', 'Indiana/Winamac'), ('America/Indianapolis', 'Indianapolis'), ('America/Inuvik', 'Inuvik'), ('America/Iqaluit', 'Iqaluit'), ('America/Jamaica', 'Jamaica'), ('America/Jujuy', 'Jujuy'), ('America/Juneau', 'Juneau'), ('America/Kentucky/Louisville', 'Kentucky/Louisville'), ('America/Kentucky/Monticello', 'Kentucky/Monticello'), ('America/Knox_IN', 'Knox_IN'), ('America/Kralendijk', 'Kralendijk'), ('America/La_Paz', 'La_Paz'), ('America/Lima', 'Lima'), ('America/Los_Angeles', 'Los_Angeles'), ('America/Louisville', 'Louisville'), ('America/Lower_Princes', 'Lower_Princes'), ('America/Maceio', 'Maceio'), ('America/Managua', 'Managua'), ('America/Manaus', 'Manaus'), ('America/Marigot', 'Marigot'), ('America/Martinique', 'Martinique'), ('America/Matamoros', 'Matamoros'), ('America/Mazatlan', 'Mazatlan'), ('America/Mendoza', 'Mendoza'), ('America/Menominee', 'Menominee'), ('America/Merida', 'Merida'), ('America/Metlakatla', 'Metlakatla'), ('America/Mexico_City', 'Mexico_City'), ('America/Miquelon', 'Miquelon'), ('America/Moncton', 'Moncton'), ('America/Monterrey', 'Monterrey'), ('America/Montevideo', 'Montevideo'), ('America/Montreal', 'Montreal'), ('America/Montserrat', 'Montserrat'), ('America/Nassau', 'Nassau'), ('America/New_York', 'New_York'), ('America/Nipigon', 'Nipigon'), ('America/Nome', 'Nome'), ('America/Noronha', 'Noronha'), ('America/North_Dakota/Beulah', 'North_Dakota/Beulah'), ('America/North_Dakota/Center', 'North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'North_Dakota/New_Salem'), ('America/Nuuk', 'Nuuk'), ('America/Ojinaga', 'Ojinaga'), ('America/Panama', 'Panama'), ('America/Pangnirtung', 'Pangnirtung'), ('America/Paramaribo', 'Paramaribo'), ('America/Phoenix', 'Phoenix'), ('America/Port-au-Prince', 'Port-au-Prince'), ('America/Port_of_Spain', 'Port_of_Spain'), ('America/Porto_Acre', 'Porto_Acre'), ('America/Porto_Velho', 'Porto_Velho'), ('America/Puerto_Rico', 'Puerto_Rico'), ('America/Punta_Arenas', 'Punta_Arenas'), ('America/Rainy_River', 'Rainy_River'), ('America/Rankin_Inlet', 'Rankin_Inlet'), ('America/Recife', 'Recife'), ('America/Regina', 'Regina'), ('America/Resolute', 'Resolute'), ('America/Rio_Branco', 'Rio_Branco'), ('America/Rosario', 'Rosario'), ('America/Santa_Isabel', 'Santa_Isabel'), ('America/Santarem', 'Santarem'), ('America/Santiago', 'Santiago'), ('America/Santo_Domingo', 'Santo_Domingo'), ('America/Sao_Paulo', 'Sao_Paulo'), ('America/Scoresbysund', 'Scoresbysund'), ('America/Shiprock', 'Shiprock'), ('America/Sitka', 'Sitka'), ('America/St_Barthelemy', 'St_Barthelemy'), ('America/St_Johns', 'St_Johns'), ('America/St_Kitts', 'St_Kitts'), ('America/St_Lucia', 'St_Lucia'), ('America/St_Thomas', 'St_Thomas'), ('America/St_Vincent', 'St_Vincent'), ('America/Swift_Current', 'Swift_Current'), ('America/Tegucigalpa', 'Tegucigalpa'), ('America/Thule', 'Thule'), ('America/Thunder_Bay', 'Thunder_Bay'), ('America/Tijuana', 'Tijuana'), ('America/Toronto', 'Toronto'), ('America/Tortola', 'Tortola'), ('America/Vancouver', 'Vancouver'), ('America/Virgin', 'Virgin'), ('America/Whitehorse', 'Whitehorse'), ('America/Winnipeg', 'Winnipeg'), ('America/Yakutat', 'Yakutat'), ('America/Yellowknife', 'Yellowknife')]), ('Antarctica', [('Antarctica/Casey', 'Casey'), ('Antarctica/Davis', 'Davis'), ('Antarctica/DumontDUrville', 'DumontDUrville'), ('Antarctica/Macquarie', 'Macquarie'), ('Antarctica/Mawson', 'Mawson'), ('Antarctica/McMurdo', 'McMurdo'), ('Antarctica/Palmer', 'Palmer'), ('Antarctica/Rothera', 'Rothera'), ('Antarctica/South_Pole', 'South_Pole'), ('Antarctica/Syowa', 'Syowa'), ('Antarctica/Troll', 'Troll'), ('Antarctica/Vostok', 'Vostok')]), ('Arctic', [('Arctic/Longyearbyen', 'Longyearbyen')]), ('Asia', [('Asia/Aden', 'Aden'), ('Asia/Almaty', 'Almaty'), ('Asia/Amman', 'Amman'), ('Asia/Anadyr', 'Anadyr'), ('Asia/Aqtau', 'Aqtau'), ('Asia/Aqtobe', 'Aqtobe'), ('Asia/Ashgabat', 'Ashgabat'), ('Asia/Ashkhabad', 'Ashkhabad'), ('Asia/Atyrau', 'Atyrau'), ('Asia/Baghdad', 'Baghdad'), ('Asia/Bahrain', 'Bahrain'), ('Asia/Baku', 'Baku'), ('Asia/Bangkok', 'Bangkok'), ('Asia/Barnaul', 'Barnaul'), ('Asia/Beirut', 'Beirut'), ('Asia/Bishkek', 'Bishkek'), ('Asia/Brunei', 'Brunei'), ('Asia/Calcutta', 'Calcutta'), ('Asia/Chita', 'Chita'), ('Asia/Choibalsan', 'Choibalsan'), ('Asia/Chongqing', 'Chongqing'), ('Asia/Chungking', 'Chungking'), ('Asia/Colombo', 'Colombo'), ('Asia/Dacca', 'Dacca'), ('Asia/Damascus', 'Damascus'), ('Asia/Dhaka', 'Dhaka'), ('Asia/Dili', 'Dili'), ('Asia/Dubai', 'Dubai'), ('Asia/Dushanbe', 'Dushanbe'), ('Asia/Famagusta', 'Famagusta'), ('Asia/Gaza', 'Gaza'), ('Asia/Harbin', 'Harbin'), ('Asia/Hebron', 'Hebron'), ('Asia/Ho_Chi_Minh', 'Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Hong_Kong'), ('Asia/Hovd', 'Hovd'), ('Asia/Irkutsk', 'Irkutsk'), ('Asia/Istanbul', 'Istanbul'), ('Asia/Jakarta', 'Jakarta'), ('Asia/Jayapura', 'Jayapura'), ('Asia/Jerusalem', 'Jerusalem'), ('Asia/Kabul', 'Kabul'), ('Asia/Kamchatka', 'Kamchatka'), ('Asia/Karachi', 'Karachi'), ('Asia/Kashgar', 'Kashgar'), ('Asia/Kathmandu', 'Kathmandu'), ('Asia/Katmandu', 'Katmandu'), ('Asia/Khandyga', 'Khandyga'), ('Asia/Kolkata', 'Kolkata'), ('Asia/Krasnoyarsk', 'Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Kuala_Lumpur'), ('Asia/Kuching', 'Kuching'), ('Asia/Kuwait', 'Kuwait'), ('Asia/Macao', 'Macao'), ('Asia/Macau', 'Macau'), ('Asia/Magadan', 'Magadan'), ('Asia/Makassar', 'Makassar'), ('Asia/Manila', 'Manila'), ('Asia/Muscat', 'Muscat'), ('Asia/Nicosia', 'Nicosia'), ('Asia/Novokuznetsk', 'Novokuznetsk'), ('Asia/Novosibirsk', 'Novosibirsk'), ('Asia/Omsk', 'Omsk'), ('Asia/Oral', 'Oral'), ('Asia/Phnom_Penh', 'Phnom_Penh'), ('Asia/Pontianak', 'Pontianak'), ('Asia/Pyongyang', 'Pyongyang'), ('Asia/Qatar', 'Qatar'), ('Asia/Qostanay', 'Qostanay'), ('Asia/Qyzylorda', 'Qyzylorda'), ('Asia/Rangoon', 'Rangoon'), ('Asia/Riyadh', 'Riyadh'), ('Asia/Saigon', 'Saigon'), ('Asia/Sakhalin', 'Sakhalin'), ('Asia/Samarkand', 'Samarkand'), ('Asia/Seoul', 'Seoul'), ('Asia/Shanghai', 'Shanghai'), ('Asia/Singapore', 'Singapore'), ('Asia/Srednekolymsk', 'Srednekolymsk'), ('Asia/Taipei', 'Taipei'), ('Asia/Tashkent', 'Tashkent'), ('Asia/Tbilisi', 'Tbilisi'), ('Asia/Tehran', 'Tehran'), ('Asia/Tel_Aviv', 'Tel_Aviv'), ('Asia/Thimbu', 'Thimbu'), ('Asia/Thimphu', 'Thimphu'), ('Asia/Tokyo', 'Tokyo'), ('Asia/Tomsk', 'Tomsk'), ('Asia/Ujung_Pandang', 'Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Ulaanbaatar'), ('Asia/Ulan_Bator', 'Ulan_Bator'), ('Asia/Urumqi', 'Urumqi'), ('Asia/Ust-Nera', 'Ust-Nera'), ('Asia/Vientiane', 'Vientiane'), ('Asia/Vladivostok', 'Vladivostok'), ('Asia/Yakutsk', 'Yakutsk'), ('Asia/Yangon', 'Yangon'), ('Asia/Yekaterinburg', 'Yekaterinburg'), ('Asia/Yerevan', 'Yerevan')]), ('Atlantic', [('Atlantic/Azores', 'Azores'), ('Atlantic/Bermuda', 'Bermuda'), ('Atlantic/Canary', 'Canary'), ('Atlantic/Cape_Verde', 'Cape_Verde'), ('Atlantic/Faeroe', 'Faeroe'), ('Atlantic/Faroe', 'Faroe'), ('Atlantic/Jan_Mayen', 'Jan_Mayen'), ('Atlantic/Madeira', 'Madeira'), ('Atlantic/Reykjavik', 'Reykjavik'), ('Atlantic/South_Georgia', 'South_Georgia'), ('Atlantic/St_Helena', 'St_Helena'), ('Atlantic/Stanley', 'Stanley')]), ('Australia', [('Australia/ACT', 'ACT'), ('Australia/Adelaide', 'Adelaide'), ('Australia/Brisbane', 'Brisbane'), ('Australia/Broken_Hill', 'Broken_Hill'), ('Australia/Canberra', 'Canberra'), ('Australia/Currie', 'Currie'), ('Australia/Darwin', 'Darwin'), ('Australia/Eucla', 'Eucla'), ('Australia/Hobart', 'Hobart'), ('Australia/LHI', 'LHI'), ('Australia/Lindeman', 'Lindeman'), ('Australia/Lord_Howe', 'Lord_Howe'), ('Australia/Melbourne', 'Melbourne'), ('Australia/NSW', 'NSW'), ('Australia/North', 'North'), ('Australia/Perth', 'Perth'), ('Australia/Queensland', 'Queensland'), ('Australia/South', 'South'), ('Australia/Sydney', 'Sydney'), ('Australia/Tasmania', 'Tasmania'), ('Australia/Victoria', 'Victoria'), ('Australia/West', 'West'), ('Australia/Yancowinna', 'Yancowinna')]), ('Brazil', [('Brazil/Acre', 'Acre'), ('Brazil/DeNoronha', 'DeNoronha'), ('Brazil/East', 'East'), ('Brazil/West', 'West')]), ('Canada', [('Canada/Atlantic', 'Atlantic'), ('Canada/Central', 'Central'), ('Canada/Eastern', 'Eastern'), ('Canada/Mountain', 'Mountain'), ('Canada/Newfoundland', 'Newfoundland'), ('Canada/Pacific', 'Pacific'), ('Canada/Saskatchewan', 'Saskatchewan'), ('Canada/Yukon', 'Yukon')]), ('Chile', [('Chile/Continental', 'Continental'), ('Chile/EasterIsland', 'EasterIsland')]), ('Etc', [('Etc/Greenwich', 'Greenwich'), ('Etc/UCT', 'UCT'), ('Etc/UTC', 'UTC'), ('Etc/Universal', 'Universal'), ('Etc/Zulu', 'Zulu')]), ('Europe', [('Europe/Amsterdam', 'Amsterdam'), ('Europe/Andorra', 'Andorra'), ('Europe/Astrakhan', 'Astrakhan'), ('Europe/Athens', 'Athens'), ('Europe/Belfast', 'Belfast'), ('Europe/Belgrade', 'Belgrade'), ('Europe/Berlin', 'Berlin'), ('Europe/Bratislava', 'Bratislava'), ('Europe/Brussels', 'Brussels'), ('Europe/Bucharest', 'Bucharest'), ('Europe/Budapest', 'Budapest'), ('Europe/Busingen', 'Busingen'), ('Europe/Chisinau', 'Chisinau'), ('Europe/Copenhagen', 'Copenhagen'), ('Europe/Dublin', 'Dublin'), ('Europe/Gibraltar', 'Gibraltar'), ('Europe/Guernsey', 'Guernsey'), ('Europe/Helsinki', 'Helsinki'), ('Europe/Isle_of_Man', 'Isle_of_Man'), ('Europe/Istanbul', 'Istanbul'), ('Europe/Jersey', 'Jersey'), ('Europe/Kaliningrad', 'Kaliningrad'), ('Europe/Kiev', 'Kiev'), ('Europe/Kirov', 'Kirov'), ('Europe/Lisbon', 'Lisbon'), ('Europe/Ljubljana', 'Ljubljana'), ('Europe/London', 'London'), ('Europe/Luxembourg', 'Luxembourg'), ('Europe/Madrid', 'Madrid'), ('Europe/Malta', 'Malta'), ('Europe/Mariehamn', 'Mariehamn'), ('Europe/Minsk', 'Minsk'), ('Europe/Monaco', 'Monaco'), ('Europe/Moscow', 'Moscow'), ('Europe/Nicosia', 'Nicosia'), ('Europe/Oslo', 'Oslo'), ('Europe/Paris', 'Paris'), ('Europe/Podgorica', 'Podgorica'), ('Europe/Prague', 'Prague'), ('Europe/Riga', 'Riga'), ('Europe/Rome', 'Rome'), ('Europe/Samara', 'Samara'), ('Europe/San_Marino', 'San_Marino'), ('Europe/Sarajevo', 'Sarajevo'), ('Europe/Saratov', 'Saratov'), ('Europe/Simferopol', 'Simferopol'), ('Europe/Skopje', 'Skopje'), ('Europe/Sofia', 'Sofia'), ('Europe/Stockholm', 'Stockholm'), ('Europe/Tallinn', 'Tallinn'), ('Europe/Tirane', 'Tirane'), ('Europe/Tiraspol', 'Tiraspol'), ('Europe/Ulyanovsk', 'Ulyanovsk'), ('Europe/Uzhgorod', 'Uzhgorod'), ('Europe/Vaduz', 'Vaduz'), ('Europe/Vatican', 'Vatican'), ('Europe/Vienna', 'Vienna'), ('Europe/Vilnius', 'Vilnius'), ('Europe/Volgograd', 'Volgograd'), ('Europe/Warsaw', 'Warsaw'), ('Europe/Zagreb', 'Zagreb'), ('Europe/Zaporozhye', 'Zaporozhye'), ('Europe/Zurich', 'Zurich')]), ('Indian', [('Indian/Antananarivo', 'Antananarivo'), ('Indian/Chagos', 'Chagos'), ('Indian/Christmas', 'Christmas'), ('Indian/Cocos', 'Cocos'), ('Indian/Comoro', 'Comoro'), ('Indian/Kerguelen', 'Kerguelen'), ('Indian/Mahe', 'Mahe'), ('Indian/Maldives', 'Maldives'), ('Indian/Mauritius', 'Mauritius'), ('Indian/Mayotte', 'Mayotte'), ('Indian/Reunion', 'Reunion')]), ('Mexico', [('Mexico/BajaNorte', 'BajaNorte'), ('Mexico/BajaSur', 'BajaSur'), ('Mexico/General', 'General')]), ('Other', [('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')]), ('Pacific', [('Pacific/Apia', 'Apia'), ('Pacific/Auckland', 'Auckland'), ('Pacific/Bougainville', 'Bougainville'), ('Pacific/Chatham', 'Chatham'), ('Pacific/Chuuk', 'Chuuk'), ('Pacific/Easter', 'Easter'), ('Pacific/Efate', 'Efate'), ('Pacific/Enderbury', 'Enderbury'), ('Pacific/Fakaofo', 'Fakaofo'), ('Pacific/Fiji', 'Fiji'), ('Pacific/Funafuti', 'Funafuti'), ('Pacific/Galapagos', 'Galapagos'), ('Pacific/Gambier', 'Gambier'), ('Pacific/Guadalcanal', 'Guadalcanal'), ('Pacific/Guam', 'Guam'), ('Pacific/Honolulu', 'Honolulu'), ('Pacific/Johnston', 'Johnston'), ('Pacific/Kanton', 'Kanton'), ('Pacific/Kiritimati', 'Kiritimati'), ('Pacific/Kosrae', 'Kosrae'), ('Pacific/Kwajalein', 'Kwajalein'), ('Pacific/Majuro', 'Majuro'), ('Pacific/Marquesas', 'Marquesas'), ('Pacific/Midway', 'Midway'), ('Pacific/Nauru', 'Nauru'), ('Pacific/Niue', 'Niue'), ('Pacific/Norfolk', 'Norfolk'), ('Pacific/Noumea', 'Noumea'), ('Pacific/Pago_Pago', 'Pago_Pago'), ('Pacific/Palau', 'Palau'), ('Pacific/Pitcairn', 'Pitcairn'), ('Pacific/Pohnpei', 'Pohnpei'), ('Pacific/Ponape', 'Ponape'), ('Pacific/Port_Moresby', 'Port_Moresby'), ('Pacific/Rarotonga', 'Rarotonga'), ('Pacific/Saipan', 'Saipan'), ('Pacific/Samoa', 'Samoa'), ('Pacific/Tahiti', 'Tahiti'), ('Pacific/Tarawa', 'Tarawa'), ('Pacific/Tongatapu', 'Tongatapu'), ('Pacific/Truk', 'Truk'), ('Pacific/Wake', 'Wake'), ('Pacific/Wallis', 'Wallis'), ('Pacific/Yap', 'Yap')]), ('US', [('US/Alaska', 'Alaska'), ('US/Aleutian', 'Aleutian'), ('US/Arizona', 'Arizona'), ('US/Central', 'Central'), ('US/East-Indiana', 'East-Indiana'), ('US/Eastern', 'Eastern'), ('US/Hawaii', 'Hawaii'), ('US/Indiana-Starke', 'Indiana-Starke'), ('US/Michigan', 'Michigan'), ('US/Mountain', 'Mountain'), ('US/Pacific', 'Pacific'), ('US/Samoa', 'Samoa')])], default='Asia/Ho_Chi_Minh', max_length=50, verbose_name='location'), ), ] diff --git a/judge/migrations/0118_rating.py b/judge/migrations/0118_rating.py index fb62906..8ad6ad2 100644 --- a/judge/migrations/0118_rating.py +++ b/judge/migrations/0118_rating.py @@ -7,7 +7,7 @@ from django.db.models.functions import Coalesce from django.utils import timezone -def tie_ranker(iterable, key=attrgetter("points")): +def tie_ranker(iterable, key=attrgetter('points')): rank = 0 delta = 1 last = None @@ -53,9 +53,7 @@ def WP(RA, RB, VA, VB): return (math.erf((RB - RA) / math.sqrt(2 * (VA * VA + VB * VB))) + 1) / 2.0 -def recalculate_ratings( - old_rating, old_volatility, actual_rank, times_rated, is_disqualified -): +def recalculate_ratings(old_rating, old_volatility, actual_rank, times_rated, is_disqualified): # actual_rank: 1 is first place, N is last place # if there are ties, use the average of places (if places 2, 3, 4, 5 tie, use 3.5 for all of them) @@ -76,9 +74,7 @@ def recalculate_ratings( for i in range(N): ERank = 0.5 for j in range(N): - ERank += WP( - old_rating[i], old_rating[j], old_volatility[i], old_volatility[j] - ) + ERank += WP(old_rating[i], old_rating[j], old_volatility[i], old_volatility[j]) EPerf = -normal_CDF_inverse((ERank - 0.5) / N) APerf = -normal_CDF_inverse((actual_rank[i] - 0.5) / N) @@ -102,10 +98,8 @@ def recalculate_ratings( if times_rated[i] == 0: new_volatility[i] = 385 else: - new_volatility[i] = math.sqrt( - ((new_rating[i] - old_rating[i]) ** 2) / Weight - + (old_volatility[i] ** 2) / (Weight + 1) - ) + new_volatility[i] = math.sqrt(((new_rating[i] - old_rating[i]) ** 2) / Weight + + (old_volatility[i] ** 2) / (Weight + 1)) if is_disqualified[i]: # DQed users can manipulate TopCoder ratings to get higher volatility in order to increase their rating @@ -118,49 +112,23 @@ def recalculate_ratings( # inflate a little if we have to so people who placed first don't lose rating best_rank = min(actual_rank) for i in range(N): - if ( - abs(actual_rank[i] - best_rank) <= 1e-3 - and new_rating[i] < old_rating[i] + 1 - ): + if abs(actual_rank[i] - best_rank) <= 1e-3 and new_rating[i] < old_rating[i] + 1: new_rating[i] = old_rating[i] + 1 - return list(map(int, map(round, new_rating))), list( - map(int, map(round, new_volatility)) - ) + return list(map(int, map(round, new_rating))), list(map(int, map(round, new_volatility))) def tc_rate_contest(contest, Rating, Profile): - rating_subquery = Rating.objects.filter(user=OuterRef("user")) - rating_sorted = rating_subquery.order_by("-contest__end_time") - users = ( - contest.users.order_by("is_disqualified", "-score", "cumtime", "tiebreaker") - .annotate( - submissions=Count("submission"), - last_rating=Coalesce(Subquery(rating_sorted.values("rating")[:1]), 1200), - volatility=Coalesce(Subquery(rating_sorted.values("volatility")[:1]), 535), - times=Coalesce( - Subquery( - rating_subquery.order_by() - .values("user_id") - .annotate(count=Count("id")) - .values("count") - ), - 0, - ), - ) - .exclude(user_id__in=contest.rate_exclude.all()) - .filter(virtual=0) - .values( - "id", - "user_id", - "score", - "cumtime", - "tiebreaker", - "is_disqualified", - "last_rating", - "volatility", - "times", - ) - ) + rating_subquery = Rating.objects.filter(user=OuterRef('user')) + rating_sorted = rating_subquery.order_by('-contest__end_time') + users = contest.users.order_by('is_disqualified', '-score', 'cumtime', 'tiebreaker') \ + .annotate(submissions=Count('submission'), + last_rating=Coalesce(Subquery(rating_sorted.values('rating')[:1]), 1200), + volatility=Coalesce(Subquery(rating_sorted.values('volatility')[:1]), 535), + times=Coalesce(Subquery(rating_subquery.order_by().values('user_id') + .annotate(count=Count('id')).values('count')), 0)) \ + .exclude(user_id__in=contest.rate_exclude.all()) \ + .filter(virtual=0).values('id', 'user_id', 'score', 'cumtime', 'tiebreaker', 'is_disqualified', + 'last_rating', 'volatility', 'times') if not contest.rate_all: users = users.filter(submissions__gt=0) if contest.rating_floor is not None: @@ -169,68 +137,46 @@ def tc_rate_contest(contest, Rating, Profile): users = users.exclude(last_rating__gt=contest.rating_ceiling) users = list(users) - participation_ids = list(map(itemgetter("id"), users)) - user_ids = list(map(itemgetter("user_id"), users)) - is_disqualified = list(map(itemgetter("is_disqualified"), users)) - ranking = list(tie_ranker(users, key=itemgetter("score", "cumtime", "tiebreaker"))) - old_rating = list(map(itemgetter("last_rating"), users)) - old_volatility = list(map(itemgetter("volatility"), users)) - times_ranked = list(map(itemgetter("times"), users)) - rating, volatility = recalculate_ratings( - old_rating, old_volatility, ranking, times_ranked, is_disqualified - ) + participation_ids = list(map(itemgetter('id'), users)) + user_ids = list(map(itemgetter('user_id'), users)) + is_disqualified = list(map(itemgetter('is_disqualified'), users)) + ranking = list(tie_ranker(users, key=itemgetter('score', 'cumtime', 'tiebreaker'))) + old_rating = list(map(itemgetter('last_rating'), users)) + old_volatility = list(map(itemgetter('volatility'), users)) + times_ranked = list(map(itemgetter('times'), users)) + rating, volatility = recalculate_ratings(old_rating, old_volatility, ranking, times_ranked, is_disqualified) now = timezone.now() - ratings = [ - Rating( - user_id=i, - contest=contest, - rating=r, - volatility=v, - last_rated=now, - participation_id=p, - rank=z, - ) - for i, p, r, v, z in zip( - user_ids, participation_ids, rating, volatility, ranking - ) - ] + ratings = [Rating(user_id=i, contest=contest, rating=r, volatility=v, last_rated=now, participation_id=p, rank=z) + for i, p, r, v, z in zip(user_ids, participation_ids, rating, volatility, ranking)] Rating.objects.bulk_create(ratings) - Profile.objects.filter( - contest_history__contest=contest, contest_history__virtual=0 - ).update( - rating=Subquery( - Rating.objects.filter(user=OuterRef("id")) - .order_by("-contest__end_time") - .values("rating")[:1] - ) - ) + Profile.objects.filter(contest_history__contest=contest, contest_history__virtual=0).update( + rating=Subquery(Rating.objects.filter(user=OuterRef('id')) + .order_by('-contest__end_time').values('rating')[:1])) # inspired by rate_all_view def rate_tc(apps, schema_editor): - Contest = apps.get_model("judge", "Contest") - Rating = apps.get_model("judge", "Rating") - Profile = apps.get_model("judge", "Profile") + Contest = apps.get_model('judge', 'Contest') + Rating = apps.get_model('judge', 'Rating') + Profile = apps.get_model('judge', 'Profile') with schema_editor.connection.cursor() as cursor: - cursor.execute("TRUNCATE TABLE `%s`" % Rating._meta.db_table) + cursor.execute('TRUNCATE TABLE `%s`' % Rating._meta.db_table) Profile.objects.update(rating=None) - for contest in Contest.objects.filter( - is_rated=True, end_time__lte=timezone.now() - ).order_by("end_time"): + for contest in Contest.objects.filter(is_rated=True, end_time__lte=timezone.now()).order_by('end_time'): tc_rate_contest(contest, Rating, Profile) # inspired by rate_all_view def rate_elo_mmr(apps, schema_editor): - Rating = apps.get_model("judge", "Rating") - Profile = apps.get_model("judge", "Profile") + Rating = apps.get_model('judge', 'Rating') + Profile = apps.get_model('judge', 'Profile') with schema_editor.connection.cursor() as cursor: - cursor.execute("TRUNCATE TABLE `%s`" % Rating._meta.db_table) + cursor.execute('TRUNCATE TABLE `%s`' % Rating._meta.db_table) Profile.objects.update(rating=None) # Don't populate Rating @@ -238,25 +184,25 @@ def rate_elo_mmr(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("judge", "0117_auto_20211209_0612"), + ('judge', '0117_auto_20211209_0612'), ] operations = [ migrations.RunPython(migrations.RunPython.noop, rate_tc, atomic=True), migrations.AddField( - model_name="rating", - name="mean", - field=models.FloatField(verbose_name="raw rating"), + model_name='rating', + name='mean', + field=models.FloatField(verbose_name='raw rating'), ), migrations.AddField( - model_name="rating", - name="performance", - field=models.FloatField(verbose_name="contest performance"), + model_name='rating', + name='performance', + field=models.FloatField(verbose_name='contest performance'), ), migrations.RemoveField( - model_name="rating", - name="volatility", - field=models.IntegerField(verbose_name="volatility"), + model_name='rating', + name='volatility', + field=models.IntegerField(verbose_name='volatility'), ), migrations.RunPython(rate_elo_mmr, migrations.RunPython.noop, atomic=True), - ] + ] \ No newline at end of file diff --git a/judge/migrations/0119_auto_20220306_0512.py b/judge/migrations/0119_auto_20220306_0512.py deleted file mode 100644 index abcf1fb..0000000 --- a/judge/migrations/0119_auto_20220306_0512.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 2.2.25 on 2022-03-05 22:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0118_rating"), - ] - - operations = [ - migrations.AlterField( - model_name="contest", - name="hide_problem_tags", - field=models.BooleanField( - default=True, - help_text="Whether problem tags should be hidden by default.", - verbose_name="hide problem tags", - ), - ), - ] diff --git a/judge/migrations/0120_auto_20220306_1124.py b/judge/migrations/0120_auto_20220306_1124.py deleted file mode 100644 index 5e02189..0000000 --- a/judge/migrations/0120_auto_20220306_1124.py +++ /dev/null @@ -1,75 +0,0 @@ -# Generated by Django 2.2.25 on 2022-03-06 04:24 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0119_auto_20220306_0512"), - ] - - operations = [ - migrations.AddField( - model_name="profile", - name="is_banned_problem_voting", - field=models.BooleanField( - default=False, - help_text="User will not be able to vote on problems' point values.", - verbose_name="banned from voting", - ), - ), - migrations.CreateModel( - name="ProblemPointsVote", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "points", - models.IntegerField( - help_text="The amount of points you think this problem deserves.", - validators=[ - django.core.validators.MinValueValidator(100), - django.core.validators.MaxValueValidator(600), - ], - verbose_name="proposed point value", - ), - ), - ( - "vote_time", - models.DateTimeField( - auto_now_add=True, verbose_name="The time this vote was cast" - ), - ), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="problem_points_votes", - to="judge.Problem", - ), - ), - ( - "voter", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="problem_points_votes", - to="judge.Profile", - ), - ), - ], - options={ - "verbose_name": "vote", - "verbose_name_plural": "votes", - }, - ), - ] diff --git a/judge/migrations/0121_auto_20220415_0135.py b/judge/migrations/0121_auto_20220415_0135.py deleted file mode 100644 index 77d3f6a..0000000 --- a/judge/migrations/0121_auto_20220415_0135.py +++ /dev/null @@ -1,76 +0,0 @@ -# Generated by Django 2.2.25 on 2022-04-14 18:35 - -import django.core.validators -from django.db import migrations, models -import judge.models.problem_data -import judge.utils.problem_data - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0120_auto_20220306_1124"), - ] - - operations = [ - migrations.AddField( - model_name="problemdata", - name="interactive_judge", - field=models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - validators=[ - django.core.validators.FileExtensionValidator( - allowed_extensions=["cpp"] - ) - ], - verbose_name="interactive judge", - ), - ), - migrations.AlterField( - model_name="problemdata", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker (PY)"), - ("customval", "Custom validator (CPP)"), - ("interact", "Interactive"), - ], - max_length=10, - verbose_name="checker", - ), - ), - migrations.AlterField( - model_name="problemtestcase", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker (PY)"), - ("customval", "Custom validator (CPP)"), - ("interact", "Interactive"), - ], - max_length=10, - verbose_name="checker", - ), - ), - ] diff --git a/judge/migrations/0122_auto_20220425_1202.py b/judge/migrations/0122_auto_20220425_1202.py deleted file mode 100644 index 427cd98..0000000 --- a/judge/migrations/0122_auto_20220425_1202.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.25 on 2022-04-25 05:02 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0121_auto_20220415_0135"), - ] - - operations = [ - migrations.AlterField( - model_name="contest", - name="time_limit", - field=models.DurationField( - blank=True, - help_text="Format hh:mm:ss. For example, if you want a 2-hour contest, enter 02:00:00", - null=True, - verbose_name="time limit", - ), - ), - ] diff --git a/judge/migrations/0123_auto_20220502_2356.py b/judge/migrations/0123_auto_20220502_2356.py deleted file mode 100644 index 54ed0c7..0000000 --- a/judge/migrations/0123_auto_20220502_2356.py +++ /dev/null @@ -1,91 +0,0 @@ -# Generated by Django 2.2.25 on 2022-05-02 16:56 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0122_auto_20220425_1202"), - ] - - operations = [ - migrations.AlterModelOptions( - name="problem", - options={ - "permissions": ( - ("see_private_problem", "See hidden problems"), - ("edit_own_problem", "Edit own problems"), - ("edit_all_problem", "Edit all problems"), - ("edit_public_problem", "Edit all public problems"), - ("clone_problem", "Clone problem"), - ("change_public_visibility", "Change is_public field"), - ("change_manually_managed", "Change is_manually_managed field"), - ("see_organization_problem", "See organization-private problems"), - ("suggest_problem_changes", "Suggest changes to problem"), - ), - "verbose_name": "problem", - "verbose_name_plural": "problems", - }, - ), - migrations.CreateModel( - name="VolunteerProblemVote", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("time", models.DateTimeField(auto_now_add=True)), - ( - "knowledge_points", - models.PositiveIntegerField( - help_text="Points awarded by knowledge difficulty", - verbose_name="knowledge points", - ), - ), - ( - "thinking_points", - models.PositiveIntegerField( - help_text="Points awarded by thinking difficulty", - verbose_name="thinking points", - ), - ), - ("feedback", models.TextField(blank=True, verbose_name="feedback")), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="volunteer_user_votes", - to="judge.Problem", - ), - ), - ( - "types", - models.ManyToManyField( - help_text="The type of problem, as shown on the problem's page.", - to="judge.ProblemType", - verbose_name="problem types", - ), - ), - ( - "voter", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="volunteer_problem_votes", - to="judge.Profile", - ), - ), - ], - options={ - "verbose_name": "volunteer vote", - "verbose_name_plural": "volunteer votes", - "unique_together": {("voter", "problem")}, - }, - ), - ] diff --git a/judge/migrations/0124_auto_20220602_1116.py b/judge/migrations/0124_auto_20220602_1116.py deleted file mode 100644 index 5b15aef..0000000 --- a/judge/migrations/0124_auto_20220602_1116.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 2.2.25 on 2022-06-02 04:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0123_auto_20220502_2356"), - ] - - operations = [ - migrations.AddField( - model_name="problemdata", - name="fileio_input", - field=models.TextField( - blank=True, null=True, verbose_name="input file name" - ), - ), - migrations.AddField( - model_name="problemdata", - name="fileio_output", - field=models.TextField( - blank=True, null=True, verbose_name="output file name" - ), - ), - ] diff --git a/judge/migrations/0125_auto_20220602_1216.py b/judge/migrations/0125_auto_20220602_1216.py deleted file mode 100644 index 18bee1c..0000000 --- a/judge/migrations/0125_auto_20220602_1216.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 2.2.25 on 2022-06-02 05:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0124_auto_20220602_1116"), - ] - - operations = [ - migrations.AlterField( - model_name="problemdata", - name="fileio_input", - field=models.TextField( - blank=True, - help_text="Leave empty for stdin", - null=True, - verbose_name="input file name", - ), - ), - migrations.AlterField( - model_name="problemdata", - name="fileio_output", - field=models.TextField( - blank=True, - help_text="Leave empty for stdout", - null=True, - verbose_name="output file name", - ), - ), - ] diff --git a/judge/migrations/0126_languagetemplate.py b/judge/migrations/0126_languagetemplate.py deleted file mode 100644 index 7555905..0000000 --- a/judge/migrations/0126_languagetemplate.py +++ /dev/null @@ -1,54 +0,0 @@ -# Generated by Django 2.2.25 on 2022-06-12 06:59 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0125_auto_20220602_1216"), - ] - - operations = [ - migrations.CreateModel( - name="LanguageTemplate", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "source", - models.TextField(max_length=65536, verbose_name="source code"), - ), - ( - "language", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.Language", - verbose_name="language", - ), - ), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="language_templates", - to="judge.Problem", - verbose_name="problem", - ), - ), - ], - options={ - "verbose_name": "language-specific template", - "verbose_name_plural": "language-specific templates", - "unique_together": {("problem", "language")}, - }, - ), - ] diff --git a/judge/migrations/0127_auto_20220616_1442.py b/judge/migrations/0127_auto_20220616_1442.py deleted file mode 100644 index e5a8af2..0000000 --- a/judge/migrations/0127_auto_20220616_1442.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 2.2.25 on 2022-06-16 07:42 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0126_languagetemplate"), - ] - - operations = [ - migrations.AlterModelOptions( - name="submission", - options={ - "permissions": ( - ("abort_any_submission", "Abort any submission"), - ("rejudge_submission", "Rejudge the submission"), - ("rejudge_submission_lot", "Rejudge a lot of submissions"), - ("spam_submission", "Submit without limit"), - ("view_all_submission", "View all submission"), - ("resubmit_other", "Resubmit others' submission"), - ("view_public_submission", "View public submissions"), - ), - "verbose_name": "submission", - "verbose_name_plural": "submissions", - }, - ), - ] diff --git a/judge/migrations/0128_auto_20220620_2210.py b/judge/migrations/0128_auto_20220620_2210.py deleted file mode 100644 index 27fed32..0000000 --- a/judge/migrations/0128_auto_20220620_2210.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 2.2.25 on 2022-06-20 15:10 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0127_auto_20220616_1442"), - ] - - operations = [ - migrations.AlterField( - model_name="problem", - name="memory_limit", - field=models.PositiveIntegerField( - help_text="The memory limit for this problem, in kilobytes (e.g. 256mb = 262144 kilobytes).", - validators=[ - django.core.validators.MinValueValidator(0), - django.core.validators.MaxValueValidator(1048576), - ], - verbose_name="memory limit", - ), - ), - ] diff --git a/judge/migrations/0129_auto_20220622_1424.py b/judge/migrations/0129_auto_20220622_1424.py deleted file mode 100644 index ab1356a..0000000 --- a/judge/migrations/0129_auto_20220622_1424.py +++ /dev/null @@ -1,59 +0,0 @@ -# Generated by Django 2.2.25 on 2022-06-22 07:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0128_auto_20220620_2210"), - ] - - operations = [ - migrations.AlterField( - model_name="problemdata", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker (PY)"), - ("customval", "Custom validator (CPP)"), - ("interact", "Interactive"), - ("testlib", "Testlib"), - ], - max_length=10, - verbose_name="checker", - ), - ), - migrations.AlterField( - model_name="problemtestcase", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker (PY)"), - ("customval", "Custom validator (CPP)"), - ("interact", "Interactive"), - ("testlib", "Testlib"), - ], - max_length=10, - verbose_name="checker", - ), - ), - ] diff --git a/judge/migrations/0130_auto_20220831_1048.py b/judge/migrations/0130_auto_20220831_1048.py deleted file mode 100644 index 6bb3478..0000000 --- a/judge/migrations/0130_auto_20220831_1048.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 2.2.25 on 2022-08-31 03:48 - -from django.db import migrations, models -import judge.models.problem -import judge.utils.problem_data - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0129_auto_20220622_1424"), - ] - - operations = [ - migrations.AddField( - model_name="problem", - name="pdf_description", - field=models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem.problem_directory_file, - verbose_name="pdf statement", - ), - ), - migrations.AlterField( - model_name="problem", - name="description", - field=models.TextField(blank=True, verbose_name="problem body"), - ), - ] diff --git a/judge/migrations/0131_auto_20220905_0027.py b/judge/migrations/0131_auto_20220905_0027.py deleted file mode 100644 index ae2e79a..0000000 --- a/judge/migrations/0131_auto_20220905_0027.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 2.2.25 on 2022-09-04 17:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0130_auto_20220831_1048"), - ] - - operations = [ - migrations.AlterField( - model_name="notification", - name="category", - field=models.CharField(max_length=50, verbose_name="category"), - ), - migrations.AlterField( - model_name="problemtranslation", - name="language", - field=models.CharField( - choices=[("en", "English"), ("vi", "Vietnamese")], - max_length=7, - verbose_name="language", - ), - ), - ] diff --git a/judge/migrations/0132_auto_20220915_1349.py b/judge/migrations/0132_auto_20220915_1349.py deleted file mode 100644 index e303f5b..0000000 --- a/judge/migrations/0132_auto_20220915_1349.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 2.2.25 on 2022-09-15 06:49 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0131_auto_20220905_0027"), - ] - - operations = [ - migrations.AlterField( - model_name="contestproblem", - name="max_submissions", - field=models.IntegerField( - default=0, - help_text="Maximum number of submissions for this problem, or 0 for no limit.", - validators=[ - django.core.validators.MinValueValidator( - 0, "Why include a problem you can't submit to?" - ) - ], - verbose_name="max submissions", - ), - ), - ] diff --git a/judge/migrations/0133_auto_20221013_0850.py b/judge/migrations/0133_auto_20221013_0850.py deleted file mode 100644 index b210016..0000000 --- a/judge/migrations/0133_auto_20221013_0850.py +++ /dev/null @@ -1,46 +0,0 @@ -# Generated by Django 2.2.25 on 2022-10-13 01:50 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0132_auto_20220915_1349"), - ] - - operations = [ - migrations.CreateModel( - name="ContestProblemClarification", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("description", models.TextField(verbose_name="clarification body")), - ( - "date", - models.DateTimeField( - auto_now_add=True, verbose_name="clarification timestamp" - ), - ), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.ContestProblem", - verbose_name="clarified problem", - ), - ), - ], - ), - migrations.DeleteModel( - name="ProblemClarification", - ), - ] diff --git a/judge/migrations/0134_auto_20221018_0124.py b/judge/migrations/0134_auto_20221018_0124.py deleted file mode 100644 index b7fda08..0000000 --- a/judge/migrations/0134_auto_20221018_0124.py +++ /dev/null @@ -1,707 +0,0 @@ -# Generated by Django 2.2.28 on 2022-10-17 18:24 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0133_auto_20221013_0850"), - ] - - operations = [ - migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Nuuk", "Nuuk"), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Kyiv", "Kyiv"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kanton", "Kanton"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), - ), - migrations.CreateModel( - name="OrganizationProfile", - fields=[ - ( - "last_visit", - models.AutoField( - primary_key=True, serialize=False, verbose_name="last visit" - ), - ), - ( - "organization", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="last_vist", - to="judge.Organization", - verbose_name="organization", - ), - ), - ( - "users", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="last_visit", - to="judge.Profile", - verbose_name="user", - ), - ), - ], - ), - ] diff --git a/judge/migrations/0135_auto_20221028_0300.py b/judge/migrations/0135_auto_20221028_0300.py deleted file mode 100644 index 6a0840e..0000000 --- a/judge/migrations/0135_auto_20221028_0300.py +++ /dev/null @@ -1,676 +0,0 @@ -# Generated by Django 2.2.25 on 2022-10-27 20:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0134_auto_20221018_0124"), - ] - - operations = [ - migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Nuuk", "Nuuk"), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kanton", "Kanton"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), - ), - ] diff --git a/judge/migrations/0136_alter_profile_timezone.py b/judge/migrations/0136_alter_profile_timezone.py deleted file mode 100644 index 75615b1..0000000 --- a/judge/migrations/0136_alter_profile_timezone.py +++ /dev/null @@ -1,677 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-01 01:05 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0135_auto_20221028_0300"), - ] - - operations = [ - migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Nuuk", "Nuuk"), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Kyiv", "Kyiv"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kanton", "Kanton"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), - ), - ] diff --git a/judge/migrations/0137_auto_20221116_2201.py b/judge/migrations/0137_auto_20221116_2201.py deleted file mode 100644 index 8439d6d..0000000 --- a/judge/migrations/0137_auto_20221116_2201.py +++ /dev/null @@ -1,84 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-16 15:01 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0136_alter_profile_timezone"), - ] - - operations = [ - migrations.CreateModel( - name="PageVote", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "page", - models.CharField( - db_index=True, max_length=30, verbose_name="associated page" - ), - ), - ("score", models.IntegerField(default=0, verbose_name="votes")), - ], - options={ - "verbose_name": "pagevote", - "verbose_name_plural": "pagevotes", - }, - ), - migrations.AlterField( - model_name="problemtranslation", - name="language", - field=models.CharField( - choices=[("vi", "Vietnamese"), ("en", "English")], - max_length=7, - verbose_name="language", - ), - ), - migrations.CreateModel( - name="PageVoteVoter", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("score", models.IntegerField()), - ( - "pagevote", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="votes", - to="judge.pagevote", - ), - ), - ( - "voter", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="voted_page", - to="judge.profile", - ), - ), - ], - options={ - "verbose_name": "pagevote vote", - "verbose_name_plural": "pagevote votes", - "unique_together": {("voter", "pagevote")}, - }, - ), - ] diff --git a/judge/migrations/0138_bookmark_makebookmark.py b/judge/migrations/0138_bookmark_makebookmark.py deleted file mode 100644 index a0e4d05..0000000 --- a/judge/migrations/0138_bookmark_makebookmark.py +++ /dev/null @@ -1,73 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-17 17:13 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0137_auto_20221116_2201"), - ] - - operations = [ - migrations.CreateModel( - name="BookMark", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "page", - models.CharField( - db_index=True, max_length=30, verbose_name="associated page" - ), - ), - ], - options={ - "verbose_name": "bookmark", - "verbose_name_plural": "bookmarks", - }, - ), - migrations.CreateModel( - name="MakeBookMark", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "bookmark", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="bookmark", - to="judge.bookmark", - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="user_bookmark", - to="judge.profile", - ), - ), - ], - options={ - "verbose_name": "make bookmark", - "verbose_name_plural": "make bookmarks", - "unique_together": {("user", "bookmark")}, - }, - ), - ] diff --git a/judge/migrations/0139_contest_freeze_after.py b/judge/migrations/0139_contest_freeze_after.py deleted file mode 100644 index 97e4a25..0000000 --- a/judge/migrations/0139_contest_freeze_after.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-18 06:11 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0138_bookmark_makebookmark"), - ] - - operations = [ - migrations.AddField( - model_name="contest", - name="freeze_after", - field=models.DurationField( - blank=True, - help_text="Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, enter 02:00:00", - null=True, - verbose_name="freeze after", - ), - ), - ] diff --git a/judge/migrations/0140_alter_contest_format_name.py b/judge/migrations/0140_alter_contest_format_name.py deleted file mode 100644 index d146e3b..0000000 --- a/judge/migrations/0140_alter_contest_format_name.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-21 22:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0139_contest_freeze_after"), - ] - - operations = [ - migrations.AlterField( - model_name="contest", - name="format_name", - field=models.CharField( - choices=[ - ("atcoder", "AtCoder"), - ("default", "Default"), - ("ecoo", "ECOO"), - ("icpc", "ICPC"), - ("ioi", "IOI"), - ("ioi16", "New IOI"), - ], - default="default", - help_text="The contest format module to use.", - max_length=32, - verbose_name="contest format", - ), - ), - ] diff --git a/judge/migrations/0141_contestproblem_frozen_subtasks.py b/judge/migrations/0141_contestproblem_frozen_subtasks.py deleted file mode 100644 index c85dc49..0000000 --- a/judge/migrations/0141_contestproblem_frozen_subtasks.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.16 on 2022-12-20 06:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0140_alter_contest_format_name"), - ] - - operations = [ - migrations.AddField( - model_name="contestproblem", - name="frozen_subtasks", - field=models.CharField( - blank=True, - help_text="Only for format new IOI. Separated by commas, e.g: 2, 3", - max_length=20, - null=True, - verbose_name="frozen subtasks", - ), - ), - ] diff --git a/judge/migrations/0142_contestparticipation_format_data_final.py b/judge/migrations/0142_contestparticipation_format_data_final.py deleted file mode 100644 index 50e8dd7..0000000 --- a/judge/migrations/0142_contestparticipation_format_data_final.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.16 on 2022-12-28 18:36 - -from django.db import migrations -import jsonfield.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0141_contestproblem_frozen_subtasks"), - ] - - operations = [ - migrations.AddField( - model_name="contestparticipation", - name="format_data_final", - field=jsonfield.fields.JSONField( - blank=True, - null=True, - verbose_name="same as format_data, but including frozen results", - ), - ), - ] diff --git a/judge/migrations/0143_auto_20221229_0153.py b/judge/migrations/0143_auto_20221229_0153.py deleted file mode 100644 index 051f192..0000000 --- a/judge/migrations/0143_auto_20221229_0153.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.16 on 2022-12-28 18:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0142_contestparticipation_format_data_final"), - ] - - operations = [ - migrations.AddField( - model_name="contestparticipation", - name="cumtime_final", - field=models.PositiveIntegerField( - default=0, verbose_name="final cumulative time" - ), - ), - migrations.AddField( - model_name="contestparticipation", - name="score_final", - field=models.FloatField(default=0, verbose_name="final score"), - ), - ] diff --git a/judge/migrations/0144_auto_20230103_0523.py b/judge/migrations/0144_auto_20230103_0523.py deleted file mode 100644 index 97f6234..0000000 --- a/judge/migrations/0144_auto_20230103_0523.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-02 22:23 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0143_auto_20221229_0153"), - ] - - operations = [ - migrations.RemoveField( - model_name="contestproblem", - name="frozen_subtasks", - ), - migrations.AddField( - model_name="contestproblem", - name="hidden_subtasks", - field=models.CharField( - blank=True, - help_text="Separated by commas, e.g: 2, 3", - max_length=20, - null=True, - verbose_name="hidden subtasks", - ), - ), - ] diff --git a/judge/migrations/0145_alter_organization_slug.py b/judge/migrations/0145_alter_organization_slug.py deleted file mode 100644 index 1cadbe2..0000000 --- a/judge/migrations/0145_alter_organization_slug.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-23 23:39 - -from django.db import migrations, models - - -def make_slug_unique(apps, schema_editor): - Organization = apps.get_model("judge", "Organization") - slugs = Organization.objects.values_list("slug", flat=True) - slugs = set([i.lower() for i in slugs]) - for slug in slugs: - orgs = Organization.objects.filter(slug=slug) - if len(orgs) > 1: - for org in orgs: - org.slug += "-" + str(org.id) - org.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0144_auto_20230103_0523"), - ] - - operations = [ - migrations.RunPython(make_slug_unique, migrations.RunPython.noop, atomic=True), - migrations.AlterField( - model_name="organization", - name="slug", - field=models.SlugField( - help_text="Organization name shown in URL", - max_length=128, - unique=True, - verbose_name="organization slug", - ), - ), - ] diff --git a/judge/migrations/0146_alter_organization_slug.py b/judge/migrations/0146_alter_organization_slug.py deleted file mode 100644 index dbd475d..0000000 --- a/judge/migrations/0146_alter_organization_slug.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-25 19:12 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0145_alter_organization_slug"), - ] - - operations = [ - migrations.AlterField( - model_name="organization", - name="slug", - field=models.SlugField( - help_text="Organization name shown in URL", - max_length=128, - unique=True, - validators=[ - django.core.validators.RegexValidator( - "^[-a-zA-Z0-9]+$", "Only alphanumeric and hyphens" - ) - ], - verbose_name="organization slug", - ), - ), - ] diff --git a/judge/migrations/0147_alter_profile_timezone.py b/judge/migrations/0147_alter_profile_timezone.py deleted file mode 100644 index 5d6331e..0000000 --- a/judge/migrations/0147_alter_profile_timezone.py +++ /dev/null @@ -1,678 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-30 11:01 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0146_alter_organization_slug"), - ] - - operations = [ - migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Ciudad_Juarez", "Ciudad_Juarez"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Nuuk", "Nuuk"), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Kyiv", "Kyiv"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kanton", "Kanton"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), - ), - ] diff --git a/judge/migrations/0148_course_courseassignment_courseresource_courserole.py b/judge/migrations/0148_course_courseassignment_courseresource_courserole.py deleted file mode 100644 index 64821b7..0000000 --- a/judge/migrations/0148_course_courseassignment_courseresource_courserole.py +++ /dev/null @@ -1,187 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-31 15:49 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0147_alter_profile_timezone"), - ] - - operations = [ - migrations.CreateModel( - name="Course", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=128, verbose_name="course name")), - ("about", models.TextField(verbose_name="organization description")), - ("ending_time", models.DateTimeField(verbose_name="ending time")), - ( - "is_public", - models.BooleanField(default=False, verbose_name="publicly visible"), - ), - ( - "slug", - models.SlugField( - help_text="Course name shown in URL", - max_length=128, - unique=True, - validators=[ - django.core.validators.RegexValidator( - "^[-a-zA-Z0-9]+$", "Only alphanumeric and hyphens" - ) - ], - verbose_name="course slug", - ), - ), - ( - "is_open", - models.BooleanField( - default=False, verbose_name="public registration" - ), - ), - ( - "image_url", - models.CharField( - blank=True, - default="", - max_length=150, - verbose_name="course image", - ), - ), - ( - "organizations", - models.ManyToManyField( - blank=True, - help_text="If private, only these organizations may see the course", - to="judge.Organization", - verbose_name="organizations", - ), - ), - ], - ), - migrations.CreateModel( - name="CourseRole", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "role", - models.CharField( - choices=[ - ("ST", "Student"), - ("AS", "Assistant"), - ("TE", "Teacher"), - ], - default="ST", - max_length=2, - ), - ), - ( - "course", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - to="judge.course", - verbose_name="course", - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="user_of_course", - to="judge.profile", - verbose_name="user", - ), - ), - ], - ), - migrations.CreateModel( - name="CourseResource", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "files", - models.FileField( - blank=True, null=True, upload_to="", verbose_name="course files" - ), - ), - ( - "description", - models.CharField( - blank=True, max_length=150, verbose_name="description" - ), - ), - ("order", models.IntegerField(default=None, null=True)), - ( - "is_public", - models.BooleanField(default=False, verbose_name="publicly visible"), - ), - ( - "course", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - to="judge.course", - verbose_name="course", - ), - ), - ], - ), - migrations.CreateModel( - name="CourseAssignment", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("points", models.FloatField(verbose_name="points")), - ( - "contest", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - to="judge.contest", - verbose_name="contest", - ), - ), - ( - "course", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - to="judge.course", - verbose_name="course", - ), - ), - ], - ), - ] diff --git a/judge/migrations/0149_auto_20230202_0902.py b/judge/migrations/0149_auto_20230202_0902.py deleted file mode 100644 index 8b0d885..0000000 --- a/judge/migrations/0149_auto_20230202_0902.py +++ /dev/null @@ -1,682 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-02 02:02 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0148_course_courseassignment_courseresource_courserole"), - ] - - operations = [ - migrations.AlterField( - model_name="notification", - name="category", - field=models.CharField(max_length=1000, verbose_name="category"), - ), - migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Nuuk", "Nuuk"), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Kyiv", "Kyiv"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kanton", "Kanton"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), - ), - ] diff --git a/judge/migrations/0150_alter_profile_timezone.py b/judge/migrations/0150_alter_profile_timezone.py deleted file mode 100644 index 20b3e12..0000000 --- a/judge/migrations/0150_alter_profile_timezone.py +++ /dev/null @@ -1,676 +0,0 @@ -# Generated by Django 3.2.17 on 2023-02-08 01:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0149_auto_20230202_0902"), - ] - - operations = [ - migrations.AlterField( - model_name="profile", - name="timezone", - field=models.CharField( - choices=[ - ( - "Africa", - [ - ("Africa/Abidjan", "Abidjan"), - ("Africa/Accra", "Accra"), - ("Africa/Addis_Ababa", "Addis_Ababa"), - ("Africa/Algiers", "Algiers"), - ("Africa/Asmara", "Asmara"), - ("Africa/Asmera", "Asmera"), - ("Africa/Bamako", "Bamako"), - ("Africa/Bangui", "Bangui"), - ("Africa/Banjul", "Banjul"), - ("Africa/Bissau", "Bissau"), - ("Africa/Blantyre", "Blantyre"), - ("Africa/Brazzaville", "Brazzaville"), - ("Africa/Bujumbura", "Bujumbura"), - ("Africa/Cairo", "Cairo"), - ("Africa/Casablanca", "Casablanca"), - ("Africa/Ceuta", "Ceuta"), - ("Africa/Conakry", "Conakry"), - ("Africa/Dakar", "Dakar"), - ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), - ("Africa/Djibouti", "Djibouti"), - ("Africa/Douala", "Douala"), - ("Africa/El_Aaiun", "El_Aaiun"), - ("Africa/Freetown", "Freetown"), - ("Africa/Gaborone", "Gaborone"), - ("Africa/Harare", "Harare"), - ("Africa/Johannesburg", "Johannesburg"), - ("Africa/Juba", "Juba"), - ("Africa/Kampala", "Kampala"), - ("Africa/Khartoum", "Khartoum"), - ("Africa/Kigali", "Kigali"), - ("Africa/Kinshasa", "Kinshasa"), - ("Africa/Lagos", "Lagos"), - ("Africa/Libreville", "Libreville"), - ("Africa/Lome", "Lome"), - ("Africa/Luanda", "Luanda"), - ("Africa/Lubumbashi", "Lubumbashi"), - ("Africa/Lusaka", "Lusaka"), - ("Africa/Malabo", "Malabo"), - ("Africa/Maputo", "Maputo"), - ("Africa/Maseru", "Maseru"), - ("Africa/Mbabane", "Mbabane"), - ("Africa/Mogadishu", "Mogadishu"), - ("Africa/Monrovia", "Monrovia"), - ("Africa/Nairobi", "Nairobi"), - ("Africa/Ndjamena", "Ndjamena"), - ("Africa/Niamey", "Niamey"), - ("Africa/Nouakchott", "Nouakchott"), - ("Africa/Ouagadougou", "Ouagadougou"), - ("Africa/Porto-Novo", "Porto-Novo"), - ("Africa/Sao_Tome", "Sao_Tome"), - ("Africa/Timbuktu", "Timbuktu"), - ("Africa/Tripoli", "Tripoli"), - ("Africa/Tunis", "Tunis"), - ("Africa/Windhoek", "Windhoek"), - ], - ), - ( - "America", - [ - ("America/Adak", "Adak"), - ("America/Anchorage", "Anchorage"), - ("America/Anguilla", "Anguilla"), - ("America/Antigua", "Antigua"), - ("America/Araguaina", "Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "Argentina/Cordoba"), - ("America/Argentina/Jujuy", "Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "Argentina/Salta"), - ("America/Argentina/San_Juan", "Argentina/San_Juan"), - ("America/Argentina/San_Luis", "Argentina/San_Luis"), - ("America/Argentina/Tucuman", "Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), - ("America/Aruba", "Aruba"), - ("America/Asuncion", "Asuncion"), - ("America/Atikokan", "Atikokan"), - ("America/Atka", "Atka"), - ("America/Bahia", "Bahia"), - ("America/Bahia_Banderas", "Bahia_Banderas"), - ("America/Barbados", "Barbados"), - ("America/Belem", "Belem"), - ("America/Belize", "Belize"), - ("America/Blanc-Sablon", "Blanc-Sablon"), - ("America/Boa_Vista", "Boa_Vista"), - ("America/Bogota", "Bogota"), - ("America/Boise", "Boise"), - ("America/Buenos_Aires", "Buenos_Aires"), - ("America/Cambridge_Bay", "Cambridge_Bay"), - ("America/Campo_Grande", "Campo_Grande"), - ("America/Cancun", "Cancun"), - ("America/Caracas", "Caracas"), - ("America/Catamarca", "Catamarca"), - ("America/Cayenne", "Cayenne"), - ("America/Cayman", "Cayman"), - ("America/Chicago", "Chicago"), - ("America/Chihuahua", "Chihuahua"), - ("America/Coral_Harbour", "Coral_Harbour"), - ("America/Cordoba", "Cordoba"), - ("America/Costa_Rica", "Costa_Rica"), - ("America/Creston", "Creston"), - ("America/Cuiaba", "Cuiaba"), - ("America/Curacao", "Curacao"), - ("America/Danmarkshavn", "Danmarkshavn"), - ("America/Dawson", "Dawson"), - ("America/Dawson_Creek", "Dawson_Creek"), - ("America/Denver", "Denver"), - ("America/Detroit", "Detroit"), - ("America/Dominica", "Dominica"), - ("America/Edmonton", "Edmonton"), - ("America/Eirunepe", "Eirunepe"), - ("America/El_Salvador", "El_Salvador"), - ("America/Ensenada", "Ensenada"), - ("America/Fort_Nelson", "Fort_Nelson"), - ("America/Fort_Wayne", "Fort_Wayne"), - ("America/Fortaleza", "Fortaleza"), - ("America/Glace_Bay", "Glace_Bay"), - ("America/Godthab", "Godthab"), - ("America/Goose_Bay", "Goose_Bay"), - ("America/Grand_Turk", "Grand_Turk"), - ("America/Grenada", "Grenada"), - ("America/Guadeloupe", "Guadeloupe"), - ("America/Guatemala", "Guatemala"), - ("America/Guayaquil", "Guayaquil"), - ("America/Guyana", "Guyana"), - ("America/Halifax", "Halifax"), - ("America/Havana", "Havana"), - ("America/Hermosillo", "Hermosillo"), - ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), - ("America/Indiana/Knox", "Indiana/Knox"), - ("America/Indiana/Marengo", "Indiana/Marengo"), - ("America/Indiana/Petersburg", "Indiana/Petersburg"), - ("America/Indiana/Tell_City", "Indiana/Tell_City"), - ("America/Indiana/Vevay", "Indiana/Vevay"), - ("America/Indiana/Vincennes", "Indiana/Vincennes"), - ("America/Indiana/Winamac", "Indiana/Winamac"), - ("America/Indianapolis", "Indianapolis"), - ("America/Inuvik", "Inuvik"), - ("America/Iqaluit", "Iqaluit"), - ("America/Jamaica", "Jamaica"), - ("America/Jujuy", "Jujuy"), - ("America/Juneau", "Juneau"), - ("America/Kentucky/Louisville", "Kentucky/Louisville"), - ("America/Kentucky/Monticello", "Kentucky/Monticello"), - ("America/Knox_IN", "Knox_IN"), - ("America/Kralendijk", "Kralendijk"), - ("America/La_Paz", "La_Paz"), - ("America/Lima", "Lima"), - ("America/Los_Angeles", "Los_Angeles"), - ("America/Louisville", "Louisville"), - ("America/Lower_Princes", "Lower_Princes"), - ("America/Maceio", "Maceio"), - ("America/Managua", "Managua"), - ("America/Manaus", "Manaus"), - ("America/Marigot", "Marigot"), - ("America/Martinique", "Martinique"), - ("America/Matamoros", "Matamoros"), - ("America/Mazatlan", "Mazatlan"), - ("America/Mendoza", "Mendoza"), - ("America/Menominee", "Menominee"), - ("America/Merida", "Merida"), - ("America/Metlakatla", "Metlakatla"), - ("America/Mexico_City", "Mexico_City"), - ("America/Miquelon", "Miquelon"), - ("America/Moncton", "Moncton"), - ("America/Monterrey", "Monterrey"), - ("America/Montevideo", "Montevideo"), - ("America/Montreal", "Montreal"), - ("America/Montserrat", "Montserrat"), - ("America/Nassau", "Nassau"), - ("America/New_York", "New_York"), - ("America/Nipigon", "Nipigon"), - ("America/Nome", "Nome"), - ("America/Noronha", "Noronha"), - ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), - ("America/North_Dakota/Center", "North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "North_Dakota/New_Salem", - ), - ("America/Nuuk", "Nuuk"), - ("America/Ojinaga", "Ojinaga"), - ("America/Panama", "Panama"), - ("America/Pangnirtung", "Pangnirtung"), - ("America/Paramaribo", "Paramaribo"), - ("America/Phoenix", "Phoenix"), - ("America/Port-au-Prince", "Port-au-Prince"), - ("America/Port_of_Spain", "Port_of_Spain"), - ("America/Porto_Acre", "Porto_Acre"), - ("America/Porto_Velho", "Porto_Velho"), - ("America/Puerto_Rico", "Puerto_Rico"), - ("America/Punta_Arenas", "Punta_Arenas"), - ("America/Rainy_River", "Rainy_River"), - ("America/Rankin_Inlet", "Rankin_Inlet"), - ("America/Recife", "Recife"), - ("America/Regina", "Regina"), - ("America/Resolute", "Resolute"), - ("America/Rio_Branco", "Rio_Branco"), - ("America/Rosario", "Rosario"), - ("America/Santa_Isabel", "Santa_Isabel"), - ("America/Santarem", "Santarem"), - ("America/Santiago", "Santiago"), - ("America/Santo_Domingo", "Santo_Domingo"), - ("America/Sao_Paulo", "Sao_Paulo"), - ("America/Scoresbysund", "Scoresbysund"), - ("America/Shiprock", "Shiprock"), - ("America/Sitka", "Sitka"), - ("America/St_Barthelemy", "St_Barthelemy"), - ("America/St_Johns", "St_Johns"), - ("America/St_Kitts", "St_Kitts"), - ("America/St_Lucia", "St_Lucia"), - ("America/St_Thomas", "St_Thomas"), - ("America/St_Vincent", "St_Vincent"), - ("America/Swift_Current", "Swift_Current"), - ("America/Tegucigalpa", "Tegucigalpa"), - ("America/Thule", "Thule"), - ("America/Thunder_Bay", "Thunder_Bay"), - ("America/Tijuana", "Tijuana"), - ("America/Toronto", "Toronto"), - ("America/Tortola", "Tortola"), - ("America/Vancouver", "Vancouver"), - ("America/Virgin", "Virgin"), - ("America/Whitehorse", "Whitehorse"), - ("America/Winnipeg", "Winnipeg"), - ("America/Yakutat", "Yakutat"), - ("America/Yellowknife", "Yellowknife"), - ], - ), - ( - "Antarctica", - [ - ("Antarctica/Casey", "Casey"), - ("Antarctica/Davis", "Davis"), - ("Antarctica/DumontDUrville", "DumontDUrville"), - ("Antarctica/Macquarie", "Macquarie"), - ("Antarctica/Mawson", "Mawson"), - ("Antarctica/McMurdo", "McMurdo"), - ("Antarctica/Palmer", "Palmer"), - ("Antarctica/Rothera", "Rothera"), - ("Antarctica/South_Pole", "South_Pole"), - ("Antarctica/Syowa", "Syowa"), - ("Antarctica/Troll", "Troll"), - ("Antarctica/Vostok", "Vostok"), - ], - ), - ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), - ( - "Asia", - [ - ("Asia/Aden", "Aden"), - ("Asia/Almaty", "Almaty"), - ("Asia/Amman", "Amman"), - ("Asia/Anadyr", "Anadyr"), - ("Asia/Aqtau", "Aqtau"), - ("Asia/Aqtobe", "Aqtobe"), - ("Asia/Ashgabat", "Ashgabat"), - ("Asia/Ashkhabad", "Ashkhabad"), - ("Asia/Atyrau", "Atyrau"), - ("Asia/Baghdad", "Baghdad"), - ("Asia/Bahrain", "Bahrain"), - ("Asia/Baku", "Baku"), - ("Asia/Bangkok", "Bangkok"), - ("Asia/Barnaul", "Barnaul"), - ("Asia/Beirut", "Beirut"), - ("Asia/Bishkek", "Bishkek"), - ("Asia/Brunei", "Brunei"), - ("Asia/Calcutta", "Calcutta"), - ("Asia/Chita", "Chita"), - ("Asia/Choibalsan", "Choibalsan"), - ("Asia/Chongqing", "Chongqing"), - ("Asia/Chungking", "Chungking"), - ("Asia/Colombo", "Colombo"), - ("Asia/Dacca", "Dacca"), - ("Asia/Damascus", "Damascus"), - ("Asia/Dhaka", "Dhaka"), - ("Asia/Dili", "Dili"), - ("Asia/Dubai", "Dubai"), - ("Asia/Dushanbe", "Dushanbe"), - ("Asia/Famagusta", "Famagusta"), - ("Asia/Gaza", "Gaza"), - ("Asia/Harbin", "Harbin"), - ("Asia/Hebron", "Hebron"), - ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Hong_Kong"), - ("Asia/Hovd", "Hovd"), - ("Asia/Irkutsk", "Irkutsk"), - ("Asia/Istanbul", "Istanbul"), - ("Asia/Jakarta", "Jakarta"), - ("Asia/Jayapura", "Jayapura"), - ("Asia/Jerusalem", "Jerusalem"), - ("Asia/Kabul", "Kabul"), - ("Asia/Kamchatka", "Kamchatka"), - ("Asia/Karachi", "Karachi"), - ("Asia/Kashgar", "Kashgar"), - ("Asia/Kathmandu", "Kathmandu"), - ("Asia/Katmandu", "Katmandu"), - ("Asia/Khandyga", "Khandyga"), - ("Asia/Kolkata", "Kolkata"), - ("Asia/Krasnoyarsk", "Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), - ("Asia/Kuching", "Kuching"), - ("Asia/Kuwait", "Kuwait"), - ("Asia/Macao", "Macao"), - ("Asia/Macau", "Macau"), - ("Asia/Magadan", "Magadan"), - ("Asia/Makassar", "Makassar"), - ("Asia/Manila", "Manila"), - ("Asia/Muscat", "Muscat"), - ("Asia/Nicosia", "Nicosia"), - ("Asia/Novokuznetsk", "Novokuznetsk"), - ("Asia/Novosibirsk", "Novosibirsk"), - ("Asia/Omsk", "Omsk"), - ("Asia/Oral", "Oral"), - ("Asia/Phnom_Penh", "Phnom_Penh"), - ("Asia/Pontianak", "Pontianak"), - ("Asia/Pyongyang", "Pyongyang"), - ("Asia/Qatar", "Qatar"), - ("Asia/Qostanay", "Qostanay"), - ("Asia/Qyzylorda", "Qyzylorda"), - ("Asia/Rangoon", "Rangoon"), - ("Asia/Riyadh", "Riyadh"), - ("Asia/Saigon", "Saigon"), - ("Asia/Sakhalin", "Sakhalin"), - ("Asia/Samarkand", "Samarkand"), - ("Asia/Seoul", "Seoul"), - ("Asia/Shanghai", "Shanghai"), - ("Asia/Singapore", "Singapore"), - ("Asia/Srednekolymsk", "Srednekolymsk"), - ("Asia/Taipei", "Taipei"), - ("Asia/Tashkent", "Tashkent"), - ("Asia/Tbilisi", "Tbilisi"), - ("Asia/Tehran", "Tehran"), - ("Asia/Tel_Aviv", "Tel_Aviv"), - ("Asia/Thimbu", "Thimbu"), - ("Asia/Thimphu", "Thimphu"), - ("Asia/Tokyo", "Tokyo"), - ("Asia/Tomsk", "Tomsk"), - ("Asia/Ujung_Pandang", "Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Ulaanbaatar"), - ("Asia/Ulan_Bator", "Ulan_Bator"), - ("Asia/Urumqi", "Urumqi"), - ("Asia/Ust-Nera", "Ust-Nera"), - ("Asia/Vientiane", "Vientiane"), - ("Asia/Vladivostok", "Vladivostok"), - ("Asia/Yakutsk", "Yakutsk"), - ("Asia/Yangon", "Yangon"), - ("Asia/Yekaterinburg", "Yekaterinburg"), - ("Asia/Yerevan", "Yerevan"), - ], - ), - ( - "Atlantic", - [ - ("Atlantic/Azores", "Azores"), - ("Atlantic/Bermuda", "Bermuda"), - ("Atlantic/Canary", "Canary"), - ("Atlantic/Cape_Verde", "Cape_Verde"), - ("Atlantic/Faeroe", "Faeroe"), - ("Atlantic/Faroe", "Faroe"), - ("Atlantic/Jan_Mayen", "Jan_Mayen"), - ("Atlantic/Madeira", "Madeira"), - ("Atlantic/Reykjavik", "Reykjavik"), - ("Atlantic/South_Georgia", "South_Georgia"), - ("Atlantic/St_Helena", "St_Helena"), - ("Atlantic/Stanley", "Stanley"), - ], - ), - ( - "Australia", - [ - ("Australia/ACT", "ACT"), - ("Australia/Adelaide", "Adelaide"), - ("Australia/Brisbane", "Brisbane"), - ("Australia/Broken_Hill", "Broken_Hill"), - ("Australia/Canberra", "Canberra"), - ("Australia/Currie", "Currie"), - ("Australia/Darwin", "Darwin"), - ("Australia/Eucla", "Eucla"), - ("Australia/Hobart", "Hobart"), - ("Australia/LHI", "LHI"), - ("Australia/Lindeman", "Lindeman"), - ("Australia/Lord_Howe", "Lord_Howe"), - ("Australia/Melbourne", "Melbourne"), - ("Australia/NSW", "NSW"), - ("Australia/North", "North"), - ("Australia/Perth", "Perth"), - ("Australia/Queensland", "Queensland"), - ("Australia/South", "South"), - ("Australia/Sydney", "Sydney"), - ("Australia/Tasmania", "Tasmania"), - ("Australia/Victoria", "Victoria"), - ("Australia/West", "West"), - ("Australia/Yancowinna", "Yancowinna"), - ], - ), - ( - "Brazil", - [ - ("Brazil/Acre", "Acre"), - ("Brazil/DeNoronha", "DeNoronha"), - ("Brazil/East", "East"), - ("Brazil/West", "West"), - ], - ), - ( - "Canada", - [ - ("Canada/Atlantic", "Atlantic"), - ("Canada/Central", "Central"), - ("Canada/Eastern", "Eastern"), - ("Canada/Mountain", "Mountain"), - ("Canada/Newfoundland", "Newfoundland"), - ("Canada/Pacific", "Pacific"), - ("Canada/Saskatchewan", "Saskatchewan"), - ("Canada/Yukon", "Yukon"), - ], - ), - ( - "Chile", - [ - ("Chile/Continental", "Continental"), - ("Chile/EasterIsland", "EasterIsland"), - ], - ), - ( - "Etc", - [ - ("Etc/Greenwich", "Greenwich"), - ("Etc/UCT", "UCT"), - ("Etc/UTC", "UTC"), - ("Etc/Universal", "Universal"), - ("Etc/Zulu", "Zulu"), - ], - ), - ( - "Europe", - [ - ("Europe/Amsterdam", "Amsterdam"), - ("Europe/Andorra", "Andorra"), - ("Europe/Astrakhan", "Astrakhan"), - ("Europe/Athens", "Athens"), - ("Europe/Belfast", "Belfast"), - ("Europe/Belgrade", "Belgrade"), - ("Europe/Berlin", "Berlin"), - ("Europe/Bratislava", "Bratislava"), - ("Europe/Brussels", "Brussels"), - ("Europe/Bucharest", "Bucharest"), - ("Europe/Budapest", "Budapest"), - ("Europe/Busingen", "Busingen"), - ("Europe/Chisinau", "Chisinau"), - ("Europe/Copenhagen", "Copenhagen"), - ("Europe/Dublin", "Dublin"), - ("Europe/Gibraltar", "Gibraltar"), - ("Europe/Guernsey", "Guernsey"), - ("Europe/Helsinki", "Helsinki"), - ("Europe/Isle_of_Man", "Isle_of_Man"), - ("Europe/Istanbul", "Istanbul"), - ("Europe/Jersey", "Jersey"), - ("Europe/Kaliningrad", "Kaliningrad"), - ("Europe/Kiev", "Kiev"), - ("Europe/Kirov", "Kirov"), - ("Europe/Lisbon", "Lisbon"), - ("Europe/Ljubljana", "Ljubljana"), - ("Europe/London", "London"), - ("Europe/Luxembourg", "Luxembourg"), - ("Europe/Madrid", "Madrid"), - ("Europe/Malta", "Malta"), - ("Europe/Mariehamn", "Mariehamn"), - ("Europe/Minsk", "Minsk"), - ("Europe/Monaco", "Monaco"), - ("Europe/Moscow", "Moscow"), - ("Europe/Nicosia", "Nicosia"), - ("Europe/Oslo", "Oslo"), - ("Europe/Paris", "Paris"), - ("Europe/Podgorica", "Podgorica"), - ("Europe/Prague", "Prague"), - ("Europe/Riga", "Riga"), - ("Europe/Rome", "Rome"), - ("Europe/Samara", "Samara"), - ("Europe/San_Marino", "San_Marino"), - ("Europe/Sarajevo", "Sarajevo"), - ("Europe/Saratov", "Saratov"), - ("Europe/Simferopol", "Simferopol"), - ("Europe/Skopje", "Skopje"), - ("Europe/Sofia", "Sofia"), - ("Europe/Stockholm", "Stockholm"), - ("Europe/Tallinn", "Tallinn"), - ("Europe/Tirane", "Tirane"), - ("Europe/Tiraspol", "Tiraspol"), - ("Europe/Ulyanovsk", "Ulyanovsk"), - ("Europe/Uzhgorod", "Uzhgorod"), - ("Europe/Vaduz", "Vaduz"), - ("Europe/Vatican", "Vatican"), - ("Europe/Vienna", "Vienna"), - ("Europe/Vilnius", "Vilnius"), - ("Europe/Volgograd", "Volgograd"), - ("Europe/Warsaw", "Warsaw"), - ("Europe/Zagreb", "Zagreb"), - ("Europe/Zaporozhye", "Zaporozhye"), - ("Europe/Zurich", "Zurich"), - ], - ), - ( - "Indian", - [ - ("Indian/Antananarivo", "Antananarivo"), - ("Indian/Chagos", "Chagos"), - ("Indian/Christmas", "Christmas"), - ("Indian/Cocos", "Cocos"), - ("Indian/Comoro", "Comoro"), - ("Indian/Kerguelen", "Kerguelen"), - ("Indian/Mahe", "Mahe"), - ("Indian/Maldives", "Maldives"), - ("Indian/Mauritius", "Mauritius"), - ("Indian/Mayotte", "Mayotte"), - ("Indian/Reunion", "Reunion"), - ], - ), - ( - "Mexico", - [ - ("Mexico/BajaNorte", "BajaNorte"), - ("Mexico/BajaSur", "BajaSur"), - ("Mexico/General", "General"), - ], - ), - ( - "Other", - [ - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - ), - ( - "Pacific", - [ - ("Pacific/Apia", "Apia"), - ("Pacific/Auckland", "Auckland"), - ("Pacific/Bougainville", "Bougainville"), - ("Pacific/Chatham", "Chatham"), - ("Pacific/Chuuk", "Chuuk"), - ("Pacific/Easter", "Easter"), - ("Pacific/Efate", "Efate"), - ("Pacific/Enderbury", "Enderbury"), - ("Pacific/Fakaofo", "Fakaofo"), - ("Pacific/Fiji", "Fiji"), - ("Pacific/Funafuti", "Funafuti"), - ("Pacific/Galapagos", "Galapagos"), - ("Pacific/Gambier", "Gambier"), - ("Pacific/Guadalcanal", "Guadalcanal"), - ("Pacific/Guam", "Guam"), - ("Pacific/Honolulu", "Honolulu"), - ("Pacific/Johnston", "Johnston"), - ("Pacific/Kanton", "Kanton"), - ("Pacific/Kiritimati", "Kiritimati"), - ("Pacific/Kosrae", "Kosrae"), - ("Pacific/Kwajalein", "Kwajalein"), - ("Pacific/Majuro", "Majuro"), - ("Pacific/Marquesas", "Marquesas"), - ("Pacific/Midway", "Midway"), - ("Pacific/Nauru", "Nauru"), - ("Pacific/Niue", "Niue"), - ("Pacific/Norfolk", "Norfolk"), - ("Pacific/Noumea", "Noumea"), - ("Pacific/Pago_Pago", "Pago_Pago"), - ("Pacific/Palau", "Palau"), - ("Pacific/Pitcairn", "Pitcairn"), - ("Pacific/Pohnpei", "Pohnpei"), - ("Pacific/Ponape", "Ponape"), - ("Pacific/Port_Moresby", "Port_Moresby"), - ("Pacific/Rarotonga", "Rarotonga"), - ("Pacific/Saipan", "Saipan"), - ("Pacific/Samoa", "Samoa"), - ("Pacific/Tahiti", "Tahiti"), - ("Pacific/Tarawa", "Tarawa"), - ("Pacific/Tongatapu", "Tongatapu"), - ("Pacific/Truk", "Truk"), - ("Pacific/Wake", "Wake"), - ("Pacific/Wallis", "Wallis"), - ("Pacific/Yap", "Yap"), - ], - ), - ( - "US", - [ - ("US/Alaska", "Alaska"), - ("US/Aleutian", "Aleutian"), - ("US/Arizona", "Arizona"), - ("US/Central", "Central"), - ("US/East-Indiana", "East-Indiana"), - ("US/Eastern", "Eastern"), - ("US/Hawaii", "Hawaii"), - ("US/Indiana-Starke", "Indiana-Starke"), - ("US/Michigan", "Michigan"), - ("US/Mountain", "Mountain"), - ("US/Pacific", "Pacific"), - ("US/Samoa", "Samoa"), - ], - ), - ], - default="Asia/Ho_Chi_Minh", - max_length=50, - verbose_name="location", - ), - ), - ] diff --git a/judge/migrations/0151_comment_content_type.py b/judge/migrations/0151_comment_content_type.py deleted file mode 100644 index d1897d9..0000000 --- a/judge/migrations/0151_comment_content_type.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 3.2.18 on 2023-02-20 21:26 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), - ("judge", "0150_alter_profile_timezone"), - ] - - operations = [ - migrations.AddField( - model_name="comment", - name="content_type", - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="contenttypes.contenttype", - ), - preserve_default=False, - ), - migrations.AddField( - model_name="comment", - name="object_id", - field=models.PositiveIntegerField(null=True), - preserve_default=False, - ), - migrations.AlterField( - model_name="solution", - name="problem", - field=models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="solution", - to="judge.problem", - verbose_name="associated problem", - ), - ), - migrations.AddIndex( - model_name="comment", - index=models.Index( - fields=["content_type", "object_id"], - name="judge_comme_content_2dce05_idx", - ), - ), - ] diff --git a/judge/migrations/0152_migrate_comments.py b/judge/migrations/0152_migrate_comments.py deleted file mode 100644 index bcf6531..0000000 --- a/judge/migrations/0152_migrate_comments.py +++ /dev/null @@ -1,54 +0,0 @@ -from django.db import migrations, models -import django.db.models.deletion -from django.core.exceptions import ObjectDoesNotExist - - -def migrate_comments(apps, schema_editor): - Comment = apps.get_model("judge", "Comment") - Problem = apps.get_model("judge", "Problem") - Solution = apps.get_model("judge", "Solution") - BlogPost = apps.get_model("judge", "BlogPost") - Contest = apps.get_model("judge", "Contest") - - for comment in Comment.objects.all(): - page = comment.page - try: - if page.startswith("p:"): - code = page[2:] - comment.linked_object = Problem.objects.get(code=code) - elif page.startswith("s:"): - code = page[2:] - comment.linked_object = Solution.objects.get(problem__code=code) - elif page.startswith("c:"): - key = page[2:] - comment.linked_object = Contest.objects.get(key=key) - elif page.startswith("b:"): - blog_id = page[2:] - comment.linked_object = BlogPost.objects.get(id=blog_id) - comment.save() - except ObjectDoesNotExist: - comment.delete() - - -class Migration(migrations.Migration): - dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), - ("judge", "0151_comment_content_type"), - ] - - operations = [ - migrations.RunPython(migrate_comments, migrations.RunPython.noop, atomic=True), - migrations.AlterField( - model_name="comment", - name="content_type", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="contenttypes.contenttype", - ), - ), - migrations.AlterField( - model_name="comment", - name="object_id", - field=models.PositiveIntegerField(), - ), - ] diff --git a/judge/migrations/0153_drop_comment_page.py b/judge/migrations/0153_drop_comment_page.py deleted file mode 100644 index a03ac09..0000000 --- a/judge/migrations/0153_drop_comment_page.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.18 on 2023-02-20 23:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0152_migrate_comments"), - ] - - operations = [ - migrations.RemoveField( - model_name="comment", - name="page", - ), - migrations.AlterField( - model_name="commentlock", - name="page", - field=models.CharField( - db_index=True, max_length=30, verbose_name="associated page" - ), - ), - ] diff --git a/judge/migrations/0154_add_submission_indexes.py b/judge/migrations/0154_add_submission_indexes.py deleted file mode 100644 index 28c5fec..0000000 --- a/judge/migrations/0154_add_submission_indexes.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-08 01:55 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0153_drop_comment_page"), - ] - - operations = [ - migrations.AddIndex( - model_name="submission", - index=models.Index( - fields=["problem", "user", "-points"], - name="judge_submi_problem_5687ea_idx", - ), - ), - migrations.AddIndex( - model_name="submission", - index=models.Index( - fields=["contest_object", "problem", "user", "-points"], - name="judge_submi_contest_31cdbb_idx", - ), - ), - ] diff --git a/judge/migrations/0155_output_only.py b/judge/migrations/0155_output_only.py deleted file mode 100644 index e122ed7..0000000 --- a/judge/migrations/0155_output_only.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-10 04:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0154_add_submission_indexes"), - ] - - operations = [ - migrations.AddField( - model_name="problemdata", - name="output_only", - field=models.BooleanField( - help_text="Support output-only problem", - null=True, - verbose_name="is output only", - ), - ), - ] diff --git a/judge/migrations/0156_auto_20230801_0107.py b/judge/migrations/0156_auto_20230801_0107.py deleted file mode 100644 index a5f2b9a..0000000 --- a/judge/migrations/0156_auto_20230801_0107.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.2.18 on 2023-07-31 18:07 - -import django.core.validators -from django.db import migrations, models -import judge.models.problem_data -import judge.utils.problem_data - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0155_output_only"), - ] - - operations = [ - migrations.AddField( - model_name="problemdata", - name="signature_handler", - field=models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - validators=[ - django.core.validators.FileExtensionValidator( - allowed_extensions=["cpp"] - ) - ], - verbose_name="signature handler", - ), - ), - migrations.AddField( - model_name="problemdata", - name="signature_header", - field=models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - validators=[ - django.core.validators.FileExtensionValidator( - allowed_extensions=["h"] - ) - ], - verbose_name="signature header", - ), - ), - ] diff --git a/judge/migrations/0157_auto_20230801_1145.py b/judge/migrations/0157_auto_20230801_1145.py deleted file mode 100644 index 802c56c..0000000 --- a/judge/migrations/0157_auto_20230801_1145.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-01 04:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0156_auto_20230801_0107"), - ] - - operations = [ - migrations.AddField( - model_name="problemdata", - name="use_ioi_signature", - field=models.BooleanField( - help_text="Use IOI Signature", - null=True, - verbose_name="is IOI signature", - ), - ), - ] diff --git a/judge/migrations/0158_migrate_pagevote.py b/judge/migrations/0158_migrate_pagevote.py deleted file mode 100644 index 5563854..0000000 --- a/judge/migrations/0158_migrate_pagevote.py +++ /dev/null @@ -1,70 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-03 07:40 - -from django.db import migrations, models -import django.db.models.deletion -from django.core.exceptions import ObjectDoesNotExist - - -def migrate_pagevote(apps, schema_editor): - PageVote = apps.get_model("judge", "PageVote") - Problem = apps.get_model("judge", "Problem") - Solution = apps.get_model("judge", "Solution") - BlogPost = apps.get_model("judge", "BlogPost") - Contest = apps.get_model("judge", "Contest") - - for vote in PageVote.objects.all(): - page = vote.page - try: - if page.startswith("p:"): - code = page[2:] - vote.linked_object = Problem.objects.get(code=code) - elif page.startswith("s:"): - code = page[2:] - vote.linked_object = Solution.objects.get(problem__code=code) - elif page.startswith("c:"): - key = page[2:] - vote.linked_object = Contest.objects.get(key=key) - elif page.startswith("b:"): - blog_id = page[2:] - vote.linked_object = BlogPost.objects.get(id=blog_id) - vote.save() - except ObjectDoesNotExist: - vote.delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), - ("judge", "0157_auto_20230801_1145"), - ] - - operations = [ - migrations.AddField( - model_name="pagevote", - name="content_type", - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="contenttypes.contenttype", - ), - ), - migrations.AddField( - model_name="pagevote", - name="object_id", - field=models.PositiveIntegerField(default=None), - preserve_default=False, - ), - migrations.AlterUniqueTogether( - name="pagevote", - unique_together={("content_type", "object_id")}, - ), - migrations.AddIndex( - model_name="pagevote", - index=models.Index( - fields=["content_type", "object_id"], - name="judge_pagev_content_ed8899_idx", - ), - ), - migrations.RunPython(migrate_pagevote, migrations.RunPython.noop, atomic=True), - ] diff --git a/judge/migrations/0159_auto_20230803_1518.py b/judge/migrations/0159_auto_20230803_1518.py deleted file mode 100644 index 691121b..0000000 --- a/judge/migrations/0159_auto_20230803_1518.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-03 08:18 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), - ("judge", "0158_migrate_pagevote"), - ] - - operations = [ - migrations.AlterField( - model_name="pagevote", - name="content_type", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="contenttypes.contenttype", - ), - ), - ] diff --git a/judge/migrations/0160_migrate_bookmark.py b/judge/migrations/0160_migrate_bookmark.py deleted file mode 100644 index 6fa1b3e..0000000 --- a/judge/migrations/0160_migrate_bookmark.py +++ /dev/null @@ -1,80 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-03 08:32 - -from django.db import migrations, models -import django.db.models.deletion -from django.core.exceptions import ObjectDoesNotExist - - -def migrate_bookmark(apps, schema_editor): - BookMark = apps.get_model("judge", "BookMark") - Problem = apps.get_model("judge", "Problem") - Solution = apps.get_model("judge", "Solution") - BlogPost = apps.get_model("judge", "BlogPost") - Contest = apps.get_model("judge", "Contest") - - for bookmark in BookMark.objects.all(): - page = bookmark.page - try: - if page.startswith("p:"): - code = page[2:] - bookmark.linked_object = Problem.objects.get(code=code) - elif page.startswith("s:"): - code = page[2:] - bookmark.linked_object = Solution.objects.get(problem__code=code) - elif page.startswith("c:"): - key = page[2:] - bookmark.linked_object = Contest.objects.get(key=key) - elif page.startswith("b:"): - blog_id = page[2:] - bookmark.linked_object = BlogPost.objects.get(id=blog_id) - bookmark.save() - except ObjectDoesNotExist: - bookmark.delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), - ("judge", "0159_auto_20230803_1518"), - ] - - operations = [ - migrations.AddField( - model_name="bookmark", - name="content_type", - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="contenttypes.contenttype", - ), - ), - migrations.AddField( - model_name="bookmark", - name="object_id", - field=models.PositiveIntegerField(default=1), - ), - migrations.AddField( - model_name="bookmark", - name="score", - field=models.IntegerField(default=0, verbose_name="votes"), - ), - migrations.AlterUniqueTogether( - name="bookmark", - unique_together={("content_type", "object_id")}, - ), - migrations.AddIndex( - model_name="bookmark", - index=models.Index( - fields=["content_type", "object_id"], - name="judge_bookm_content_964329_idx", - ), - ), - migrations.AddIndex( - model_name="makebookmark", - index=models.Index( - fields=["user", "bookmark"], name="judge_makeb_user_id_f0e226_idx" - ), - ), - migrations.RunPython(migrate_bookmark, migrations.RunPython.noop, atomic=True), - ] diff --git a/judge/migrations/0161_auto_20230803_1536.py b/judge/migrations/0161_auto_20230803_1536.py deleted file mode 100644 index 7e12b9a..0000000 --- a/judge/migrations/0161_auto_20230803_1536.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-03 08:36 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), - ("judge", "0160_migrate_bookmark"), - ] - - operations = [ - migrations.AlterField( - model_name="bookmark", - name="content_type", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="contenttypes.contenttype", - ), - ), - migrations.AlterField( - model_name="bookmark", - name="object_id", - field=models.PositiveIntegerField(), - ), - ] diff --git a/judge/migrations/0162_profile_image.py b/judge/migrations/0162_profile_image.py deleted file mode 100644 index b371f39..0000000 --- a/judge/migrations/0162_profile_image.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-24 00:50 - -from django.db import migrations, models -import judge.models.profile - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0161_auto_20230803_1536"), - ] - - operations = [ - migrations.AddField( - model_name="profile", - name="profile_image", - field=models.ImageField( - null=True, upload_to=judge.models.profile.profile_image_path - ), - ), - ] diff --git a/judge/migrations/0163_email_change.py b/judge/migrations/0163_email_change.py deleted file mode 100644 index 985121d..0000000 --- a/judge/migrations/0163_email_change.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-25 00:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0162_profile_image"), - ] - - operations = [ - migrations.AddField( - model_name="profile", - name="email_change_pending", - field=models.EmailField(blank=True, max_length=254, null=True), - ), - ] diff --git a/judge/migrations/0164_show_testcase.py b/judge/migrations/0164_show_testcase.py deleted file mode 100644 index 33cbdf8..0000000 --- a/judge/migrations/0164_show_testcase.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-25 23:03 - -from django.db import migrations, models - - -def migrate_show_testcases(apps, schema_editor): - ContestProblem = apps.get_model("judge", "ContestProblem") - - for c in ContestProblem.objects.all(): - if c.output_prefix_override == 1: - c.show_testcases = True - c.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0163_email_change"), - ] - - operations = [ - migrations.AddField( - model_name="contestproblem", - name="show_testcases", - field=models.BooleanField(default=False, verbose_name="visible testcases"), - ), - migrations.RunPython( - migrate_show_testcases, migrations.RunPython.noop, atomic=True - ), - ] diff --git a/judge/migrations/0165_drop_output_prefix_override.py b/judge/migrations/0165_drop_output_prefix_override.py deleted file mode 100644 index ea9b17a..0000000 --- a/judge/migrations/0165_drop_output_prefix_override.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-25 23:11 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0164_show_testcase"), - ] - - operations = [ - migrations.RemoveField( - model_name="contestproblem", - name="output_prefix_override", - ), - ] diff --git a/judge/migrations/0166_display_rank_index.py b/judge/migrations/0166_display_rank_index.py deleted file mode 100644 index ffea311..0000000 --- a/judge/migrations/0166_display_rank_index.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.18 on 2023-08-28 01:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0165_drop_output_prefix_override"), - ] - - operations = [ - migrations.AlterField( - model_name="profile", - name="display_rank", - field=models.CharField( - choices=[ - ("user", "Normal User"), - ("setter", "Problem Setter"), - ("admin", "Admin"), - ], - db_index=True, - default="user", - max_length=10, - verbose_name="display rank", - ), - ), - ] diff --git a/judge/migrations/0167_ultimate_contest_format.py b/judge/migrations/0167_ultimate_contest_format.py deleted file mode 100644 index 636e882..0000000 --- a/judge/migrations/0167_ultimate_contest_format.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 3.2.18 on 2023-09-01 00:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0166_display_rank_index"), - ] - - operations = [ - migrations.AlterField( - model_name="contest", - name="format_name", - field=models.CharField( - choices=[ - ("atcoder", "AtCoder"), - ("default", "Default"), - ("ecoo", "ECOO"), - ("icpc", "ICPC"), - ("ioi", "IOI"), - ("ioi16", "New IOI"), - ("ultimate", "Ultimate"), - ], - default="default", - help_text="The contest format module to use.", - max_length=32, - verbose_name="contest format", - ), - ), - ] diff --git a/judge/migrations/0168_css_background.py b/judge/migrations/0168_css_background.py deleted file mode 100644 index 6210a26..0000000 --- a/judge/migrations/0168_css_background.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.18 on 2023-09-02 00:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0167_ultimate_contest_format"), - ] - - operations = [ - migrations.AddField( - model_name="profile", - name="css_background", - field=models.TextField( - blank=True, - help_text='CSS custom background properties: url("image_url"), color, etc', - max_length=300, - null=True, - verbose_name="Custom background", - ), - ), - ] diff --git a/judge/migrations/0169_public_scoreboard.py b/judge/migrations/0169_public_scoreboard.py deleted file mode 100644 index 0dc7338..0000000 --- a/judge/migrations/0169_public_scoreboard.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.2.18 on 2023-09-17 01:55 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0168_css_background"), - ] - - operations = [ - migrations.AddField( - model_name="contest", - name="public_scoreboard", - field=models.BooleanField( - default=False, - help_text="Ranking page is public even for private contests.", - verbose_name="public scoreboard", - ), - ), - ] diff --git a/judge/migrations/0170_contests_summary.py b/judge/migrations/0170_contests_summary.py deleted file mode 100644 index a2b19de..0000000 --- a/judge/migrations/0170_contests_summary.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.21 on 2023-10-02 03:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0169_public_scoreboard"), - ] - - operations = [ - migrations.CreateModel( - name="ContestsSummary", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("scores", models.JSONField(blank=True, null=True)), - ("key", models.CharField(max_length=20, unique=True)), - ("contests", models.ManyToManyField(to="judge.Contest")), - ], - ), - ] diff --git a/judge/migrations/0171_update_notification.py b/judge/migrations/0171_update_notification.py deleted file mode 100644 index 7803e4b..0000000 --- a/judge/migrations/0171_update_notification.py +++ /dev/null @@ -1,68 +0,0 @@ -# Generated by Django 3.2.18 on 2023-10-10 21:17 - -from django.db import migrations, models -import django.db.models.deletion -from django.urls import reverse - -from collections import defaultdict - - -# Run this in shell -def migrate_notif(apps, schema_editor): - Notification = apps.get_model("judge", "Notification") - Profile = apps.get_model("judge", "Profile") - NotificationProfile = apps.get_model("judge", "NotificationProfile") - - unread_count = defaultdict(int) - for c in Notification.objects.all(): - if c.comment: - c.html_link = ( - f'{c.comment.page_title}' - ) - c.author = c.comment.author - c.save() - if c.read is False: - unread_count[c.author] += 1 - - for user in unread_count: - np = NotificationProfile(user=user) - np.unread_count = unread_count[user] - np.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0170_contests_summary"), - ] - - operations = [ - migrations.AlterModelOptions( - name="contestssummary", - options={ - "verbose_name": "contests summary", - "verbose_name_plural": "contests summaries", - }, - ), - migrations.CreateModel( - name="NotificationProfile", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("unread_count", models.IntegerField(default=0)), - ( - "user", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, to="judge.profile" - ), - ), - ], - ), - ] diff --git a/judge/migrations/0172_index_rating.py b/judge/migrations/0172_index_rating.py deleted file mode 100644 index 157c005..0000000 --- a/judge/migrations/0172_index_rating.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-10-10 23:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0171_update_notification"), - ] - - operations = [ - migrations.AlterField( - model_name="profile", - name="rating", - field=models.IntegerField(db_index=True, default=None, null=True), - ), - ] diff --git a/judge/migrations/0173_fulltext.py b/judge/migrations/0173_fulltext.py deleted file mode 100644 index a47ef7b..0000000 --- a/judge/migrations/0173_fulltext.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.18 on 2023-10-14 00:53 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0172_index_rating"), - ] - - operations = [ - migrations.RunSQL( - ( - "CREATE FULLTEXT INDEX IF NOT EXISTS code_name_index ON judge_problem (code, name)", - ), - reverse_sql=migrations.RunSQL.noop, - ), - migrations.RunSQL( - ( - "CREATE FULLTEXT INDEX IF NOT EXISTS key_name_index ON judge_contest (`key`, name)", - ), - reverse_sql=migrations.RunSQL.noop, - ), - ] diff --git a/judge/migrations/0174_contest_summary_result.py b/judge/migrations/0174_contest_summary_result.py deleted file mode 100644 index 1a4c9cc..0000000 --- a/judge/migrations/0174_contest_summary_result.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 3.2.18 on 2023-11-24 05:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0173_fulltext"), - ] - - operations = [ - migrations.AddField( - model_name="contestssummary", - name="results", - field=models.JSONField(blank=True, null=True), - ), - migrations.AlterField( - model_name="contest", - name="authors", - field=models.ManyToManyField( - help_text="These users will be able to edit the contest.", - related_name="_judge_contest_authors_+", - to="judge.Profile", - verbose_name="authors", - ), - ), - migrations.AlterField( - model_name="contest", - name="curators", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to edit the contest, but will not be listed as authors.", - related_name="_judge_contest_curators_+", - to="judge.Profile", - verbose_name="curators", - ), - ), - migrations.AlterField( - model_name="contest", - name="testers", - field=models.ManyToManyField( - blank=True, - help_text="These users will be able to view the contest, but not edit it.", - related_name="_judge_contest_testers_+", - to="judge.Profile", - verbose_name="testers", - ), - ), - ] diff --git a/judge/migrations/0175_add_profile_index.py b/judge/migrations/0175_add_profile_index.py deleted file mode 100644 index d9ec296..0000000 --- a/judge/migrations/0175_add_profile_index.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.18 on 2023-11-29 02:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0174_contest_summary_result"), - ] - - operations = [ - migrations.AddIndex( - model_name="profile", - index=models.Index( - fields=["is_unlisted", "performance_points"], - name="judge_profi_is_unli_d4034c_idx", - ), - ), - ] diff --git a/judge/migrations/0176_comment_revision_count.py b/judge/migrations/0176_comment_revision_count.py deleted file mode 100644 index ed2a246..0000000 --- a/judge/migrations/0176_comment_revision_count.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.18 on 2023-12-06 01:28 - -from django.db import migrations, models - - -# Run this in shell -def migrate_revision(apps, schema_editor): - Comment = apps.get_model("judge", "Comment") - - for c in Comment.objects.all(): - c.revision_count = c.versions.count() - c.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0175_add_profile_index"), - ] - - operations = [ - migrations.AddField( - model_name="comment", - name="revision_count", - field=models.PositiveIntegerField(default=1), - ), - # migrations.RunPython( - # migrate_revision, migrations.RunPython.noop, atomic=True - # ), - ] diff --git a/judge/migrations/0177_test_formatter.py b/judge/migrations/0177_test_formatter.py deleted file mode 100644 index 78eadb5..0000000 --- a/judge/migrations/0177_test_formatter.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.db import migrations, models -import judge.models.test_formatter - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0176_comment_revision_count"), - ] - - operations = [ - migrations.CreateModel( - name="TestFormatterModel", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "file", - models.FileField( - blank=True, - null=True, - upload_to=judge.models.test_formatter.test_formatter_path, - verbose_name="testcase file", - ), - ), - ], - ) - ] diff --git a/judge/migrations/0178_remove_user_script.py b/judge/migrations/0178_remove_user_script.py deleted file mode 100644 index dc4b560..0000000 --- a/judge/migrations/0178_remove_user_script.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.18 on 2024-01-14 01:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0177_test_formatter"), - ] - - operations = [ - migrations.RemoveField( - model_name="profile", - name="user_script", - ), - ] diff --git a/judge/migrations/0179_submission_result_lang_index.py b/judge/migrations/0179_submission_result_lang_index.py deleted file mode 100644 index 3e43918..0000000 --- a/judge/migrations/0179_submission_result_lang_index.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2024-01-23 00:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0178_remove_user_script"), - ] - - operations = [ - migrations.AddIndex( - model_name="submission", - index=models.Index( - fields=["language", "result"], name="judge_submi_languag_874af4_idx" - ), - ), - ] diff --git a/judge/migrations/0180_course.py b/judge/migrations/0180_course.py deleted file mode 100644 index 439d32e..0000000 --- a/judge/migrations/0180_course.py +++ /dev/null @@ -1,78 +0,0 @@ -# Generated by Django 3.2.18 on 2024-02-15 02:12 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0179_submission_result_lang_index"), - ] - - operations = [ - migrations.CreateModel( - name="CourseLesson", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("title", models.TextField(verbose_name="course title")), - ("content", models.TextField(verbose_name="course content")), - ("order", models.IntegerField(default=0, verbose_name="order")), - ("points", models.IntegerField(verbose_name="points")), - ], - ), - migrations.RemoveField( - model_name="courseresource", - name="course", - ), - migrations.RemoveField( - model_name="course", - name="ending_time", - ), - migrations.AlterField( - model_name="courserole", - name="course", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.course", - verbose_name="course", - ), - ), - migrations.DeleteModel( - name="CourseAssignment", - ), - migrations.DeleteModel( - name="CourseResource", - ), - migrations.AddField( - model_name="courselesson", - name="course", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.course", - verbose_name="course", - ), - ), - migrations.AddField( - model_name="courselesson", - name="problems", - field=models.ManyToManyField(to="judge.Problem"), - ), - migrations.AlterUniqueTogether( - name="courserole", - unique_together={("course", "user")}, - ), - migrations.AlterField( - model_name="courselesson", - name="problems", - field=models.ManyToManyField(blank=True, to="judge.Problem"), - ), - ] diff --git a/judge/migrations/0181_remove_math_engine.py b/judge/migrations/0181_remove_math_engine.py deleted file mode 100644 index 99e4a85..0000000 --- a/judge/migrations/0181_remove_math_engine.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 3.2.18 on 2024-02-26 20:43 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0180_course"), - ] - - operations = [ - migrations.RemoveField( - model_name="profile", - name="math_engine", - ), - migrations.AlterField( - model_name="course", - name="about", - field=models.TextField(verbose_name="course description"), - ), - migrations.AlterField( - model_name="courselesson", - name="course", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="lessons", - to="judge.course", - verbose_name="course", - ), - ), - migrations.AlterField( - model_name="courselesson", - name="problems", - field=models.ManyToManyField( - blank=True, to="judge.Problem", verbose_name="problem" - ), - ), - migrations.AlterField( - model_name="courserole", - name="user", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="course_roles", - to="judge.profile", - verbose_name="user", - ), - ), - ] diff --git a/judge/migrations/0182_rename_customcpp.py b/judge/migrations/0182_rename_customcpp.py deleted file mode 100644 index 5f991be..0000000 --- a/judge/migrations/0182_rename_customcpp.py +++ /dev/null @@ -1,75 +0,0 @@ -# Generated by Django 3.2.18 on 2024-03-19 04:28 - -from django.db import migrations, models - - -def migrate_checker(apps, schema_editor): - ProblemData = apps.get_model("judge", "ProblemData") - ProblemTestCase = apps.get_model("judge", "ProblemTestCase") - - for p in ProblemData.objects.all(): - if p.checker == "customval": - p.checker = "customcpp" - p.save() - - for p in ProblemTestCase.objects.all(): - if p.checker == "customval": - p.checker = "customcpp" - p.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0181_remove_math_engine"), - ] - - operations = [ - migrations.RunPython(migrate_checker, migrations.RunPython.noop, atomic=True), - migrations.AlterField( - model_name="problemdata", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker (PY)"), - ("customcpp", "Custom checker (CPP)"), - ("interact", "Interactive"), - ("testlib", "Testlib"), - ], - max_length=10, - verbose_name="checker", - ), - ), - migrations.AlterField( - model_name="problemtestcase", - name="checker", - field=models.CharField( - blank=True, - choices=[ - ("standard", "Standard"), - ("floats", "Floats"), - ("floatsabs", "Floats (absolute)"), - ("floatsrel", "Floats (relative)"), - ("rstripped", "Non-trailing spaces"), - ("sorted", "Unordered"), - ("identical", "Byte identical"), - ("linecount", "Line-by-line"), - ("custom", "Custom checker (PY)"), - ("customcpp", "Custom checker (CPP)"), - ("interact", "Interactive"), - ("testlib", "Testlib"), - ], - max_length=10, - verbose_name="checker", - ), - ), - ] diff --git a/judge/migrations/0183_rename_custom_checker_cpp.py b/judge/migrations/0183_rename_custom_checker_cpp.py deleted file mode 100644 index 65280ae..0000000 --- a/judge/migrations/0183_rename_custom_checker_cpp.py +++ /dev/null @@ -1,45 +0,0 @@ -# Generated by Django 3.2.18 on 2024-03-19 04:45 - -import django.core.validators -from django.db import migrations, models -import judge.models.problem_data -import judge.utils.problem_data - - -def migrate_checker(apps, schema_editor): - ProblemData = apps.get_model("judge", "ProblemData") - - for p in ProblemData.objects.all(): - p.custom_checker_cpp = p.custom_validator - p.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0182_rename_customcpp"), - ] - - operations = [ - migrations.AddField( - model_name="problemdata", - name="custom_checker_cpp", - field=models.FileField( - blank=True, - null=True, - storage=judge.utils.problem_data.ProblemDataStorage(), - upload_to=judge.models.problem_data.problem_directory_file, - validators=[ - django.core.validators.FileExtensionValidator( - allowed_extensions=["cpp"] - ) - ], - verbose_name="custom cpp checker file", - ), - ), - migrations.RunPython(migrate_checker, migrations.RunPython.noop, atomic=True), - migrations.RemoveField( - model_name="problemdata", - name="custom_validator", - ), - ] diff --git a/judge/migrations/0184_contest_rate_limit.py b/judge/migrations/0184_contest_rate_limit.py deleted file mode 100644 index c05174d..0000000 --- a/judge/migrations/0184_contest_rate_limit.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.18 on 2024-03-23 04:07 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0183_rename_custom_checker_cpp"), - ] - - operations = [ - migrations.AddField( - model_name="contest", - name="rate_limit", - field=models.PositiveIntegerField( - blank=True, - help_text="Maximum number of submissions per minute. Leave empty if you don't want rate limit.", - null=True, - validators=[ - django.core.validators.MinValueValidator(1), - django.core.validators.MaxValueValidator(5), - ], - verbose_name="rate limit", - ), - ), - ] diff --git a/judge/migrations/0185_rename_org_profile_colum.py b/judge/migrations/0185_rename_org_profile_colum.py deleted file mode 100644 index c184169..0000000 --- a/judge/migrations/0185_rename_org_profile_colum.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2024-04-12 05:50 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0184_contest_rate_limit"), - ] - - operations = [ - migrations.RenameField( - model_name="organizationprofile", - old_name="users", - new_name="profile", - ), - ] diff --git a/judge/migrations/0186_change_about_fields_max_len.py b/judge/migrations/0186_change_about_fields_max_len.py deleted file mode 100644 index 9e8cf01..0000000 --- a/judge/migrations/0186_change_about_fields_max_len.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.2.18 on 2024-04-12 17:04 - -from django.db import migrations, models - - -def truncate_about_text(apps, schema_editor): - Organization = apps.get_model("judge", "Organization") - Profile = apps.get_model("judge", "Profile") - - for org in Organization.objects.all(): - if len(org.about) > 10000: - org.about = org.about[:10000] - org.save() - - for profile in Profile.objects.all(): - if profile.about and len(profile.about) > 10000: - profile.about = profile.about[:10000] - profile.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0185_rename_org_profile_colum"), - ] - - operations = [ - migrations.RunPython(truncate_about_text), - migrations.AlterField( - model_name="organization", - name="about", - field=models.CharField( - max_length=10000, verbose_name="organization description" - ), - ), - migrations.AlterField( - model_name="profile", - name="about", - field=models.CharField( - blank=True, max_length=10000, null=True, verbose_name="self-description" - ), - ), - ] diff --git a/judge/migrations/0187_profile_info.py b/judge/migrations/0187_profile_info.py deleted file mode 100644 index c3b02c2..0000000 --- a/judge/migrations/0187_profile_info.py +++ /dev/null @@ -1,69 +0,0 @@ -# Generated by Django 3.2.18 on 2024-04-27 03:35 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0186_change_about_fields_max_len"), - ] - - operations = [ - migrations.RemoveField( - model_name="profile", - name="is_banned_problem_voting", - ), - migrations.CreateModel( - name="ProfileInfo", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "tshirt_size", - models.CharField( - blank=True, - choices=[ - ("S", "Small (S)"), - ("M", "Medium (M)"), - ("L", "Large (L)"), - ("XL", "Extra Large (XL)"), - ("XXL", "2 Extra Large (XXL)"), - ], - max_length=5, - null=True, - verbose_name="t-shirt size", - ), - ), - ( - "date_of_birth", - models.DateField( - blank=True, null=True, verbose_name="date of birth" - ), - ), - ( - "address", - models.CharField( - blank=True, max_length=255, null=True, verbose_name="address" - ), - ), - ( - "profile", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="info", - to="judge.profile", - verbose_name="profile associated", - ), - ), - ], - ), - ] diff --git a/judge/migrations/0188_official_contest.py b/judge/migrations/0188_official_contest.py deleted file mode 100644 index 89414a7..0000000 --- a/judge/migrations/0188_official_contest.py +++ /dev/null @@ -1,110 +0,0 @@ -# Generated by Django 3.2.18 on 2024-05-30 04:32 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0187_profile_info"), - ] - - operations = [ - migrations.CreateModel( - name="OfficialContestCategory", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "name", - models.CharField( - max_length=50, - unique=True, - verbose_name="official contest category", - ), - ), - ], - options={ - "verbose_name": "official contest category", - "verbose_name_plural": "official contest categories", - }, - ), - migrations.CreateModel( - name="OfficialContestLocation", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "name", - models.CharField( - max_length=50, - unique=True, - verbose_name="official contest location", - ), - ), - ], - options={ - "verbose_name": "official contest location", - "verbose_name_plural": "official contest locations", - }, - ), - migrations.CreateModel( - name="OfficialContest", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("year", models.PositiveIntegerField(verbose_name="year")), - ( - "category", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.officialcontestcategory", - verbose_name="contest category", - ), - ), - ( - "contest", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="official", - to="judge.contest", - verbose_name="contest", - ), - ), - ( - "location", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="judge.officialcontestlocation", - verbose_name="contest location", - ), - ), - ], - options={ - "verbose_name": "official contest", - "verbose_name_plural": "official contests", - }, - ), - ] diff --git a/judge/migrations/0189_organization_image.py b/judge/migrations/0189_organization_image.py deleted file mode 100644 index 4307a24..0000000 --- a/judge/migrations/0189_organization_image.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.2.21 on 2024-07-08 00:05 - -from django.db import migrations, models -import judge.models.profile - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0188_official_contest"), - ] - - operations = [ - migrations.AddField( - model_name="organization", - name="organization_image", - field=models.ImageField( - null=True, upload_to=judge.models.profile.organization_image_path - ), - ), - ] diff --git a/judge/migrations/0190_deprecate_old_notif_fields.py b/judge/migrations/0190_deprecate_old_notif_fields.py deleted file mode 100644 index dbf6f00..0000000 --- a/judge/migrations/0190_deprecate_old_notif_fields.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.2.18 on 2024-08-13 10:36 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0189_organization_image"), - ] - - operations = [ - migrations.RemoveField( - model_name="notification", - name="comment", - ), - migrations.RemoveField( - model_name="notification", - name="read", - ), - ] diff --git a/judge/migrations/0191_deprecate_old_org_image.py b/judge/migrations/0191_deprecate_old_org_image.py deleted file mode 100644 index 76d76f3..0000000 --- a/judge/migrations/0191_deprecate_old_org_image.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.18 on 2024-08-22 03:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0190_deprecate_old_notif_fields"), - ] - - operations = [ - migrations.RemoveField( - model_name="organization", - name="logo_override_image", - ), - ] diff --git a/judge/migrations/0192_course_lesson_problem.py b/judge/migrations/0192_course_lesson_problem.py deleted file mode 100644 index 89a1185..0000000 --- a/judge/migrations/0192_course_lesson_problem.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 3.2.21 on 2024-09-02 05:28 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0191_deprecate_old_org_image"), - ] - - operations = [ - migrations.CreateModel( - name="CourseLessonProblem", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("order", models.IntegerField(default=0, verbose_name="order")), - ("score", models.IntegerField(default=0, verbose_name="score")), - ( - "lesson", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="lesson_problems", - to="judge.courselesson", - ), - ), - ( - "problem", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="judge.problem" - ), - ), - ], - ), - ] diff --git a/judge/migrations/0193_remove_old_course_problems.py b/judge/migrations/0193_remove_old_course_problems.py deleted file mode 100644 index 809b3e9..0000000 --- a/judge/migrations/0193_remove_old_course_problems.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.18 on 2024-09-03 14:33 - -from django.db import migrations, models - - -def migrate_problems_to_courselessonproblem(apps, schema_editor): - CourseLesson = apps.get_model("judge", "CourseLesson") - CourseLessonProblem = apps.get_model("judge", "CourseLessonProblem") - - for lesson in CourseLesson.objects.all(): - for problem in lesson.problems.all(): - CourseLessonProblem.objects.create( - lesson=lesson, problem=problem, order=1, score=1 - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0192_course_lesson_problem"), - ] - - operations = [ - migrations.RunPython(migrate_problems_to_courselessonproblem), - migrations.RemoveField( - model_name="courselesson", - name="problems", - ), - ] diff --git a/judge/migrations/0194_course_contest.py b/judge/migrations/0194_course_contest.py deleted file mode 100644 index 6c3e534..0000000 --- a/judge/migrations/0194_course_contest.py +++ /dev/null @@ -1,67 +0,0 @@ -# Generated by Django 3.2.21 on 2024-09-30 22:31 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("judge", "0193_remove_old_course_problems"), - ] - - operations = [ - migrations.AddField( - model_name="contest", - name="is_in_course", - field=models.BooleanField(default=False, verbose_name="contest in course"), - ), - migrations.AddField( - model_name="courselesson", - name="is_visible", - field=models.BooleanField(default=True, verbose_name="publicly visible"), - ), - migrations.AlterField( - model_name="courselesson", - name="content", - field=models.TextField(verbose_name="lesson content"), - ), - migrations.AlterField( - model_name="courselesson", - name="title", - field=models.TextField(verbose_name="lesson title"), - ), - migrations.CreateModel( - name="CourseContest", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("order", models.IntegerField(default=0, verbose_name="order")), - ("points", models.IntegerField(verbose_name="points")), - ( - "contest", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="course", - to="judge.contest", - unique=True, - ), - ), - ( - "course", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="contests", - to="judge.course", - ), - ), - ], - ), - ] diff --git a/judge/ml/collab_filter.py b/judge/ml/collab_filter.py deleted file mode 100644 index 105a534..0000000 --- a/judge/ml/collab_filter.py +++ /dev/null @@ -1,82 +0,0 @@ -import numpy as np -import os -import hashlib - -from django.core.cache import cache -from django.conf import settings - -from judge.caching import cache_wrapper - - -class CollabFilter: - DOT = "dot" - COSINE = "cosine" - - # name = 'collab_filter' or 'collab_filter_time' - def __init__(self, name): - self.embeddings = np.load( - os.path.join(settings.ML_OUTPUT_PATH, name + "/embeddings.npz"), - allow_pickle=True, - ) - _, problem_arr = self.embeddings.files - self.name = name - self.problem_embeddings = self.embeddings[problem_arr].item() - - def __str__(self): - return self.name - - def compute_scores(self, query_embedding, item_embeddings, measure=DOT): - """Return {id: score}""" - u = query_embedding - V = np.stack(list(item_embeddings.values())) - if measure == self.COSINE: - V = V / np.linalg.norm(V, axis=1, keepdims=True) - u = u / np.linalg.norm(u) - scores = u.dot(V.T) - scores_by_id = {id_: s for id_, s in zip(item_embeddings.keys(), scores)} - return scores_by_id - - def _get_embedding_version(self): - first_problem = self.problem_embeddings[0] - array_bytes = first_problem.tobytes() - hash_object = hashlib.sha256(array_bytes) - hash_bytes = hash_object.digest() - return hash_bytes.hex()[:5] - - @cache_wrapper(prefix="CFgue", timeout=86400) - def _get_user_embedding(self, user_id, embedding_version): - user_arr, _ = self.embeddings.files - user_embeddings = self.embeddings[user_arr].item() - if user_id not in user_embeddings: - return user_embeddings[0] - return user_embeddings[user_id] - - def get_user_embedding(self, user_id): - version = self._get_embedding_version() - return self._get_user_embedding(user_id, version) - - @cache_wrapper(prefix="user_recommendations", timeout=3600) - def user_recommendations(self, user_id, problems, measure=DOT, limit=None): - user_embedding = self.get_user_embedding(user_id) - scores = self.compute_scores(user_embedding, self.problem_embeddings, measure) - res = [] # [(score, problem)] - for pid in problems: - if pid in scores: - res.append((scores[pid], pid)) - - res.sort(reverse=True, key=lambda x: x[0]) - return res[:limit] - - # return a list of pid - def problem_neighbors(self, problem, problemset, measure=DOT, limit=None): - pid = problem.id - if pid not in self.problem_embeddings: - return [] - embedding = self.problem_embeddings[pid] - scores = self.compute_scores(embedding, self.problem_embeddings, measure) - res = [] - for p in problemset: - if p in scores: - res.append((scores[p], p)) - res.sort(reverse=True, key=lambda x: x[0]) - return res[:limit] diff --git a/judge/models/__init__.py b/judge/models/__init__.py index ef62797..23e0734 100644 --- a/judge/models/__init__.py +++ b/judge/models/__init__.py @@ -1,99 +1,29 @@ from reversion import revisions -from judge.models.choices import ( - ACE_THEMES, - TIMEZONE, -) -from judge.models.comment import Comment, CommentLock, CommentVote -from judge.models.contest import ( - Contest, - ContestMoss, - ContestParticipation, - ContestProblem, - ContestSubmission, - ContestTag, - Rating, - ContestProblemClarification, - ContestsSummary, - OfficialContestCategory, - OfficialContestLocation, - OfficialContest, -) +from judge.models.choices import ACE_THEMES, EFFECTIVE_MATH_ENGINES, MATH_ENGINES_CHOICES, TIMEZONE +from judge.models.comment import Comment, CommentLock, CommentVote, Notification +from judge.models.contest import Contest, ContestMoss, ContestParticipation, ContestProblem, ContestSubmission, \ + ContestTag, Rating from judge.models.interface import BlogPost, MiscConfig, NavigationBar, validate_regex from judge.models.message import PrivateMessage, PrivateMessageThread -from judge.models.problem import ( - LanguageLimit, - LanguageTemplate, - License, - Problem, - ProblemGroup, - ProblemTranslation, - ProblemType, - Solution, - TranslatedProblemQuerySet, - ProblemPointsVote, -) -from judge.models.problem_data import ( - CHECKERS, - ProblemData, - ProblemTestCase, - problem_data_storage, - problem_directory_file, -) -from judge.models.profile import ( - Organization, - OrganizationRequest, - Profile, - Friend, - OrganizationProfile, - ProfileInfo, -) +from judge.models.problem import LanguageLimit, License, Problem, ProblemClarification, ProblemGroup, \ + ProblemTranslation, ProblemType, Solution, TranslatedProblemForeignKeyQuerySet, TranslatedProblemQuerySet +from judge.models.problem_data import CHECKERS, ProblemData, ProblemTestCase, problem_data_storage, \ + problem_directory_file +from judge.models.profile import Organization, OrganizationRequest, Profile, Friend from judge.models.runtime import Judge, Language, RuntimeVersion -from judge.models.submission import ( - SUBMISSION_RESULT, - Submission, - SubmissionSource, - SubmissionTestCase, -) - -from judge.models.test_formatter import TestFormatterModel +from judge.models.submission import SUBMISSION_RESULT, Submission, SubmissionSource, SubmissionTestCase from judge.models.ticket import Ticket, TicketMessage -from judge.models.volunteer import VolunteerProblemVote -from judge.models.pagevote import PageVote, PageVoteVoter -from judge.models.bookmark import BookMark, MakeBookMark -from judge.models.course import ( - Course, - CourseRole, - CourseLesson, - CourseLessonProblem, - CourseContest, -) -from judge.models.notification import Notification, NotificationProfile -from judge.models.test_formatter import TestFormatterModel -revisions.register(Profile, exclude=["points", "last_access", "ip", "rating"]) -revisions.register(Problem, follow=["language_limits"]) +revisions.register(Profile, exclude=['points', 'last_access', 'ip', 'rating']) +revisions.register(Problem, follow=['language_limits']) revisions.register(LanguageLimit) -revisions.register(LanguageTemplate) -revisions.register(Contest, follow=["contest_problems"]) +revisions.register(Contest, follow=['contest_problems']) revisions.register(ContestProblem) revisions.register(Organization) revisions.register(BlogPost) revisions.register(Solution) -revisions.register(Judge, fields=["name", "created", "auth_key", "description"]) +revisions.register(Judge, fields=['name', 'created', 'auth_key', 'description']) revisions.register(Language) -revisions.register( - Comment, fields=["author", "time", "page", "score", "body", "hidden", "parent"] -) -revisions.register(ProblemTranslation) -revisions.register(ProblemPointsVote) -revisions.register(ContestMoss) -revisions.register(ProblemData) -revisions.register(ProblemTestCase) -revisions.register(ContestParticipation) -revisions.register(Rating) -revisions.register(PageVoteVoter) -revisions.register(VolunteerProblemVote) -revisions.register(MakeBookMark) -revisions.register(Course) +revisions.register(Comment, fields=['author', 'time', 'page', 'score', 'body', 'hidden', 'parent']) del revisions diff --git a/judge/models/bookmark.py b/judge/models/bookmark.py deleted file mode 100644 index 06be327..0000000 --- a/judge/models/bookmark.py +++ /dev/null @@ -1,74 +0,0 @@ -from django.db import models -from django.db.models import CASCADE -from django.utils.translation import gettext_lazy as _ -from django.core.exceptions import ObjectDoesNotExist -from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.contenttypes.models import ContentType - -from judge.models.profile import Profile -from judge.caching import cache_wrapper - -__all__ = ["BookMark"] - - -class BookMark(models.Model): - page = models.CharField( - max_length=30, - verbose_name=_("associated page"), - db_index=True, - ) # deprecated - score = models.IntegerField(verbose_name=_("votes"), default=0) - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) - object_id = models.PositiveIntegerField() - linked_object = GenericForeignKey("content_type", "object_id") - - @cache_wrapper(prefix="BMgb") - def is_bookmarked_by(self, user): - return MakeBookMark.objects.filter(bookmark=self, user=user).exists() - - class Meta: - verbose_name = _("bookmark") - verbose_name_plural = _("bookmarks") - indexes = [ - models.Index(fields=["content_type", "object_id"]), - ] - unique_together = ("content_type", "object_id") - - def __str__(self): - return f"bookmark for {self.linked_object}" - - -class MakeBookMark(models.Model): - bookmark = models.ForeignKey(BookMark, related_name="bookmark", on_delete=CASCADE) - user = models.ForeignKey( - Profile, related_name="user_bookmark", on_delete=CASCADE, db_index=True - ) - - class Meta: - indexes = [ - models.Index(fields=["user", "bookmark"]), - ] - unique_together = ["user", "bookmark"] - verbose_name = _("make bookmark") - verbose_name_plural = _("make bookmarks") - - -@cache_wrapper(prefix="gocb", expected_type=BookMark) -def _get_or_create_bookmark(content_type, object_id): - bookmark, created = BookMark.objects.get_or_create( - content_type=content_type, - object_id=object_id, - ) - return bookmark - - -class Bookmarkable: - def get_or_create_bookmark(self): - content_type = ContentType.objects.get_for_model(self) - object_id = self.pk - return _get_or_create_bookmark(content_type, object_id) - - -def dirty_bookmark(bookmark, profile): - bookmark.is_bookmarked_by.dirty(bookmark, profile) - _get_or_create_bookmark.dirty(bookmark.content_type, bookmark.object_id) diff --git a/judge/models/choices.py b/judge/models/choices.py index 105e7e7..16c57de 100644 --- a/judge/models/choices.py +++ b/judge/models/choices.py @@ -8,11 +8,11 @@ from django.utils.translation import gettext_lazy as _ def make_timezones(): data = defaultdict(list) for tz in pytz.all_timezones: - if "/" in tz: - area, loc = tz.split("/", 1) + if '/' in tz: + area, loc = tz.split('/', 1) else: - area, loc = "Other", tz - if not loc.startswith("GMT"): + area, loc = 'Other', tz + if not loc.startswith('GMT'): data[area].append((tz, loc)) return sorted(data.items(), key=itemgetter(0)) @@ -21,36 +21,46 @@ TIMEZONE = make_timezones() del make_timezones ACE_THEMES = ( - ("ambiance", "Ambiance"), - ("chaos", "Chaos"), - ("chrome", "Chrome"), - ("clouds", "Clouds"), - ("clouds_midnight", "Clouds Midnight"), - ("cobalt", "Cobalt"), - ("crimson_editor", "Crimson Editor"), - ("dawn", "Dawn"), - ("dreamweaver", "Dreamweaver"), - ("eclipse", "Eclipse"), - ("github", "Github"), - ("idle_fingers", "Idle Fingers"), - ("katzenmilch", "Katzenmilch"), - ("kr_theme", "KR Theme"), - ("kuroir", "Kuroir"), - ("merbivore", "Merbivore"), - ("merbivore_soft", "Merbivore Soft"), - ("mono_industrial", "Mono Industrial"), - ("monokai", "Monokai"), - ("pastel_on_dark", "Pastel on Dark"), - ("solarized_dark", "Solarized Dark"), - ("solarized_light", "Solarized Light"), - ("terminal", "Terminal"), - ("textmate", "Textmate"), - ("tomorrow", "Tomorrow"), - ("tomorrow_night", "Tomorrow Night"), - ("tomorrow_night_blue", "Tomorrow Night Blue"), - ("tomorrow_night_bright", "Tomorrow Night Bright"), - ("tomorrow_night_eighties", "Tomorrow Night Eighties"), - ("twilight", "Twilight"), - ("vibrant_ink", "Vibrant Ink"), - ("xcode", "XCode"), + ('ambiance', 'Ambiance'), + ('chaos', 'Chaos'), + ('chrome', 'Chrome'), + ('clouds', 'Clouds'), + ('clouds_midnight', 'Clouds Midnight'), + ('cobalt', 'Cobalt'), + ('crimson_editor', 'Crimson Editor'), + ('dawn', 'Dawn'), + ('dreamweaver', 'Dreamweaver'), + ('eclipse', 'Eclipse'), + ('github', 'Github'), + ('idle_fingers', 'Idle Fingers'), + ('katzenmilch', 'Katzenmilch'), + ('kr_theme', 'KR Theme'), + ('kuroir', 'Kuroir'), + ('merbivore', 'Merbivore'), + ('merbivore_soft', 'Merbivore Soft'), + ('mono_industrial', 'Mono Industrial'), + ('monokai', 'Monokai'), + ('pastel_on_dark', 'Pastel on Dark'), + ('solarized_dark', 'Solarized Dark'), + ('solarized_light', 'Solarized Light'), + ('terminal', 'Terminal'), + ('textmate', 'Textmate'), + ('tomorrow', 'Tomorrow'), + ('tomorrow_night', 'Tomorrow Night'), + ('tomorrow_night_blue', 'Tomorrow Night Blue'), + ('tomorrow_night_bright', 'Tomorrow Night Bright'), + ('tomorrow_night_eighties', 'Tomorrow Night Eighties'), + ('twilight', 'Twilight'), + ('vibrant_ink', 'Vibrant Ink'), + ('xcode', 'XCode'), ) + +MATH_ENGINES_CHOICES = ( + ('tex', _('Leave as LaTeX')), + ('svg', _('SVG with PNG fallback')), + ('mml', _('MathML only')), + ('jax', _('MathJax with SVG/PNG fallback')), + ('auto', _('Detect best quality')), +) + +EFFECTIVE_MATH_ENGINES = ('svg', 'mml', 'tex', 'jax') diff --git a/judge/models/comment.py b/judge/models/comment.py index 7e9f5bf..a58ab1d 100644 --- a/judge/models/comment.py +++ b/judge/models/comment.py @@ -9,171 +9,188 @@ from django.db.models import CASCADE from django.urls import reverse from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ -from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.contenttypes.models import ContentType from mptt.fields import TreeForeignKey from mptt.models import MPTTModel from reversion.models import Version from judge.models.contest import Contest from judge.models.interface import BlogPost -from judge.models.problem import Problem, Solution +from judge.models.problem import Problem from judge.models.profile import Profile from judge.utils.cachedict import CacheDict -from judge.caching import cache_wrapper -__all__ = ["Comment", "CommentLock", "CommentVote", "Notification"] +__all__ = ['Comment', 'CommentLock', 'CommentVote', 'Notification'] + +comment_validator = RegexValidator(r'^[pcs]:[a-z0-9]+$|^b:\d+$', + _(r'Page code must be ^[pcs]:[a-z0-9]+$|^b:\d+$')) class VersionRelation(GenericRelation): def __init__(self): - super(VersionRelation, self).__init__(Version, object_id_field="object_id") + super(VersionRelation, self).__init__(Version, object_id_field='object_id') def get_extra_restriction(self, where_class, alias, remote_alias): - cond = super(VersionRelation, self).get_extra_restriction( - where_class, alias, remote_alias - ) - field = self.remote_field.model._meta.get_field("db") - lookup = field.get_lookup("exact")(field.get_col(remote_alias), "default") - cond.add(lookup, "AND") + cond = super(VersionRelation, self).get_extra_restriction(where_class, alias, remote_alias) + field = self.remote_field.model._meta.get_field('db') + lookup = field.get_lookup('exact')(field.get_col(remote_alias), 'default') + cond.add(lookup, 'AND') return cond class Comment(MPTTModel): - author = models.ForeignKey(Profile, verbose_name=_("commenter"), on_delete=CASCADE) - time = models.DateTimeField(verbose_name=_("posted time"), auto_now_add=True) - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) - object_id = models.PositiveIntegerField() - linked_object = GenericForeignKey("content_type", "object_id") - score = models.IntegerField(verbose_name=_("votes"), default=0) - body = models.TextField(verbose_name=_("body of comment"), max_length=8192) - hidden = models.BooleanField(verbose_name=_("hide the comment"), default=0) - parent = TreeForeignKey( - "self", - verbose_name=_("parent"), - null=True, - blank=True, - related_name="replies", - on_delete=CASCADE, - ) - revision_count = models.PositiveIntegerField(default=1) - + author = models.ForeignKey(Profile, verbose_name=_('commenter'), on_delete=CASCADE) + time = models.DateTimeField(verbose_name=_('posted time'), auto_now_add=True) + page = models.CharField(max_length=30, verbose_name=_('associated page'), db_index=True, + validators=[comment_validator]) + score = models.IntegerField(verbose_name=_('votes'), default=0) + body = models.TextField(verbose_name=_('body of comment'), max_length=8192) + hidden = models.BooleanField(verbose_name=_('hide the comment'), default=0) + parent = TreeForeignKey('self', verbose_name=_('parent'), null=True, blank=True, related_name='replies', + on_delete=CASCADE) versions = VersionRelation() class Meta: - verbose_name = _("comment") - verbose_name_plural = _("comments") - indexes = [ - models.Index(fields=["content_type", "object_id"]), - ] + verbose_name = _('comment') + verbose_name_plural = _('comments') class MPTTMeta: - order_insertion_by = ["-time"] + order_insertion_by = ['-time'] @classmethod - def most_recent(cls, user, n, batch=None, organization=None): - queryset = cls.objects.filter(hidden=False).order_by("-id") + def most_recent(cls, user, n, batch=None): + queryset = cls.objects.filter(hidden=False).select_related('author__user') \ + .defer('author__about', 'body').order_by('-id') - if organization: - queryset = queryset.filter(author__in=organization.members.all()) + problem_access = CacheDict(lambda code: Problem.objects.get(code=code).is_accessible_by(user)) + contest_access = CacheDict(lambda key: Contest.objects.get(key=key).is_accessible_by(user)) + blog_access = CacheDict(lambda id: BlogPost.objects.get(id=id).can_see(user)) - problem_access = CacheDict(lambda p: p.is_accessible_by(user)) - contest_access = CacheDict(lambda c: c.is_accessible_by(user)) - blog_access = CacheDict(lambda b: b.is_accessible_by(user)) - - if n == -1: - n = len(queryset) if user.is_superuser: return queryset[:n] if batch is None: batch = 2 * n output = [] for i in itertools.count(0): - slice = queryset[i * batch : i * batch + batch] + slice = queryset[i * batch:i * batch + batch] if not slice: break for comment in slice: - if isinstance(comment.linked_object, Problem): - if problem_access[comment.linked_object]: - output.append(comment) - elif isinstance(comment.linked_object, Contest): - if contest_access[comment.linked_object]: - output.append(comment) - elif isinstance(comment.linked_object, BlogPost): - if blog_access[comment.linked_object]: - output.append(comment) - elif isinstance(comment.linked_object, Solution): - if problem_access[comment.linked_object.problem]: - output.append(comment) + if comment.page.startswith('p:') or comment.page.startswith('s:'): + try: + if problem_access[comment.page[2:]]: + output.append(comment) + except Problem.DoesNotExist: + pass + elif comment.page.startswith('c:'): + try: + if contest_access[comment.page[2:]]: + output.append(comment) + except Contest.DoesNotExist: + pass + elif comment.page.startswith('b:'): + try: + if blog_access[comment.page[2:]]: + output.append(comment) + except BlogPost.DoesNotExist: + pass + else: + output.append(comment) if len(output) >= n: return output return output @cached_property - def get_replies(self): - query = Comment.filter(parent=self) - return len(query) + def link(self): + try: + link = None + if self.page.startswith('p:'): + link = reverse('problem_comments', args=(self.page[2:],)) + elif self.page.startswith('c:'): + link = reverse('contest_view', args=(self.page[2:],)) + elif self.page.startswith('b:'): + key = 'blog_slug:%s' % self.page[2:] + slug = cache.get(key) + if slug is None: + try: + slug = BlogPost.objects.get(id=self.page[2:]).slug + except ObjectDoesNotExist: + slug = '' + cache.set(key, slug, 3600) + link = reverse('blog_post', args=(self.page[2:], slug)) + elif self.page.startswith('s:'): + link = reverse('problem_editorial', args=(self.page[2:],)) + except Exception: + link = 'invalid' + return link + + @classmethod + def get_page_title(cls, page): + try: + if page.startswith('p:'): + return Problem.objects.values_list('name', flat=True).get(code=page[2:]) + elif page.startswith('c:'): + return Contest.objects.values_list('name', flat=True).get(key=page[2:]) + elif page.startswith('b:'): + return BlogPost.objects.values_list('title', flat=True).get(id=page[2:]) + elif page.startswith('s:'): + return _('Editorial for %s') % Problem.objects.values_list('name', flat=True).get(code=page[2:]) + return '' + except ObjectDoesNotExist: + return '' @cached_property def page_title(self): - if isinstance(self.linked_object, Problem): - return self.linked_object.name - elif isinstance(self.linked_object, Contest): - return self.linked_object.name - elif isinstance(self.linked_object, Solution): - return _("Editorial for ") + self.linked_object.problem.name - elif isinstance(self.linked_object, BlogPost): - return self.linked_object.title - - @cached_property - def link(self): - if isinstance(self.linked_object, Problem): - return reverse("problem_detail", args=(self.linked_object.code,)) - elif isinstance(self.linked_object, Contest): - return reverse("contest_view", args=(self.linked_object.key,)) - elif isinstance(self.linked_object, Solution): - return reverse("problem_editorial", args=(self.linked_object.problem.code,)) - elif isinstance(self.linked_object, BlogPost): - return reverse( - "blog_post", - args=( - self.object_id, - self.linked_object.slug, - ), - ) + return self.get_page_title(self.page) def get_absolute_url(self): - return "%s?comment-id=%d#comment-%d" % (self.link, self.id, self.id) + return '%s#comment-%d' % (self.link, self.id) + + def __str__(self): + return '%(page)s by %(user)s' % {'page': self.page, 'user': self.author.user.username} + + # Only use this when queried with + # .prefetch_related(Prefetch('votes', queryset=CommentVote.objects.filter(voter_id=profile_id))) + # It's rather stupid to put a query specific property on the model, but the alternative requires + # digging Django internals, and could not be guaranteed to work forever. + # Hence it is left here for when the alternative breaks. + # @property + # def vote_score(self): + # queryset = self.votes.all() + # if not queryset: + # return 0 + # return queryset[0].score class CommentVote(models.Model): - voter = models.ForeignKey(Profile, related_name="voted_comments", on_delete=CASCADE) - comment = models.ForeignKey(Comment, related_name="votes", on_delete=CASCADE) + voter = models.ForeignKey(Profile, related_name='voted_comments', on_delete=CASCADE) + comment = models.ForeignKey(Comment, related_name='votes', on_delete=CASCADE) score = models.IntegerField() class Meta: - unique_together = ["voter", "comment"] - verbose_name = _("comment vote") - verbose_name_plural = _("comment votes") + unique_together = ['voter', 'comment'] + verbose_name = _('comment vote') + verbose_name_plural = _('comment votes') class CommentLock(models.Model): - page = models.CharField( - max_length=30, - verbose_name=_("associated page"), - db_index=True, - ) + page = models.CharField(max_length=30, verbose_name=_('associated page'), db_index=True, + validators=[comment_validator]) class Meta: - permissions = (("override_comment_lock", _("Override comment lock")),) + permissions = ( + ('override_comment_lock', _('Override comment lock')), + ) def __str__(self): return str(self.page) -@cache_wrapper(prefix="gcc") -def get_visible_comment_count(content_type, object_id): - return Comment.objects.filter( - content_type=content_type, object_id=object_id, hidden=False - ).count() +class Notification(models.Model): + owner = models.ForeignKey(Profile, verbose_name=_('owner'), related_name="notifications", on_delete=CASCADE) + time = models.DateTimeField(verbose_name=_('posted time'), auto_now_add=True) + comment = models.ForeignKey(Comment, null=True, verbose_name=_('comment'), on_delete=CASCADE) + read = models.BooleanField(verbose_name=_('read'), default=False) + category = models.CharField(verbose_name=_('category'), max_length=10) + html_link = models.TextField(default='', verbose_name=_('html link to comments, used for non-comments'), max_length=1000) + author = models.ForeignKey(Profile, null=True, verbose_name=_('who trigger, used for non-comment'), on_delete=CASCADE) \ No newline at end of file diff --git a/judge/models/contest.py b/judge/models/contest.py index 625841d..8d75ee3 100644 --- a/judge/models/contest.py +++ b/judge/models/contest.py @@ -2,73 +2,36 @@ from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator from django.db import models, transaction from django.db.models import CASCADE, Q -from django.db.models.signals import m2m_changed from django.urls import reverse from django.utils import timezone from django.utils.functional import cached_property from django.utils.translation import gettext, gettext_lazy as _ -from django.contrib.contenttypes.fields import GenericRelation -from django.dispatch import receiver - from jsonfield import JSONField from lupa import LuaRuntime -from moss import ( - MOSS_LANG_C, - MOSS_LANG_CC, - MOSS_LANG_JAVA, - MOSS_LANG_PYTHON, - MOSS_LANG_PASCAL, -) -from datetime import timedelta, datetime +from moss import MOSS_LANG_C, MOSS_LANG_CC, MOSS_LANG_JAVA, MOSS_LANG_PYTHON, MOSS_LANG_PASCAL from judge import contest_format from judge.models.problem import Problem from judge.models.profile import Organization, Profile from judge.models.submission import Submission from judge.ratings import rate_contest -from judge.models.pagevote import PageVotable -from judge.models.bookmark import Bookmarkable -from judge.fulltext import SearchManager -from judge.caching import cache_wrapper -__all__ = [ - "Contest", - "ContestTag", - "ContestParticipation", - "ContestProblem", - "ContestSubmission", - "Rating", - "ContestProblemClarification", - "ContestsSummary", - "OfficialContest", - "OfficialContestCategory", - "OfficialContestLocation", -] +__all__ = ['Contest', 'ContestTag', 'ContestParticipation', 'ContestProblem', 'ContestSubmission', 'Rating'] class ContestTag(models.Model): - color_validator = RegexValidator("^#(?:[A-Fa-f0-9]{3}){1,2}$", _("Invalid colour.")) + color_validator = RegexValidator('^#(?:[A-Fa-f0-9]{3}){1,2}$', _('Invalid colour.')) - name = models.CharField( - max_length=20, - verbose_name=_("tag name"), - unique=True, - validators=[ - RegexValidator( - r"^[a-z-]+$", message=_("Lowercase letters and hyphens only.") - ) - ], - ) - color = models.CharField( - max_length=7, verbose_name=_("tag colour"), validators=[color_validator] - ) - description = models.TextField(verbose_name=_("tag description"), blank=True) + name = models.CharField(max_length=20, verbose_name=_('tag name'), unique=True, + validators=[RegexValidator(r'^[a-z-]+$', message=_('Lowercase letters and hyphens only.'))]) + color = models.CharField(max_length=7, verbose_name=_('tag colour'), validators=[color_validator]) + description = models.TextField(verbose_name=_('tag description'), blank=True) def __str__(self): return self.name def get_absolute_url(self): - return reverse("contest_tag", args=[self.name]) + return reverse('contest_tag', args=[self.name]) @property def text_color(self, cache={}): @@ -77,265 +40,104 @@ class ContestTag(models.Model): r, g, b = [ord(bytes.fromhex(i * 2)) for i in self.color[1:]] else: r, g, b = [i for i in bytes.fromhex(self.color[1:])] - cache[self.color] = ( - "#000" if 299 * r + 587 * g + 144 * b > 140000 else "#fff" - ) + cache[self.color] = '#000' if 299 * r + 587 * g + 144 * b > 140000 else '#fff' return cache[self.color] class Meta: - verbose_name = _("contest tag") - verbose_name_plural = _("contest tags") + verbose_name = _('contest tag') + verbose_name_plural = _('contest tags') -class Contest(models.Model, PageVotable, Bookmarkable): - SCOREBOARD_VISIBLE = "V" - SCOREBOARD_AFTER_CONTEST = "C" - SCOREBOARD_AFTER_PARTICIPATION = "P" +class Contest(models.Model): + SCOREBOARD_VISIBLE = 'V' + SCOREBOARD_AFTER_CONTEST = 'C' + SCOREBOARD_AFTER_PARTICIPATION = 'P' SCOREBOARD_VISIBILITY = ( - (SCOREBOARD_VISIBLE, _("Visible")), - (SCOREBOARD_AFTER_CONTEST, _("Hidden for duration of contest")), - (SCOREBOARD_AFTER_PARTICIPATION, _("Hidden for duration of participation")), + (SCOREBOARD_VISIBLE, _('Visible')), + (SCOREBOARD_AFTER_CONTEST, _('Hidden for duration of contest')), + (SCOREBOARD_AFTER_PARTICIPATION, _('Hidden for duration of participation')), ) - key = models.CharField( - max_length=20, - verbose_name=_("contest id"), - unique=True, - validators=[RegexValidator("^[a-z0-9]+$", _("Contest id must be ^[a-z0-9]+$"))], - ) - name = models.CharField( - max_length=100, verbose_name=_("contest name"), db_index=True - ) - authors = models.ManyToManyField( - Profile, - verbose_name=_("authors"), - help_text=_("These users will be able to edit the contest."), - related_name="authors+", - ) - curators = models.ManyToManyField( - Profile, - verbose_name=_("curators"), - help_text=_( - "These users will be able to edit the contest, " - "but will not be listed as authors." - ), - related_name="curators+", - blank=True, - ) - testers = models.ManyToManyField( - Profile, - verbose_name=_("testers"), - help_text=_( - "These users will be able to view the contest, " "but not edit it." - ), - blank=True, - related_name="testers+", - ) - description = models.TextField(verbose_name=_("description"), blank=True) - problems = models.ManyToManyField( - Problem, verbose_name=_("problems"), through="ContestProblem" - ) - start_time = models.DateTimeField(verbose_name=_("start time"), db_index=True) - end_time = models.DateTimeField(verbose_name=_("end time"), db_index=True) - time_limit = models.DurationField( - verbose_name=_("time limit"), - blank=True, - null=True, - help_text=_( - "Format hh:mm:ss. For example, if you want a 2-hour contest, enter 02:00:00" - ), - ) - freeze_after = models.DurationField( - verbose_name=_("freeze after"), - blank=True, - null=True, - help_text=_( - "Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, enter 02:00:00" - ), - ) - is_visible = models.BooleanField( - verbose_name=_("publicly visible"), - default=False, - help_text=_( - "Should be set even for organization-private contests, where it " - "determines whether the contest is visible to members of the " - "specified organizations." - ), - ) - is_rated = models.BooleanField( - verbose_name=_("contest rated"), - help_text=_("Whether this contest can be rated."), - default=False, - ) - scoreboard_visibility = models.CharField( - verbose_name=_("scoreboard visibility"), - default=SCOREBOARD_VISIBLE, - max_length=1, - help_text=_("Scoreboard visibility through the duration " "of the contest"), - choices=SCOREBOARD_VISIBILITY, - ) - view_contest_scoreboard = models.ManyToManyField( - Profile, - verbose_name=_("view contest scoreboard"), - blank=True, - related_name="view_contest_scoreboard", - help_text=_("These users will be able to view the scoreboard."), - ) - public_scoreboard = models.BooleanField( - verbose_name=_("public scoreboard"), - help_text=_("Ranking page is public even for private contests."), - default=False, - ) - use_clarifications = models.BooleanField( - verbose_name=_("no comments"), - help_text=_("Use clarification system instead of comments."), - default=True, - ) - rating_floor = models.IntegerField( - verbose_name=("rating floor"), - help_text=_("Rating floor for contest"), - null=True, - blank=True, - ) - rating_ceiling = models.IntegerField( - verbose_name=("rating ceiling"), - help_text=_("Rating ceiling for contest"), - null=True, - blank=True, - ) - rate_all = models.BooleanField( - verbose_name=_("rate all"), - help_text=_("Rate all users who joined."), - default=False, - ) - rate_exclude = models.ManyToManyField( - Profile, - verbose_name=_("exclude from ratings"), - blank=True, - related_name="rate_exclude+", - ) - is_private = models.BooleanField( - verbose_name=_("private to specific users"), default=False - ) - private_contestants = models.ManyToManyField( - Profile, - blank=True, - verbose_name=_("private contestants"), - help_text=_("If private, only these users may see the contest"), - related_name="private_contestants+", - ) - hide_problem_tags = models.BooleanField( - verbose_name=_("hide problem tags"), - help_text=_("Whether problem tags should be hidden by default."), - default=True, - ) - run_pretests_only = models.BooleanField( - verbose_name=_("run pretests only"), - help_text=_( - "Whether judges should grade pretests only, versus all " - "testcases. Commonly set during a contest, then unset " - "prior to rejudging user submissions when the contest ends." - ), - default=False, - ) - is_organization_private = models.BooleanField( - verbose_name=_("private to organizations"), default=False - ) - organizations = models.ManyToManyField( - Organization, - blank=True, - verbose_name=_("organizations"), - help_text=_("If private, only these organizations may see the contest"), - ) - is_in_course = models.BooleanField( - verbose_name=_("contest in course"), - default=False, - ) - og_image = models.CharField( - verbose_name=_("OpenGraph image"), default="", max_length=150, blank=True - ) - logo_override_image = models.CharField( - verbose_name=_("Logo override image"), - default="", - max_length=150, - blank=True, - help_text=_( - "This image will replace the default site logo for users " - "inside the contest." - ), - ) - tags = models.ManyToManyField( - ContestTag, verbose_name=_("contest tags"), blank=True, related_name="contests" - ) - user_count = models.IntegerField( - verbose_name=_("the amount of live participants"), default=0 - ) - summary = models.TextField( - blank=True, - verbose_name=_("contest summary"), - help_text=_( - "Plain-text, shown in meta description tag, e.g. for social media." - ), - ) - access_code = models.CharField( - verbose_name=_("access code"), - blank=True, - default="", - max_length=255, - help_text=_( - "An optional code to prompt contestants before they are allowed " - "to join the contest. Leave it blank to disable." - ), - ) - banned_users = models.ManyToManyField( - Profile, - verbose_name=_("personae non gratae"), - blank=True, - help_text=_("Bans the selected users from joining this contest."), - ) - format_name = models.CharField( - verbose_name=_("contest format"), - default="default", - max_length=32, - choices=contest_format.choices(), - help_text=_("The contest format module to use."), - ) - format_config = JSONField( - verbose_name=_("contest format configuration"), - null=True, - blank=True, - help_text=_( - "A JSON object to serve as the configuration for the chosen contest format " - "module. Leave empty to use None. Exact format depends on the contest format " - "selected." - ), - ) - problem_label_script = models.TextField( - verbose_name="contest problem label script", - blank=True, - help_text="A custom Lua function to generate problem labels. Requires a " - "single function with an integer parameter, the zero-indexed " - "contest problem index, and returns a string, the label.", - ) - points_precision = models.IntegerField( - verbose_name=_("precision points"), - default=2, - validators=[MinValueValidator(0), MaxValueValidator(10)], - help_text=_("Number of digits to round points to."), - ) - rate_limit = models.PositiveIntegerField( - verbose_name=(_("rate limit")), - null=True, - blank=True, - validators=[MinValueValidator(1), MaxValueValidator(5)], - help_text=_( - "Maximum number of submissions per minute. Leave empty if you don't want rate limit." - ), - ) - comments = GenericRelation("Comment") - pagevote = GenericRelation("PageVote") - bookmark = GenericRelation("BookMark") - objects = SearchManager(("key", "name")) - + key = models.CharField(max_length=20, verbose_name=_('contest id'), unique=True, + validators=[RegexValidator('^[a-z0-9]+$', _('Contest id must be ^[a-z0-9]+$'))]) + name = models.CharField(max_length=100, verbose_name=_('contest name'), db_index=True) + authors = models.ManyToManyField(Profile, help_text=_('These users will be able to edit the contest.'), + related_name='authors+') + curators = models.ManyToManyField(Profile, help_text=_('These users will be able to edit the contest, ' + 'but will not be listed as authors.'), + related_name='curators+', blank=True) + testers = models.ManyToManyField(Profile, help_text=_('These users will be able to view the contest, ' + 'but not edit it.'), + blank=True, related_name='testers+') + description = models.TextField(verbose_name=_('description'), blank=True) + problems = models.ManyToManyField(Problem, verbose_name=_('problems'), through='ContestProblem') + start_time = models.DateTimeField(verbose_name=_('start time'), db_index=True) + end_time = models.DateTimeField(verbose_name=_('end time'), db_index=True) + time_limit = models.DurationField(verbose_name=_('time limit'), blank=True, null=True) + is_visible = models.BooleanField(verbose_name=_('publicly visible'), default=False, + help_text=_('Should be set even for organization-private contests, where it ' + 'determines whether the contest is visible to members of the ' + 'specified organizations.')) + is_rated = models.BooleanField(verbose_name=_('contest rated'), help_text=_('Whether this contest can be rated.'), + default=False) + scoreboard_visibility = models.CharField(verbose_name=_('scoreboard visibility'), default=SCOREBOARD_VISIBLE, + max_length=1, help_text=_('Scoreboard visibility through the duration ' + 'of the contest'), choices=SCOREBOARD_VISIBILITY) + view_contest_scoreboard = models.ManyToManyField(Profile, verbose_name=_('view contest scoreboard'), blank=True, + related_name='view_contest_scoreboard', + help_text=_('These users will be able to view the scoreboard.')) + use_clarifications = models.BooleanField(verbose_name=_('no comments'), + help_text=_("Use clarification system instead of comments."), + default=True) + rating_floor = models.IntegerField(verbose_name=('rating floor'), help_text=_('Rating floor for contest'), + null=True, blank=True) + rating_ceiling = models.IntegerField(verbose_name=('rating ceiling'), help_text=_('Rating ceiling for contest'), + null=True, blank=True) + rate_all = models.BooleanField(verbose_name=_('rate all'), help_text=_('Rate all users who joined.'), default=False) + rate_exclude = models.ManyToManyField(Profile, verbose_name=_('exclude from ratings'), blank=True, + related_name='rate_exclude+') + is_private = models.BooleanField(verbose_name=_('private to specific users'), default=False) + private_contestants = models.ManyToManyField(Profile, blank=True, verbose_name=_('private contestants'), + help_text=_('If private, only these users may see the contest'), + related_name='private_contestants+') + hide_problem_tags = models.BooleanField(verbose_name=_('hide problem tags'), + help_text=_('Whether problem tags should be hidden by default.'), + default=False) + run_pretests_only = models.BooleanField(verbose_name=_('run pretests only'), + help_text=_('Whether judges should grade pretests only, versus all ' + 'testcases. Commonly set during a contest, then unset ' + 'prior to rejudging user submissions when the contest ends.'), + default=False) + is_organization_private = models.BooleanField(verbose_name=_('private to organizations'), default=False) + organizations = models.ManyToManyField(Organization, blank=True, verbose_name=_('organizations'), + help_text=_('If private, only these organizations may see the contest')) + og_image = models.CharField(verbose_name=_('OpenGraph image'), default='', max_length=150, blank=True) + logo_override_image = models.CharField(verbose_name=_('Logo override image'), default='', max_length=150, + blank=True, + help_text=_('This image will replace the default site logo for users ' + 'inside the contest.')) + tags = models.ManyToManyField(ContestTag, verbose_name=_('contest tags'), blank=True, related_name='contests') + user_count = models.IntegerField(verbose_name=_('the amount of live participants'), default=0) + summary = models.TextField(blank=True, verbose_name=_('contest summary'), + help_text=_('Plain-text, shown in meta description tag, e.g. for social media.')) + access_code = models.CharField(verbose_name=_('access code'), blank=True, default='', max_length=255, + help_text=_('An optional code to prompt contestants before they are allowed ' + 'to join the contest. Leave it blank to disable.')) + banned_users = models.ManyToManyField(Profile, verbose_name=_('personae non gratae'), blank=True, + help_text=_('Bans the selected users from joining this contest.')) + format_name = models.CharField(verbose_name=_('contest format'), default='default', max_length=32, + choices=contest_format.choices(), help_text=_('The contest format module to use.')) + format_config = JSONField(verbose_name=_('contest format configuration'), null=True, blank=True, + help_text=_('A JSON object to serve as the configuration for the chosen contest format ' + 'module. Leave empty to use None. Exact format depends on the contest format ' + 'selected.')) + problem_label_script = models.TextField(verbose_name='contest problem label script', blank=True, + help_text='A custom Lua function to generate problem labels. Requires a ' + 'single function with an integer parameter, the zero-indexed ' + 'contest problem index, and returns a string, the label.') + points_precision = models.IntegerField(verbose_name=_('precision points'), default=2, + validators=[MinValueValidator(0), MaxValueValidator(10)], + help_text=_('Number of digits to round points to.')) + @cached_property def format_class(self): return contest_format.formats[self.format_name] @@ -348,18 +150,13 @@ class Contest(models.Model, PageVotable, Bookmarkable): def get_label_for_problem(self): def DENY_ALL(obj, attr_name, is_setting): raise AttributeError() - - lua = LuaRuntime( - attribute_filter=DENY_ALL, register_eval=False, register_builtins=False - ) - return lua.eval( - self.problem_label_script or self.format.get_contest_problem_label_script() - ) + lua = LuaRuntime(attribute_filter=DENY_ALL, register_eval=False, register_builtins=False) + return lua.eval(self.problem_label_script or self.format.get_contest_problem_label_script()) def clean(self): # Django will complain if you didn't fill in start_time or end_time, so we don't have to. if self.start_time and self.end_time and self.start_time >= self.end_time: - raise ValidationError(_("End time must be after start time")) + raise ValidationError('What is this? A contest that ended before it starts?') self.format_class.validate(self.format_config) try: @@ -367,39 +164,15 @@ class Contest(models.Model, PageVotable, Bookmarkable): # so test it to see if the script returns a valid label. label = self.get_label_for_problem(0) except Exception as e: - raise ValidationError("Contest problem label script: %s" % e) + raise ValidationError('Contest problem label script: %s' % e) else: if not isinstance(label, str): - raise ValidationError( - "Contest problem label script: script should return a string." - ) - - def save(self, *args, **kwargs): - earliest_start_time = datetime(2020, 1, 1).replace(tzinfo=timezone.utc) - if self.start_time < earliest_start_time: - self.start_time = earliest_start_time - - if self.end_time < self.start_time: - self.end_time = self.start_time + timedelta(hours=1) - - one_year_later = self.start_time + timedelta(days=365) - if self.end_time > one_year_later: - self.end_time = one_year_later - - max_duration = timedelta(days=7) - if self.time_limit and self.time_limit > max_duration: - self.time_limit = max_duration - - super().save(*args, **kwargs) + raise ValidationError('Contest problem label script: script should return a string.') def is_in_contest(self, user): if user.is_authenticated: profile = user.profile - return ( - profile - and profile.current_contest is not None - and profile.current_contest.contest == self - ) + return profile and profile.current_contest is not None and profile.current_contest.contest == self return False def can_see_own_scoreboard(self, user): @@ -416,26 +189,19 @@ class Contest(models.Model, PageVotable, Bookmarkable): return True if not user.is_authenticated: return False - if user.has_perm("judge.see_private_contest") or user.has_perm( - "judge.edit_all_contest" - ): + if user.has_perm('judge.see_private_contest') or user.has_perm('judge.edit_all_contest'): return True if user.profile.id in self.editor_ids: return True if self.view_contest_scoreboard.filter(id=user.profile.id).exists(): return True - if ( - self.scoreboard_visibility == self.SCOREBOARD_AFTER_PARTICIPATION - and self.has_completed_contest(user) - ): + if self.scoreboard_visibility == self.SCOREBOARD_AFTER_PARTICIPATION and self.has_completed_contest(user): return True return False def has_completed_contest(self, user): if user.is_authenticated: - participation = self.users.filter( - virtual=ContestParticipation.LIVE, user=user.profile - ).first() + participation = self.users.filter(virtual=ContestParticipation.LIVE, user=user.profile).first() if participation and participation.ended: return True return False @@ -444,11 +210,8 @@ class Contest(models.Model, PageVotable, Bookmarkable): def show_scoreboard(self): if not self.can_join: return False - if ( - self.scoreboard_visibility - in (self.SCOREBOARD_AFTER_CONTEST, self.SCOREBOARD_AFTER_PARTICIPATION) - and not self.ended - ): + if (self.scoreboard_visibility in (self.SCOREBOARD_AFTER_CONTEST, self.SCOREBOARD_AFTER_PARTICIPATION) and + not self.ended): return False return True @@ -483,47 +246,24 @@ class Contest(models.Model, PageVotable, Bookmarkable): def ended(self): return self.end_time < self._now - @cache_wrapper(prefix="Coai") - def _author_ids(self): - return set( - Contest.authors.through.objects.filter(contest=self).values_list( - "profile_id", flat=True - ) - ) - - @cache_wrapper(prefix="Coci") - def _curator_ids(self): - return set( - Contest.curators.through.objects.filter(contest=self).values_list( - "profile_id", flat=True - ) - ) - - @cache_wrapper(prefix="Coti") - def _tester_ids(self): - return set( - Contest.testers.through.objects.filter(contest=self).values_list( - "profile_id", flat=True - ) - ) - @cached_property def author_ids(self): - return self._author_ids() + return Contest.authors.through.objects.filter(contest=self).values_list('profile_id', flat=True) @cached_property def editor_ids(self): - return self.author_ids.union(self._curator_ids()) + return self.author_ids.union( + Contest.curators.through.objects.filter(contest=self).values_list('profile_id', flat=True)) @cached_property def tester_ids(self): - return self._tester_ids() + return Contest.testers.through.objects.filter(contest=self).values_list('profile_id', flat=True) def __str__(self): - return f"{self.name} ({self.key})" + return self.name def get_absolute_url(self): - return reverse("contest_view", args=(self.key,)) + return reverse('contest_view', args=(self.key,)) def update_user_count(self): self.user_count = self.users.filter(virtual=0).count() @@ -548,9 +288,7 @@ class Contest(models.Model, PageVotable, Bookmarkable): return # If the user can view or edit all contests - if user.has_perm("judge.see_private_contest") or user.has_perm( - "judge.edit_all_contest" - ): + if user.has_perm('judge.see_private_contest') or user.has_perm('judge.edit_all_contest'): return # User is organizer or curator for contest @@ -565,30 +303,20 @@ class Contest(models.Model, PageVotable, Bookmarkable): if not self.is_visible: raise self.Inaccessible() - if self.is_in_course: - from judge.models import Course, CourseContest - - course_contest = CourseContest.objects.filter(contest=self).first() - if Course.is_accessible_by(course_contest.course, user.profile): - return - raise self.Inaccessible() - # Contest is not private if not self.is_private and not self.is_organization_private: return if self.view_contest_scoreboard.filter(id=user.profile.id).exists(): return - - in_org = self.organizations.filter( - id__in=user.profile.organizations.all() - ).exists() + + in_org = self.organizations.filter(id__in=user.profile.organizations.all()).exists() in_users = self.private_contestants.filter(id=user.profile.id).exists() if not self.is_private and self.is_organization_private: if in_org: return - raise self.PrivateContest() + raise self.PrivateContest() if self.is_private and not self.is_organization_private: if in_users: @@ -610,57 +338,31 @@ class Contest(models.Model, PageVotable, Bookmarkable): def is_editable_by(self, user): # If the user can edit all contests - if user.has_perm("judge.edit_all_contest"): + if user.has_perm('judge.edit_all_contest'): return True # If the user is a contest organizer or curator - if hasattr(user, "profile") and user.profile.id in self.editor_ids: + if user.has_perm('judge.edit_own_contest') and user.profile.id in self.editor_ids: return True return False @classmethod - def get_visible_contests(cls, user, show_own_contests_only=False): + def get_visible_contests(cls, user): if not user.is_authenticated: - return ( - cls.objects.filter( - is_visible=True, - is_organization_private=False, - is_private=False, - is_in_course=False, - ) - .defer("description") - .distinct() - ) + return cls.objects.filter(is_visible=True, is_organization_private=False, is_private=False) \ + .defer('description').distinct() - queryset = cls.objects.defer("description") - if ( - not ( - user.has_perm("judge.see_private_contest") - or user.has_perm("judge.edit_all_contest") - ) - or show_own_contests_only - ): - q = Q(is_visible=True, is_in_course=False) + queryset = cls.objects.defer('description') + if not (user.has_perm('judge.see_private_contest') or user.has_perm('judge.edit_all_contest')): + q = Q(is_visible=True) q &= ( - Q(view_contest_scoreboard=user.profile) - | Q(is_organization_private=False, is_private=False) - | Q( - is_organization_private=False, - is_private=True, - private_contestants=user.profile, - ) - | Q( - is_organization_private=True, - is_private=False, - organizations__in=user.profile.organizations.all(), - ) - | Q( - is_organization_private=True, - is_private=True, - organizations__in=user.profile.organizations.all(), - private_contestants=user.profile, - ) + Q(view_contest_scoreboard=user.profile) | + Q(is_organization_private=False, is_private=False) | + Q(is_organization_private=False, is_private=True, private_contestants=user.profile) | + Q(is_organization_private=True, is_private=False, organizations__in=user.profile.organizations.all()) | + Q(is_organization_private=True, is_private=True, organizations__in=user.profile.organizations.all(), + private_contestants=user.profile) ) q |= Q(authors=user.profile) @@ -670,98 +372,51 @@ class Contest(models.Model, PageVotable, Bookmarkable): return queryset.distinct() def rate(self): - Rating.objects.filter( - contest__end_time__range=(self.end_time, self._now) - ).delete() + Rating.objects.filter(contest__end_time__range=(self.end_time, self._now)).delete() for contest in Contest.objects.filter( - is_rated=True, - end_time__range=(self.end_time, self._now), - ).order_by("end_time"): + is_rated=True, end_time__range=(self.end_time, self._now), + ).order_by('end_time'): rate_contest(contest) class Meta: permissions = ( - ("see_private_contest", _("See private contests")), - ("edit_own_contest", _("Edit own contests")), - ("edit_all_contest", _("Edit all contests")), - ("clone_contest", _("Clone contest")), - ("moss_contest", _("MOSS contest")), - ("contest_rating", _("Rate contests")), - ("contest_access_code", _("Contest access codes")), - ("create_private_contest", _("Create private contests")), - ("change_contest_visibility", _("Change contest visibility")), - ("contest_problem_label", _("Edit contest problem label script")), + ('see_private_contest', _('See private contests')), + ('edit_own_contest', _('Edit own contests')), + ('edit_all_contest', _('Edit all contests')), + ('clone_contest', _('Clone contest')), + ('moss_contest', _('MOSS contest')), + ('contest_rating', _('Rate contests')), + ('contest_access_code', _('Contest access codes')), + ('create_private_contest', _('Create private contests')), + ('change_contest_visibility', _('Change contest visibility')), + ('contest_problem_label', _('Edit contest problem label script')), ) - verbose_name = _("contest") - verbose_name_plural = _("contests") - - -@receiver(m2m_changed, sender=Contest.organizations.through) -def update_organization_private(sender, instance, **kwargs): - if kwargs["action"] in ["post_add", "post_remove", "post_clear"]: - instance.is_organization_private = instance.organizations.exists() - instance.save(update_fields=["is_organization_private"]) - - -@receiver(m2m_changed, sender=Contest.private_contestants.through) -def update_private(sender, instance, **kwargs): - if kwargs["action"] in ["post_add", "post_remove", "post_clear"]: - instance.is_private = instance.private_contestants.exists() - instance.save(update_fields=["is_private"]) + verbose_name = _('contest') + verbose_name_plural = _('contests') class ContestParticipation(models.Model): LIVE = 0 SPECTATE = -1 - contest = models.ForeignKey( - Contest, - verbose_name=_("associated contest"), - related_name="users", - on_delete=CASCADE, - ) - user = models.ForeignKey( - Profile, - verbose_name=_("user"), - related_name="contest_history", - on_delete=CASCADE, - ) - real_start = models.DateTimeField( - verbose_name=_("start time"), default=timezone.now, db_column="start" - ) - score = models.FloatField(verbose_name=_("score"), default=0, db_index=True) - cumtime = models.PositiveIntegerField(verbose_name=_("cumulative time"), default=0) - is_disqualified = models.BooleanField( - verbose_name=_("is disqualified"), - default=False, - help_text=_("Whether this participation is disqualified."), - ) - tiebreaker = models.FloatField(verbose_name=_("tie-breaking field"), default=0.0) - virtual = models.IntegerField( - verbose_name=_("virtual participation id"), - default=LIVE, - help_text=_("0 means non-virtual, otherwise the n-th virtual participation."), - ) - format_data = JSONField( - verbose_name=_("contest format specific data"), null=True, blank=True - ) - format_data_final = JSONField( - verbose_name=_("same as format_data, but including frozen results"), - null=True, - blank=True, - ) - score_final = models.FloatField(verbose_name=_("final score"), default=0) - cumtime_final = models.PositiveIntegerField( - verbose_name=_("final cumulative time"), default=0 - ) + contest = models.ForeignKey(Contest, verbose_name=_('associated contest'), related_name='users', on_delete=CASCADE) + user = models.ForeignKey(Profile, verbose_name=_('user'), related_name='contest_history', on_delete=CASCADE) + real_start = models.DateTimeField(verbose_name=_('start time'), default=timezone.now, db_column='start') + score = models.FloatField(verbose_name=_('score'), default=0, db_index=True) + cumtime = models.PositiveIntegerField(verbose_name=_('cumulative time'), default=0) + is_disqualified = models.BooleanField(verbose_name=_('is disqualified'), default=False, + help_text=_('Whether this participation is disqualified.')) + tiebreaker = models.FloatField(verbose_name=_('tie-breaking field'), default=0.0) + virtual = models.IntegerField(verbose_name=_('virtual participation id'), default=LIVE, + help_text=_('0 means non-virtual, otherwise the n-th virtual participation.')) + format_data = JSONField(verbose_name=_('contest format specific data'), null=True, blank=True) def recompute_results(self): with transaction.atomic(): self.contest.format.update_participation(self) if self.is_disqualified: self.score = -9999 - self.save(update_fields=["score"]) - + self.save(update_fields=['score']) recompute_results.alters_data = True def set_disqualified(self, disqualified): @@ -775,7 +430,6 @@ class ContestParticipation(models.Model): self.contest.banned_users.add(self.user) else: self.contest.banned_users.remove(self.user) - set_disqualified.alters_data = True @property @@ -789,11 +443,7 @@ class ContestParticipation(models.Model): @cached_property def start(self): contest = self.contest - return ( - contest.start_time - if contest.time_limit is None and (self.live or self.spectate) - else self.real_start - ) + return contest.start_time if contest.time_limit is None and (self.live or self.spectate) else self.real_start @cached_property def end_time(self): @@ -805,11 +455,8 @@ class ContestParticipation(models.Model): return self.real_start + contest.time_limit else: return self.real_start + (contest.end_time - contest.start_time) - return ( - contest.end_time - if contest.time_limit is None - else min(self.real_start + contest.time_limit, contest.end_time) - ) + return contest.end_time if contest.time_limit is None else \ + min(self.real_start + contest.time_limit, contest.end_time) @cached_property def _now(self): @@ -828,234 +475,88 @@ class ContestParticipation(models.Model): def __str__(self): if self.spectate: - return gettext("%s spectating in %s") % ( - self.user.username, - self.contest.name, - ) + return gettext('%s spectating in %s') % (self.user.username, self.contest.name) if self.virtual: - return gettext("%s in %s, v%d") % ( - self.user.username, - self.contest.name, - self.virtual, - ) - return gettext("%s in %s") % (self.user.username, self.contest.name) + return gettext('%s in %s, v%d') % (self.user.username, self.contest.name, self.virtual) + return gettext('%s in %s') % (self.user.username, self.contest.name) class Meta: - verbose_name = _("contest participation") - verbose_name_plural = _("contest participations") + verbose_name = _('contest participation') + verbose_name_plural = _('contest participations') - unique_together = ("contest", "user", "virtual") + unique_together = ('contest', 'user', 'virtual') class ContestProblem(models.Model): - problem = models.ForeignKey( - Problem, verbose_name=_("problem"), related_name="contests", on_delete=CASCADE - ) - contest = models.ForeignKey( - Contest, - verbose_name=_("contest"), - related_name="contest_problems", - on_delete=CASCADE, - ) - points = models.IntegerField(verbose_name=_("points")) - partial = models.BooleanField(default=True, verbose_name=_("partial")) - is_pretested = models.BooleanField(default=False, verbose_name=_("is pretested")) - order = models.PositiveIntegerField(db_index=True, verbose_name=_("order")) - show_testcases = models.BooleanField( - verbose_name=_("visible testcases"), - default=False, - ) - max_submissions = models.IntegerField( - help_text=_( - "Maximum number of submissions for this problem, " "or 0 for no limit." - ), - verbose_name=_("max submissions"), - default=0, - validators=[ - MinValueValidator(0, _("Why include a problem you " "can't submit to?")) - ], - ) - hidden_subtasks = models.CharField( - help_text=_("Separated by commas, e.g: 2, 3"), - verbose_name=_("hidden subtasks"), - null=True, - blank=True, - max_length=20, - ) - - @property - def clarifications(self): - return ContestProblemClarification.objects.filter(problem=self) + problem = models.ForeignKey(Problem, verbose_name=_('problem'), related_name='contests', on_delete=CASCADE) + contest = models.ForeignKey(Contest, verbose_name=_('contest'), related_name='contest_problems', on_delete=CASCADE) + points = models.IntegerField(verbose_name=_('points')) + partial = models.BooleanField(default=True, verbose_name=_('partial')) + is_pretested = models.BooleanField(default=False, verbose_name=_('is pretested')) + order = models.PositiveIntegerField(db_index=True, verbose_name=_('order')) + output_prefix_override = models.IntegerField(help_text=_('0 to not show testcases, 1 to show'), + verbose_name=_('visible testcases'), null=True, blank=True, default=0) + max_submissions = models.IntegerField(help_text=_('Maximum number of submissions for this problem, ' + 'or 0 for no limit.'), default=0, + validators=[MinValueValidator(0, _('Why include a problem you ' + 'can\'t submit to?'))]) class Meta: - unique_together = ("problem", "contest") - verbose_name = _("contest problem") - verbose_name_plural = _("contest problems") + unique_together = ('problem', 'contest') + verbose_name = _('contest problem') + verbose_name_plural = _('contest problems') class ContestSubmission(models.Model): - submission = models.OneToOneField( - Submission, - verbose_name=_("submission"), - related_name="contest", - on_delete=CASCADE, - ) - problem = models.ForeignKey( - ContestProblem, - verbose_name=_("problem"), - on_delete=CASCADE, - related_name="submissions", - related_query_name="submission", - ) - participation = models.ForeignKey( - ContestParticipation, - verbose_name=_("participation"), - on_delete=CASCADE, - related_name="submissions", - related_query_name="submission", - ) - points = models.FloatField(default=0.0, verbose_name=_("points")) - is_pretest = models.BooleanField( - verbose_name=_("is pretested"), - help_text=_("Whether this submission was ran only on pretests."), - default=False, - ) + submission = models.OneToOneField(Submission, verbose_name=_('submission'), + related_name='contest', on_delete=CASCADE) + problem = models.ForeignKey(ContestProblem, verbose_name=_('problem'), on_delete=CASCADE, + related_name='submissions', related_query_name='submission') + participation = models.ForeignKey(ContestParticipation, verbose_name=_('participation'), on_delete=CASCADE, + related_name='submissions', related_query_name='submission') + points = models.FloatField(default=0.0, verbose_name=_('points')) + is_pretest = models.BooleanField(verbose_name=_('is pretested'), + help_text=_('Whether this submission was ran only on pretests.'), + default=False) class Meta: - verbose_name = _("contest submission") - verbose_name_plural = _("contest submissions") + verbose_name = _('contest submission') + verbose_name_plural = _('contest submissions') class Rating(models.Model): - user = models.ForeignKey( - Profile, verbose_name=_("user"), related_name="ratings", on_delete=CASCADE - ) - contest = models.ForeignKey( - Contest, verbose_name=_("contest"), related_name="ratings", on_delete=CASCADE - ) - participation = models.OneToOneField( - ContestParticipation, - verbose_name=_("participation"), - related_name="rating", - on_delete=CASCADE, - ) - rank = models.IntegerField(verbose_name=_("rank")) - rating = models.IntegerField(verbose_name=_("rating")) - mean = models.FloatField(verbose_name=_("raw rating")) - performance = models.FloatField(verbose_name=_("contest performance")) - last_rated = models.DateTimeField(db_index=True, verbose_name=_("last rated")) + user = models.ForeignKey(Profile, verbose_name=_('user'), related_name='ratings', on_delete=CASCADE) + contest = models.ForeignKey(Contest, verbose_name=_('contest'), related_name='ratings', on_delete=CASCADE) + participation = models.OneToOneField(ContestParticipation, verbose_name=_('participation'), + related_name='rating', on_delete=CASCADE) + rank = models.IntegerField(verbose_name=_('rank')) + rating = models.IntegerField(verbose_name=_('rating')) + mean = models.FloatField(verbose_name=_('raw rating')) + performance = models.FloatField(verbose_name=_('contest performance')) + last_rated = models.DateTimeField(db_index=True, verbose_name=_('last rated')) class Meta: - unique_together = ("user", "contest") - verbose_name = _("contest rating") - verbose_name_plural = _("contest ratings") + unique_together = ('user', 'contest') + verbose_name = _('contest rating') + verbose_name_plural = _('contest ratings') class ContestMoss(models.Model): LANG_MAPPING = [ - ("C", MOSS_LANG_C), - ("C++", MOSS_LANG_CC), - ("Java", MOSS_LANG_JAVA), - ("Python", MOSS_LANG_PYTHON), - ("Pascal", MOSS_LANG_PASCAL), + ('C', MOSS_LANG_C), + ('C++', MOSS_LANG_CC), + ('Java', MOSS_LANG_JAVA), + ('Python', MOSS_LANG_PYTHON), + ('Pascal', MOSS_LANG_PASCAL), ] - contest = models.ForeignKey( - Contest, verbose_name=_("contest"), related_name="moss", on_delete=CASCADE - ) - problem = models.ForeignKey( - Problem, verbose_name=_("problem"), related_name="moss", on_delete=CASCADE - ) + contest = models.ForeignKey(Contest, verbose_name=_('contest'), related_name='moss', on_delete=CASCADE) + problem = models.ForeignKey(Problem, verbose_name=_('problem'), related_name='moss', on_delete=CASCADE) language = models.CharField(max_length=10) submission_count = models.PositiveIntegerField(default=0) url = models.URLField(null=True, blank=True) class Meta: - unique_together = ("contest", "problem", "language") - verbose_name = _("contest moss result") - verbose_name_plural = _("contest moss results") - - -class ContestProblemClarification(models.Model): - problem = models.ForeignKey( - ContestProblem, verbose_name=_("clarified problem"), on_delete=CASCADE - ) - description = models.TextField(verbose_name=_("clarification body")) - date = models.DateTimeField( - verbose_name=_("clarification timestamp"), auto_now_add=True - ) - - -class ContestsSummary(models.Model): - contests = models.ManyToManyField( - Contest, - ) - scores = models.JSONField( - null=True, - blank=True, - ) - key = models.CharField( - max_length=20, - unique=True, - ) - results = models.JSONField(null=True, blank=True) - - class Meta: - verbose_name = _("contests summary") - verbose_name_plural = _("contests summaries") - - def __str__(self): - return self.key - - def get_absolute_url(self): - return reverse("contests_summary", args=[self.key]) - - -class OfficialContestCategory(models.Model): - name = models.CharField( - max_length=50, verbose_name=_("official contest category"), unique=True - ) - - def __str__(self): - return self.name - - class Meta: - verbose_name = _("official contest category") - verbose_name_plural = _("official contest categories") - - -class OfficialContestLocation(models.Model): - name = models.CharField( - max_length=50, verbose_name=_("official contest location"), unique=True - ) - - def __str__(self): - return self.name - - class Meta: - verbose_name = _("official contest location") - verbose_name_plural = _("official contest locations") - - -class OfficialContest(models.Model): - contest = models.OneToOneField( - Contest, - verbose_name=_("contest"), - related_name="official", - on_delete=CASCADE, - ) - category = models.ForeignKey( - OfficialContestCategory, - verbose_name=_("contest category"), - on_delete=CASCADE, - ) - year = models.PositiveIntegerField(verbose_name=_("year")) - location = models.ForeignKey( - OfficialContestLocation, - verbose_name=_("contest location"), - on_delete=CASCADE, - ) - - class Meta: - verbose_name = _("official contest") - verbose_name_plural = _("official contests") + unique_together = ('contest', 'problem', 'language') + verbose_name = _('contest moss result') + verbose_name_plural = _('contest moss results') diff --git a/judge/models/course.py b/judge/models/course.py deleted file mode 100644 index df4df21..0000000 --- a/judge/models/course.py +++ /dev/null @@ -1,201 +0,0 @@ -from django.core.validators import RegexValidator -from django.db import models -from django.utils.translation import gettext, gettext_lazy as _ -from django.urls import reverse -from django.db.models import Q - -from judge.models import BlogPost, Problem, Contest -from judge.models.profile import Organization, Profile - - -class RoleInCourse(models.TextChoices): - STUDENT = "ST", _("Student") - ASSISTANT = "AS", _("Assistant") - TEACHER = "TE", _("Teacher") - - -EDITABLE_ROLES = (RoleInCourse.TEACHER, RoleInCourse.ASSISTANT) - - -class Course(models.Model): - name = models.CharField( - max_length=128, - verbose_name=_("course name"), - ) - about = models.TextField(verbose_name=_("course description")) - is_public = models.BooleanField( - verbose_name=_("publicly visible"), - default=False, - ) - organizations = models.ManyToManyField( - Organization, - blank=True, - verbose_name=_("organizations"), - help_text=_("If private, only these organizations may see the course"), - ) - slug = models.SlugField( - max_length=128, - verbose_name=_("course slug"), - help_text=_("Course name shown in URL"), - unique=True, - validators=[ - RegexValidator("^[-a-zA-Z0-9]+$", _("Only alphanumeric and hyphens")) - ], - ) - is_open = models.BooleanField( - verbose_name=_("public registration"), - default=False, - ) - image_url = models.CharField( - verbose_name=_("course image"), - default="", - max_length=150, - blank=True, - ) - - def __str__(self): - return self.name - - def get_absolute_url(self): - return reverse("course_detail", args=(self.slug,)) - - @classmethod - def is_editable_by(cls, course, profile): - try: - course_role = CourseRole.objects.get(course=course, user=profile) - return course_role.role in EDITABLE_ROLES - except CourseRole.DoesNotExist: - return False - - @classmethod - def is_accessible_by(cls, course, profile): - if not profile: - return False - try: - course_role = CourseRole.objects.get(course=course, user=profile) - if course_role.course.is_public: - return True - return course_role.role in EDITABLE_ROLES - except CourseRole.DoesNotExist: - return False - - @classmethod - def get_accessible_courses(cls, profile): - return Course.objects.filter( - Q(is_public=True) | Q(courserole__role__in=EDITABLE_ROLES), - courserole__user=profile, - ).distinct() - - def _get_users_by_role(self, role): - course_roles = CourseRole.objects.filter(course=self, role=role).select_related( - "user" - ) - return [course_role.user for course_role in course_roles] - - def get_students(self): - return self._get_users_by_role(RoleInCourse.STUDENT) - - def get_assistants(self): - return self._get_users_by_role(RoleInCourse.ASSISTANT) - - def get_teachers(self): - return self._get_users_by_role(RoleInCourse.TEACHER) - - @classmethod - def add_student(cls, course, profiles): - for profile in profiles: - CourseRole.make_role(course=course, user=profile, role="ST") - - @classmethod - def add_teachers(cls, course, profiles): - for profile in profiles: - CourseRole.make_role(course=course, user=profile, role="TE") - - @classmethod - def add_assistants(cls, course, profiles): - for profile in profiles: - CourseRole.make_role(course=course, user=profile, role="AS") - - -class CourseRole(models.Model): - course = models.ForeignKey( - Course, - verbose_name=_("course"), - on_delete=models.CASCADE, - db_index=True, - ) - user = models.ForeignKey( - Profile, - verbose_name=_("user"), - on_delete=models.CASCADE, - related_name="course_roles", - ) - - role = models.CharField( - max_length=2, - choices=RoleInCourse.choices, - default=RoleInCourse.STUDENT, - ) - - @classmethod - def make_role(self, course, user, role): - userqueryset = CourseRole.objects.filter(course=course, user=user) - if userqueryset.exists(): - userqueryset[0].role = role - else: - couresrole = CourseRole() - couresrole.course = course - couresrole.user = user - couresrole.role = role - couresrole.save() - - class Meta: - unique_together = ("course", "user") - - -class CourseLesson(models.Model): - course = models.ForeignKey( - Course, - verbose_name=_("course"), - related_name="lessons", - on_delete=models.CASCADE, - ) - title = models.TextField(verbose_name=_("lesson title")) - content = models.TextField(verbose_name=_("lesson content")) - order = models.IntegerField(verbose_name=_("order"), default=0) - points = models.IntegerField(verbose_name=_("points")) - is_visible = models.BooleanField(verbose_name=_("publicly visible"), default=True) - - def get_absolute_url(self): - return reverse( - "course_lesson_detail", - args=( - self.course.slug, - self.id, - ), - ) - - -class CourseLessonProblem(models.Model): - lesson = models.ForeignKey( - CourseLesson, on_delete=models.CASCADE, related_name="lesson_problems" - ) - problem = models.ForeignKey(Problem, on_delete=models.CASCADE) - order = models.IntegerField(verbose_name=_("order"), default=0) - score = models.IntegerField(verbose_name=_("score"), default=0) - - -class CourseContest(models.Model): - course = models.ForeignKey( - Course, on_delete=models.CASCADE, related_name="contests" - ) - contest = models.ForeignKey( - Contest, unique=True, on_delete=models.CASCADE, related_name="course" - ) - order = models.IntegerField(verbose_name=_("order"), default=0) - points = models.IntegerField(verbose_name=_("points")) - - def get_course_of_contest(contest): - course_contest = contest.course.get() - course = course_contest.course - return course diff --git a/judge/models/interface.py b/judge/models/interface.py index d9c6e2c..6d94d52 100644 --- a/judge/models/interface.py +++ b/judge/models/interface.py @@ -5,17 +5,12 @@ from django.db import models from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from django.utils.functional import cached_property -from django.contrib.contenttypes.fields import GenericRelation from mptt.fields import TreeForeignKey from mptt.models import MPTTModel from judge.models.profile import Organization, Profile -from judge.models.pagevote import PageVotable -from judge.models.bookmark import Bookmarkable -from judge.caching import cache_wrapper -__all__ = ["MiscConfig", "validate_regex", "NavigationBar", "BlogPost"] +__all__ = ['MiscConfig', 'validate_regex', 'NavigationBar', 'BlogPost'] class MiscConfig(models.Model): @@ -26,40 +21,32 @@ class MiscConfig(models.Model): return self.key class Meta: - verbose_name = _("configuration item") - verbose_name_plural = _("miscellaneous configuration") + verbose_name = _('configuration item') + verbose_name_plural = _('miscellaneous configuration') def validate_regex(regex): try: re.compile(regex, re.VERBOSE) except re.error as e: - raise ValidationError("Invalid regex: %s" % e.message) + raise ValidationError('Invalid regex: %s' % e.message) class NavigationBar(MPTTModel): class Meta: - verbose_name = _("navigation item") - verbose_name_plural = _("navigation bar") + verbose_name = _('navigation item') + verbose_name_plural = _('navigation bar') class MPTTMeta: - order_insertion_by = ["order"] + order_insertion_by = ['order'] - order = models.PositiveIntegerField(db_index=True, verbose_name=_("order")) - key = models.CharField(max_length=10, unique=True, verbose_name=_("identifier")) - label = models.CharField(max_length=20, verbose_name=_("label")) - path = models.CharField(max_length=255, verbose_name=_("link path")) - regex = models.TextField( - verbose_name=_("highlight regex"), validators=[validate_regex] - ) - parent = TreeForeignKey( - "self", - verbose_name=_("parent item"), - null=True, - blank=True, - related_name="children", - on_delete=models.CASCADE, - ) + order = models.PositiveIntegerField(db_index=True, verbose_name=_('order')) + key = models.CharField(max_length=10, unique=True, verbose_name=_('identifier')) + label = models.CharField(max_length=20, verbose_name=_('label')) + path = models.CharField(max_length=255, verbose_name=_('link path')) + regex = models.TextField(verbose_name=_('highlight regex'), validators=[validate_regex]) + parent = TreeForeignKey('self', verbose_name=_('parent item'), null=True, blank=True, + related_name='children', on_delete=models.CASCADE) def __str__(self): return self.label @@ -75,69 +62,47 @@ class NavigationBar(MPTTModel): return pattern -class BlogPost(models.Model, PageVotable, Bookmarkable): - title = models.CharField(verbose_name=_("post title"), max_length=100) - authors = models.ManyToManyField(Profile, verbose_name=_("authors"), blank=True) - slug = models.SlugField(verbose_name=_("slug")) - visible = models.BooleanField(verbose_name=_("public visibility"), default=False) - sticky = models.BooleanField(verbose_name=_("sticky"), default=False) - publish_on = models.DateTimeField(verbose_name=_("publish after")) - content = models.TextField(verbose_name=_("post content")) - summary = models.TextField(verbose_name=_("post summary"), blank=True) - og_image = models.CharField( - verbose_name=_("openGraph image"), default="", max_length=150, blank=True - ) - organizations = models.ManyToManyField( - Organization, - blank=True, - verbose_name=_("organizations"), - help_text=_("If private, only these organizations may see the blog post."), - ) - is_organization_private = models.BooleanField( - verbose_name=_("private to organizations"), default=False - ) - comments = GenericRelation("Comment") - pagevote = GenericRelation("PageVote") - bookmark = GenericRelation("BookMark") +class BlogPost(models.Model): + title = models.CharField(verbose_name=_('post title'), max_length=100) + authors = models.ManyToManyField(Profile, verbose_name=_('authors'), blank=True) + slug = models.SlugField(verbose_name=_('slug')) + visible = models.BooleanField(verbose_name=_('public visibility'), default=False) + sticky = models.BooleanField(verbose_name=_('sticky'), default=False) + publish_on = models.DateTimeField(verbose_name=_('publish after')) + content = models.TextField(verbose_name=_('post content')) + summary = models.TextField(verbose_name=_('post summary'), blank=True) + og_image = models.CharField(verbose_name=_('openGraph image'), default='', max_length=150, blank=True) + organizations = models.ManyToManyField(Organization, blank=True, verbose_name=_('organizations'), + help_text=_('If private, only these organizations may see the blog post.')) + is_organization_private = models.BooleanField(verbose_name=_('private to organizations'), default=False) def __str__(self): return self.title def get_absolute_url(self): - return reverse("blog_post", args=(self.id, self.slug)) + return reverse('blog_post', args=(self.id, self.slug)) - def is_accessible_by(self, user): + def can_see(self, user): if self.visible and self.publish_on <= timezone.now(): if not self.is_organization_private: return True - if ( - user.is_authenticated - and self.organizations.filter( - id__in=user.profile.organizations.all() - ).exists() - ): + if user.is_authenticated and \ + self.organizations.filter(id__in=user.profile.organizations.all()).exists(): return True - if user.has_perm("judge.edit_all_post"): + if user.has_perm('judge.edit_all_post'): return True - return ( - user.is_authenticated and self.authors.filter(id=user.profile.id).exists() - ) + return user.is_authenticated and self.authors.filter(id=user.profile.id).exists() def is_editable_by(self, user): - if not user.is_authenticated: - return False - if user.has_perm("judge.edit_all_post"): - return True - return ( - user.has_perm("judge.change_blogpost") - and self.authors.filter(id=user.profile.id).exists() - ) - - @cache_wrapper(prefix="BPga", expected_type=models.query.QuerySet) - def get_authors(self): - return self.authors.only("id") + if not user.is_authenticated: + return False + if user.has_perm('judge.edit_all_post'): + return True + return user.has_perm('judge.change_blogpost') and self.authors.filter(id=user.profile.id).exists() class Meta: - permissions = (("edit_all_post", _("Edit all posts")),) - verbose_name = _("blog post") - verbose_name_plural = _("blog posts") + permissions = ( + ('edit_all_post', _('Edit all posts')), + ) + verbose_name = _('blog post') + verbose_name_plural = _('blog posts') diff --git a/judge/models/message.py b/judge/models/message.py index c2e8291..541d63f 100644 --- a/judge/models/message.py +++ b/judge/models/message.py @@ -4,31 +4,17 @@ from django.utils.translation import gettext_lazy as _ from judge.models.profile import Profile -__all__ = ["PrivateMessage", "PrivateMessageThread"] +__all__ = ['PrivateMessage', 'PrivateMessageThread'] class PrivateMessage(models.Model): - title = models.CharField(verbose_name=_("message title"), max_length=50) - content = models.TextField(verbose_name=_("message body")) - sender = models.ForeignKey( - Profile, - verbose_name=_("sender"), - related_name="sent_messages", - on_delete=CASCADE, - ) - target = models.ForeignKey( - Profile, - verbose_name=_("target"), - related_name="received_messages", - on_delete=CASCADE, - ) - timestamp = models.DateTimeField( - verbose_name=_("message timestamp"), auto_now_add=True - ) - read = models.BooleanField(verbose_name=_("read"), default=False) + title = models.CharField(verbose_name=_('message title'), max_length=50) + content = models.TextField(verbose_name=_('message body')) + sender = models.ForeignKey(Profile, verbose_name=_('sender'), related_name='sent_messages', on_delete=CASCADE) + target = models.ForeignKey(Profile, verbose_name=_('target'), related_name='received_messages', on_delete=CASCADE) + timestamp = models.DateTimeField(verbose_name=_('message timestamp'), auto_now_add=True) + read = models.BooleanField(verbose_name=_('read'), default=False) class PrivateMessageThread(models.Model): - messages = models.ManyToManyField( - PrivateMessage, verbose_name=_("messages in the thread") - ) + messages = models.ManyToManyField(PrivateMessage, verbose_name=_('messages in the thread')) diff --git a/judge/models/notification.py b/judge/models/notification.py deleted file mode 100644 index f5f40a6..0000000 --- a/judge/models/notification.py +++ /dev/null @@ -1,94 +0,0 @@ -from django.db import models -from django.utils.translation import gettext_lazy as _ -from django.db.models import CASCADE, F -from django.core.exceptions import ObjectDoesNotExist - -from judge.models import Profile, Comment -from judge.caching import cache_wrapper - - -category_to_verbose_message = { - "Add blog": _("Added a post"), - "Added to group": _("You are added to a group"), - "Comment": _("You have a new comment"), - "Delete blog": _("Deleted a post"), - "Reject blog": _("Rejected a post"), - "Approve blog": _("Approved a post"), - "Edit blog": _("Edited a post"), - "Mention": _("Mentioned you"), - "Reply": _("Replied you"), - "Ticket": _("Ticket"), -} - - -class Notification(models.Model): - owner = models.ForeignKey( - Profile, - verbose_name=_("owner"), - related_name="notifications", - on_delete=CASCADE, - ) - time = models.DateTimeField(verbose_name=_("posted time"), auto_now_add=True) - category = models.CharField(verbose_name=_("category"), max_length=1000) - html_link = models.TextField( - default="", - verbose_name=_("html link to comments, used for non-comments"), - max_length=1000, - ) - author = models.ForeignKey( - Profile, - null=True, - verbose_name=_("who trigger, used for non-comment"), - on_delete=CASCADE, - ) - - def verbose_activity(self): - if self.category in category_to_verbose_message: - return category_to_verbose_message[self.category] - - if "Problem public" in self.category: - is_public = "True" in self.category - if "(" in self.category and ")" in self.category: - groups = self.category.split("(", 1)[1].strip(")") - if is_public: - verbose_message = _("The problem is public to: ") + groups - else: - verbose_message = _("The problem is private to: ") + groups - else: - verbose_message = ( - _("The problem is public to everyone.") - if is_public - else _("The problem is private.") - ) - - return verbose_message - - return self.category - - -class NotificationProfile(models.Model): - unread_count = models.IntegerField(default=0) - user = models.OneToOneField(Profile, on_delete=CASCADE) - - -def make_notification(to_users, category, html_link, author): - for user in to_users: - if user == author: - continue - notif = Notification( - owner=user, category=category, html_link=html_link, author=author - ) - notif.save() - NotificationProfile.objects.get_or_create(user=user) - NotificationProfile.objects.filter(user=user).update( - unread_count=F("unread_count") + 1 - ) - unseen_notifications_count.dirty(user) - - -@cache_wrapper(prefix="unc") -def unseen_notifications_count(profile): - try: - return NotificationProfile.objects.get(user=profile).unread_count - except ObjectDoesNotExist: - return 0 diff --git a/judge/models/pagevote.py b/judge/models/pagevote.py deleted file mode 100644 index bc0224e..0000000 --- a/judge/models/pagevote.py +++ /dev/null @@ -1,70 +0,0 @@ -from django.db import models -from django.db.models import CASCADE -from django.utils.translation import gettext_lazy as _ -from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.contenttypes.models import ContentType - -from judge.models.profile import Profile -from judge.caching import cache_wrapper - -__all__ = ["PageVote", "PageVoteVoter"] - - -class PageVote(models.Model): - page = models.CharField( - max_length=30, - verbose_name=_("associated page"), - db_index=True, - ) # deprecated - score = models.IntegerField(verbose_name=_("votes"), default=0) - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) - object_id = models.PositiveIntegerField() - linked_object = GenericForeignKey("content_type", "object_id") - - class Meta: - verbose_name = _("pagevote") - verbose_name_plural = _("pagevotes") - indexes = [ - models.Index(fields=["content_type", "object_id"]), - ] - unique_together = ("content_type", "object_id") - - @cache_wrapper(prefix="PVvs") - def vote_score(self, user): - page_vote = PageVoteVoter.objects.filter(pagevote=self, voter=user).first() - return page_vote.score if page_vote else 0 - - def __str__(self): - return f"pagevote for {self.linked_object}" - - -class PageVoteVoter(models.Model): - voter = models.ForeignKey(Profile, related_name="voted_page", on_delete=CASCADE) - pagevote = models.ForeignKey(PageVote, related_name="votes", on_delete=CASCADE) - score = models.IntegerField() - - class Meta: - unique_together = ["voter", "pagevote"] - verbose_name = _("pagevote vote") - verbose_name_plural = _("pagevote votes") - - -@cache_wrapper(prefix="gocp", expected_type=PageVote) -def _get_or_create_pagevote(content_type, object_id): - pagevote, created = PageVote.objects.get_or_create( - content_type=content_type, - object_id=object_id, - ) - return pagevote - - -class PageVotable: - def get_or_create_pagevote(self): - content_type = ContentType.objects.get_for_model(self) - object_id = self.pk - return _get_or_create_pagevote(content_type, object_id) - - -def dirty_pagevote(pagevote, profile): - pagevote.vote_score.dirty(pagevote, profile) - _get_or_create_pagevote.dirty(pagevote.content_type, pagevote.object_id) diff --git a/judge/models/problem.py b/judge/models/problem.py index 8114fb7..4f7e545 100644 --- a/judge/models/problem.py +++ b/judge/models/problem.py @@ -1,4 +1,3 @@ -import errno from operator import attrgetter from django.conf import settings @@ -6,291 +5,156 @@ from django.contrib.contenttypes.fields import GenericRelation from django.core.cache import cache from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator from django.db import models -from django.db.models import CASCADE, F, FilteredRelation, Q, SET_NULL, Exists, OuterRef +from django.db.models import CASCADE, F, Q, QuerySet, SET_NULL +from django.db.models.expressions import RawSQL from django.db.models.functions import Coalesce from django.urls import reverse from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ -from django.db.models.signals import m2m_changed -from django.dispatch import receiver from judge.fulltext import SearchQuerySet -from judge.models.pagevote import PageVotable -from judge.models.bookmark import Bookmarkable from judge.models.profile import Organization, Profile from judge.models.runtime import Language from judge.user_translations import gettext as user_gettext -from judge.models.problem_data import ( - problem_data_storage, - problem_directory_file_helper, -) -from judge.caching import cache_wrapper +from judge.utils.raw_sql import RawSQLColumn, unique_together_left_join -__all__ = [ - "ProblemGroup", - "ProblemType", - "Problem", - "ProblemTranslation", - "License", - "Solution", - "TranslatedProblemQuerySet", -] - - -def problem_directory_file(data, filename): - return problem_directory_file_helper(data.code, filename) +__all__ = ['ProblemGroup', 'ProblemType', 'Problem', 'ProblemTranslation', 'ProblemClarification', + 'License', 'Solution', 'TranslatedProblemQuerySet', 'TranslatedProblemForeignKeyQuerySet'] class ProblemType(models.Model): - name = models.CharField( - max_length=20, verbose_name=_("problem category ID"), unique=True - ) - full_name = models.CharField( - max_length=100, verbose_name=_("problem category name") - ) + name = models.CharField(max_length=20, verbose_name=_('problem category ID'), unique=True) + full_name = models.CharField(max_length=100, verbose_name=_('problem category name')) def __str__(self): return self.full_name class Meta: - ordering = ["full_name"] - verbose_name = _("problem type") - verbose_name_plural = _("problem types") + ordering = ['full_name'] + verbose_name = _('problem type') + verbose_name_plural = _('problem types') class ProblemGroup(models.Model): - name = models.CharField( - max_length=20, verbose_name=_("problem group ID"), unique=True - ) - full_name = models.CharField(max_length=100, verbose_name=_("problem group name")) + name = models.CharField(max_length=20, verbose_name=_('problem group ID'), unique=True) + full_name = models.CharField(max_length=100, verbose_name=_('problem group name')) def __str__(self): return self.full_name class Meta: - ordering = ["full_name"] - verbose_name = _("problem group") - verbose_name_plural = _("problem groups") + ordering = ['full_name'] + verbose_name = _('problem group') + verbose_name_plural = _('problem groups') class License(models.Model): - key = models.CharField( - max_length=20, - unique=True, - verbose_name=_("key"), - validators=[RegexValidator(r"^[-\w.]+$", r"License key must be ^[-\w.]+$")], - ) - link = models.CharField(max_length=256, verbose_name=_("link")) - name = models.CharField(max_length=256, verbose_name=_("full name")) - display = models.CharField( - max_length=256, - blank=True, - verbose_name=_("short name"), - help_text=_("Displayed on pages under this license"), - ) - icon = models.CharField( - max_length=256, - blank=True, - verbose_name=_("icon"), - help_text=_("URL to the icon"), - ) - text = models.TextField(verbose_name=_("license text")) + key = models.CharField(max_length=20, unique=True, verbose_name=_('key'), + validators=[RegexValidator(r'^[-\w.]+$', r'License key must be ^[-\w.]+$')]) + link = models.CharField(max_length=256, verbose_name=_('link')) + name = models.CharField(max_length=256, verbose_name=_('full name')) + display = models.CharField(max_length=256, blank=True, verbose_name=_('short name'), + help_text=_('Displayed on pages under this license')) + icon = models.CharField(max_length=256, blank=True, verbose_name=_('icon'), help_text=_('URL to the icon')) + text = models.TextField(verbose_name=_('license text')) def __str__(self): return self.name def get_absolute_url(self): - return reverse("license", args=(self.key,)) + return reverse('license', args=(self.key,)) class Meta: - verbose_name = _("license") - verbose_name_plural = _("licenses") + verbose_name = _('license') + verbose_name_plural = _('licenses') class TranslatedProblemQuerySet(SearchQuerySet): def __init__(self, **kwargs): - super(TranslatedProblemQuerySet, self).__init__(("code", "name"), **kwargs) + super(TranslatedProblemQuerySet, self).__init__(('code', 'name', 'description'), **kwargs) def add_i18n_name(self, language): - return self.annotate( - i18n_translation=FilteredRelation( - "translations", - condition=Q(translations__language=language), - ) - ).annotate( - i18n_name=Coalesce( - F("i18n_translation__name"), F("name"), output_field=models.CharField() - ) - ) + queryset = self._clone() + alias = unique_together_left_join(queryset, ProblemTranslation, 'problem', 'language', language) + return queryset.annotate(i18n_name=Coalesce(RawSQL('%s.name' % alias, ()), F('name'), + output_field=models.CharField())) -class Problem(models.Model, PageVotable, Bookmarkable): - code = models.CharField( - max_length=20, - verbose_name=_("problem code"), - unique=True, - validators=[ - RegexValidator("^[a-z0-9]+$", _("Problem code must be ^[a-z0-9]+$")) - ], - help_text=_( - "A short, unique code for the problem, " "used in the url after /problem/" - ), - ) - name = models.CharField( - max_length=100, - verbose_name=_("problem name"), - db_index=True, - help_text=_("The full name of the problem, " "as shown in the problem list."), - ) - description = models.TextField(verbose_name=_("problem body"), blank=True) - authors = models.ManyToManyField( - Profile, - verbose_name=_("creators"), - blank=True, - related_name="authored_problems", - help_text=_( - "These users will be able to edit the problem, " "and be listed as authors." - ), - ) - curators = models.ManyToManyField( - Profile, - verbose_name=_("curators"), - blank=True, - related_name="curated_problems", - help_text=_( - "These users will be able to edit the problem, " - "but not be listed as authors." - ), - ) - testers = models.ManyToManyField( - Profile, - verbose_name=_("testers"), - blank=True, - related_name="tested_problems", - help_text=_( - "These users will be able to view the private problem, but not edit it." - ), - ) - types = models.ManyToManyField( - ProblemType, - verbose_name=_("problem types"), - help_text=_("The type of problem, " "as shown on the problem's page."), - ) - group = models.ForeignKey( - ProblemGroup, - verbose_name=_("problem group"), - on_delete=CASCADE, - help_text=_("The group of problem, shown under Category in the problem list."), - ) - time_limit = models.FloatField( - verbose_name=_("time limit"), - help_text=_( - "The time limit for this problem, in seconds. " - "Fractional seconds (e.g. 1.5) are supported." - ), - validators=[ - MinValueValidator(settings.DMOJ_PROBLEM_MIN_TIME_LIMIT), - MaxValueValidator(settings.DMOJ_PROBLEM_MAX_TIME_LIMIT), - ], - ) - memory_limit = models.PositiveIntegerField( - verbose_name=_("memory limit"), - help_text=_( - "The memory limit for this problem, in kilobytes " - "(e.g. 256mb = 262144 kilobytes)." - ), - validators=[ - MinValueValidator(settings.DMOJ_PROBLEM_MIN_MEMORY_LIMIT), - MaxValueValidator(settings.DMOJ_PROBLEM_MAX_MEMORY_LIMIT), - ], - ) +class TranslatedProblemForeignKeyQuerySet(QuerySet): + def add_problem_i18n_name(self, key, language, name_field=None): + queryset = self._clone() if name_field is None else self.annotate(_name=F(name_field)) + alias = unique_together_left_join(queryset, ProblemTranslation, 'problem', 'language', language, + parent_model=Problem) + # You must specify name_field if Problem is not yet joined into the QuerySet. + kwargs = {key: Coalesce(RawSQL('%s.name' % alias, ()), + F(name_field) if name_field else RawSQLColumn(Problem, 'name'), + output_field=models.CharField())} + return queryset.annotate(**kwargs) + + +class Problem(models.Model): + code = models.CharField(max_length=20, verbose_name=_('problem code'), unique=True, + validators=[RegexValidator('^[a-z0-9]+$', _('Problem code must be ^[a-z0-9]+$'))], + help_text=_('A short, unique code for the problem, ' + 'used in the url after /problem/')) + name = models.CharField(max_length=100, verbose_name=_('problem name'), db_index=True, + help_text=_('The full name of the problem, ' + 'as shown in the problem list.')) + description = models.TextField(verbose_name=_('problem body')) + authors = models.ManyToManyField(Profile, verbose_name=_('creators'), blank=True, related_name='authored_problems', + help_text=_('These users will be able to edit the problem, ' + 'and be listed as authors.')) + curators = models.ManyToManyField(Profile, verbose_name=_('curators'), blank=True, related_name='curated_problems', + help_text=_('These users will be able to edit the problem, ' + 'but not be listed as authors.')) + testers = models.ManyToManyField(Profile, verbose_name=_('testers'), blank=True, related_name='tested_problems', + help_text=_( + 'These users will be able to view the private problem, but not edit it.')) + types = models.ManyToManyField(ProblemType, verbose_name=_('problem types'), + help_text=_('The type of problem, ' + "as shown on the problem's page.")) + group = models.ForeignKey(ProblemGroup, verbose_name=_('problem group'), on_delete=CASCADE, + help_text=_('The group of problem, shown under Category in the problem list.')) + time_limit = models.FloatField(verbose_name=_('time limit'), + help_text=_('The time limit for this problem, in seconds. ' + 'Fractional seconds (e.g. 1.5) are supported.'), + validators=[MinValueValidator(settings.DMOJ_PROBLEM_MIN_TIME_LIMIT), + MaxValueValidator(settings.DMOJ_PROBLEM_MAX_TIME_LIMIT)]) + memory_limit = models.PositiveIntegerField(verbose_name=_('memory limit'), + help_text=_('The memory limit for this problem, in kilobytes ' + '(e.g. 64mb = 65536 kilobytes).'), + validators=[MinValueValidator(settings.DMOJ_PROBLEM_MIN_MEMORY_LIMIT), + MaxValueValidator(settings.DMOJ_PROBLEM_MAX_MEMORY_LIMIT)]) short_circuit = models.BooleanField(default=False) - points = models.FloatField( - verbose_name=_("points"), - help_text=_( - "Points awarded for problem completion. " - "Points are displayed with a 'p' suffix if partial." - ), - validators=[MinValueValidator(settings.DMOJ_PROBLEM_MIN_PROBLEM_POINTS)], - ) - partial = models.BooleanField( - verbose_name=_("allows partial points"), default=False - ) - allowed_languages = models.ManyToManyField( - Language, - verbose_name=_("allowed languages"), - help_text=_("List of allowed submission languages."), - ) - is_public = models.BooleanField( - verbose_name=_("publicly visible"), db_index=True, default=False - ) - is_manually_managed = models.BooleanField( - verbose_name=_("manually managed"), - db_index=True, - default=False, - help_text=_("Whether judges should be allowed to manage data or not."), - ) - date = models.DateTimeField( - verbose_name=_("date of publishing"), - null=True, - blank=True, - db_index=True, - help_text=_( - "Doesn't have magic ability to auto-publish due to backward compatibility" - ), - ) - banned_users = models.ManyToManyField( - Profile, - verbose_name=_("personae non gratae"), - blank=True, - help_text=_("Bans the selected users from submitting to this problem."), - ) - license = models.ForeignKey( - License, - null=True, - blank=True, - on_delete=SET_NULL, - help_text=_("The license under which this problem is published."), - ) - og_image = models.CharField( - verbose_name=_("OpenGraph image"), max_length=150, blank=True - ) - summary = models.TextField( - blank=True, - verbose_name=_("problem summary"), - help_text=_( - "Plain-text, shown in meta description tag, e.g. for social media." - ), - ) - user_count = models.IntegerField( - verbose_name=_("number of users"), - default=0, - help_text=_("The number of users who solved the problem."), - ) - ac_rate = models.FloatField(verbose_name=_("solve rate"), default=0) + points = models.FloatField(verbose_name=_('points'), + help_text=_('Points awarded for problem completion. ' + "Points are displayed with a 'p' suffix if partial."), + validators=[MinValueValidator(settings.DMOJ_PROBLEM_MIN_PROBLEM_POINTS)]) + partial = models.BooleanField(verbose_name=_('allows partial points'), default=False) + allowed_languages = models.ManyToManyField(Language, verbose_name=_('allowed languages'), + help_text=_('List of allowed submission languages.')) + is_public = models.BooleanField(verbose_name=_('publicly visible'), db_index=True, default=False) + is_manually_managed = models.BooleanField(verbose_name=_('manually managed'), db_index=True, default=False, + help_text=_('Whether judges should be allowed to manage data or not.')) + date = models.DateTimeField(verbose_name=_('date of publishing'), null=True, blank=True, db_index=True, + help_text=_("Doesn't have magic ability to auto-publish due to backward compatibility")) + banned_users = models.ManyToManyField(Profile, verbose_name=_('personae non gratae'), blank=True, + help_text=_('Bans the selected users from submitting to this problem.')) + license = models.ForeignKey(License, null=True, blank=True, on_delete=SET_NULL, + help_text=_('The license under which this problem is published.')) + og_image = models.CharField(verbose_name=_('OpenGraph image'), max_length=150, blank=True) + summary = models.TextField(blank=True, verbose_name=_('problem summary'), + help_text=_('Plain-text, shown in meta description tag, e.g. for social media.')) + user_count = models.IntegerField(verbose_name=_('number of users'), default=0, + help_text=_('The number of users who solved the problem.')) + ac_rate = models.FloatField(verbose_name=_('solve rate'), default=0) objects = TranslatedProblemQuerySet.as_manager() - tickets = GenericRelation("Ticket") - comments = GenericRelation("Comment") - pagevote = GenericRelation("PageVote") - bookmark = GenericRelation("BookMark") + tickets = GenericRelation('Ticket') - organizations = models.ManyToManyField( - Organization, - blank=True, - verbose_name=_("organizations"), - help_text=_("If private, only these organizations may see the problem."), - ) - is_organization_private = models.BooleanField( - verbose_name=_("private to organizations"), default=False - ) - pdf_description = models.FileField( - verbose_name=_("pdf statement"), - storage=problem_data_storage, - null=True, - blank=True, - upload_to=problem_directory_file, - ) + organizations = models.ManyToManyField(Organization, blank=True, verbose_name=_('organizations'), + help_text=_('If private, only these organizations may see the problem.')) + is_organization_private = models.BooleanField(verbose_name=_('private to organizations'), default=False) def __init__(self, *args, **kwargs): super(Problem, self).__init__(*args, **kwargs) @@ -300,32 +164,22 @@ class Problem(models.Model, PageVotable, Bookmarkable): @cached_property def types_list(self): - return list(map(user_gettext, map(attrgetter("full_name"), self.types.all()))) + return list(map(user_gettext, map(attrgetter('full_name'), self.types.all()))) def languages_list(self): - return ( - self.allowed_languages.values_list("common_name", flat=True) - .distinct() - .order_by("common_name") - ) + return self.allowed_languages.values_list('common_name', flat=True).distinct().order_by('common_name') def is_editor(self, profile): - return ( - self.authors.filter(id=profile.id) | self.curators.filter(id=profile.id) - ).exists() + return (self.authors.filter(id=profile.id) | self.curators.filter(id=profile.id)).exists() def is_editable_by(self, user): if not user.is_authenticated: return False - if ( - user.has_perm("judge.edit_all_problem") - or user.has_perm("judge.edit_public_problem") - and self.is_public - ): + if user.has_perm('judge.edit_all_problem') or user.has_perm('judge.edit_public_problem') and self.is_public: return True - return user.has_perm("judge.edit_own_problem") and self.is_editor(user.profile) + return user.has_perm('judge.edit_own_problem') and self.is_editor(user.profile) - def is_accessible_by(self, user, in_contest_mode=True): + def is_accessible_by(self, user): # Problem is public. if self.is_public: # Problem is not private to an organization. @@ -333,24 +187,23 @@ class Problem(models.Model, PageVotable, Bookmarkable): return True # If the user can see all organization private problems. - if user.has_perm("judge.see_organization_problem"): + if user.has_perm('judge.see_organization_problem'): return True # If the user is in the organization. - if user.is_authenticated and self.organizations.filter( - id__in=user.profile.organizations.all() - ): + if user.is_authenticated and \ + self.organizations.filter(id__in=user.profile.organizations.all()): return True # If the user can view all problems. - if user.has_perm("judge.see_private_problem"): + if user.has_perm('judge.see_private_problem'): return True if not user.is_authenticated: return False # If the user authored the problem or is a curator. - if user.has_perm("judge.edit_own_problem") and self.is_editor(user.profile): + if user.has_perm('judge.edit_own_problem') and self.is_editor(user.profile): return True # If user is a tester. @@ -359,25 +212,18 @@ class Problem(models.Model, PageVotable, Bookmarkable): # If user is currently in a contest containing that problem. current = user.profile.current_contest_id - if not in_contest_mode or current is None: + if current is None: return False from judge.models import ContestProblem - - return ContestProblem.objects.filter( - problem_id=self.id, contest__users__id=current - ).exists() + return ContestProblem.objects.filter(problem_id=self.id, contest__users__id=current).exists() def is_subs_manageable_by(self, user): - return ( - user.is_staff - and user.has_perm("judge.rejudge_submission") - and self.is_editable_by(user) - ) - + return user.is_staff and user.has_perm('judge.rejudge_submission') and self.is_editable_by(user) + @classmethod - def get_visible_problems(cls, user, profile=None): + def get_visible_problems(cls, user): # Do unauthenticated check here so we can skip authentication checks later on. - if not user.is_authenticated or not user: + if not user.is_authenticated: return cls.get_public_problems() # Conditions for visible problem: @@ -388,94 +234,61 @@ class Problem(models.Model, PageVotable, Bookmarkable): # - is_public problems # - not is_organization_private or in organization or `judge.see_organization_problem` # - author or curator or tester - queryset = cls.objects.defer("description") - profile = profile or user.profile - if not ( - user.has_perm("judge.see_private_problem") - or user.has_perm("judge.edit_all_problem") - ): + queryset = cls.objects.defer('description') + + if not (user.has_perm('judge.see_private_problem') or user.has_perm('judge.edit_all_problem')): q = Q(is_public=True) - if not user.has_perm("judge.see_organization_problem"): + if not user.has_perm('judge.see_organization_problem'): # Either not organization private or in the organization. - q &= Q(is_organization_private=False) | Q( - is_organization_private=True, - organizations__in=profile.organizations.all(), + q &= ( + Q(is_organization_private=False) | + Q(is_organization_private=True, organizations__in=user.profile.organizations.all()) ) # Authors, curators, and testers should always have access, so OR at the very end. - q |= Exists( - Problem.authors.through.objects.filter( - problem=OuterRef("pk"), profile=profile - ) - ) - q |= Exists( - Problem.curators.through.objects.filter( - problem=OuterRef("pk"), profile=profile - ) - ) - q |= Exists( - Problem.testers.through.objects.filter( - problem=OuterRef("pk"), profile=profile - ) - ) + q |= Q(authors=user.profile) + q |= Q(curators=user.profile) + q |= Q(testers=user.profile) queryset = queryset.filter(q) return queryset @classmethod def get_public_problems(cls): - return cls.objects.filter(is_public=True, is_organization_private=False).defer( - "description" - ) + return cls.objects.filter(is_public=True, is_organization_private=False).defer('description') def __str__(self): - return "%s (%s)" % (self.name, self.code) + return self.name def get_absolute_url(self): - return reverse("problem_detail", args=(self.code,)) + return reverse('problem_detail', args=(self.code,)) @cached_property def author_ids(self): - return Problem.authors.through.objects.filter(problem=self).values_list( - "profile_id", flat=True - ) - - @cache_wrapper(prefix="Pga", expected_type=models.query.QuerySet) - def get_authors(self): - return self.authors.only("id") + return self.authors.values_list('id', flat=True) @cached_property def editor_ids(self): - return self.author_ids.union( - Problem.curators.through.objects.filter(problem=self).values_list( - "profile_id", flat=True - ) - ) + return self.author_ids | self.curators.values_list('id', flat=True) @cached_property def tester_ids(self): - return Problem.testers.through.objects.filter(problem=self).values_list( - "profile_id", flat=True - ) + return self.testers.values_list('id', flat=True) @cached_property def usable_common_names(self): - return set(self.usable_languages.values_list("common_name", flat=True)) + return set(self.usable_languages.values_list('common_name', flat=True)) @property def usable_languages(self): - return self.allowed_languages.filter( - judges__in=self.judges.filter(online=True) - ).distinct() + return self.allowed_languages.filter(judges__in=self.judges.filter(online=True)).distinct() def translated_name(self, language): if language in self._translated_name_cache: return self._translated_name_cache[language] # Hits database despite prefetch_related. try: - name = self.translations.filter(language=language).values_list( - "name", flat=True - )[0] + name = self.translations.filter(language=language).values_list('name', flat=True)[0] except IndexError: name = self.name self._translated_name_cache[language] = name @@ -491,24 +304,17 @@ class Problem(models.Model, PageVotable, Bookmarkable): def i18n_name(self, value): self._i18n_name = value + @property + def clarifications(self): + return ProblemClarification.objects.filter(problem=self) + def update_stats(self): - self.user_count = ( - self.submission_set.filter( - points__gte=self.points, result="AC", user__is_unlisted=False - ) - .values("user") - .distinct() - .count() - ) + self.user_count = self.submission_set.filter(points__gte=self.points, result='AC', + user__is_unlisted=False).values('user').distinct().count() submissions = self.submission_set.count() if submissions: - self.ac_rate = ( - 100.0 - * self.submission_set.filter( - points__gte=self.points, result="AC", user__is_unlisted=False - ).count() - / submissions - ) + self.ac_rate = 100.0 * self.submission_set.filter(points__gte=self.points, result='AC', + user__is_unlisted=False).count() / submissions else: self.ac_rate = 0 self.save() @@ -517,13 +323,9 @@ class Problem(models.Model, PageVotable, Bookmarkable): def _get_limits(self, key): global_limit = getattr(self, key) - limits = { - limit["language_id"]: (limit["language__name"], limit[key]) - for limit in self.language_limits.values( - "language_id", "language__name", key - ) - if limit[key] != global_limit - } + limits = {limit['language_id']: (limit['language__name'], limit[key]) + for limit in self.language_limits.values('language_id', 'language__name', key) + if limit[key] != global_limit} limit_ids = set(limits.keys()) common = [] @@ -543,212 +345,106 @@ class Problem(models.Model, PageVotable, Bookmarkable): @property def language_time_limit(self): - key = "problem_tls:%d" % self.id + key = 'problem_tls:%d' % self.id result = cache.get(key) if result is not None: return result - result = self._get_limits("time_limit") + result = self._get_limits('time_limit') cache.set(key, result) return result @property def language_memory_limit(self): - key = "problem_mls:%d" % self.id + key = 'problem_mls:%d' % self.id result = cache.get(key) if result is not None: return result - result = self._get_limits("memory_limit") + result = self._get_limits('memory_limit') cache.set(key, result) return result - def handle_code_change(self): - has_data = hasattr(self, "data_files") - has_pdf = bool(self.pdf_description) - if not has_data and not has_pdf: - return - - try: - problem_data_storage.rename(self.__original_code, self.code) - except OSError as e: - if e.errno != errno.ENOENT: - raise - - if has_pdf: - self.pdf_description.name = problem_directory_file_helper( - self.code, self.pdf_description.name - ) - super().save(update_fields=["pdf_description"]) - - if has_data: - self.data_files._update_code(self.__original_code, self.code) - - def save(self, should_move_data=True, *args, **kwargs): - code_changed = self.__original_code and self.code != self.__original_code + def save(self, *args, **kwargs): super(Problem, self).save(*args, **kwargs) - if code_changed and should_move_data: - self.handle_code_change() - - def delete(self, *args, **kwargs): - super().delete(*args, **kwargs) - problem_data_storage.delete_directory(self.code) + if self.code != self.__original_code: + try: + problem_data = self.data_files + except AttributeError: + pass + else: + problem_data._update_code(self.__original_code, self.code) save.alters_data = True class Meta: permissions = ( - ("see_private_problem", "See hidden problems"), - ("edit_own_problem", "Edit own problems"), - ("edit_all_problem", "Edit all problems"), - ("edit_public_problem", "Edit all public problems"), - ("clone_problem", "Clone problem"), - ("change_public_visibility", "Change is_public field"), - ("change_manually_managed", "Change is_manually_managed field"), - ("see_organization_problem", "See organization-private problems"), - ("suggest_problem_changes", "Suggest changes to problem"), + ('see_private_problem', 'See hidden problems'), + ('edit_own_problem', 'Edit own problems'), + ('edit_all_problem', 'Edit all problems'), + ('edit_public_problem', 'Edit all public problems'), + ('clone_problem', 'Clone problem'), + ('change_public_visibility', 'Change is_public field'), + ('change_manually_managed', 'Change is_manually_managed field'), + ('see_organization_problem', 'See organization-private problems'), ) - verbose_name = _("problem") - verbose_name_plural = _("problems") + verbose_name = _('problem') + verbose_name_plural = _('problems') class ProblemTranslation(models.Model): - problem = models.ForeignKey( - Problem, - verbose_name=_("problem"), - related_name="translations", - on_delete=CASCADE, - ) - language = models.CharField( - verbose_name=_("language"), max_length=7, choices=settings.LANGUAGES - ) - name = models.CharField( - verbose_name=_("translated name"), max_length=100, db_index=True - ) - description = models.TextField(verbose_name=_("translated description")) + problem = models.ForeignKey(Problem, verbose_name=_('problem'), related_name='translations', on_delete=CASCADE) + language = models.CharField(verbose_name=_('language'), max_length=7, choices=settings.LANGUAGES) + name = models.CharField(verbose_name=_('translated name'), max_length=100, db_index=True) + description = models.TextField(verbose_name=_('translated description')) class Meta: - unique_together = ("problem", "language") - verbose_name = _("problem translation") - verbose_name_plural = _("problem translations") + unique_together = ('problem', 'language') + verbose_name = _('problem translation') + verbose_name_plural = _('problem translations') + + +class ProblemClarification(models.Model): + problem = models.ForeignKey(Problem, verbose_name=_('clarified problem'), on_delete=CASCADE) + description = models.TextField(verbose_name=_('clarification body')) + date = models.DateTimeField(verbose_name=_('clarification timestamp'), auto_now_add=True) class LanguageLimit(models.Model): - problem = models.ForeignKey( - Problem, - verbose_name=_("problem"), - related_name="language_limits", - on_delete=CASCADE, - ) - language = models.ForeignKey( - Language, verbose_name=_("language"), on_delete=CASCADE - ) - time_limit = models.FloatField( - verbose_name=_("time limit"), - validators=[ - MinValueValidator(settings.DMOJ_PROBLEM_MIN_TIME_LIMIT), - MaxValueValidator(settings.DMOJ_PROBLEM_MAX_TIME_LIMIT), - ], - ) - memory_limit = models.IntegerField( - verbose_name=_("memory limit"), - validators=[ - MinValueValidator(settings.DMOJ_PROBLEM_MIN_MEMORY_LIMIT), - MaxValueValidator(settings.DMOJ_PROBLEM_MAX_MEMORY_LIMIT), - ], - ) + problem = models.ForeignKey(Problem, verbose_name=_('problem'), related_name='language_limits', on_delete=CASCADE) + language = models.ForeignKey(Language, verbose_name=_('language'), on_delete=CASCADE) + time_limit = models.FloatField(verbose_name=_('time limit'), + validators=[MinValueValidator(settings.DMOJ_PROBLEM_MIN_TIME_LIMIT), + MaxValueValidator(settings.DMOJ_PROBLEM_MAX_TIME_LIMIT)]) + memory_limit = models.IntegerField(verbose_name=_('memory limit'), + validators=[MinValueValidator(settings.DMOJ_PROBLEM_MIN_MEMORY_LIMIT), + MaxValueValidator(settings.DMOJ_PROBLEM_MAX_MEMORY_LIMIT)]) class Meta: - unique_together = ("problem", "language") - verbose_name = _("language-specific resource limit") - verbose_name_plural = _("language-specific resource limits") + unique_together = ('problem', 'language') + verbose_name = _('language-specific resource limit') + verbose_name_plural = _('language-specific resource limits') -class LanguageTemplate(models.Model): - problem = models.ForeignKey( - Problem, - verbose_name=_("problem"), - related_name="language_templates", - on_delete=CASCADE, - ) - language = models.ForeignKey( - Language, verbose_name=_("language"), on_delete=CASCADE - ) - source = models.TextField(verbose_name=_("source code"), max_length=65536) - - class Meta: - unique_together = ("problem", "language") - verbose_name = _("language-specific template") - verbose_name_plural = _("language-specific templates") - - -class Solution(models.Model, PageVotable, Bookmarkable): - problem = models.OneToOneField( - Problem, - on_delete=CASCADE, - verbose_name=_("associated problem"), - null=True, - blank=True, - related_name="solution", - ) - is_public = models.BooleanField(verbose_name=_("public visibility"), default=False) - publish_on = models.DateTimeField(verbose_name=_("publish date")) - authors = models.ManyToManyField(Profile, verbose_name=_("authors"), blank=True) - content = models.TextField(verbose_name=_("editorial content")) - comments = GenericRelation("Comment") - pagevote = GenericRelation("PageVote") - bookmark = GenericRelation("BookMark") +class Solution(models.Model): + problem = models.OneToOneField(Problem, on_delete=SET_NULL, verbose_name=_('associated problem'), + null=True, blank=True, related_name='solution') + is_public = models.BooleanField(verbose_name=_('public visibility'), default=False) + publish_on = models.DateTimeField(verbose_name=_('publish date')) + authors = models.ManyToManyField(Profile, verbose_name=_('authors'), blank=True) + content = models.TextField(verbose_name=_('editorial content')) def get_absolute_url(self): problem = self.problem if problem is None: - return reverse("home") + return reverse('home') else: - return reverse("problem_editorial", args=[problem.code]) - - @cache_wrapper(prefix="Sga", expected_type=models.query.QuerySet) - def get_authors(self): - return self.authors.only("id") + return reverse('problem_editorial', args=[problem.code]) def __str__(self): - return _("Editorial for %s") % self.problem.name + return _('Editorial for %s') % self.problem.name class Meta: - permissions = (("see_private_solution", "See hidden solutions"),) - verbose_name = _("solution") - verbose_name_plural = _("solutions") - - -class ProblemPointsVote(models.Model): - points = models.IntegerField( - verbose_name=_("proposed point value"), - help_text=_("The amount of points you think this problem deserves."), - validators=[ - MinValueValidator(100), - MaxValueValidator(600), - ], - ) - - voter = models.ForeignKey( - Profile, related_name="problem_points_votes", on_delete=CASCADE, db_index=True - ) - problem = models.ForeignKey( - Problem, related_name="problem_points_votes", on_delete=CASCADE, db_index=True - ) - vote_time = models.DateTimeField( - verbose_name=_("The time this vote was cast"), - auto_now_add=True, - blank=True, - ) - - class Meta: - verbose_name = _("vote") - verbose_name_plural = _("votes") - - def __str__(self): - return f"{self.voter}: {self.points} for {self.problem.code}" - - -@receiver(m2m_changed, sender=Problem.organizations.through) -def update_organization_private(sender, instance, **kwargs): - if kwargs["action"] in ["post_add", "post_remove", "post_clear"]: - instance.is_organization_private = instance.organizations.exists() - instance.save(update_fields=["is_organization_private"]) + permissions = ( + ('see_private_solution', 'See hidden solutions'), + ) + verbose_name = _('solution') + verbose_name_plural = _('solutions') diff --git a/judge/models/problem_data.py b/judge/models/problem_data.py index adb92d8..fe79c49 100644 --- a/judge/models/problem_data.py +++ b/judge/models/problem_data.py @@ -9,142 +9,58 @@ from django.utils.translation import gettext_lazy as _ from judge.utils.problem_data import ProblemDataStorage, get_file_cachekey -__all__ = [ - "problem_data_storage", - "problem_directory_file", - "ProblemData", - "ProblemTestCase", - "CHECKERS", -] +__all__ = ['problem_data_storage', 'problem_directory_file', 'ProblemData', 'ProblemTestCase', 'CHECKERS'] problem_data_storage = ProblemDataStorage() -def problem_directory_file_helper(code, filename): +def _problem_directory_file(code, filename): return os.path.join(code, os.path.basename(filename)) def problem_directory_file(data, filename): - return problem_directory_file_helper(data.problem.code, filename) + return _problem_directory_file(data.problem.code, filename) CHECKERS = ( - ("standard", _("Standard")), - ("floats", _("Floats")), - ("floatsabs", _("Floats (absolute)")), - ("floatsrel", _("Floats (relative)")), - ("rstripped", _("Non-trailing spaces")), - ("sorted", _("Unordered")), - ("identical", _("Byte identical")), - ("linecount", _("Line-by-line")), - ("custom", _("Custom checker (PY)")), - ("customcpp", _("Custom checker (CPP)")), - ("interact", _("Interactive")), - ("testlib", _("Testlib")), + ('standard', _('Standard')), + ('floats', _('Floats')), + ('floatsabs', _('Floats (absolute)')), + ('floatsrel', _('Floats (relative)')), + ('rstripped', _('Non-trailing spaces')), + ('sorted', _('Unordered')), + ('identical', _('Byte identical')), + ('linecount', _('Line-by-line')), + ('custom', _('Custom checker (PY)')), + ('customval', _('Custom validator (CPP)')), ) class ProblemData(models.Model): - problem = models.OneToOneField( - "Problem", - verbose_name=_("problem"), - related_name="data_files", - on_delete=models.CASCADE, - ) - zipfile = models.FileField( - verbose_name=_("data zip file"), - storage=problem_data_storage, - null=True, - blank=True, - upload_to=problem_directory_file, - ) - generator = models.FileField( - verbose_name=_("generator file"), - storage=problem_data_storage, - null=True, - blank=True, - upload_to=problem_directory_file, - ) - output_prefix = models.IntegerField( - verbose_name=_("output prefix length"), blank=True, null=True - ) - output_limit = models.IntegerField( - verbose_name=_("output limit length"), blank=True, null=True - ) - feedback = models.TextField( - verbose_name=_("init.yml generation feedback"), blank=True - ) - checker = models.CharField( - max_length=10, verbose_name=_("checker"), choices=CHECKERS, blank=True - ) - checker_args = models.TextField( - verbose_name=_("checker arguments"), - blank=True, - help_text=_("checker arguments as a JSON object"), - ) - custom_checker = models.FileField( - verbose_name=_("custom checker file"), - storage=problem_data_storage, - null=True, - blank=True, - upload_to=problem_directory_file, - validators=[FileExtensionValidator(allowed_extensions=["py"])], - ) - custom_checker_cpp = models.FileField( - verbose_name=_("custom cpp checker file"), - storage=problem_data_storage, - null=True, - blank=True, - upload_to=problem_directory_file, - validators=[FileExtensionValidator(allowed_extensions=["cpp"])], - ) - interactive_judge = models.FileField( - verbose_name=_("interactive judge"), - storage=problem_data_storage, - null=True, - blank=True, - upload_to=problem_directory_file, - validators=[FileExtensionValidator(allowed_extensions=["cpp"])], - ) - fileio_input = models.TextField( - verbose_name=_("input file name"), - blank=True, - null=True, - help_text=_("Leave empty for stdin"), - ) - fileio_output = models.TextField( - verbose_name=_("output file name"), - blank=True, - null=True, - help_text=_("Leave empty for stdout"), - ) - output_only = models.BooleanField( - verbose_name=_("is output only"), - help_text=_("Support output-only problem"), - null=True, - ) - use_ioi_signature = models.BooleanField( - verbose_name=_("is IOI signature"), - help_text=_("Use IOI Signature"), - null=True, - ) - signature_handler = models.FileField( - verbose_name=_("signature handler"), - storage=problem_data_storage, - null=True, - blank=True, - upload_to=problem_directory_file, - validators=[FileExtensionValidator(allowed_extensions=["cpp"])], - ) - signature_header = models.FileField( - verbose_name=_("signature header"), - storage=problem_data_storage, - null=True, - blank=True, - upload_to=problem_directory_file, - validators=[FileExtensionValidator(allowed_extensions=["h"])], - ) - + problem = models.OneToOneField('Problem', verbose_name=_('problem'), related_name='data_files', + on_delete=models.CASCADE) + zipfile = models.FileField(verbose_name=_('data zip file'), storage=problem_data_storage, null=True, blank=True, + upload_to=problem_directory_file) + generator = models.FileField(verbose_name=_('generator file'), storage=problem_data_storage, null=True, blank=True, + upload_to=problem_directory_file) + output_prefix = models.IntegerField(verbose_name=_('output prefix length'), blank=True, null=True) + output_limit = models.IntegerField(verbose_name=_('output limit length'), blank=True, null=True) + feedback = models.TextField(verbose_name=_('init.yml generation feedback'), blank=True) + checker = models.CharField(max_length=10, verbose_name=_('checker'), choices=CHECKERS, blank=True) + checker_args = models.TextField(verbose_name=_('checker arguments'), blank=True, + help_text=_('checker arguments as a JSON object')) + custom_checker = models.FileField(verbose_name=_('custom checker file'), + storage=problem_data_storage, + null=True, + blank=True, + upload_to=problem_directory_file, + validators=[FileExtensionValidator(allowed_extensions=['py'])]) + custom_validator = models.FileField(verbose_name=_('custom validator file'), + storage=problem_data_storage, + null=True, + blank=True, + upload_to=problem_directory_file, + validators=[FileExtensionValidator(allowed_extensions=['cpp'])]) __original_zipfile = None def __init__(self, *args, **kwargs): @@ -152,99 +68,58 @@ class ProblemData(models.Model): self.__original_zipfile = self.zipfile def save(self, *args, **kwargs): - # Delete caches - if self.__original_zipfile: - try: + if self.zipfile != self.__original_zipfile and self.__original_zipfile: + # Delete caches + try: files = ZipFile(self.__original_zipfile.path).namelist() for file in files: - cache_key = "problem_archive:%s:%s" % ( - self.problem.code, - get_file_cachekey(file), - ) + cache_key = 'problem_archive:%s:%s' % (self.problem.code, get_file_cachekey(file)) cache.delete(cache_key) - except (BadZipFile, FileNotFoundError): + except BadZipFile: pass - if self.zipfile != self.__original_zipfile: - self.__original_zipfile.delete(save=False) + + self.__original_zipfile.delete(save=False) return super(ProblemData, self).save(*args, **kwargs) def has_yml(self): - return problem_data_storage.exists("%s/init.yml" % self.problem.code) + return problem_data_storage.exists('%s/init.yml' % self.problem.code) def _update_code(self, original, new): + try: + problem_data_storage.rename(original, new) + except OSError as e: + if e.errno != errno.ENOENT: + raise if self.zipfile: - self.zipfile.name = problem_directory_file_helper(new, self.zipfile.name) + self.zipfile.name = _problem_directory_file(new, self.zipfile.name) if self.generator: - self.generator.name = problem_directory_file_helper( - new, self.generator.name - ) + self.generator.name = _problem_directory_file(new, self.generator.name) if self.custom_checker: - self.custom_checker.name = problem_directory_file_helper( - new, self.custom_checker.name - ) + self.custom_checker.name = _problem_directory_file(new, self.custom_checker.name) if self.custom_checker: - self.custom_checker.name = problem_directory_file_helper( - new, self.custom_checker.name - ) - if self.custom_checker_cpp: - self.custom_checker_cpp.name = problem_directory_file_helper( - new, self.custom_checker_cpp.name - ) - if self.interactive_judge: - self.interactive_judge.name = problem_directory_file_helper( - new, self.interactive_judge.name - ) - if self.signature_header: - self.signature_header.name = problem_directory_file_helper( - new, self.signature_header.name - ) - if self.signature_handler: - self.signature_handler.name = problem_directory_file_helper( - new, self.signature_handler.name - ) + self.custom_checker.name = _problem_directory_file(new, self.custom_checker.name) + if self.custom_validator: + self.custom_validator.name = _problem_directory_file(new, self.custom_validator.name) self.save() - _update_code.alters_data = True class ProblemTestCase(models.Model): - dataset = models.ForeignKey( - "Problem", - verbose_name=_("problem data set"), - related_name="cases", - on_delete=models.CASCADE, - ) - order = models.IntegerField(verbose_name=_("case position")) - type = models.CharField( - max_length=1, - verbose_name=_("case type"), - choices=( - ("C", _("Normal case")), - ("S", _("Batch start")), - ("E", _("Batch end")), - ), - default="C", - ) - input_file = models.CharField( - max_length=100, verbose_name=_("input file name"), blank=True - ) - output_file = models.CharField( - max_length=100, verbose_name=_("output file name"), blank=True - ) - generator_args = models.TextField(verbose_name=_("generator arguments"), blank=True) - points = models.IntegerField(verbose_name=_("point value"), blank=True, null=True) - is_pretest = models.BooleanField(verbose_name=_("case is pretest?")) - output_prefix = models.IntegerField( - verbose_name=_("output prefix length"), blank=True, null=True - ) - output_limit = models.IntegerField( - verbose_name=_("output limit length"), blank=True, null=True - ) - checker = models.CharField( - max_length=10, verbose_name=_("checker"), choices=CHECKERS, blank=True - ) - checker_args = models.TextField( - verbose_name=_("checker arguments"), - blank=True, - help_text=_("checker arguments as a JSON object"), - ) + dataset = models.ForeignKey('Problem', verbose_name=_('problem data set'), related_name='cases', + on_delete=models.CASCADE) + order = models.IntegerField(verbose_name=_('case position')) + type = models.CharField(max_length=1, verbose_name=_('case type'), + choices=(('C', _('Normal case')), + ('S', _('Batch start')), + ('E', _('Batch end'))), + default='C') + input_file = models.CharField(max_length=100, verbose_name=_('input file name'), blank=True) + output_file = models.CharField(max_length=100, verbose_name=_('output file name'), blank=True) + generator_args = models.TextField(verbose_name=_('generator arguments'), blank=True) + points = models.IntegerField(verbose_name=_('point value'), blank=True, null=True) + is_pretest = models.BooleanField(verbose_name=_('case is pretest?')) + output_prefix = models.IntegerField(verbose_name=_('output prefix length'), blank=True, null=True) + output_limit = models.IntegerField(verbose_name=_('output limit length'), blank=True, null=True) + checker = models.CharField(max_length=10, verbose_name=_('checker'), choices=CHECKERS, blank=True) + checker_args = models.TextField(verbose_name=_('checker arguments'), blank=True, + help_text=_('checker arguments as a JSON object')) diff --git a/judge/models/profile.py b/judge/models/profile.py index 4572b69..7e2dc7f 100644 --- a/judge/models/profile.py +++ b/judge/models/profile.py @@ -1,5 +1,4 @@ from operator import mul -import os from django.conf import settings from django.contrib.auth.models import User @@ -10,29 +9,14 @@ from django.urls import reverse from django.utils.functional import cached_property from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ -from django.dispatch import receiver -from django.db.models.signals import post_save, pre_save - - from fernet_fields import EncryptedCharField from sortedm2m.fields import SortedManyToManyField -from judge.models.choices import ACE_THEMES, TIMEZONE +from judge.models.choices import ACE_THEMES, MATH_ENGINES_CHOICES, TIMEZONE from judge.models.runtime import Language from judge.ratings import rating_class -from judge.caching import cache_wrapper - -__all__ = ["Organization", "Profile", "OrganizationRequest", "Friend"] - - -TSHIRT_SIZES = ( - ("S", "Small (S)"), - ("M", "Medium (M)"), - ("L", "Large (L)"), - ("XL", "Extra Large (XL)"), - ("XXL", "2 Extra Large (XXL)"), -) +__all__ = ['Organization', 'Profile', 'OrganizationRequest', 'Friend'] class EncryptedNullCharField(EncryptedCharField): @@ -42,75 +26,29 @@ class EncryptedNullCharField(EncryptedCharField): return super(EncryptedNullCharField, self).get_prep_value(value) -def profile_image_path(profile, filename): - tail = filename.split(".")[-1] - new_filename = f"user_{profile.id}.{tail}" - return os.path.join(settings.DMOJ_PROFILE_IMAGE_ROOT, new_filename) - - -def organization_image_path(organization, filename): - tail = filename.split(".")[-1] - new_filename = f"organization_{organization.id}.{tail}" - return os.path.join(settings.DMOJ_ORGANIZATION_IMAGE_ROOT, new_filename) - - class Organization(models.Model): - name = models.CharField(max_length=128, verbose_name=_("organization title")) - slug = models.SlugField( - max_length=128, - verbose_name=_("organization slug"), - help_text=_("Organization name shown in URL"), - unique=True, - validators=[ - RegexValidator("^[-a-zA-Z0-9]+$", _("Only alphanumeric and hyphens")) - ], - ) - short_name = models.CharField( - max_length=20, - verbose_name=_("short name"), - help_text=_("Displayed beside user name during contests"), - ) - about = models.CharField( - max_length=10000, verbose_name=_("organization description") - ) - registrant = models.ForeignKey( - "Profile", - verbose_name=_("registrant"), - on_delete=models.CASCADE, - related_name="registrant+", - help_text=_("User who registered this organization"), - ) - admins = models.ManyToManyField( - "Profile", - verbose_name=_("administrators"), - related_name="admin_of", - help_text=_("Those who can edit this organization"), - ) - creation_date = models.DateTimeField( - verbose_name=_("creation date"), auto_now_add=True - ) - is_open = models.BooleanField( - verbose_name=_("is open organization?"), - help_text=_("Allow joining organization"), - default=True, - ) - slots = models.IntegerField( - verbose_name=_("maximum size"), - null=True, - blank=True, - help_text=_( - "Maximum amount of users in this organization, " - "only applicable to private organizations" - ), - ) - access_code = models.CharField( - max_length=7, - help_text=_("Student access code"), - verbose_name=_("access code"), - null=True, - blank=True, - ) - organization_image = models.ImageField(upload_to=organization_image_path, null=True) + name = models.CharField(max_length=128, verbose_name=_('organization title')) + slug = models.SlugField(max_length=128, verbose_name=_('organization slug'), + help_text=_('Organization name shown in URL')) + short_name = models.CharField(max_length=20, verbose_name=_('short name'), + help_text=_('Displayed beside user name during contests')) + about = models.TextField(verbose_name=_('organization description')) + registrant = models.ForeignKey('Profile', verbose_name=_('registrant'), on_delete=models.CASCADE, + related_name='registrant+', help_text=_('User who registered this organization')) + admins = models.ManyToManyField('Profile', verbose_name=_('administrators'), related_name='admin_of', + help_text=_('Those who can edit this organization')) + creation_date = models.DateTimeField(verbose_name=_('creation date'), auto_now_add=True) + is_open = models.BooleanField(verbose_name=_('is open organization?'), + help_text=_('Allow joining organization'), default=True) + slots = models.IntegerField(verbose_name=_('maximum size'), null=True, blank=True, + help_text=_('Maximum amount of users in this organization, ' + 'only applicable to private organizations')) + access_code = models.CharField(max_length=7, help_text=_('Student access code'), + verbose_name=_('access code'), null=True, blank=True) + logo_override_image = models.CharField(verbose_name=_('Logo override image'), default='', max_length=150, + blank=True, + help_text=_('This image will replace the default site logo for users ' + 'viewing the organization.')) def __contains__(self, item): if isinstance(item, int): @@ -118,152 +56,64 @@ class Organization(models.Model): elif isinstance(item, Profile): return self.members.filter(id=item.id).exists() else: - raise TypeError( - "Organization membership test must be Profile or primany key" - ) - - def delete(self, *args, **kwargs): - contests = self.contest_set - for contest in contests.all(): - if contest.organizations.count() == 1: - contest.delete() - super().delete(*args, **kwargs) + raise TypeError('Organization membership test must be Profile or primany key') def __str__(self): return self.name def get_absolute_url(self): - return reverse("organization_home", args=(self.id, self.slug)) + return reverse('organization_home', args=(self.id, self.slug)) def get_users_url(self): - return reverse("organization_users", args=(self.id, self.slug)) - - def get_problems_url(self): - return reverse("organization_problems", args=(self.id, self.slug)) - - def get_contests_url(self): - return reverse("organization_contests", args=(self.id, self.slug)) - - def get_submissions_url(self): - return reverse("organization_submissions", args=(self.id, self.slug)) - - def is_admin(self, profile): - return profile.id in self.get_admin_ids() - - @cache_wrapper(prefix="Orgai", expected_type=list) - def get_admin_ids(self): - return list(self.admins.values_list("id", flat=True)) - - def is_member(self, profile): - return profile in self + return reverse('organization_users', args=(self.id, self.slug)) class Meta: - ordering = ["name"] + ordering = ['name'] permissions = ( - ("organization_admin", "Administer organizations"), - ("edit_all_organization", "Edit all organizations"), + ('organization_admin', 'Administer organizations'), + ('edit_all_organization', 'Edit all organizations'), ) - verbose_name = _("organization") - verbose_name_plural = _("organizations") - app_label = "judge" + verbose_name = _('organization') + verbose_name_plural = _('organizations') class Profile(models.Model): - user = models.OneToOneField( - User, verbose_name=_("user associated"), on_delete=models.CASCADE - ) - about = models.CharField( - max_length=10000, verbose_name=_("self-description"), null=True, blank=True - ) - timezone = models.CharField( - max_length=50, - verbose_name=_("location"), - choices=TIMEZONE, - default=settings.DEFAULT_USER_TIME_ZONE, - ) - language = models.ForeignKey( - "Language", - verbose_name=_("preferred language"), - on_delete=models.SET_DEFAULT, - default=Language.get_default_language_pk, - ) + user = models.OneToOneField(User, verbose_name=_('user associated'), on_delete=models.CASCADE) + about = models.TextField(verbose_name=_('self-description'), null=True, blank=True) + timezone = models.CharField(max_length=50, verbose_name=_('location'), choices=TIMEZONE, + default=settings.DEFAULT_USER_TIME_ZONE) + language = models.ForeignKey('Language', verbose_name=_('preferred language'), on_delete=models.SET_DEFAULT, + default=Language.get_default_language_pk) points = models.FloatField(default=0, db_index=True) performance_points = models.FloatField(default=0, db_index=True) problem_count = models.IntegerField(default=0, db_index=True) - ace_theme = models.CharField(max_length=30, choices=ACE_THEMES, default="github") - last_access = models.DateTimeField(verbose_name=_("last access time"), default=now) - ip = models.GenericIPAddressField(verbose_name=_("last IP"), blank=True, null=True) - organizations = SortedManyToManyField( - Organization, - verbose_name=_("organization"), - blank=True, - related_name="members", - related_query_name="member", - ) - display_rank = models.CharField( - max_length=10, - default="user", - verbose_name=_("display rank"), - choices=( - ("user", "Normal User"), - ("setter", "Problem Setter"), - ("admin", "Admin"), - ), - db_index=True, - ) - mute = models.BooleanField( - verbose_name=_("comment mute"), - help_text=_("Some users are at their best when silent."), - default=False, - ) - is_unlisted = models.BooleanField( - verbose_name=_("unlisted user"), - help_text=_("User will not be ranked."), - default=False, - ) - rating = models.IntegerField(null=True, default=None, db_index=True) - current_contest = models.OneToOneField( - "ContestParticipation", - verbose_name=_("current contest"), - null=True, - blank=True, - related_name="+", - on_delete=models.SET_NULL, - ) - is_totp_enabled = models.BooleanField( - verbose_name=_("2FA enabled"), - default=False, - help_text=_("check to enable TOTP-based two factor authentication"), - ) - totp_key = EncryptedNullCharField( - max_length=32, - null=True, - blank=True, - verbose_name=_("TOTP key"), - help_text=_("32 character base32-encoded key for TOTP"), - validators=[ - RegexValidator("^$|^[A-Z2-7]{32}$", _("TOTP key must be empty or base32")) - ], - ) - notes = models.TextField( - verbose_name=_("internal notes"), - null=True, - blank=True, - help_text=_("Notes for administrators regarding this user."), - ) - profile_image = models.ImageField(upload_to=profile_image_path, null=True) - email_change_pending = models.EmailField(blank=True, null=True) - css_background = models.TextField( - verbose_name=_("Custom background"), - null=True, - blank=True, - help_text=_('CSS custom background properties: url("image_url"), color, etc'), - max_length=300, - ) - - @cached_property - def _cached_info(self): - return _get_basic_info(self.id) + ace_theme = models.CharField(max_length=30, choices=ACE_THEMES, default='github') + last_access = models.DateTimeField(verbose_name=_('last access time'), default=now) + ip = models.GenericIPAddressField(verbose_name=_('last IP'), blank=True, null=True) + organizations = SortedManyToManyField(Organization, verbose_name=_('organization'), blank=True, + related_name='members', related_query_name='member') + display_rank = models.CharField(max_length=10, default='user', verbose_name=_('display rank'), + choices=(('user', 'Normal User'), ('setter', 'Problem Setter'), ('admin', 'Admin'))) + mute = models.BooleanField(verbose_name=_('comment mute'), help_text=_('Some users are at their best when silent.'), + default=False) + is_unlisted = models.BooleanField(verbose_name=_('unlisted user'), help_text=_('User will not be ranked.'), + default=False) + rating = models.IntegerField(null=True, default=None) + user_script = models.TextField(verbose_name=_('user script'), default='', blank=True, max_length=65536, + help_text=_('User-defined JavaScript for site customization.')) + current_contest = models.OneToOneField('ContestParticipation', verbose_name=_('current contest'), + null=True, blank=True, related_name='+', on_delete=models.SET_NULL) + math_engine = models.CharField(verbose_name=_('math engine'), choices=MATH_ENGINES_CHOICES, max_length=4, + default=settings.MATHOID_DEFAULT_TYPE, + help_text=_('the rendering engine used to render math')) + is_totp_enabled = models.BooleanField(verbose_name=_('2FA enabled'), default=False, + help_text=_('check to enable TOTP-based two factor authentication')) + totp_key = EncryptedNullCharField(max_length=32, null=True, blank=True, verbose_name=_('TOTP key'), + help_text=_('32 character base32-encoded key for TOTP'), + validators=[RegexValidator('^$|^[A-Z2-7]{32}$', + _('TOTP key must be empty or base32'))]) + notes = models.TextField(verbose_name=_('internal notes'), null=True, blank=True, + help_text=_('Notes for administrators regarding this user.')) @cached_property def organization(self): @@ -273,86 +123,38 @@ class Profile(models.Model): @cached_property def username(self): - try: - return self._cached_info["username"] - except KeyError: - _get_basic_info.dirty(self.id) - - @cached_property - def first_name(self): - return self._cached_info.get("first_name", "") - - @cached_property - def last_name(self): - return self._cached_info.get("last_name", "") - - @cached_property - def email(self): - return self._cached_info["email"] - - @cached_property - def is_muted(self): - return self._cached_info["mute"] - - @cached_property - def cached_display_rank(self): - return self._cached_info.get("display_rank") - - @cached_property - def cached_rating(self): - return self._cached_info.get("rating") - - @cached_property - def profile_image_url(self): - return self._cached_info.get("profile_image_url") + return self.user.username @cached_property def count_unseen_notifications(self): - from judge.models.notification import unseen_notifications_count - - return unseen_notifications_count(self) - - @cached_property - def count_unread_chat_boxes(self): - from chat_box.utils import get_unread_boxes - - return get_unread_boxes(self) - + query = { + 'read': False, + } + return self.notifications.filter(**query).count() + _pp_table = [pow(settings.DMOJ_PP_STEP, i) for i in range(settings.DMOJ_PP_ENTRIES)] def calculate_points(self, table=_pp_table): from judge.models import Problem - public_problems = Problem.get_public_problems() data = ( - public_problems.filter( - submission__user=self, submission__points__isnull=False - ) - .annotate(max_points=Max("submission__points")) - .order_by("-max_points") - .values_list("max_points", flat=True) - .filter(max_points__gt=0) + public_problems.filter(submission__user=self, submission__points__isnull=False) + .annotate(max_points=Max('submission__points')).order_by('-max_points') + .values_list('max_points', flat=True).filter(max_points__gt=0) ) extradata = ( - public_problems.filter(submission__user=self, submission__result="AC") - .values("id") - .distinct() - .count() + public_problems.filter(submission__user=self, submission__result='AC').values('id').distinct().count() ) bonus_function = settings.DMOJ_PP_BONUS_FUNCTION points = sum(data) problems = len(data) entries = min(len(data), len(table)) pp = sum(map(mul, table[:entries], data[:entries])) + bonus_function(extradata) - if ( - self.points != points - or problems != self.problem_count - or self.performance_points != pp - ): + if self.points != points or problems != self.problem_count or self.performance_points != pp: self.points = points self.problem_count = problems self.performance_points = pp - self.save(update_fields=["points", "problem_count", "performance_points"]) + self.save(update_fields=['points', 'problem_count', 'performance_points']) return points calculate_points.alters_data = True @@ -365,12 +167,9 @@ class Profile(models.Model): def update_contest(self): from judge.models import ContestParticipation - try: contest = self.current_contest - if contest is not None and ( - contest.ended or not contest.contest.is_accessible_by(self.user) - ): + if contest is not None and (contest.ended or not contest.contest.is_accessible_by(self.user)): self.remove_contest() except ContestParticipation.DoesNotExist: self.remove_contest() @@ -378,145 +177,86 @@ class Profile(models.Model): update_contest.alters_data = True def get_absolute_url(self): - return reverse("user_page", args=(self.user.username,)) + return reverse('user_page', args=(self.user.username,)) def __str__(self): return self.user.username @classmethod - def get_user_css_class( - cls, display_rank, rating, rating_colors=settings.DMOJ_RATING_COLORS - ): + def get_user_css_class(cls, display_rank, rating, rating_colors=settings.DMOJ_RATING_COLORS): if rating_colors: - return "rating %s %s" % ( - rating_class(rating) if rating is not None else "rate-none", - display_rank, - ) + return 'rating %s %s' % (rating_class(rating) if rating is not None else 'rate-none', display_rank) return display_rank @cached_property def css_class(self): - return self.get_user_css_class(self.cached_display_rank, self.cached_rating) + return self.get_user_css_class(self.display_rank, self.rating) - def get_friends(self): # list of ids, including you - friend_obj = self.following_users.prefetch_related("users").first() - friend_ids = ( - [friend.id for friend in friend_obj.users.all()] if friend_obj else [] - ) - friend_ids.append(self.id) + def get_friends(self): #list of usernames, including you + friend_obj = self.following_users.all() + ret = set() - return friend_ids - - def can_edit_organization(self, org): - if not self.user.is_authenticated: - return False - profile_id = self.id - return org.is_admin(self) or self.user.is_superuser - - @classmethod - def prefetch_profile_cache(self, profile_ids): - _get_basic_info.prefetch_multi([(pid,) for pid in profile_ids]) + if (friend_obj): + ret = set(friend.username for friend in friend_obj[0].users.all()) + + ret.add(self.username) + return ret class Meta: - indexes = [ - models.Index(fields=["is_unlisted", "performance_points"]), - ] permissions = ( - ("test_site", "Shows in-progress development stuff"), - ("totp", "Edit TOTP settings"), + ('test_site', 'Shows in-progress development stuff'), + ('totp', 'Edit TOTP settings'), ) - verbose_name = _("user profile") - verbose_name_plural = _("user profiles") - - -class ProfileInfo(models.Model): - profile = models.OneToOneField( - Profile, - verbose_name=_("profile associated"), - on_delete=models.CASCADE, - related_name="info", - ) - tshirt_size = models.CharField( - max_length=5, - choices=TSHIRT_SIZES, - verbose_name=_("t-shirt size"), - null=True, - blank=True, - ) - date_of_birth = models.DateField( - verbose_name=_("date of birth"), - null=True, - blank=True, - ) - address = models.CharField( - max_length=255, - verbose_name=_("address"), - null=True, - blank=True, - ) - - def __str__(self): - return f"{self.profile.user.username}'s Info" + verbose_name = _('user profile') + verbose_name_plural = _('user profiles') class OrganizationRequest(models.Model): - user = models.ForeignKey( - Profile, - verbose_name=_("user"), - related_name="requests", - on_delete=models.CASCADE, - ) - organization = models.ForeignKey( - Organization, - verbose_name=_("organization"), - related_name="requests", - on_delete=models.CASCADE, - ) - time = models.DateTimeField(verbose_name=_("request time"), auto_now_add=True) - state = models.CharField( - max_length=1, - verbose_name=_("state"), - choices=( - ("P", "Pending"), - ("A", "Approved"), - ("R", "Rejected"), - ), - ) - reason = models.TextField(verbose_name=_("reason")) + user = models.ForeignKey(Profile, verbose_name=_('user'), related_name='requests', on_delete=models.CASCADE) + organization = models.ForeignKey(Organization, verbose_name=_('organization'), related_name='requests', + on_delete=models.CASCADE) + time = models.DateTimeField(verbose_name=_('request time'), auto_now_add=True) + state = models.CharField(max_length=1, verbose_name=_('state'), choices=( + ('P', 'Pending'), + ('A', 'Approved'), + ('R', 'Rejected'), + )) + reason = models.TextField(verbose_name=_('reason')) class Meta: - verbose_name = _("organization join request") - verbose_name_plural = _("organization join requests") + verbose_name = _('organization join request') + verbose_name_plural = _('organization join requests') class Friend(models.Model): users = models.ManyToManyField(Profile) - current_user = models.ForeignKey( - Profile, - related_name="following_users", - on_delete=CASCADE, - ) + current_user = models.ForeignKey(Profile, related_name="following_users", on_delete=CASCADE) @classmethod def is_friend(self, current_user, new_friend): try: - return current_user.following_users.filter(users=new_friend).exists() + return current_user.following_users.get().users \ + .filter(user=new_friend.user).exists() except: return False @classmethod def make_friend(self, current_user, new_friend): - friend, created = self.objects.get_or_create(current_user=current_user) + friend, created = self.objects.get_or_create( + current_user = current_user + ) friend.users.add(new_friend) @classmethod def remove_friend(self, current_user, new_friend): - friend, created = self.objects.get_or_create(current_user=current_user) + friend, created = self.objects.get_or_create( + current_user = current_user + ) friend.users.remove(new_friend) @classmethod def toggle_friend(self, current_user, new_friend): - if self.is_friend(current_user, new_friend): + if (self.is_friend(current_user, new_friend)): self.remove_friend(current_user, new_friend) else: self.make_friend(current_user, new_friend) @@ -531,88 +271,3 @@ class Friend(models.Model): def __str__(self): return str(self.current_user) - - -class OrganizationProfile(models.Model): - profile = models.ForeignKey( - Profile, - verbose_name=_("user"), - related_name="last_visit", - on_delete=models.CASCADE, - db_index=True, - ) - organization = models.ForeignKey( - Organization, - verbose_name=_("organization"), - related_name="last_vist", - on_delete=models.CASCADE, - ) - last_visit = models.AutoField( - verbose_name=_("last visit"), - primary_key=True, - ) - - @classmethod - def remove_organization(self, profile, organization): - organization_profile = self.objects.filter( - profile=profile, organization=organization - ) - if organization_profile.exists(): - organization_profile.delete() - - @classmethod - def add_organization(self, profile, organization): - self.remove_organization(profile, organization) - new_row = OrganizationProfile(profile=profile, organization=organization) - new_row.save() - - @classmethod - def get_most_recent_organizations(cls, profile): - queryset = cls.objects.filter(profile=profile).order_by("-last_visit")[:5] - queryset = queryset.select_related("organization").defer("organization__about") - organizations = [op.organization for op in queryset] - - return organizations - - -@receiver([post_save], sender=User) -def on_user_save(sender, instance, **kwargs): - try: - profile = instance.profile - _get_basic_info.dirty(profile.id) - except: - pass - - -@cache_wrapper(prefix="Pgbi3", expected_type=dict) -def _get_basic_info(profile_id): - profile = ( - Profile.objects.select_related("user") - .only( - "id", - "mute", - "profile_image", - "user__username", - "user__email", - "user__first_name", - "user__last_name", - "display_rank", - "rating", - ) - .get(id=profile_id) - ) - user = profile.user - res = { - "email": user.email, - "username": user.username, - "mute": profile.mute, - "first_name": user.first_name or None, - "last_name": user.last_name or None, - "profile_image_url": profile.profile_image.url - if profile.profile_image - else None, - "display_rank": profile.display_rank, - "rating": profile.rating, - } - res = {k: v for k, v in res.items() if v is not None} - return res diff --git a/judge/models/runtime.py b/judge/models/runtime.py index 97db711..16cdd66 100644 --- a/judge/models/runtime.py +++ b/judge/models/runtime.py @@ -11,84 +11,39 @@ from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ from judge.judgeapi import disconnect_judge -from judge.caching import cache_wrapper -__all__ = ["Language", "RuntimeVersion", "Judge"] +__all__ = ['Language', 'RuntimeVersion', 'Judge'] class Language(models.Model): - key = models.CharField( - max_length=6, - verbose_name=_("short identifier"), - help_text=_( - "The identifier for this language; the same as its executor id for judges." - ), - unique=True, - ) - name = models.CharField( - max_length=20, - verbose_name=_("long name"), - help_text=_('Longer name for the language, e.g. "Python 2" or "C++11".'), - ) - short_name = models.CharField( - max_length=10, - verbose_name=_("short name"), - help_text=_( - 'More readable, but short, name to display publicly; e.g. "PY2" or ' - '"C++11". If left blank, it will default to the ' - "short identifier." - ), - null=True, - blank=True, - ) - common_name = models.CharField( - max_length=10, - verbose_name=_("common name"), - help_text=_( - "Common name for the language. For example, the common name for C++03, " - 'C++11, and C++14 would be "C++"' - ), - ) - ace = models.CharField( - max_length=20, - verbose_name=_("ace mode name"), - help_text=_( - 'Language ID for Ace.js editor highlighting, appended to "mode-" to determine ' - 'the Ace JavaScript file to use, e.g., "python".' - ), - ) - pygments = models.CharField( - max_length=20, - verbose_name=_("pygments name"), - help_text=_("Language ID for Pygments highlighting in source windows."), - ) - template = models.TextField( - verbose_name=_("code template"), - help_text=_("Code template to display in submission editor."), - blank=True, - ) - info = models.CharField( - max_length=50, - verbose_name=_("runtime info override"), - blank=True, - help_text=_( - "Do not set this unless you know what you're doing! It will override the " - "usually more specific, judge-provided runtime info!" - ), - ) - description = models.TextField( - verbose_name=_("language description"), - help_text=_( - "Use this field to inform users of quirks with your environment, " - "additional restrictions, etc." - ), - blank=True, - ) - extension = models.CharField( - max_length=10, - verbose_name=_("extension"), - help_text=_('The extension of source files, e.g., "py" or "cpp".'), - ) + key = models.CharField(max_length=6, verbose_name=_('short identifier'), + help_text=_('The identifier for this language; the same as its executor id for judges.'), + unique=True) + name = models.CharField(max_length=20, verbose_name=_('long name'), + help_text=_('Longer name for the language, e.g. "Python 2" or "C++11".')) + short_name = models.CharField(max_length=10, verbose_name=_('short name'), + help_text=_('More readable, but short, name to display publicly; e.g. "PY2" or ' + '"C++11". If left blank, it will default to the ' + 'short identifier.'), + null=True, blank=True) + common_name = models.CharField(max_length=10, verbose_name=_('common name'), + help_text=_('Common name for the language. For example, the common name for C++03, ' + 'C++11, and C++14 would be "C++"')) + ace = models.CharField(max_length=20, verbose_name=_('ace mode name'), + help_text=_('Language ID for Ace.js editor highlighting, appended to "mode-" to determine ' + 'the Ace JavaScript file to use, e.g., "python".')) + pygments = models.CharField(max_length=20, verbose_name=_('pygments name'), + help_text=_('Language ID for Pygments highlighting in source windows.')) + template = models.TextField(verbose_name=_('code template'), + help_text=_('Code template to display in submission editor.'), blank=True) + info = models.CharField(max_length=50, verbose_name=_('runtime info override'), blank=True, + help_text=_("Do not set this unless you know what you're doing! It will override the " + "usually more specific, judge-provided runtime info!")) + description = models.TextField(verbose_name=_('language description'), + help_text=_('Use this field to inform users of quirks with your environment, ' + 'additional restrictions, etc.'), blank=True) + extension = models.CharField(max_length=10, verbose_name=_('extension'), + help_text=_('The extension of source files, e.g., "py" or "cpp".')) def runtime_versions(self): runtimes = OrderedDict() @@ -97,29 +52,25 @@ class Language(models.Model): id = runtime.name if id not in runtimes: runtimes[id] = set() - if ( - not runtime.version - ): # empty str == error determining version on judge side + if not runtime.version: # empty str == error determining version on judge side continue runtimes[id].add(runtime.version) lang_versions = [] for id, version_list in runtimes.items(): - lang_versions.append( - (id, sorted(version_list, key=lambda a: tuple(map(int, a.split("."))))) - ) + lang_versions.append((id, sorted(version_list, key=lambda a: tuple(map(int, a.split('.')))))) return lang_versions @classmethod def get_common_name_map(cls): - result = cache.get("lang:cn_map") + result = cache.get('lang:cn_map') if result is not None: return result result = defaultdict(set) - for id, cn in Language.objects.values_list("id", "common_name"): + for id, cn in Language.objects.values_list('id', 'common_name'): result[cn].add(id) result = {id: cns for id, cns in result.items() if len(cns) > 1} - cache.set("lang:cn_map", result, 86400) + cache.set('lang:cn_map', result, 86400) return result @cached_property @@ -132,98 +83,60 @@ class Language(models.Model): @cached_property def display_name(self): if self.info: - return "%s (%s)" % (self.name, self.info) + return '%s (%s)' % (self.name, self.info) else: return self.name @classmethod def get_python3(cls): # We really need a default language, and this app is in Python 3 - return Language.objects.get_or_create(key="PY3", defaults={"name": "Python 3"})[ - 0 - ] + return Language.objects.get_or_create(key='PY3', defaults={'name': 'Python 3'})[0] def get_absolute_url(self): - return reverse("runtime_list") + "#" + self.key + return reverse('runtime_list') + '#' + self.key @classmethod def get_default_language(cls): - return _get_default_language() + try: + return Language.objects.get(key=settings.DEFAULT_USER_LANGUAGE) + except Language.DoesNotExist: + return cls.get_python3() @classmethod def get_default_language_pk(cls): - return _get_default_language().pk + return cls.get_default_language().pk class Meta: - ordering = ["key"] - verbose_name = _("language") - verbose_name_plural = _("languages") - - -@cache_wrapper(prefix="gdl") -def _get_default_language(): - try: - return Language.objects.get(key=settings.DEFAULT_USER_LANGUAGE) - except Language.DoesNotExist: - return cls.get_python3() + ordering = ['key'] + verbose_name = _('language') + verbose_name_plural = _('languages') class RuntimeVersion(models.Model): - language = models.ForeignKey( - Language, - verbose_name=_("language to which this runtime belongs"), - on_delete=CASCADE, - ) - judge = models.ForeignKey( - "Judge", verbose_name=_("judge on which this runtime exists"), on_delete=CASCADE - ) - name = models.CharField(max_length=64, verbose_name=_("runtime name")) - version = models.CharField( - max_length=64, verbose_name=_("runtime version"), blank=True - ) - priority = models.IntegerField( - verbose_name=_("order in which to display this runtime"), default=0 - ) + language = models.ForeignKey(Language, verbose_name=_('language to which this runtime belongs'), on_delete=CASCADE) + judge = models.ForeignKey('Judge', verbose_name=_('judge on which this runtime exists'), on_delete=CASCADE) + name = models.CharField(max_length=64, verbose_name=_('runtime name')) + version = models.CharField(max_length=64, verbose_name=_('runtime version'), blank=True) + priority = models.IntegerField(verbose_name=_('order in which to display this runtime'), default=0) class Judge(models.Model): - name = models.CharField( - max_length=50, help_text=_("Server name, hostname-style"), unique=True - ) - created = models.DateTimeField( - auto_now_add=True, verbose_name=_("time of creation") - ) - auth_key = models.CharField( - max_length=100, - help_text=_("A key to authenticate this judge"), - verbose_name=_("authentication key"), - ) - is_blocked = models.BooleanField( - verbose_name=_("block judge"), - default=False, - help_text=_( - "Whether this judge should be blocked from connecting, " - "even if its key is correct." - ), - ) - online = models.BooleanField(verbose_name=_("judge online status"), default=False) - start_time = models.DateTimeField(verbose_name=_("judge start time"), null=True) - ping = models.FloatField(verbose_name=_("response time"), null=True) - load = models.FloatField( - verbose_name=_("system load"), - null=True, - help_text=_("Load for the last minute, divided by processors to be fair."), - ) - description = models.TextField(blank=True, verbose_name=_("description")) - last_ip = models.GenericIPAddressField( - verbose_name="Last connected IP", blank=True, null=True - ) - problems = models.ManyToManyField( - "Problem", verbose_name=_("problems"), related_name="judges" - ) - runtimes = models.ManyToManyField( - Language, verbose_name=_("judges"), related_name="judges" - ) + name = models.CharField(max_length=50, help_text=_('Server name, hostname-style'), unique=True) + created = models.DateTimeField(auto_now_add=True, verbose_name=_('time of creation')) + auth_key = models.CharField(max_length=100, help_text=_('A key to authenticate this judge'), + verbose_name=_('authentication key')) + is_blocked = models.BooleanField(verbose_name=_('block judge'), default=False, + help_text=_('Whether this judge should be blocked from connecting, ' + 'even if its key is correct.')) + online = models.BooleanField(verbose_name=_('judge online status'), default=False) + start_time = models.DateTimeField(verbose_name=_('judge start time'), null=True) + ping = models.FloatField(verbose_name=_('response time'), null=True) + load = models.FloatField(verbose_name=_('system load'), null=True, + help_text=_('Load for the last minute, divided by processors to be fair.')) + description = models.TextField(blank=True, verbose_name=_('description')) + last_ip = models.GenericIPAddressField(verbose_name='Last connected IP', blank=True, null=True) + problems = models.ManyToManyField('Problem', verbose_name=_('problems'), related_name='judges') + runtimes = models.ManyToManyField(Language, verbose_name=_('judges'), related_name='judges') def __str__(self): return self.name @@ -235,23 +148,22 @@ class Judge(models.Model): @cached_property def runtime_versions(self): - qs = self.runtimeversion_set.values( - "language__key", "language__name", "version", "name" - ).order_by("language__key", "priority") + qs = (self.runtimeversion_set.values('language__key', 'language__name', 'version', 'name') + .order_by('language__key', 'priority')) ret = OrderedDict() for data in qs: - key = data["language__key"] + key = data['language__key'] if key not in ret: - ret[key] = {"name": data["language__name"], "runtime": []} - ret[key]["runtime"].append((data["name"], (data["version"],))) + ret[key] = {'name': data['language__name'], 'runtime': []} + ret[key]['runtime'].append((data['name'], (data['version'],))) return list(ret.items()) @cached_property def uptime(self): - return timezone.now() - self.start_time if self.online else "N/A" + return timezone.now() - self.start_time if self.online else 'N/A' @cached_property def ping_ms(self): @@ -259,9 +171,9 @@ class Judge(models.Model): @cached_property def runtime_list(self): - return map(attrgetter("name"), self.runtimes.all()) + return map(attrgetter('name'), self.runtimes.all()) class Meta: - ordering = ["name"] - verbose_name = _("judge") - verbose_name_plural = _("judges") + ordering = ['name'] + verbose_name = _('judge') + verbose_name_plural = _('judges') diff --git a/judge/models/submission.py b/judge/models/submission.py index 814344d..e6c0208 100644 --- a/judge/models/submission.py +++ b/judge/models/submission.py @@ -9,133 +9,97 @@ from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ from judge.judgeapi import abort_submission, judge_submission -from judge.models.problem import Problem +from judge.models.problem import Problem, TranslatedProblemForeignKeyQuerySet from judge.models.profile import Profile from judge.models.runtime import Language from judge.utils.unicode import utf8bytes -__all__ = ["SUBMISSION_RESULT", "Submission", "SubmissionSource", "SubmissionTestCase"] +__all__ = ['SUBMISSION_RESULT', 'Submission', 'SubmissionSource', 'SubmissionTestCase'] SUBMISSION_RESULT = ( - ("AC", _("Accepted")), - ("WA", _("Wrong Answer")), - ("TLE", _("Time Limit Exceeded")), - ("MLE", _("Memory Limit Exceeded")), - ("OLE", _("Output Limit Exceeded")), - ("IR", _("Invalid Return")), - ("RTE", _("Runtime Error")), - ("CE", _("Compile Error")), - ("IE", _("Internal Error")), - ("SC", _("Short circuit")), - ("AB", _("Aborted")), + ('AC', _('Accepted')), + ('WA', _('Wrong Answer')), + ('TLE', _('Time Limit Exceeded')), + ('MLE', _('Memory Limit Exceeded')), + ('OLE', _('Output Limit Exceeded')), + ('IR', _('Invalid Return')), + ('RTE', _('Runtime Error')), + ('CE', _('Compile Error')), + ('IE', _('Internal Error')), + ('SC', _('Short circuit')), + ('AB', _('Aborted')), ) class Submission(models.Model): STATUS = ( - ("QU", _("Queued")), - ("P", _("Processing")), - ("G", _("Grading")), - ("D", _("Completed")), - ("IE", _("Internal Error")), - ("CE", _("Compile Error")), - ("AB", _("Aborted")), + ('QU', _('Queued')), + ('P', _('Processing')), + ('G', _('Grading')), + ('D', _('Completed')), + ('IE', _('Internal Error')), + ('CE', _('Compile Error')), + ('AB', _('Aborted')), ) - IN_PROGRESS_GRADING_STATUS = ("QU", "P", "G") + IN_PROGRESS_GRADING_STATUS = ('QU', 'P', 'G') RESULT = SUBMISSION_RESULT USER_DISPLAY_CODES = { - "AC": _("Accepted"), - "WA": _("Wrong Answer"), - "SC": "Short Circuited", - "TLE": _("Time Limit Exceeded"), - "MLE": _("Memory Limit Exceeded"), - "OLE": _("Output Limit Exceeded"), - "IR": _("Invalid Return"), - "RTE": _("Runtime Error"), - "CE": _("Compile Error"), - "IE": _("Internal Error (judging server error)"), - "QU": _("Queued"), - "P": _("Processing"), - "G": _("Grading"), - "D": _("Completed"), - "AB": _("Aborted"), + 'AC': _('Accepted'), + 'WA': _('Wrong Answer'), + 'SC': "Short Circuited", + 'TLE': _('Time Limit Exceeded'), + 'MLE': _('Memory Limit Exceeded'), + 'OLE': _('Output Limit Exceeded'), + 'IR': _('Invalid Return'), + 'RTE': _('Runtime Error'), + 'CE': _('Compile Error'), + 'IE': _('Internal Error (judging server error)'), + 'QU': _('Queued'), + 'P': _('Processing'), + 'G': _('Grading'), + 'D': _('Completed'), + 'AB': _('Aborted'), } user = models.ForeignKey(Profile, on_delete=models.CASCADE) problem = models.ForeignKey(Problem, on_delete=models.CASCADE) - date = models.DateTimeField( - verbose_name=_("submission time"), auto_now_add=True, db_index=True - ) - time = models.FloatField(verbose_name=_("execution time"), null=True, db_index=True) - memory = models.FloatField(verbose_name=_("memory usage"), null=True) - points = models.FloatField( - verbose_name=_("points granted"), null=True, db_index=True - ) - language = models.ForeignKey( - Language, verbose_name=_("submission language"), on_delete=models.CASCADE - ) - status = models.CharField( - verbose_name=_("status"), - max_length=2, - choices=STATUS, - default="QU", - db_index=True, - ) - result = models.CharField( - verbose_name=_("result"), - max_length=3, - choices=SUBMISSION_RESULT, - default=None, - null=True, - blank=True, - db_index=True, - ) - error = models.TextField(verbose_name=_("compile errors"), null=True, blank=True) + date = models.DateTimeField(verbose_name=_('submission time'), auto_now_add=True, db_index=True) + time = models.FloatField(verbose_name=_('execution time'), null=True, db_index=True) + memory = models.FloatField(verbose_name=_('memory usage'), null=True) + points = models.FloatField(verbose_name=_('points granted'), null=True, db_index=True) + language = models.ForeignKey(Language, verbose_name=_('submission language'), on_delete=models.CASCADE) + status = models.CharField(verbose_name=_('status'), max_length=2, choices=STATUS, default='QU', db_index=True) + result = models.CharField(verbose_name=_('result'), max_length=3, choices=SUBMISSION_RESULT, + default=None, null=True, blank=True, db_index=True) + error = models.TextField(verbose_name=_('compile errors'), null=True, blank=True) current_testcase = models.IntegerField(default=0) - batch = models.BooleanField(verbose_name=_("batched cases"), default=False) - case_points = models.FloatField(verbose_name=_("test case points"), default=0) - case_total = models.FloatField(verbose_name=_("test case total points"), default=0) - judged_on = models.ForeignKey( - "Judge", - verbose_name=_("judged on"), - null=True, - blank=True, - on_delete=models.SET_NULL, - ) - judged_date = models.DateTimeField( - verbose_name=_("submission judge time"), default=None, null=True - ) - was_rejudged = models.BooleanField( - verbose_name=_("was rejudged by admin"), default=False - ) - is_pretested = models.BooleanField( - verbose_name=_("was ran on pretests only"), default=False - ) - contest_object = models.ForeignKey( - "Contest", - verbose_name=_("contest"), - null=True, - blank=True, - on_delete=models.SET_NULL, - related_name="+", - ) + batch = models.BooleanField(verbose_name=_('batched cases'), default=False) + case_points = models.FloatField(verbose_name=_('test case points'), default=0) + case_total = models.FloatField(verbose_name=_('test case total points'), default=0) + judged_on = models.ForeignKey('Judge', verbose_name=_('judged on'), null=True, blank=True, + on_delete=models.SET_NULL) + judged_date = models.DateTimeField(verbose_name=_('submission judge time'), default=None, null=True) + was_rejudged = models.BooleanField(verbose_name=_('was rejudged by admin'), default=False) + is_pretested = models.BooleanField(verbose_name=_('was ran on pretests only'), default=False) + contest_object = models.ForeignKey('Contest', verbose_name=_('contest'), null=True, blank=True, + on_delete=models.SET_NULL, related_name='+') + + objects = TranslatedProblemForeignKeyQuerySet.as_manager() @classmethod def result_class_from_code(cls, result, case_points, case_total): - if result == "AC": + if result == 'AC': if case_points == case_total: - return "AC" - return "_AC" + return 'AC' + return '_AC' return result @property def result_class(self): # This exists to save all these conditionals from being executed (slowly) in each row.jade template - if self.status in ("IE", "CE"): + if self.status in ('IE', 'CE'): return self.status - return Submission.result_class_from_code( - self.result, self.case_points, self.case_total - ) + return Submission.result_class_from_code(self.result, self.case_points, self.case_total) @property def memory_bytes(self): @@ -147,11 +111,12 @@ class Submission(models.Model): @property def long_status(self): - return Submission.USER_DISPLAY_CODES.get(self.short_status, "") + return Submission.USER_DISPLAY_CODES.get(self.short_status, '') def judge(self, *args, **kwargs): judge_submission(self, *args, **kwargs) + judge.alters_data = True def abort(self): @@ -166,12 +131,8 @@ class Submission(models.Model): return contest_problem = contest.problem - contest.points = round( - self.case_points / self.case_total * contest_problem.points - if self.case_total > 0 - else 0, - 3, - ) + contest.points = round(self.case_points / self.case_total * contest_problem.points + if self.case_total > 0 else 0, 3) if not contest_problem.partial and contest.points != contest_problem.points: contest.points = 0 contest.save() @@ -181,22 +142,18 @@ class Submission(models.Model): @property def is_graded(self): - return self.status not in ("QU", "P", "G") + return self.status not in ('QU', 'P', 'G') @cached_property def contest_key(self): - if hasattr(self, "contest"): + if hasattr(self, 'contest'): return self.contest_object.key def __str__(self): - return "Submission %d of %s by %s" % ( - self.id, - self.problem, - self.user.user.username, - ) + return 'Submission %d of %s by %s' % (self.id, self.problem, self.user.user.username) def get_absolute_url(self): - return reverse("submission_status", args=(self.id,)) + return reverse('submission_status', args=(self.id,)) @cached_property def contest_or_none(self): @@ -207,124 +164,56 @@ class Submission(models.Model): @classmethod def get_id_secret(cls, sub_id): - return ( - hmac.new( - utf8bytes(settings.EVENT_DAEMON_SUBMISSION_KEY), - b"%d" % sub_id, - hashlib.sha512, - ).hexdigest()[:16] - + "%08x" % sub_id - ) + return (hmac.new(utf8bytes(settings.EVENT_DAEMON_SUBMISSION_KEY), b'%d' % sub_id, hashlib.sha512) + .hexdigest()[:16] + '%08x' % sub_id) @cached_property def id_secret(self): return self.get_id_secret(self.id) - def is_accessible_by(self, profile, check_contest=True): - if not profile: - return False - - problem_id = self.problem_id - user = profile.user - - if profile.id == self.user_id: - return True - - if user.has_perm("judge.change_submission"): - return True - - if user.has_perm("judge.view_all_submission"): - return True - - if self.problem.is_public and user.has_perm("judge.view_public_submission"): - return True - - if check_contest: - contest = self.contest_object - if contest and contest.is_editable_by(user): - return True - - from judge.utils.problems import ( - user_completed_ids, - user_tester_ids, - user_editable_ids, - ) - - if problem_id in user_editable_ids(profile): - return True - - if self.problem_id in user_completed_ids(profile): - if self.problem.is_public: - return True - if problem_id in user_tester_ids(profile): - return True - - return False - class Meta: permissions = ( - ("abort_any_submission", "Abort any submission"), - ("rejudge_submission", "Rejudge the submission"), - ("rejudge_submission_lot", "Rejudge a lot of submissions"), - ("spam_submission", "Submit without limit"), - ("view_all_submission", "View all submission"), - ("resubmit_other", "Resubmit others' submission"), - ("view_public_submission", "View public submissions"), + ('abort_any_submission', 'Abort any submission'), + ('rejudge_submission', 'Rejudge the submission'), + ('rejudge_submission_lot', 'Rejudge a lot of submissions'), + ('spam_submission', 'Submit without limit'), + ('view_all_submission', 'View all submission'), + ('resubmit_other', "Resubmit others' submission"), ) - verbose_name = _("submission") - verbose_name_plural = _("submissions") - - indexes = [ - models.Index(fields=["problem", "user", "-points"]), - models.Index(fields=["contest_object", "problem", "user", "-points"]), - models.Index(fields=["language", "result"]), - ] + verbose_name = _('submission') + verbose_name_plural = _('submissions') class SubmissionSource(models.Model): - submission = models.OneToOneField( - Submission, - on_delete=models.CASCADE, - verbose_name=_("associated submission"), - related_name="source", - ) - source = models.TextField(verbose_name=_("source code"), max_length=65536) + submission = models.OneToOneField(Submission, on_delete=models.CASCADE, verbose_name=_('associated submission'), + related_name='source') + source = models.TextField(verbose_name=_('source code'), max_length=65536) def __str__(self): - return "Source of %s" % self.submission + return 'Source of %s' % self.submission class SubmissionTestCase(models.Model): RESULT = SUBMISSION_RESULT - submission = models.ForeignKey( - Submission, - verbose_name=_("associated submission"), - related_name="test_cases", - on_delete=models.CASCADE, - ) - case = models.IntegerField(verbose_name=_("test case ID")) - status = models.CharField( - max_length=3, verbose_name=_("status flag"), choices=SUBMISSION_RESULT - ) - time = models.FloatField(verbose_name=_("execution time"), null=True) - memory = models.FloatField(verbose_name=_("memory usage"), null=True) - points = models.FloatField(verbose_name=_("points granted"), null=True) - total = models.FloatField(verbose_name=_("points possible"), null=True) - batch = models.IntegerField(verbose_name=_("batch number"), null=True) - feedback = models.CharField( - max_length=50, verbose_name=_("judging feedback"), blank=True - ) - extended_feedback = models.TextField( - verbose_name=_("extended judging feedback"), blank=True - ) - output = models.TextField(verbose_name=_("program output"), blank=True) + submission = models.ForeignKey(Submission, verbose_name=_('associated submission'), + related_name='test_cases', on_delete=models.CASCADE) + case = models.IntegerField(verbose_name=_('test case ID')) + status = models.CharField(max_length=3, verbose_name=_('status flag'), choices=SUBMISSION_RESULT) + time = models.FloatField(verbose_name=_('execution time'), null=True) + memory = models.FloatField(verbose_name=_('memory usage'), null=True) + points = models.FloatField(verbose_name=_('points granted'), null=True) + total = models.FloatField(verbose_name=_('points possible'), null=True) + batch = models.IntegerField(verbose_name=_('batch number'), null=True) + feedback = models.CharField(max_length=50, verbose_name=_('judging feedback'), blank=True) + extended_feedback = models.TextField(verbose_name=_('extended judging feedback'), blank=True) + output = models.TextField(verbose_name=_('program output'), blank=True) @property def long_status(self): - return Submission.USER_DISPLAY_CODES.get(self.status, "") + return Submission.USER_DISPLAY_CODES.get(self.status, '') class Meta: - unique_together = ("submission", "case") - verbose_name = _("submission test case") - verbose_name_plural = _("submission test cases") + unique_together = ('submission', 'case') + verbose_name = _('submission test case') + verbose_name_plural = _('submission test cases') diff --git a/judge/models/test_formatter.py b/judge/models/test_formatter.py deleted file mode 100644 index b613b14..0000000 --- a/judge/models/test_formatter.py +++ /dev/null @@ -1,26 +0,0 @@ -import os -from django.db import models -from dmoj import settings -from django.utils.translation import gettext_lazy as _ - -__all__ = [ - "TestFormatterModel", -] - - -def test_formatter_path(test_formatter, filename): - tail = filename.split(".")[-1] - head = filename.split(".")[0] - if str(tail).lower() != "zip": - raise Exception("400: Only ZIP files are supported") - new_filename = f"{head}.{tail}" - return os.path.join(settings.DMOJ_TEST_FORMATTER_ROOT, new_filename) - - -class TestFormatterModel(models.Model): - file = models.FileField( - verbose_name=_("testcase file"), - null=True, - blank=True, - upload_to=test_formatter_path, - ) diff --git a/judge/models/ticket.py b/judge/models/ticket.py index 27d6fb4..9b3fc54 100644 --- a/judge/models/ticket.py +++ b/judge/models/ticket.py @@ -7,43 +7,24 @@ from judge.models.profile import Profile class Ticket(models.Model): - title = models.CharField(max_length=100, verbose_name=_("ticket title")) - user = models.ForeignKey( - Profile, - verbose_name=_("ticket creator"), - related_name="tickets", - on_delete=models.CASCADE, - ) - time = models.DateTimeField(verbose_name=_("creation time"), auto_now_add=True) - assignees = models.ManyToManyField( - Profile, verbose_name=_("assignees"), related_name="assigned_tickets" - ) - notes = models.TextField( - verbose_name=_("quick notes"), - blank=True, - help_text=_("Staff notes for this issue to aid in processing."), - ) - content_type = models.ForeignKey( - ContentType, verbose_name=_("linked item type"), on_delete=models.CASCADE - ) - object_id = models.PositiveIntegerField(verbose_name=_("linked item ID")) + title = models.CharField(max_length=100, verbose_name=_('ticket title')) + user = models.ForeignKey(Profile, verbose_name=_('ticket creator'), related_name='tickets', + on_delete=models.CASCADE) + time = models.DateTimeField(verbose_name=_('creation time'), auto_now_add=True) + assignees = models.ManyToManyField(Profile, verbose_name=_('assignees'), related_name='assigned_tickets') + notes = models.TextField(verbose_name=_('quick notes'), blank=True, + help_text=_('Staff notes for this issue to aid in processing.')) + content_type = models.ForeignKey(ContentType, verbose_name=_('linked item type'), + on_delete=models.CASCADE) + object_id = models.PositiveIntegerField(verbose_name=_('linked item ID')) linked_item = GenericForeignKey() - is_open = models.BooleanField(verbose_name=_("is ticket open?"), default=True) + is_open = models.BooleanField(verbose_name=_('is ticket open?'), default=True) class TicketMessage(models.Model): - ticket = models.ForeignKey( - Ticket, - verbose_name=_("ticket"), - related_name="messages", - related_query_name="message", - on_delete=models.CASCADE, - ) - user = models.ForeignKey( - Profile, - verbose_name=_("poster"), - related_name="ticket_messages", - on_delete=models.CASCADE, - ) - body = models.TextField(verbose_name=_("message body")) - time = models.DateTimeField(verbose_name=_("message time"), auto_now_add=True) + ticket = models.ForeignKey(Ticket, verbose_name=_('ticket'), related_name='messages', + related_query_name='message', on_delete=models.CASCADE) + user = models.ForeignKey(Profile, verbose_name=_('poster'), related_name='ticket_messages', + on_delete=models.CASCADE) + body = models.TextField(verbose_name=_('message body')) + time = models.DateTimeField(verbose_name=_('message time'), auto_now_add=True) diff --git a/judge/models/volunteer.py b/judge/models/volunteer.py deleted file mode 100644 index 5d3babd..0000000 --- a/judge/models/volunteer.py +++ /dev/null @@ -1,39 +0,0 @@ -from django.db import models -from django.db.models import CASCADE -from django.utils.translation import gettext_lazy as _ - -from judge.models import Profile, Problem, ProblemType - -__all__ = ["VolunteerProblemVote"] - - -class VolunteerProblemVote(models.Model): - voter = models.ForeignKey( - Profile, related_name="volunteer_problem_votes", on_delete=CASCADE - ) - problem = models.ForeignKey( - Problem, related_name="volunteer_user_votes", on_delete=CASCADE - ) - time = models.DateTimeField(auto_now_add=True) - knowledge_points = models.PositiveIntegerField( - verbose_name=_("knowledge points"), - help_text=_("Points awarded by knowledge difficulty"), - ) - thinking_points = models.PositiveIntegerField( - verbose_name=_("thinking points"), - help_text=_("Points awarded by thinking difficulty"), - ) - types = models.ManyToManyField( - ProblemType, - verbose_name=_("problem types"), - help_text=_("The type of problem, " "as shown on the problem's page."), - ) - feedback = models.TextField(verbose_name=_("feedback"), blank=True) - - class Meta: - verbose_name = _("volunteer vote") - verbose_name_plural = _("volunteer votes") - unique_together = ["voter", "problem"] - - def __str__(self): - return f"{self.voter} for {self.problem.code}" diff --git a/judge/pdf_problems.py b/judge/pdf_problems.py index 4cd4a14..a5b318f 100644 --- a/judge/pdf_problems.py +++ b/judge/pdf_problems.py @@ -11,7 +11,7 @@ import uuid from django.conf import settings from django.utils.translation import gettext -logger = logging.getLogger("judge.problem.pdf") +logger = logging.getLogger('judge.problem.pdf') HAS_SELENIUM = False if settings.USE_SELENIUM: @@ -21,10 +21,9 @@ if settings.USE_SELENIUM: from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait - HAS_SELENIUM = True except ImportError: - logger.warning("Failed to import Selenium", exc_info=True) + logger.warning('Failed to import Selenium', exc_info=True) HAS_PHANTOMJS = os.access(settings.PHANTOMJS, os.X_OK) HAS_SLIMERJS = os.access(settings.SLIMERJS, os.X_OK) @@ -33,30 +32,27 @@ NODE_PATH = settings.NODEJS PUPPETEER_MODULE = settings.PUPPETEER_MODULE HAS_PUPPETEER = os.access(NODE_PATH, os.X_OK) and os.path.isdir(PUPPETEER_MODULE) -HAS_PDF = os.path.isdir(settings.DMOJ_PDF_PROBLEM_CACHE) and ( - HAS_PHANTOMJS or HAS_SLIMERJS or HAS_PUPPETEER or HAS_SELENIUM -) +HAS_PDF = (os.path.isdir(settings.DMOJ_PDF_PROBLEM_CACHE) and + (HAS_PHANTOMJS or HAS_SLIMERJS or HAS_PUPPETEER or HAS_SELENIUM)) EXIFTOOL = settings.EXIFTOOL HAS_EXIFTOOL = os.access(EXIFTOOL, os.X_OK) class BasePdfMaker(object): - math_engine = "jax" + math_engine = 'jax' title = None def __init__(self, dir=None, clean_up=True): - self.dir = dir or os.path.join( - settings.DMOJ_PDF_PROBLEM_TEMP_DIR, str(uuid.uuid1()) - ) + self.dir = dir or os.path.join(settings.DMOJ_PDF_PROBLEM_TEMP_DIR, str(uuid.uuid1())) self.proc = None self.log = None - self.htmlfile = os.path.join(self.dir, "input.html") - self.pdffile = os.path.join(self.dir, "output.pdf") + self.htmlfile = os.path.join(self.dir, 'input.html') + self.pdffile = os.path.join(self.dir, 'output.pdf') self.clean_up = clean_up def load(self, file, source): - with open(os.path.join(self.dir, file), "w") as target, open(source) as source: + with open(os.path.join(self.dir, file), 'w') as target, open(source) as source: target.write(source.read()) def make(self, debug=False): @@ -64,27 +60,21 @@ class BasePdfMaker(object): if self.title and HAS_EXIFTOOL: try: - subprocess.check_output( - [EXIFTOOL, "-Title=%s" % (self.title,), self.pdffile] - ) + subprocess.check_output([EXIFTOOL, '-Title=%s' % (self.title,), self.pdffile]) except subprocess.CalledProcessError as e: - logger.error( - "Failed to run exiftool to set title for: %s\n%s", - self.title, - e.output, - ) + logger.error('Failed to run exiftool to set title for: %s\n%s', self.title, e.output) def _make(self, debug): raise NotImplementedError() @property def html(self): - with io.open(self.htmlfile, encoding="utf-8") as f: + with io.open(self.htmlfile, encoding='utf-8') as f: return f.read() @html.setter def html(self, data): - with io.open(self.htmlfile, "w", encoding="utf-8") as f: + with io.open(self.htmlfile, 'w', encoding='utf-8') as f: f.write(data) @property @@ -109,7 +99,7 @@ class BasePdfMaker(object): class PhantomJSPdfMaker(BasePdfMaker): - template = """\ + template = '''\ "use strict"; var page = require('webpage').create(); var param = {params}; @@ -146,37 +136,29 @@ page.open(param.input, function (status) { }, param.timeout); } }); -""" +''' def get_render_script(self): - return self.template.replace( - "{params}", - json.dumps( - { - "zoom": settings.PHANTOMJS_PDF_ZOOM, - "timeout": int(settings.PHANTOMJS_PDF_TIMEOUT * 1000), - "input": "input.html", - "output": "output.pdf", - "paper": settings.PHANTOMJS_PAPER_SIZE, - "footer": gettext("Page [page] of [topage]"), - } - ), - ) + return self.template.replace('{params}', json.dumps({ + 'zoom': settings.PHANTOMJS_PDF_ZOOM, + 'timeout': int(settings.PHANTOMJS_PDF_TIMEOUT * 1000), + 'input': 'input.html', 'output': 'output.pdf', + 'paper': settings.PHANTOMJS_PAPER_SIZE, + 'footer': gettext('Page [page] of [topage]'), + })) def _make(self, debug): - with io.open(os.path.join(self.dir, "_render.js"), "w", encoding="utf-8") as f: + with io.open(os.path.join(self.dir, '_render.js'), 'w', encoding='utf-8') as f: f.write(self.get_render_script()) - cmdline = [settings.PHANTOMJS, "_render.js"] - self.proc = subprocess.Popen( - cmdline, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.dir - ) + cmdline = [settings.PHANTOMJS, '_render.js'] + self.proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.dir) self.log = self.proc.communicate()[0] class SlimerJSPdfMaker(BasePdfMaker): - math_engine = "mml" + math_engine = 'mml' - template = """\ + template = '''\ "use strict"; try { var param = {params}; @@ -207,47 +189,33 @@ try { console.error(e); slimer.exit(1); } -""" +''' def get_render_script(self): - return self.template.replace( - "{params}", - json.dumps( - { - "zoom": settings.SLIMERJS_PDF_ZOOM, - "input": "input.html", - "output": "output.pdf", - "paper": settings.SLIMERJS_PAPER_SIZE, - "footer": gettext("Page [page] of [topage]") - .replace("[page]", "&P") - .replace("[topage]", "&L"), - } - ), - ) + return self.template.replace('{params}', json.dumps({ + 'zoom': settings.SLIMERJS_PDF_ZOOM, + 'input': 'input.html', 'output': 'output.pdf', + 'paper': settings.SLIMERJS_PAPER_SIZE, + 'footer': gettext('Page [page] of [topage]').replace('[page]', '&P').replace('[topage]', '&L'), + })) def _make(self, debug): - with io.open(os.path.join(self.dir, "_render.js"), "w", encoding="utf-8") as f: + with io.open(os.path.join(self.dir, '_render.js'), 'w', encoding='utf-8') as f: f.write(self.get_render_script()) env = None firefox = settings.SLIMERJS_FIREFOX_PATH if firefox: env = os.environ.copy() - env["SLIMERJSLAUNCHER"] = firefox + env['SLIMERJSLAUNCHER'] = firefox - cmdline = [settings.SLIMERJS, "--headless", "_render.js"] - self.proc = subprocess.Popen( - cmdline, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - cwd=self.dir, - env=env, - ) + cmdline = [settings.SLIMERJS, '--headless', '_render.js'] + self.proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.dir, env=env) self.log = self.proc.communicate()[0] class PuppeteerPDFRender(BasePdfMaker): - template = """\ + template = '''\ "use strict"; const param = {params}; const puppeteer = require('puppeteer'); @@ -281,81 +249,67 @@ puppeteer.launch().then(browser => Promise.resolve() console.error(e); process.exit(1); }); -""" +''' def get_render_script(self): - return self.template.replace( - "{params}", - json.dumps( - { - "input": "file://%s" % self.htmlfile, - "output": self.pdffile, - "paper": settings.PUPPETEER_PAPER_SIZE, - "footer": gettext("Page [page] of [topage]"), - } - ), - ) + return self.template.replace('{params}', json.dumps({ + 'input': 'file://%s' % self.htmlfile, + 'output': self.pdffile, + 'paper': settings.PUPPETEER_PAPER_SIZE, + 'footer': gettext('Page [page] of [topage]'), + })) def _make(self, debug): - with io.open(os.path.join(self.dir, "_render.js"), "w", encoding="utf-8") as f: + with io.open(os.path.join(self.dir, '_render.js'), 'w', encoding='utf-8') as f: f.write(self.get_render_script()) env = os.environ.copy() - env["NODE_PATH"] = os.path.dirname(PUPPETEER_MODULE) + env['NODE_PATH'] = os.path.dirname(PUPPETEER_MODULE) - cmdline = [NODE_PATH, "_render.js"] - self.proc = subprocess.Popen( - cmdline, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - cwd=self.dir, - env=env, - ) + cmdline = [NODE_PATH, '_render.js'] + self.proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.dir, env=env) self.log = self.proc.communicate()[0] - class SeleniumPDFRender(BasePdfMaker): success = False template = { - "printBackground": True, - "displayHeaderFooter": True, - "headerTemplate": "
", - "footerTemplate": '
' - + gettext("Page %s of %s") - % ('', '') - + "
", + 'printBackground': True, + 'displayHeaderFooter': True, + 'headerTemplate': '
', + 'footerTemplate': '
' + + gettext('Page %s of %s') % + ('', '') + + '
', } def get_log(self, driver): - return "\n".join(map(str, driver.get_log("driver") + driver.get_log("browser"))) + return '\n'.join(map(str, driver.get_log('driver') + driver.get_log('browser'))) def _make(self, debug): options = webdriver.ChromeOptions() options.add_argument("--headless") - options.add_argument("--no-sandbox") # for root + options.add_argument("--no-sandbox") # for root options.binary_location = settings.SELENIUM_CUSTOM_CHROME_PATH browser = webdriver.Chrome(settings.SELENIUM_CHROMEDRIVER_PATH, options=options) - browser.get("file://%s" % self.htmlfile) + browser.get('file://%s' % self.htmlfile) self.log = self.get_log(browser) try: - WebDriverWait(browser, 15).until( - EC.presence_of_element_located((By.CLASS_NAME, "math-loaded")) - ) + WebDriverWait(browser, 15).until(EC.presence_of_element_located((By.CLASS_NAME, 'math-loaded'))) except TimeoutException: - logger.error("PDF math rendering timed out") - self.log = self.get_log(browser) + "\nPDF math rendering timed out" + logger.error('PDF math rendering timed out') + self.log = self.get_log(browser) + '\nPDF math rendering timed out' browser.quit() return - response = browser.execute_cdp_cmd("Page.printToPDF", self.template) + response = browser.execute_cdp_cmd('Page.printToPDF', self.template) self.log = self.get_log(browser) if not response: browser.quit() return - with open(self.pdffile, "wb") as f: - f.write(base64.b64decode(response["data"])) + with open(self.pdffile, 'wb') as f: + f.write(base64.b64decode(response['data'])) self.success = True browser.quit() diff --git a/judge/performance_points.py b/judge/performance_points.py index c806081..eee3c10 100644 --- a/judge/performance_points.py +++ b/judge/performance_points.py @@ -6,22 +6,16 @@ from django.db import connection from judge.models import Submission from judge.timezone import from_database_time -PP_WEIGHT_TABLE = [ - pow(settings.DMOJ_PP_STEP, i) for i in range(settings.DMOJ_PP_ENTRIES) -] +PP_WEIGHT_TABLE = [pow(settings.DMOJ_PP_STEP, i) for i in range(settings.DMOJ_PP_ENTRIES)] -PPBreakdown = namedtuple( - "PPBreakdown", - "points weight scaled_points problem_name problem_code " - "sub_id sub_date sub_points sub_total sub_result_class " - "sub_short_status sub_long_status sub_lang", -) +PPBreakdown = namedtuple('PPBreakdown', 'points weight scaled_points problem_name problem_code ' + 'sub_id sub_date sub_points sub_total sub_result_class ' + 'sub_short_status sub_long_status sub_lang') def get_pp_breakdown(user, start=0, end=settings.DMOJ_PP_ENTRIES): with connection.cursor() as cursor: - cursor.execute( - f""" + cursor.execute(''' SELECT max_points_table.problem_code, max_points_table.problem_name, max_points_table.max_points, @@ -32,72 +26,53 @@ def get_pp_breakdown(user, start=0, end=settings.DMOJ_PP_ENTRIES): judge_submission.result, judge_language.short_name, judge_language.key - FROM ( - SELECT judge_problem.id problem_id, - judge_problem.name problem_name, - judge_problem.code problem_code, - MAX(judge_submission.points) AS max_points + FROM judge_submission + JOIN (SELECT judge_problem.id problem_id, + judge_problem.name problem_name, + judge_problem.code problem_code, + MAX(judge_submission.points) AS max_points FROM judge_problem INNER JOIN judge_submission ON (judge_problem.id = judge_submission.problem_id) - WHERE (judge_problem.is_public AND - NOT judge_problem.is_organization_private AND + WHERE (judge_problem.is_public = True AND + judge_problem.is_organization_private = False AND judge_submission.points IS NOT NULL AND judge_submission.user_id = %s) GROUP BY judge_problem.id - HAVING MAX(judge_submission.points) > 0.0 - ) AS max_points_table - INNER JOIN judge_submission ON ( - judge_submission.problem_id = max_points_table.problem_id AND + HAVING MAX(judge_submission.points) > 0.0) AS max_points_table + ON (judge_submission.problem_id = max_points_table.problem_id AND judge_submission.points = max_points_table.max_points AND - judge_submission.user_id = %s - ) - INNER JOIN judge_language ON (judge_submission.language_id = judge_language.id) + judge_submission.user_id = %s) + JOIN judge_language + ON judge_submission.language_id = judge_language.id GROUP BY max_points_table.problem_id ORDER BY max_points DESC, judge_submission.date DESC LIMIT %s OFFSET %s - """, - (user.id, user.id, end - start + 1, start), - ) + ''', (user.id, user.id, end - start + 1, start)) data = cursor.fetchall() breakdown = [] for weight, contrib in zip(PP_WEIGHT_TABLE[start:end], data): - ( - code, - name, - points, - id, - date, - case_points, - case_total, - result, - lang_short_name, - lang_key, - ) = contrib + code, name, points, id, date, case_points, case_total, result, lang_short_name, lang_key = contrib # Replicates a lot of the logic usually done on Submission objects lang_short_display_name = lang_short_name or lang_key - result_class = Submission.result_class_from_code( - result, case_points, case_total - ) - long_status = Submission.USER_DISPLAY_CODES.get(result, "") + result_class = Submission.result_class_from_code(result, case_points, case_total) + long_status = Submission.USER_DISPLAY_CODES.get(result, '') - breakdown.append( - PPBreakdown( - points=points, - weight=weight * 100, - scaled_points=points * weight, - problem_name=name, - problem_code=code, - sub_id=id, - sub_date=from_database_time(date), - sub_points=case_points, - sub_total=case_total, - sub_short_status=result, - sub_long_status=long_status, - sub_result_class=result_class, - sub_lang=lang_short_display_name, - ) - ) + breakdown.append(PPBreakdown( + points=points, + weight=weight * 100, + scaled_points=points * weight, + problem_name=name, + problem_code=code, + sub_id=id, + sub_date=from_database_time(date), + sub_points=case_points, + sub_total=case_total, + sub_short_status=result, + sub_long_status=long_status, + sub_result_class=result_class, + sub_lang=lang_short_display_name, + )) has_more = end < min(len(PP_WEIGHT_TABLE), start + len(data)) return breakdown, has_more diff --git a/judge/ratings.py b/judge/ratings.py index 854bdcb..9d0f36d 100644 --- a/judge/ratings.py +++ b/judge/ratings.py @@ -7,21 +7,20 @@ from django.db.models import Count, OuterRef, Subquery from django.db.models.functions import Coalesce from django.utils import timezone -BETA2 = 328.33**2 -RATING_INIT = 1200 # Newcomer's rating when applying the rating floor/ceiling -MEAN_INIT = 1400.0 + +BETA2 = 328.33 ** 2 +RATING_INIT = 1200 # Newcomer's rating when applying the rating floor/ceiling +MEAN_INIT = 1400. VAR_INIT = 250**2 * (BETA2 / 212**2) SD_INIT = sqrt(VAR_INIT) VALID_RANGE = MEAN_INIT - 20 * SD_INIT, MEAN_INIT + 20 * SD_INIT VAR_PER_CONTEST = 1219.047619 * (BETA2 / 212**2) -VAR_LIM = ( - sqrt(VAR_PER_CONTEST**2 + 4 * BETA2 * VAR_PER_CONTEST) - VAR_PER_CONTEST -) / 2 +VAR_LIM = (sqrt(VAR_PER_CONTEST**2 + 4 * BETA2 * VAR_PER_CONTEST) - VAR_PER_CONTEST) / 2 SD_LIM = sqrt(VAR_LIM) TANH_C = sqrt(3) / pi -def tie_ranker(iterable, key=attrgetter("points")): +def tie_ranker(iterable, key=attrgetter('points')): rank = 0 delta = 1 last = None @@ -72,15 +71,15 @@ def solve(tanh_terms, y_tg, lin_factor=0, bounds=VALID_RANGE): def get_var(times_ranked, cache=[VAR_INIT]): while times_ranked >= len(cache): - next_var = 1.0 / (1.0 / (cache[-1] + VAR_PER_CONTEST) + 1.0 / BETA2) + next_var = 1. / (1. / (cache[-1] + VAR_PER_CONTEST) + 1. / BETA2) cache.append(next_var) return cache[times_ranked] def recalculate_ratings(ranking, old_mean, times_ranked, historical_p): n = len(ranking) - new_p = [0.0] * n - new_mean = [0.0] * n + new_p = [0.] * n + new_mean = [0.] * n # Note: pre-multiply delta by TANH_C to improve efficiency. delta = [TANH_C * sqrt(get_var(t) + VAR_PER_CONTEST + BETA2) for t in times_ranked] @@ -91,10 +90,10 @@ def recalculate_ratings(ranking, old_mean, times_ranked, historical_p): r = ranking[i] y_tg = 0 for d, s in zip(delta, ranking): - if s > r: # s loses to r - y_tg += 1.0 / d - elif s < r: # s beats r - y_tg -= 1.0 / d + if s > r: # s loses to r + y_tg += 1. / d + elif s < r: # s beats r + y_tg -= 1. / d # Otherwise, this is a tie that counts as half a win, as per Elo-MMR. new_p[i] = solve(p_tanh_terms, y_tg, bounds=bounds) @@ -118,10 +117,10 @@ def recalculate_ratings(ranking, old_mean, times_ranked, historical_p): # Calculate mean. for i, r in enumerate(ranking): tanh_terms = [] - w_prev = 1.0 - w_sum = 0.0 + w_prev = 1. + w_sum = 0. for j, h in enumerate([new_p[i]] + historical_p[i]): - gamma2 = VAR_PER_CONTEST if j > 0 else 0 + gamma2 = (VAR_PER_CONTEST if j > 0 else 0) h_var = get_var(times_ranked[i] + 1 - j) k = h_var / (h_var + gamma2) w = w_prev * k**2 @@ -129,58 +128,31 @@ def recalculate_ratings(ranking, old_mean, times_ranked, historical_p): tanh_terms.append((h, sqrt(BETA2) * TANH_C, w)) w_prev = w w_sum += w / BETA2 - w0 = 1.0 / get_var(times_ranked[i] + 1) - w_sum + w0 = 1. / get_var(times_ranked[i] + 1) - w_sum p0 = eval_tanhs(tanh_terms[1:], old_mean[i]) / w0 + old_mean[i] new_mean[i] = solve(tanh_terms, w0 * p0, lin_factor=w0) # Display a slightly lower rating to incentivize participation. # As times_ranked increases, new_rating converges to new_mean. - new_rating = [ - max(1, round(m - (sqrt(get_var(t + 1)) - SD_LIM))) - for m, t in zip(new_mean, times_ranked) - ] + new_rating = [max(1, round(m - (sqrt(get_var(t + 1)) - SD_LIM))) for m, t in zip(new_mean, times_ranked)] return new_rating, new_mean, new_p def rate_contest(contest): from judge.models import Rating, Profile - from judge.models.profile import _get_basic_info - from judge.utils.users import get_contest_ratings, get_rating_rank - rating_subquery = Rating.objects.filter(user=OuterRef("user")) - rating_sorted = rating_subquery.order_by("-contest__end_time") - users = ( - contest.users.order_by("is_disqualified", "-score", "cumtime", "tiebreaker") - .annotate( - submissions=Count("submission"), - last_rating=Coalesce( - Subquery(rating_sorted.values("rating")[:1]), RATING_INIT - ), - last_mean=Coalesce(Subquery(rating_sorted.values("mean")[:1]), MEAN_INIT), - times=Coalesce( - Subquery( - rating_subquery.order_by() - .values("user_id") - .annotate(count=Count("id")) - .values("count") - ), - 0, - ), - ) - .exclude(user_id__in=contest.rate_exclude.all()) - .filter(virtual=0) - .values( - "id", - "user_id", - "score", - "cumtime", - "tiebreaker", - "last_rating", - "last_mean", - "times", - ) - ) + rating_subquery = Rating.objects.filter(user=OuterRef('user')) + rating_sorted = rating_subquery.order_by('-contest__end_time') + users = contest.users.order_by('is_disqualified', '-score', 'cumtime', 'tiebreaker') \ + .annotate(submissions=Count('submission'), + last_rating=Coalesce(Subquery(rating_sorted.values('rating')[:1]), RATING_INIT), + last_mean=Coalesce(Subquery(rating_sorted.values('mean')[:1]), MEAN_INIT), + times=Coalesce(Subquery(rating_subquery.order_by().values('user_id') + .annotate(count=Count('id')).values('count')), 0)) \ + .exclude(user_id__in=contest.rate_exclude.all()) \ + .filter(virtual=0).values('id', 'user_id', 'score', 'cumtime', 'tiebreaker', + 'last_rating', 'last_mean', 'times') if not contest.rate_all: users = users.filter(submissions__gt=0) if contest.rating_floor is not None: @@ -189,80 +161,38 @@ def rate_contest(contest): users = users.exclude(last_rating__gt=contest.rating_ceiling) users = list(users) - participation_ids = list(map(itemgetter("id"), users)) - user_ids = list(map(itemgetter("user_id"), users)) - ranking = list(tie_ranker(users, key=itemgetter("score", "cumtime", "tiebreaker"))) - old_mean = list(map(itemgetter("last_mean"), users)) - times_ranked = list(map(itemgetter("times"), users)) + participation_ids = list(map(itemgetter('id'), users)) + user_ids = list(map(itemgetter('user_id'), users)) + ranking = list(tie_ranker(users, key=itemgetter('score', 'cumtime', 'tiebreaker'))) + old_mean = list(map(itemgetter('last_mean'), users)) + times_ranked = list(map(itemgetter('times'), users)) historical_p = [[] for _ in users] user_id_to_idx = {uid: i for i, uid in enumerate(user_ids)} - for h in ( - Rating.objects.filter(user_id__in=user_ids) - .order_by("-contest__end_time") - .values("user_id", "performance") - ): - idx = user_id_to_idx[h["user_id"]] - historical_p[idx].append(h["performance"]) + for h in Rating.objects.filter(user_id__in=user_ids) \ + .order_by('-contest__end_time') \ + .values('user_id', 'performance'): + idx = user_id_to_idx[h['user_id']] + historical_p[idx].append(h['performance']) - rating, mean, performance = recalculate_ratings( - ranking, old_mean, times_ranked, historical_p - ) + rating, mean, performance = recalculate_ratings(ranking, old_mean, times_ranked, historical_p) now = timezone.now() - ratings = [ - Rating( - user_id=i, - contest=contest, - rating=r, - mean=m, - performance=perf, - last_rated=now, - participation_id=pid, - rank=z, - ) - for i, pid, r, m, perf, z in zip( - user_ids, participation_ids, rating, mean, performance, ranking - ) - ] + ratings = [Rating(user_id=i, contest=contest, rating=r, mean=m, performance=perf, + last_rated=now, participation_id=pid, rank=z) + for i, pid, r, m, perf, z in zip(user_ids, participation_ids, rating, mean, performance, ranking)] with transaction.atomic(): Rating.objects.bulk_create(ratings) - Profile.objects.filter( - contest_history__contest=contest, contest_history__virtual=0 - ).update( - rating=Subquery( - Rating.objects.filter(user=OuterRef("id")) - .order_by("-contest__end_time") - .values("rating")[:1] - ) - ) - - _get_basic_info.dirty_multi([(uid,) for uid in user_ids]) - get_contest_ratings.dirty_multi([(uid,) for uid in user_ids]) - get_rating_rank.dirty_multi([(uid,) for uid in user_ids]) + Profile.objects.filter(contest_history__contest=contest, contest_history__virtual=0).update( + rating=Subquery(Rating.objects.filter(user=OuterRef('id')) + .order_by('-contest__end_time').values('rating')[:1])) -RATING_LEVELS = [ - "Newbie", - "Amateur", - "Expert", - "Candidate Master", - "Master", - "Grandmaster", - "Target", -] +RATING_LEVELS = ['Newbie', 'Amateur', 'Expert', 'Candidate Master', 'Master', 'Grandmaster', 'Target'] RATING_VALUES = [1000, 1400, 1700, 1900, 2100, 2400, 3000] -RATING_CLASS = [ - "rate-newbie", - "rate-amateur", - "rate-specialist", - "rate-expert", - "rate-candidate-master", - "rate-master", - "rate-grandmaster", - "rate-target", -] +RATING_CLASS = ['rate-newbie', 'rate-amateur', 'rate-specialist', 'rate-expert', 'rate-candidate-master', + 'rate-master', 'rate-grandmaster', 'rate-target'] def rating_level(rating): @@ -283,4 +213,4 @@ def rating_progress(rating): return 1.0 prev = 0 if not level else RATING_VALUES[level - 1] next = RATING_VALUES[level] - return (rating - prev + 0.0) / (next - prev) + return (rating - prev + 0.0) / (next - prev) \ No newline at end of file diff --git a/judge/scripts/migrate_organization_image.py b/judge/scripts/migrate_organization_image.py deleted file mode 100644 index 9134d21..0000000 --- a/judge/scripts/migrate_organization_image.py +++ /dev/null @@ -1,64 +0,0 @@ -# Download organization images from "logo_override_image" and upload to organization_images folder to use "organization_image" -# In folder online_judge, run python3 manage.py shell < judge/scripts/migrate_organization_image.py - -import os -import requests -from urllib.parse import urlparse -from django.core.files.base import ContentFile -from django.core.files.storage import default_storage -from django.conf import settings -from django.db import transaction -from judge.models import Organization - - -def is_valid_image_url(url): - try: - parsed_url = urlparse(url) - _, ext = os.path.splitext(parsed_url.path) - return ext.lower() in [".jpg", ".jpeg", ".png", ".gif", ".svg"] - except Exception as e: - return False - - -def download_image(url): - response = requests.get(url) - response.raise_for_status() - return ContentFile(response.content) - - -def organization_image_path(organization, filename): - tail = filename.split(".")[-1] - new_filename = f"organization_{organization.id}.{tail}" - return os.path.join(settings.DMOJ_ORGANIZATION_IMAGE_ROOT, new_filename) - - -@transaction.atomic -def migrate_images(): - print("Start") - organizations = Organization.objects.all() - for org in organizations: - if org.logo_override_image: - if is_valid_image_url(org.logo_override_image): - try: - # Download the image - image_content = download_image(org.logo_override_image) - # Determine the file extension - file_ext = org.logo_override_image.split(".")[-1] - filename = f"organization_{org.id}.{file_ext}" - # Save the image to the new location - new_path = organization_image_path(org, filename) - saved_path = default_storage.save(new_path, image_content) - # Update the organization_image field - org.organization_image = saved_path - org.save() - print(f"Image for organization {org.id} migrated successfully.") - except Exception as e: - print(f"Failed to migrate image for organization {org.id}: {e}") - else: - print( - f"Invalid image URL for organization {org.id}: {org.logo_override_image}" - ) - print("Finish") - - -migrate_images() diff --git a/judge/signals.py b/judge/signals.py index c482c1f..e03c17c 100644 --- a/judge/signals.py +++ b/judge/signals.py @@ -8,26 +8,9 @@ from django.core.cache.utils import make_template_fragment_key from django.db.models.signals import post_delete, post_save from django.dispatch import receiver -import judge -from judge import template_context -from judge.utils.problems import finished_submission -from .models import ( - BlogPost, - Comment, - Contest, - ContestSubmission, - Judge, - Language, - License, - MiscConfig, - Organization, - Problem, - Profile, - Submission, - NavigationBar, - Solution, - ContestProblem, -) +from .caching import finished_submission +from .models import BlogPost, Comment, Contest, ContestSubmission, EFFECTIVE_MATH_ENGINES, Judge, Language, License, \ + MiscConfig, Organization, Problem, Profile, Submission def get_pdf_path(basename): @@ -44,89 +27,75 @@ def unlink_if_exists(file): @receiver(post_save, sender=Problem) def problem_update(sender, instance, **kwargs): - if hasattr(instance, "_updating_stats_only"): + if hasattr(instance, '_updating_stats_only'): return - cache.delete_many( - [ - make_template_fragment_key("submission_problem", (instance.id,)), - "problem_tls:%s" % instance.id, - "problem_mls:%s" % instance.id, - ] - ) - cache.delete_many( - [ - make_template_fragment_key("problem_html", (instance.id, lang)) - for lang, _ in settings.LANGUAGES - ] - ) - cache.delete_many( - [ - "generated-meta-problem:%s:%d" % (lang, instance.id) - for lang, _ in settings.LANGUAGES - ] - ) - Problem.get_authors.dirty(instance) + cache.delete_many([ + make_template_fragment_key('submission_problem', (instance.id,)), + make_template_fragment_key('problem_feed', (instance.id,)), + 'problem_tls:%s' % instance.id, 'problem_mls:%s' % instance.id, + ]) + cache.delete_many([make_template_fragment_key('problem_html', (instance.id, engine, lang)) + for lang, _ in settings.LANGUAGES for engine in EFFECTIVE_MATH_ENGINES]) + cache.delete_many([make_template_fragment_key('problem_authors', (instance.id, lang)) + for lang, _ in settings.LANGUAGES]) + cache.delete_many(['generated-meta-problem:%s:%d' % (lang, instance.id) for lang, _ in settings.LANGUAGES]) for lang, _ in settings.LANGUAGES: - unlink_if_exists(get_pdf_path("%s.%s.pdf" % (instance.code, lang))) + unlink_if_exists(get_pdf_path('%s.%s.pdf' % (instance.code, lang))) @receiver(post_save, sender=Profile) def profile_update(sender, instance, **kwargs): - judge.utils.users.get_points_rank.dirty(instance.id) - judge.utils.users.get_rating_rank.dirty(instance.id) - if hasattr(instance, "_updating_stats_only"): + if hasattr(instance, '_updating_stats_only'): return - cache.delete_many( - [make_template_fragment_key("user_about", (instance.id,))] - + [ - make_template_fragment_key("org_member_count", (org_id,)) - for org_id in instance.organizations.values_list("id", flat=True) - ] - ) - - judge.models.profile._get_basic_info.dirty(instance.id) + cache.delete_many([make_template_fragment_key('user_about', (instance.id, engine)) + for engine in EFFECTIVE_MATH_ENGINES] + + [make_template_fragment_key('org_member_count', (org_id,)) + for org_id in instance.organizations.values_list('id', flat=True)]) @receiver(post_save, sender=Contest) def contest_update(sender, instance, **kwargs): - if hasattr(instance, "_updating_stats_only"): + if hasattr(instance, '_updating_stats_only'): return - cache.delete_many( - ["generated-meta-contest:%d" % instance.id] - + [make_template_fragment_key("contest_html", (instance.id,))] - ) + cache.delete_many(['generated-meta-contest:%d' % instance.id] + + [make_template_fragment_key('contest_html', (instance.id, engine)) + for engine in EFFECTIVE_MATH_ENGINES]) @receiver(post_save, sender=License) def license_update(sender, instance, **kwargs): - cache.delete(make_template_fragment_key("license_html", (instance.id,))) + cache.delete(make_template_fragment_key('license_html', (instance.id,))) @receiver(post_save, sender=Language) def language_update(sender, instance, **kwargs): - cache.delete_many( - [make_template_fragment_key("language_html", (instance.id,)), "lang:cn_map"] - ) + cache.delete_many([make_template_fragment_key('language_html', (instance.id,)), + 'lang:cn_map']) @receiver(post_save, sender=Judge) def judge_update(sender, instance, **kwargs): - cache.delete(make_template_fragment_key("judge_html", (instance.id,))) + cache.delete(make_template_fragment_key('judge_html', (instance.id,))) @receiver(post_save, sender=Comment) def comment_update(sender, instance, **kwargs): - cache.delete("comment_feed:%d" % instance.id) + cache.delete('comment_feed:%d' % instance.id) @receiver(post_save, sender=BlogPost) def post_update(sender, instance, **kwargs): - cache.delete(make_template_fragment_key("post_content", (instance.id,))) - BlogPost.get_authors.dirty(instance) + cache.delete_many([ + make_template_fragment_key('post_summary', (instance.id,)), + 'blog_slug:%d' % instance.id, + 'blog_feed:%d' % instance.id, + ]) + cache.delete_many([make_template_fragment_key('post_content', (instance.id, engine)) + for engine in EFFECTIVE_MATH_ENGINES]) @receiver(post_delete, sender=Submission) @@ -143,44 +112,21 @@ def contest_submission_delete(sender, instance, **kwargs): @receiver(post_save, sender=Organization) def organization_update(sender, instance, **kwargs): - cache.delete_many([make_template_fragment_key("organization_html", (instance.id,))]) - Organization.get_admin_ids.dirty(instance) + cache.delete_many([make_template_fragment_key('organization_html', (instance.id, engine)) + for engine in EFFECTIVE_MATH_ENGINES]) _misc_config_i18n = [code for code, _ in settings.LANGUAGES] -_misc_config_i18n.append("") +_misc_config_i18n.append('') @receiver(post_save, sender=MiscConfig) def misc_config_update(sender, instance, **kwargs): - cache.delete_many( - [ - "misc_config:%s:%s:%s" % (domain, lang, instance.key.split(".")[0]) - for lang in _misc_config_i18n - for domain in Site.objects.values_list("domain", flat=True) - ] - ) + cache.delete_many(['misc_config:%s:%s:%s' % (domain, lang, instance.key.split('.')[0]) + for lang in _misc_config_i18n + for domain in Site.objects.values_list('domain', flat=True)]) @receiver(post_save, sender=ContestSubmission) def contest_submission_update(sender, instance, **kwargs): - Submission.objects.filter(id=instance.submission_id).update( - contest_object_id=instance.participation.contest_id - ) - - -@receiver(post_save, sender=NavigationBar) -def navbar_update(sender, instance, **kwargs): - template_context._nav_bar.dirty() - - -@receiver(post_save, sender=Solution) -def solution_update(sender, instance, **kwargs): - cache.delete(make_template_fragment_key("solution_content", (instance.id,))) - - -@receiver(post_delete, sender=ContestProblem) -def contest_problem_delete(sender, instance, **kwargs): - Submission.objects.filter( - contest_object=instance.contest, contest__isnull=True - ).update(contest_object=None) + Submission.objects.filter(id=instance.submission_id).update(contest_object_id=instance.participation.contest_id) diff --git a/judge/sitemap.py b/judge/sitemap.py index dd71525..2deea97 100644 --- a/judge/sitemap.py +++ b/judge/sitemap.py @@ -7,83 +7,80 @@ from judge.models import BlogPost, Contest, Organization, Problem, Solution class ProblemSitemap(Sitemap): - changefreq = "daily" + changefreq = 'daily' priority = 0.8 def items(self): - return Problem.get_public_problems().values_list("code") + return Problem.get_public_problems().values_list('code') def location(self, obj): - return reverse("problem_detail", args=obj) + return reverse('problem_detail', args=obj) class UserSitemap(Sitemap): - changefreq = "hourly" + changefreq = 'hourly' priority = 0.5 def items(self): - return User.objects.values_list("username") + return User.objects.values_list('username') def location(self, obj): - return reverse("user_page", args=obj) + return reverse('user_page', args=obj) class ContestSitemap(Sitemap): - changefreq = "hourly" + changefreq = 'hourly' priority = 0.5 def items(self): - return Contest.objects.filter( - is_visible=True, is_private=False, is_organization_private=False - ).values_list("key") + return Contest.objects.filter(is_visible=True, is_private=False, + is_organization_private=False).values_list('key') def location(self, obj): - return reverse("contest_view", args=obj) + return reverse('contest_view', args=obj) class OrganizationSitemap(Sitemap): - changefreq = "hourly" + changefreq = 'hourly' priority = 0.5 def items(self): - return Organization.objects.values_list("id", "slug") + return Organization.objects.values_list('id', 'slug') def location(self, obj): - return reverse("organization_home", args=obj) + return reverse('organization_home', args=obj) class BlogPostSitemap(Sitemap): - changefreq = "hourly" + changefreq = 'hourly' priority = 0.7 def items(self): - return BlogPost.objects.filter( - visible=True, is_organization_private=False, publish_on__lte=timezone.now() - ).values_list("id", "slug") - + return (BlogPost.objects.filter(visible=True, is_organization_private=False, publish_on__lte=timezone.now()) + .values_list('id', 'slug')) + def location(self, obj): - return reverse("blog_post", args=obj) + return reverse('blog_post', args=obj) class SolutionSitemap(Sitemap): - changefreq = "hourly" + changefreq = 'hourly' priority = 0.8 def items(self): - return Solution.objects.filter( - is_public=True, publish_on__lte=timezone.now(), problem__isnull=False - ).values_list("problem__code") + return (Solution.objects.filter(is_public=True, publish_on__lte=timezone.now(), problem__isnull=False) + .values_list('problem__code')) def location(self, obj): - return reverse("problem_editorial", args=obj) + return reverse('problem_editorial', args=obj) class HomePageSitemap(Sitemap): priority = 1.0 - changefreq = "daily" + changefreq = 'daily' def items(self): - return ["home"] + return ['home'] def location(self, obj): return reverse(obj) @@ -97,10 +94,10 @@ class UrlSitemap(Sitemap): return self.pages def location(self, obj): - return obj["location"] if isinstance(obj, dict) else obj + return obj['location'] if isinstance(obj, dict) else obj def priority(self, obj): - return obj.get("priority", 0.5) if isinstance(obj, dict) else 0.5 + return obj.get('priority', 0.5) if isinstance(obj, dict) else 0.5 def changefreq(self, obj): - return obj.get("changefreq", "daily") if isinstance(obj, dict) else "daily" + return obj.get('changefreq', 'daily') if isinstance(obj, dict) else 'daily' diff --git a/judge/social_auth.py b/judge/social_auth.py index 3f3bdcd..7546010 100644 --- a/judge/social_auth.py +++ b/judge/social_auth.py @@ -9,71 +9,58 @@ from django.db import transaction from django.http import HttpResponseRedirect from django.shortcuts import render from django.urls import reverse -from django.utils.translation import gettext as _ from requests import HTTPError from reversion import revisions from social_core.backends.github import GithubOAuth2 from social_core.exceptions import InvalidEmail, SocialAuthBaseException from social_core.pipeline.partial import partial -from social_django.middleware import ( - SocialAuthExceptionMiddleware as OldSocialAuthExceptionMiddleware, -) +from social_django.middleware import SocialAuthExceptionMiddleware as OldSocialAuthExceptionMiddleware from judge.forms import ProfileForm from judge.models import Language, Profile -logger = logging.getLogger("judge.social_auth") +logger = logging.getLogger('judge.social_auth') class GitHubSecureEmailOAuth2(GithubOAuth2): - name = "github-secure" + name = 'github-secure' def user_data(self, access_token, *args, **kwargs): data = self._user_data(access_token) try: - emails = self._user_data(access_token, "/emails") + emails = self._user_data(access_token, '/emails') except (HTTPError, ValueError, TypeError): emails = [] - emails = [ - (e.get("email"), e.get("primary"), 0) - for e in emails - if isinstance(e, dict) and e.get("verified") - ] + emails = [(e.get('email'), e.get('primary'), 0) for e in emails if isinstance(e, dict) and e.get('verified')] emails.sort(key=itemgetter(1), reverse=True) emails = list(map(itemgetter(0), emails)) if emails: - data["email"] = emails[0] + data['email'] = emails[0] else: - data["email"] = None + data['email'] = None return data -def slugify_username(username, renotword=re.compile(r"[^\w]")): - return renotword.sub("", username.replace("-", "_")) +def slugify_username(username, renotword=re.compile(r'[^\w]')): + return renotword.sub('', username.replace('-', '_')) def verify_email(backend, details, *args, **kwargs): - if not details["email"]: + if not details['email']: raise InvalidEmail(backend) class UsernameForm(forms.Form): - username = forms.RegexField( - regex=r"^\w+$", - max_length=30, - label="Username", - error_messages={ - "invalid": _("A username must contain letters, numbers, or underscores") - }, - ) + username = forms.RegexField(regex=r'^\w+$', max_length=30, label='Username', + error_messages={'invalid': 'A username must contain letters, numbers, or underscores'}) def clean_username(self): - if User.objects.filter(username=self.cleaned_data["username"]).exists(): - raise forms.ValidationError(_("Sorry, the username is taken.")) - return self.cleaned_data["username"] + if User.objects.filter(username=self.cleaned_data['username']).exists(): + raise forms.ValidationError('Sorry, the username is taken.') + return self.cleaned_data['username'] @partial @@ -83,26 +70,21 @@ def choose_username(backend, user, username=None, *args, **kwargs): if request.POST: form = UsernameForm(request.POST) if form.is_valid(): - return {"username": form.cleaned_data["username"]} + return {'username': form.cleaned_data['username']} else: - form = UsernameForm(initial={"username": username}) - return render( - request, - "registration/username_select.html", - { - "title": _("Choose a username"), - "form": form, - }, - ) + form = UsernameForm(initial={'username': username}) + return render(request, 'registration/username_select.html', { + 'title': 'Choose a username', 'form': form, + }) @partial def make_profile(backend, user, response, is_new=False, *args, **kwargs): if is_new: - if not hasattr(user, "profile"): + if not hasattr(user, 'profile'): profile = Profile(user=user) profile.language = Language.get_default_language() - logger.info("Info from %s: %s", backend.name, response) + logger.info('Info from %s: %s', backend.name, response) profile.save() form = ProfileForm(instance=profile, user=user) else: @@ -113,25 +95,15 @@ def make_profile(backend, user, response, is_new=False, *args, **kwargs): with transaction.atomic(), revisions.create_revision(): form.save() revisions.set_user(user) - revisions.set_comment("Updated on registration") + revisions.set_comment('Updated on registration') return - return render( - backend.strategy.request, - "registration/profile_creation.html", - { - "title": _("Create your profile"), - "form": form, - }, - ) + return render(backend.strategy.request, 'registration/profile_creation.html', { + 'title': 'Create your profile', 'form': form, + }) class SocialAuthExceptionMiddleware(OldSocialAuthExceptionMiddleware): def process_exception(self, request, exception): if isinstance(exception, SocialAuthBaseException): - return HttpResponseRedirect( - "%s?message=%s" - % ( - reverse("social_auth_error"), - quote(self.get_message(request, exception)), - ) - ) + return HttpResponseRedirect('%s?message=%s' % (reverse('social_auth_error'), + quote(self.get_message(request, exception)))) diff --git a/judge/tasks/contest.py b/judge/tasks/contest.py index beaf2d5..0c36e23 100644 --- a/judge/tasks/contest.py +++ b/judge/tasks/contest.py @@ -7,7 +7,7 @@ from moss import MOSS from judge.models import Contest, ContestMoss, ContestParticipation, Submission from judge.utils.celery import Progress -__all__ = ("rescore_contest", "run_moss") +__all__ = ('rescore_contest', 'run_moss') @shared_task(bind=True) @@ -16,27 +16,8 @@ def rescore_contest(self, contest_key): participations = contest.users rescored = 0 - with Progress( - self, participations.count(), stage=_("Recalculating contest scores") - ) as p: + with Progress(self, participations.count(), stage=_('Recalculating contest scores')) as p: for participation in participations.iterator(): - for contest_submission in participation.submissions.iterator(): - submission = contest_submission.submission - contest_problem = contest_submission.problem - contest_submission.points = round( - submission.case_points - / submission.case_total - * contest_problem.points - if submission.case_total > 0 - else 0, - 3, - ) - if ( - not contest_problem.partial - and contest_submission.points != contest_problem.points - ): - contest_submission.points = 0 - contest_submission.save() participation.recompute_results() rescored += 1 if rescored % 10 == 0: @@ -48,7 +29,7 @@ def rescore_contest(self, contest_key): def run_moss(self, contest_key): moss_api_key = settings.MOSS_API_KEY if moss_api_key is None: - raise ImproperlyConfigured("No MOSS API Key supplied") + raise ImproperlyConfigured('No MOSS API Key supplied') contest = Contest.objects.get(key=contest_key) ContestMoss.objects.filter(contest=contest).delete() @@ -56,34 +37,21 @@ def run_moss(self, contest_key): length = len(ContestMoss.LANG_MAPPING) * contest.problems.count() moss_results = [] - with Progress(self, length, stage=_("Running MOSS")) as p: + with Progress(self, length, stage=_('Running MOSS')) as p: for problem in contest.problems.all(): for dmoj_lang, moss_lang in ContestMoss.LANG_MAPPING: - result = ContestMoss( - contest=contest, problem=problem, language=dmoj_lang - ) + result = ContestMoss(contest=contest, problem=problem, language=dmoj_lang) - subs = ( - Submission.objects.filter( - contest__participation__virtual__in=( - ContestParticipation.LIVE, - ContestParticipation.SPECTATE, - ), - contest_object=contest, - problem=problem, - language__common_name=dmoj_lang, - ) - .order_by("-points") - .values_list("user__user__username", "source__source") - ) + subs = Submission.objects.filter( + contest__participation__virtual__in=(ContestParticipation.LIVE, ContestParticipation.SPECTATE), + contest_object=contest, + problem=problem, + language__common_name=dmoj_lang, + ).order_by('-points').values_list('user__user__username', 'source__source') if subs.exists(): - moss_call = MOSS( - moss_api_key, - language=moss_lang, - matching_file_limit=100, - comment="%s - %s" % (contest.key, problem.code), - ) + moss_call = MOSS(moss_api_key, language=moss_lang, matching_file_limit=100, + comment='%s - %s' % (contest.key, problem.code)) users = set() @@ -91,7 +59,7 @@ def run_moss(self, contest_key): if username in users: continue users.add(username) - moss_call.add_file_from_memory(username, source.encode("utf-8")) + moss_call.add_file_from_memory(username, source.encode('utf-8')) result.url = moss_call.process() result.submission_count = len(users) diff --git a/judge/tasks/demo.py b/judge/tasks/demo.py index bd97401..c09dcbf 100644 --- a/judge/tasks/demo.py +++ b/judge/tasks/demo.py @@ -4,7 +4,7 @@ from celery import shared_task from judge.utils.celery import Progress -__all__ = ("success", "failure", "progress") +__all__ = ('success', 'failure', 'progress') @shared_task @@ -14,7 +14,7 @@ def success(): @shared_task def failure(): - raise RuntimeError("This task always fails.") + raise RuntimeError('This task always fails.') @shared_task(bind=True) diff --git a/judge/tasks/experiment.py b/judge/tasks/experiment.py index 978c643..0e991c4 100644 --- a/judge/tasks/experiment.py +++ b/judge/tasks/experiment.py @@ -2,20 +2,19 @@ from judge.models import SubmissionTestCase, Problem from collections import defaultdict - def generate_report(problem): testcases = SubmissionTestCase.objects.filter(submission__problem=problem).all() - + score = defaultdict(int) total = defaultdict(int) rate = defaultdict(int) for case in testcases.iterator(): - score[case.case] += int(case.status == "AC") + score[case.case] += int(case.status == 'AC') total[case.case] += 1 for i in score: rate[i] = score[i] / total[i] for i, _ in sorted(rate.items(), key=lambda x: x[1], reverse=True): - print(i, score[i], total[i], rate[i]) + print(i, score[i], total[i], rate[i]) \ No newline at end of file diff --git a/judge/tasks/import_users.py b/judge/tasks/import_users.py index 07acd4e..aeaeca5 100644 --- a/judge/tasks/import_users.py +++ b/judge/tasks/import_users.py @@ -1,5 +1,5 @@ import csv -import re +from tempfile import mktemp from django.conf import settings from django.contrib.auth.models import User @@ -7,23 +7,20 @@ from django.contrib.auth.models import User from judge.models import Profile, Language, Organization -fields = ["username", "password", "name", "school", "email", "organizations"] -descriptions = [ - "my_username(edit old one if exist)", - "123456 (must have)", - "Le Van A (can be empty)", - "Le Quy Don (can be empty)", - "email@email.com (can be empty)", - "org1&org2&org3&... (can be empty - org slug in URL)", -] - +fields = ['username', 'password', 'name', 'school', 'email', 'organizations'] +descriptions = ['my_username(edit old one if exist)', + '123456 (must have)', + 'Le Van A (can be empty)', + 'Le Quy Don (can be empty)', + 'email@email.com (can be empty)', + 'org1&org2&org3&... (can be empty - org slug in URL)'] def csv_to_dict(csv_file): - rows = csv.reader(csv_file.read().decode().split("\n")) + rows = csv.reader(csv_file.read().decode().split('\n')) header = next(rows) header = [i.lower() for i in header] - if "username" not in header: + if 'username' not in header: return [] res = [] @@ -31,67 +28,55 @@ def csv_to_dict(csv_file): for row in rows: if len(row) != len(header): continue - cur_dict = {i: "" for i in fields} + cur_dict = {i: '' for i in fields} for i in range(len(header)): if header[i] not in fields: continue cur_dict[header[i]] = row[i] - if cur_dict["username"]: + if cur_dict['username']: res.append(cur_dict) return res - -def is_valid_username(username): - match = re.match(r"\w+", username) - return match is not None and match.group() == username - - + # return result log def import_users(users): - log = "" + log = '' for i, row in enumerate(users): - cur_log = str(i + 1) + ". " + cur_log = str(i + 1) + '. ' - username = row["username"] - if not is_valid_username(username): - log += username + ": Invalid username\n" - continue + username = row['username'] + cur_log += username + ': ' - cur_log += username + ": " - pwd = row["password"] - user, created = User.objects.get_or_create( - username=username, - defaults={ - "is_active": True, - }, - ) - profile, _ = Profile.objects.get_or_create( - user=user, - defaults={ - "language": Language.get_python3(), - "timezone": settings.DEFAULT_USER_TIME_ZONE, - }, - ) + pwd = row['password'] + + user, created = User.objects.get_or_create(username=username, defaults={ + 'is_active': True, + }) + + profile, _ = Profile.objects.get_or_create(user=user, defaults={ + 'language': Language.get_python3(), + 'timezone': settings.DEFAULT_USER_TIME_ZONE, + }) if created: - cur_log += "Create new - " + cur_log += 'Create new - ' else: - cur_log += "Edit - " + cur_log += 'Edit - ' if pwd: user.set_password(pwd) elif created: - user.set_password("lqdoj") - cur_log += "Missing password, set password = lqdoj - " + user.set_password('lqdoj') + cur_log += 'Missing password, set password = lqdoj - ' - if "name" in row.keys() and row["name"]: - user.first_name = row["name"] + if 'name' in row.keys() and row['name']: + user.first_name = row['name'] - if "school" in row.keys() and row["school"]: - user.last_name = row["school"] + if 'school' in row.keys() and row['school']: + user.last_name = row['school'] - if row["organizations"]: - orgs = row["organizations"].split("&") + if row['organizations']: + orgs = row['organizations'].split('&') added_orgs = [] for o in orgs: try: @@ -101,15 +86,15 @@ def import_users(users): except Organization.DoesNotExist: continue if added_orgs: - cur_log += "Added to " + ", ".join(added_orgs) + " - " - - if row["email"]: - user.email = row["email"] + cur_log += 'Added to ' + ', '.join(added_orgs) + ' - ' + if row['email']: + user.email = row['email'] + user.save() profile.save() - cur_log += "Saved\n" + cur_log += 'Saved\n' log += cur_log - log += "FINISH" + log += 'FINISH' - return log + return log \ No newline at end of file diff --git a/judge/tasks/submission.py b/judge/tasks/submission.py index 5333dba..a11de8c 100644 --- a/judge/tasks/submission.py +++ b/judge/tasks/submission.py @@ -5,10 +5,10 @@ from django.utils.translation import gettext as _ from judge.models import Problem, Profile, Submission from judge.utils.celery import Progress -__all__ = ("apply_submission_filter", "rejudge_problem_filter", "rescore_problem") +__all__ = ('apply_submission_filter', 'rejudge_problem_filter', 'rescore_problem') -def apply_submission_filter(queryset, id_range, languages, results, contests): +def apply_submission_filter(queryset, id_range, languages, results): if id_range: start, end = id_range queryset = queryset.filter(id__gte=start, id__lte=end) @@ -16,18 +16,14 @@ def apply_submission_filter(queryset, id_range, languages, results, contests): queryset = queryset.filter(language_id__in=languages) if results: queryset = queryset.filter(result__in=results) - if contests: - queryset = queryset.filter(contest_object__in=contests) queryset = queryset.exclude(status__in=Submission.IN_PROGRESS_GRADING_STATUS) return queryset @shared_task(bind=True) -def rejudge_problem_filter( - self, problem_id, id_range=None, languages=None, results=None, contest=None -): +def rejudge_problem_filter(self, problem_id, id_range=None, languages=None, results=None): queryset = Submission.objects.filter(problem_id=problem_id) - queryset = apply_submission_filter(queryset, id_range, languages, results, contest) + queryset = apply_submission_filter(queryset, id_range, languages, results) rejudged = 0 with Progress(self, queryset.count()) as p: @@ -44,37 +40,27 @@ def rescore_problem(self, problem_id): problem = Problem.objects.get(id=problem_id) submissions = Submission.objects.filter(problem_id=problem_id) - with Progress(self, submissions.count(), stage=_("Modifying submissions")) as p: + with Progress(self, submissions.count(), stage=_('Modifying submissions')) as p: rescored = 0 for submission in submissions.iterator(): - submission.points = round( - submission.case_points / submission.case_total * problem.points - if submission.case_total - else 0, - 1, - ) + submission.points = round(submission.case_points / submission.case_total * problem.points + if submission.case_total else 0, 1) if not problem.partial and submission.points < problem.points: submission.points = 0 - submission.save(update_fields=["points"]) + submission.save(update_fields=['points']) submission.update_contest() rescored += 1 if rescored % 10 == 0: p.done = rescored - with Progress( - self, - submissions.values("user_id").distinct().count(), - stage=_("Recalculating user points"), - ) as p: + with Progress(self, submissions.values('user_id').distinct().count(), stage=_('Recalculating user points')) as p: users = 0 - profiles = Profile.objects.filter( - id__in=submissions.values_list("user_id", flat=True).distinct() - ) + profiles = Profile.objects.filter(id__in=submissions.values_list('user_id', flat=True).distinct()) for profile in profiles.iterator(): profile._updating_stats_only = True profile.calculate_points() - cache.delete("user_complete:%d" % profile.id) - cache.delete("user_attempted:%d" % profile.id) + cache.delete('user_complete:%d' % profile.id) + cache.delete('user_attempted:%d' % profile.id) users += 1 if users % 10 == 0: p.done = users diff --git a/judge/template_context.py b/judge/template_context.py index 337d32a..6348407 100644 --- a/judge/template_context.py +++ b/judge/template_context.py @@ -1,4 +1,3 @@ -import re from functools import partial from django.conf import settings @@ -7,34 +6,31 @@ from django.contrib.sites.shortcuts import get_current_site from django.core.cache import cache from django.utils.functional import SimpleLazyObject, new_method_proxy -from mptt.querysets import TreeQuerySet - +from judge.utils.caniuse import CanIUse, SUPPORT from .models import MiscConfig, NavigationBar, Profile -from judge.caching import cache_wrapper class FixedSimpleLazyObject(SimpleLazyObject): - if not hasattr(SimpleLazyObject, "__iter__"): + if not hasattr(SimpleLazyObject, '__iter__'): __iter__ = new_method_proxy(iter) def get_resource(request): use_https = settings.DMOJ_SSL if use_https == 1: - scheme = "https" if request.is_secure() else "http" + scheme = 'https' if request.is_secure() else 'http' elif use_https > 1: - scheme = "https" + scheme = 'https' else: - scheme = "http" - + scheme = 'http' return { - "INLINE_JQUERY": settings.INLINE_JQUERY, - "INLINE_FONTAWESOME": settings.INLINE_FONTAWESOME, - "JQUERY_JS": settings.JQUERY_JS, - "FONTAWESOME_CSS": settings.FONTAWESOME_CSS, - "DMOJ_SCHEME": scheme, - "DMOJ_CANONICAL": settings.DMOJ_CANONICAL, - "use_darkmode": request.session.get("darkmode", False) == True, + 'PYGMENT_THEME': settings.PYGMENT_THEME, + 'INLINE_JQUERY': settings.INLINE_JQUERY, + 'INLINE_FONTAWESOME': settings.INLINE_FONTAWESOME, + 'JQUERY_JS': settings.JQUERY_JS, + 'FONTAWESOME_CSS': settings.FONTAWESOME_CSS, + 'DMOJ_SCHEME': scheme, + 'DMOJ_CANONICAL': settings.DMOJ_CANONICAL, } @@ -46,69 +42,56 @@ def get_profile(request): def comet_location(request): if request.is_secure(): - websocket = getattr(settings, "EVENT_DAEMON_GET_SSL", settings.EVENT_DAEMON_GET) - poll = getattr(settings, "EVENT_DAEMON_POLL_SSL", settings.EVENT_DAEMON_POLL) + websocket = getattr(settings, 'EVENT_DAEMON_GET_SSL', settings.EVENT_DAEMON_GET) + poll = getattr(settings, 'EVENT_DAEMON_POLL_SSL', settings.EVENT_DAEMON_POLL) else: websocket = settings.EVENT_DAEMON_GET poll = settings.EVENT_DAEMON_POLL - return {"EVENT_DAEMON_LOCATION": websocket, "EVENT_DAEMON_POLL_LOCATION": poll} - - -@cache_wrapper(prefix="nb", expected_type=TreeQuerySet) -def _nav_bar(): - return NavigationBar.objects.all() + return {'EVENT_DAEMON_LOCATION': websocket, + 'EVENT_DAEMON_POLL_LOCATION': poll} def __nav_tab(path): - nav_bar_list = list(_nav_bar()) - nav_bar_dict = {nb.id: nb for nb in nav_bar_list} - result = next((nb for nb in nav_bar_list if re.match(nb.regex, path)), None) - if result: - while result.parent_id: - result = nav_bar_dict.get(result.parent_id) - return result.key - else: - return [] + result = list(NavigationBar.objects.extra(where=['%s REGEXP BINARY regex'], params=[path])[:1]) + return result[0].get_ancestors(include_self=True).values_list('key', flat=True) if result else [] def general_info(request): path = request.get_full_path() return { - "nav_tab": FixedSimpleLazyObject(partial(__nav_tab, request.path)), - "nav_bar": _nav_bar(), - "LOGIN_RETURN_PATH": "" if path.startswith("/accounts/") else path, - "perms": PermWrapper(request.user), + 'nav_tab': FixedSimpleLazyObject(partial(__nav_tab, request.path)), + 'nav_bar': NavigationBar.objects.all(), + 'LOGIN_RETURN_PATH': '' if path.startswith('/accounts/') else path, + 'perms': PermWrapper(request.user), } def site(request): - return {"site": get_current_site(request)} + return {'site': get_current_site(request)} class MiscConfigDict(dict): - __slots__ = ("language", "site") + __slots__ = ('language', 'site') - def __init__(self, language="", domain=None): + def __init__(self, language='', domain=None): self.language = language self.site = domain super(MiscConfigDict, self).__init__() def __missing__(self, key): - cache_key = "misc_config:%s:%s:%s" % (self.site, self.language, key) + cache_key = 'misc_config:%s:%s:%s' % (self.site, self.language, key) value = cache.get(cache_key) if value is None: - keys = ["%s.%s" % (key, self.language), key] if self.language else [key] + keys = ['%s.%s' % (key, self.language), key] if self.language else [key] if self.site is not None: - keys = ["%s:%s" % (self.site, key) for key in keys] + keys - map = dict( - MiscConfig.objects.values_list("key", "value").filter(key__in=keys) - ) + keys = ['%s:%s' % (self.site, key) for key in keys] + keys + map = dict(MiscConfig.objects.values_list('key', 'value').filter(key__in=keys)) for item in keys: if item in map: value = map[item] break else: - value = "" + value = '' cache.set(cache_key, value, 86400) self[key] = value return value @@ -116,15 +99,23 @@ class MiscConfigDict(dict): def misc_config(request): domain = get_current_site(request).domain - return { - "misc_config": MiscConfigDict(domain=domain), - "i18n_config": MiscConfigDict(language=request.LANGUAGE_CODE, domain=domain), - } + return {'misc_config': MiscConfigDict(domain=domain), + 'i18n_config': MiscConfigDict(language=request.LANGUAGE_CODE, domain=domain)} def site_name(request): - return { - "SITE_NAME": settings.SITE_NAME, - "SITE_LONG_NAME": settings.SITE_LONG_NAME, - "SITE_ADMIN_EMAIL": settings.SITE_ADMIN_EMAIL, - } + return {'SITE_NAME': settings.SITE_NAME, + 'SITE_LONG_NAME': settings.SITE_LONG_NAME, + 'SITE_ADMIN_EMAIL': settings.SITE_ADMIN_EMAIL} + + +def math_setting(request): + caniuse = CanIUse(request.META.get('HTTP_USER_AGENT', '')) + + if request.user.is_authenticated: + engine = request.profile.math_engine + else: + engine = settings.MATHOID_DEFAULT_TYPE + if engine == 'auto': + engine = 'mml' if bool(settings.MATHOID_URL) and caniuse.mathml == SUPPORT else 'jax' + return {'MATH_ENGINE': engine, 'REQUIRE_JAX': engine == 'jax', 'caniuse': caniuse} diff --git a/judge/templatetags/code_highlight.py b/judge/templatetags/code_highlight.py index 06ff6d0..e47e165 100644 --- a/judge/templatetags/code_highlight.py +++ b/judge/templatetags/code_highlight.py @@ -6,5 +6,5 @@ register = template.Library() @register.filter -def highlight(code, language, linenos=True): - return highlight_code(code, language, linenos) +def highlight(code, language): + return highlight_code(code, language) diff --git a/judge/templatetags/dicts.py b/judge/templatetags/dicts.py index efb321c..3afc15b 100644 --- a/judge/templatetags/dicts.py +++ b/judge/templatetags/dicts.py @@ -3,6 +3,6 @@ from django import template register = template.Library() -@register.filter(name="get_dict_item") +@register.filter(name='get_dict_item') def get_item(dictionary, key): return dictionary.get(key) diff --git a/judge/templatetags/list_processor.py b/judge/templatetags/list_processor.py index 15109cf..dc37105 100644 --- a/judge/templatetags/list_processor.py +++ b/judge/templatetags/list_processor.py @@ -5,7 +5,7 @@ from django import template register = template.Library() -@register.filter(name="list_attr") +@register.filter(name='list_attr') def list_attr(iterable, prop): result = [] for item in iterable: @@ -15,43 +15,43 @@ def list_attr(iterable, prop): try: result.append(item[prop]) except KeyError: - result.append("") + result.append('') except TypeError: try: result.append(item[int(prop)]) except (IndexError, ValueError, TypeError): - result.append("") + result.append('') return result -@register.filter(name="list_getitem") +@register.filter(name='list_getitem') def list_getitem(iterable, prop): return list(map(itemgetter(prop), iterable)) -@register.filter(name="list_getindex") +@register.filter(name='list_getindex') def list_getindex(iterable, index): return list(map(itemgetter(int(index)), iterable)) -@register.filter(name="list_getattr") +@register.filter(name='list_getattr') def list_getattr(iterable, prop): return list(map(attrgetter(prop), iterable)) -@register.filter(name="sum_list") +@register.filter(name='sum_list') def sum_list(iterable): return sum(iterable) -@register.filter(name="max_list") +@register.filter(name='max_list') def max_list(iterable): if not iterable: return 0 return max(iterable) -@register.filter(name="min_list") +@register.filter(name='min_list') def min_list(iterable): if not iterable: return 0 diff --git a/judge/templatetags/strings.py b/judge/templatetags/strings.py index 475a615..6bd685a 100644 --- a/judge/templatetags/strings.py +++ b/judge/templatetags/strings.py @@ -3,16 +3,16 @@ from django import template register = template.Library() -@register.filter(name="split") +@register.filter(name='split') def split(value): - return value.split("\n") + return value.split('\n') -@register.filter(name="cutoff") +@register.filter(name='cutoff') def cutoff(value, length): - return value[: int(length)] + return value[:int(length)] -@register.filter(name="roundfloat") +@register.filter(name='roundfloat') def roundfloat(value, at): return str(round(value, int(at))) diff --git a/judge/timezone.py b/judge/timezone.py index c609649..c3b5b04 100644 --- a/judge/timezone.py +++ b/judge/timezone.py @@ -2,7 +2,7 @@ import pytz from django.conf import settings from django.db import connection from django.utils import timezone -from django.utils.timezone import make_aware, make_naive +from django.utils.timezone import make_aware class TimezoneMiddleware(object): @@ -25,10 +25,3 @@ def from_database_time(datetime): if tz is None: return datetime return make_aware(datetime, tz) - - -def to_database_time(datetime): - tz = connection.timezone - if tz is None: - return datetime - return make_naive(datetime, tz) diff --git a/judge/user_log.py b/judge/user_log.py index b718b30..d5304ff 100644 --- a/judge/user_log.py +++ b/judge/user_log.py @@ -1,6 +1,4 @@ from django.utils.timezone import now -from django.conf import settings -from django.core.cache import cache from judge.models import Profile @@ -12,17 +10,12 @@ class LogUserAccessMiddleware(object): def __call__(self, request): response = self.get_response(request) - if ( - hasattr(request, "user") - and request.user.is_authenticated - and not getattr(request, "no_profile_update", False) - and not cache.get(f"user_log_update_{request.user.id}") - ): - updates = {"last_access": now()} + if (hasattr(request, 'user') and request.user.is_authenticated and + not getattr(request, 'no_profile_update', False)): + updates = {'last_access': now()} # Decided on using REMOTE_ADDR as nginx will translate it to the external IP that hits it. - if request.META.get(settings.META_REMOTE_ADDRESS_KEY): - updates["ip"] = request.META.get(settings.META_REMOTE_ADDRESS_KEY) + if request.META.get('REMOTE_ADDR'): + updates['ip'] = request.META.get('REMOTE_ADDR') Profile.objects.filter(user_id=request.user.pk).update(**updates) - cache.set(f"user_log_update_{request.user.id}", True, 120) return response diff --git a/judge/user_translations.py b/judge/user_translations.py index f93deaa..edc32ae 100644 --- a/judge/user_translations.py +++ b/judge/user_translations.py @@ -1,4 +1,5 @@ from django.conf import settings +from django.utils import six from django.utils.safestring import SafeData, mark_safe if settings.USE_I18N: @@ -9,25 +10,23 @@ if settings.USE_I18N: def translation(language): global _translations if language not in _translations: - _translations[language] = DjangoTranslation(language, domain="dmoj-user") + _translations[language] = DjangoTranslation(language, domain='dmoj-user') return _translations[language] def do_translate(message, translation_function): """Copied from django.utils.translation.trans_real""" # str() is allowing a bytestring message to remain bytestring on Python 2 - eol_message = message.replace(str("\r\n"), str("\n")).replace( - str("\r"), str("\n") - ) + eol_message = message.replace(str('\r\n'), str('\n')).replace(str('\r'), str('\n')) if len(eol_message) == 0: # Returns an empty value of the corresponding type if an empty message # is given, instead of metadata, which is the default gettext behavior. - result = "" + result = '' else: translation_object = translation(get_language()) result = getattr(translation_object, translation_function)(eol_message) - if not isinstance(result, str): - result = result.decode("utf-8") + if not isinstance(result, six.text_type): + result = result.decode('utf-8') if isinstance(message, SafeData): return mark_safe(result) @@ -35,9 +34,7 @@ if settings.USE_I18N: return result def gettext(message): - return do_translate(message, "gettext") - + return do_translate(message, 'gettext') else: - def gettext(message): return message diff --git a/judge/utils/camo.py b/judge/utils/camo.py index 025963f..a3c90a5 100644 --- a/judge/utils/camo.py +++ b/judge/utils/camo.py @@ -10,44 +10,39 @@ class CamoClient(object): """Based on https://github.com/sionide21/camo-client""" def __init__(self, server, key, excluded=(), https=False): - self.server = server.rstrip("/") + self.server = server.rstrip('/') self.key = key self.https = https self.excluded = excluded def image_url(self, url): - return "%s/%s/%s" % ( - self.server, - hmac.new(utf8bytes(self.key), utf8bytes(url), sha1).hexdigest(), - utf8bytes(url).hex(), - ) + return '%s/%s/%s' % (self.server, + hmac.new(utf8bytes(self.key), utf8bytes(url), sha1).hexdigest(), + utf8bytes(url).hex()) def rewrite_url(self, url): if url.startswith(self.server) or url.startswith(self.excluded): return url - elif url.startswith(("http://", "https://")): + elif url.startswith(('http://', 'https://')): return self.image_url(url) - elif url.startswith("//"): - return self.rewrite_url(("https:" if self.https else "http:") + url) + elif url.startswith('//'): + return self.rewrite_url(('https:' if self.https else 'http:') + url) else: return url def update_tree(self, doc): - for img in doc.xpath(".//img"): - for attr in ("src", "data-src"): + for img in doc.xpath('.//img'): + for attr in ('src', 'data-src'): if img.get(attr): img.set(attr, self.rewrite_url(img.get(attr))) - for obj in doc.xpath(".//object"): - if obj.get("data"): - obj.set("data", self.rewrite_url(obj.get("data"))) + for obj in doc.xpath('.//object'): + if obj.get('data'): + obj.set('data', self.rewrite_url(obj.get('data'))) if settings.DMOJ_CAMO_URL and settings.DMOJ_CAMO_KEY: - client = CamoClient( - settings.DMOJ_CAMO_URL, - key=settings.DMOJ_CAMO_KEY, - excluded=settings.DMOJ_CAMO_EXCLUDE, - https=settings.DMOJ_CAMO_HTTPS, - ) + client = CamoClient(settings.DMOJ_CAMO_URL, key=settings.DMOJ_CAMO_KEY, + excluded=settings.DMOJ_CAMO_EXCLUDE, + https=settings.DMOJ_CAMO_HTTPS) else: client = None diff --git a/judge/utils/caniuse.py b/judge/utils/caniuse.py index 2414785..bd4bb52 100644 --- a/judge/utils/caniuse.py +++ b/judge/utils/caniuse.py @@ -1,17 +1,15 @@ import requests from ua_parser import user_agent_parser -_SUPPORT_DATA = requests.get( - "https://raw.githubusercontent.com/Fyrd/caniuse/master/data.json" -).json()["data"] +_SUPPORT_DATA = requests.get('https://raw.githubusercontent.com/Fyrd/caniuse/master/data.json').json()['data'] -SUPPORT = "y" -PARTIAL_SUPPORT = "a" -UNSUPPORTED = "n" -POLYFILL = "p" -UNKNOWN = "u" -PREFIX = "x" -DISABLED = "d" +SUPPORT = 'y' +PARTIAL_SUPPORT = 'a' +UNSUPPORTED = 'n' +POLYFILL = 'p' +UNKNOWN = 'u' +PREFIX = 'x' +DISABLED = 'd' def safe_int(string): @@ -30,19 +28,19 @@ class BrowserFamily(object): max_support = UNKNOWN for version, support in data.items(): - if version == "all": + if version == 'all': self.max_support = support - elif "-" in version: - start, end = version.split("-") - start = tuple(map(int, start.split("."))) - end = tuple(map(int, end.split("."))) + (1e3000,) + elif '-' in version: + start, end = version.split('-') + start = tuple(map(int, start.split('.'))) + end = tuple(map(int, end.split('.'))) + (1e3000,) ranges.append((start, end, support)) if end > max_version: max_version = end max_support = support else: try: - version = tuple(map(int, version.split("."))) + version = tuple(map(int, version.split('.'))) except ValueError: pass else: @@ -61,12 +59,7 @@ class BrowserFamily(object): if version > self.max_version: return self.max_support - for key in ( - (int_major, int_minor, int_patch), - (int_major, int_minor), - (int_major,), - major, - ): + for key in ((int_major, int_minor, int_patch), (int_major, int_minor), (int_major,), major): try: return self._versions[key] except KeyError: @@ -82,9 +75,7 @@ class BrowserFamily(object): class Feat(object): def __init__(self, data): self._data = data - self._family = { - name: BrowserFamily(data) for name, data in data["stats"].items() - } + self._family = {name: BrowserFamily(data) for name, data in data['stats'].items()} def __getitem__(self, item): return self._family[item] @@ -106,31 +97,31 @@ class CanIUse(object): def __init__(self, ua): self._agent = user_agent_parser.Parse(ua) - os_family = self._agent["os"]["family"] - browser_family = self._agent["user_agent"]["family"] + os_family = self._agent['os']['family'] + browser_family = self._agent['user_agent']['family'] family = None - if os_family == "Android": - if "Firefox" in browser_family: - family = "and_ff" - elif "Chrome" in browser_family: - family = "and_chr" - elif "Android" in browser_family: - family = "android" + if os_family == 'Android': + if 'Firefox' in browser_family: + family = 'and_ff' + elif 'Chrome' in browser_family: + family = 'and_chr' + elif 'Android' in browser_family: + family = 'android' else: - if "Edge" in browser_family: - family = "edge" - elif "Firefox" in browser_family: - family = "firefox" - elif "Chrome" in browser_family: - family = "chrome" - elif "IE" in browser_family: - family = "ie" - elif "Opera" in browser_family: - family = "opera" - elif "Safari" in browser_family: - family = "safari" + if 'Edge' in browser_family: + family = 'edge' + elif 'Firefox' in browser_family: + family = 'firefox' + elif 'Chrome' in browser_family: + family = 'chrome' + elif 'IE' in browser_family: + family = 'ie' + elif 'Opera' in browser_family: + family = 'opera' + elif 'Safari' in browser_family: + family = 'safari' self._family = family @@ -143,12 +134,12 @@ class CanIUse(object): except KeyError: return UNKNOWN else: - ua = self._agent["user_agent"] - return stats.check(ua["major"], ua["minor"], ua["patch"])[0] + ua = self._agent['user_agent'] + return stats.check(ua['major'], ua['minor'], ua['patch'])[0] def __getattr__(self, attr): try: - feat = database[attr.replace("_", "-")] + feat = database[attr.replace('_', '-')] except KeyError: raise AttributeError(attr) else: diff --git a/judge/utils/celery.py b/judge/utils/celery.py index a632ddb..c905eae 100644 --- a/judge/utils/celery.py +++ b/judge/utils/celery.py @@ -12,11 +12,11 @@ class Progress: def _update_state(self): self.task.update_state( - state="PROGRESS", + state='PROGRESS', meta={ - "done": self._done, - "total": self._total, - "stage": self._stage, + 'done': self._done, + 'total': self._total, + 'stage': self._stage, }, ) @@ -54,12 +54,12 @@ class Progress: def task_status_url(result, message=None, redirect=None): args = {} if message: - args["message"] = message + args['message'] = message if redirect: - args["redirect"] = redirect - url = reverse("task_status", args=[result.id]) + args['redirect'] = redirect + url = reverse('task_status', args=[result.id]) if args: - url += "?" + urlencode(args) + url += '?' + urlencode(args) return url diff --git a/judge/utils/contest.py b/judge/utils/contest.py deleted file mode 100644 index 959749e..0000000 --- a/judge/utils/contest.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.db import transaction -from judge.tasks import rescore_contest -from judge.models import ( - Contest, -) - - -def maybe_trigger_contest_rescore(form, contest, force_rescore=False): - if ( - any( - f in form.changed_data - for f in ( - "start_time", - "end_time", - "time_limit", - "format_config", - "format_name", - "freeze_after", - ) - ) - or force_rescore - ): - transaction.on_commit(rescore_contest.s(contest.key).delay) - - if any( - f in form.changed_data - for f in ( - "authors", - "curators", - "testers", - ) - ): - Contest._author_ids.dirty(contest) - Contest._curator_ids.dirty(contest) - Contest._tester_ids.dirty(contest) diff --git a/judge/utils/diggpaginator.py b/judge/utils/diggpaginator.py index 2ba8581..2832d2d 100644 --- a/judge/utils/diggpaginator.py +++ b/judge/utils/diggpaginator.py @@ -4,10 +4,10 @@ from functools import reduce from django.core.paginator import InvalidPage, Page, Paginator __all__ = ( - "InvalidPage", - "ExPaginator", - "DiggPaginator", - "QuerySetDiggPaginator", + 'InvalidPage', + 'ExPaginator', + 'DiggPaginator', + 'QuerySetDiggPaginator', ) @@ -182,20 +182,15 @@ class DiggPaginator(ExPaginator): """ def __init__(self, *args, **kwargs): - self.body = kwargs.pop("body", 10) - self.tail = kwargs.pop("tail", 2) - self.align_left = kwargs.pop("align_left", False) - self.margin = kwargs.pop( - "margin", 4 - ) # TODO: make the default relative to body? + self.body = kwargs.pop('body', 10) + self.tail = kwargs.pop('tail', 2) + self.align_left = kwargs.pop('align_left', False) + self.margin = kwargs.pop('margin', 4) # TODO: make the default relative to body? # validate padding value max_padding = int(math.ceil(self.body / 2.0) - 1) - self.padding = kwargs.pop("padding", min(4, max_padding)) - count_override = kwargs.pop("count", None) - if count_override is not None: - self.__dict__["count"] = count_override + self.padding = kwargs.pop('padding', min(4, max_padding)) if self.padding > max_padding: - raise ValueError("padding too large for body (max %d)" % max_padding) + raise ValueError('padding too large for body (max %d)' % max_padding) super(DiggPaginator, self).__init__(*args, **kwargs) def page(self, number, *args, **kwargs): @@ -207,24 +202,13 @@ class DiggPaginator(ExPaginator): number = int(number) # we know this will work # easier access - num_pages, body, tail, padding, margin = ( - self.num_pages, - self.body, - self.tail, - self.padding, - self.margin, - ) + num_pages, body, tail, padding, margin = \ + self.num_pages, self.body, self.tail, self.padding, self.margin # put active page in middle of main range - main_range = list( - map( - int, - [ - math.floor(number - body / 2.0) + 1, # +1 = shift odd body to right - math.floor(number + body / 2.0), - ], - ) - ) + main_range = list(map(int, [ + math.floor(number - body / 2.0) + 1, # +1 = shift odd body to right + math.floor(number + body / 2.0)])) # adjust bounds if main_range[0] < 1: main_range = list(map(abs(main_range[0] - 1).__add__, main_range)) @@ -265,10 +249,7 @@ class DiggPaginator(ExPaginator): # section, again. main_range = [1, num_pages] else: - main_range = [ - min(num_pages - body + 1, max(number - padding, main_range[0])), - num_pages, - ] + main_range = [min(num_pages - body + 1, max(number - padding, main_range[0])), num_pages] else: trailing = list(range(num_pages - tail + 1, num_pages + 1)) @@ -282,10 +263,8 @@ class DiggPaginator(ExPaginator): page.main_range = list(range(main_range[0], main_range[1] + 1)) page.leading_range = leading page.trailing_range = trailing - page.page_range = reduce( - lambda x, y: x + ((x and y) and [False]) + y, - [page.leading_range, page.main_range, page.trailing_range], - ) + page.page_range = reduce(lambda x, y: x + ((x and y) and [False]) + y, + [page.leading_range, page.main_range, page.trailing_range]) page.__class__ = DiggPage return page @@ -293,16 +272,10 @@ class DiggPaginator(ExPaginator): class DiggPage(Page): def __str__(self): - return " ... ".join( - filter( - None, - [ - " ".join(map(str, self.leading_range)), - " ".join(map(str, self.main_range)), - " ".join(map(str, self.trailing_range)), - ], - ) - ) + return " ... ".join(filter(None, [ + " ".join(map(str, self.leading_range)), + " ".join(map(str, self.main_range)), + " ".join(map(str, self.trailing_range))])) @property def num_pages(self): diff --git a/judge/utils/email_render.py b/judge/utils/email_render.py deleted file mode 100644 index a092f88..0000000 --- a/judge/utils/email_render.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.template.loader import render_to_string -from django.contrib.sites.shortcuts import get_current_site -from django.conf import settings - - -def render_email_message(request, contexts): - current_site = get_current_site(request) - email_contexts = { - "username": request.user.username, - "domain": current_site.domain, - "site_name": settings.SITE_NAME, - "message": None, - "title": None, - "button_text": "Click here", - "url_path": None, - } - email_contexts.update(contexts) - message = render_to_string("general_email.html", email_contexts) - return message diff --git a/judge/utils/file_cache.py b/judge/utils/file_cache.py index 3b19b4c..3864f59 100644 --- a/judge/utils/file_cache.py +++ b/judge/utils/file_cache.py @@ -24,10 +24,10 @@ class HashFileCache(object): return os.path.join(self.root, hash, file) def get_url(self, hash, file): - return urljoin(self.url, "%s/%s" % (hash, file)) + return urljoin(self.url, '%s/%s' % (hash, file)) def read_file(self, hash, file): - return open(self.get_path(hash, file), "rb") + return open(self.get_path(hash, file), 'rb') def read_data(self, hash, file): with self.read_file(hash, file) as f: @@ -35,10 +35,10 @@ class HashFileCache(object): def cache_data(self, hash, file, data, url=True, gzip=True): if gzip and self.gzip: - with gzip_open(self.get_path(hash, file + ".gz"), "wb") as f: + with gzip_open(self.get_path(hash, file + '.gz'), 'wb') as f: f.write(data) - with open(self.get_path(hash, file), "wb") as f: + with open(self.get_path(hash, file), 'wb') as f: f.write(data) if url: diff --git a/judge/utils/fine_uploader.py b/judge/utils/fine_uploader.py index 8f64d78..b38fcac 100644 --- a/judge/utils/fine_uploader.py +++ b/judge/utils/fine_uploader.py @@ -5,27 +5,30 @@ from django import forms from django.forms import ClearableFileInput import os, os.path +import tempfile import shutil -__all__ = ("handle_upload", "save_upload", "FineUploadForm", "FineUploadFileInput") +__all__ = ( + 'handle_upload', 'save_upload', 'FineUploadForm', 'FineUploadFileInput' +) def combine_chunks(total_parts, total_size, source_folder, dest): if not os.path.exists(os.path.dirname(dest)): os.makedirs(os.path.dirname(dest)) - with open(dest, "wb+") as destination: + with open(dest, 'wb+') as destination: for i in range(total_parts): part = os.path.join(source_folder, str(i)) - with open(part, "rb") as source: + with open(part, 'rb') as source: destination.write(source.read()) def save_upload(f, path): if not os.path.exists(os.path.dirname(path)): os.makedirs(os.path.dirname(path)) - with open(path, "wb+") as destination: - if hasattr(f, "multiple_chunks") and f.multiple_chunks(): + with open(path, 'wb+') as destination: + if hasattr(f, 'multiple_chunks') and f.multiple_chunks(): for chunk in f.chunks(): destination.write(chunk) else: @@ -34,35 +37,29 @@ def save_upload(f, path): # pass callback function to post_upload def handle_upload(f, fileattrs, upload_dir, post_upload=None): - chunks_dir = settings.CHUNK_UPLOAD_DIR + chunks_dir = os.path.join(tempfile.gettempdir(), 'chunk_upload_tmp') if not os.path.exists(os.path.dirname(chunks_dir)): os.makedirs(os.path.dirname(chunks_dir)) chunked = False dest_folder = upload_dir - dest = os.path.join(dest_folder, fileattrs["qqfilename"]) + dest = os.path.join(dest_folder, fileattrs['qqfilename']) # Chunked - if fileattrs.get("qqtotalparts") and int(fileattrs["qqtotalparts"]) > 1: + if fileattrs.get('qqtotalparts') and int(fileattrs['qqtotalparts']) > 1: chunked = True - dest_folder = os.path.join(chunks_dir, fileattrs["qquuid"]) - dest = os.path.join( - dest_folder, fileattrs["qqfilename"], str(fileattrs["qqpartindex"]) - ) + dest_folder = os.path.join(chunks_dir, fileattrs['qquuid']) + dest = os.path.join(dest_folder, fileattrs['qqfilename'], str(fileattrs['qqpartindex'])) save_upload(f, dest) # If the last chunk has been sent, combine the parts. - if chunked and (fileattrs["qqtotalparts"] - 1 == fileattrs["qqpartindex"]): - combine_chunks( - fileattrs["qqtotalparts"], - fileattrs["qqtotalfilesize"], + if chunked and (fileattrs['qqtotalparts'] - 1 == fileattrs['qqpartindex']): + combine_chunks(fileattrs['qqtotalparts'], + fileattrs['qqtotalfilesize'], source_folder=os.path.dirname(dest), - dest=os.path.join(upload_dir, fileattrs["qqfilename"]), - ) + dest=os.path.join(upload_dir, fileattrs['qqfilename'])) shutil.rmtree(os.path.dirname(os.path.dirname(dest))) - - if post_upload and ( - not chunked or fileattrs["qqtotalparts"] - 1 == fileattrs["qqpartindex"] - ): + + if post_upload and (not chunked or fileattrs['qqtotalparts'] - 1 == fileattrs['qqpartindex']): post_upload() @@ -78,16 +75,13 @@ class FineUploadForm(forms.Form): class FineUploadFileInput(ClearableFileInput): - template_name = "widgets/fine_uploader.html" - + template_name = 'widgets/fine_uploader.html' def fine_uploader_id(self, name): - return name + "_" + "fine_uploader" + return name + '_' + 'fine_uploader' def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) - context["widget"].update( - { - "fine_uploader_id": self.fine_uploader_id(name), - } - ) - return context + context['widget'].update({ + 'fine_uploader_id': self.fine_uploader_id(name), + }) + return context \ No newline at end of file diff --git a/judge/utils/infinite_paginator.py b/judge/utils/infinite_paginator.py deleted file mode 100644 index afd004f..0000000 --- a/judge/utils/infinite_paginator.py +++ /dev/null @@ -1,152 +0,0 @@ -import collections -import inspect -from math import ceil - -from django.core.paginator import EmptyPage, InvalidPage -from django.http import Http404 -from django.utils.functional import cached_property -from django.utils.inspect import method_has_no_args - - -class InfinitePage(collections.abc.Sequence): - def __init__( - self, object_list, number, unfiltered_queryset, page_size, pad_pages, paginator - ): - self.object_list = list(object_list) - self.number = number - self.unfiltered_queryset = unfiltered_queryset - self.page_size = page_size - self.pad_pages = pad_pages - self.num_pages = 1e3000 - self.paginator = paginator - - def __repr__(self): - return "" % self.number - - def __len__(self): - return len(self.object_list) - - def __getitem__(self, index): - return self.object_list[index] - - @cached_property - def _after_up_to_pad(self): - first_after = self.number * self.page_size - padding_length = self.pad_pages * self.page_size - queryset = self.unfiltered_queryset[ - first_after : first_after + padding_length + 1 - ] - c = getattr(queryset, "count", None) - if callable(c) and not inspect.isbuiltin(c) and method_has_no_args(c): - return c() - return len(queryset) - - def has_next(self): - return self._after_up_to_pad > 0 - - def has_previous(self): - return self.number > 1 - - def has_other_pages(self): - return self.has_previous() or self.has_next() - - def next_page_number(self): - if not self.has_next(): - raise EmptyPage() - return self.number + 1 - - def previous_page_number(self): - if self.number <= 1: - raise EmptyPage() - return self.number - 1 - - def start_index(self): - return (self.page_size * (self.number - 1)) + 1 - - def end_index(self): - return self.start_index() + len(self.object_list) - - @cached_property - def main_range(self): - start = max(1, self.number - self.pad_pages) - end = self.number + min( - int(ceil(self._after_up_to_pad / self.page_size)), self.pad_pages - ) - return range(start, end + 1) - - @cached_property - def leading_range(self): - return range(1, min(3, self.main_range[0])) - - @cached_property - def has_trailing(self): - return self._after_up_to_pad > self.pad_pages * self.page_size - - @cached_property - def page_range(self): - result = list(self.leading_range) - main_range = self.main_range - - # Add ... element if there is space in between. - if result and result[-1] + 1 < self.main_range[0]: - result.append(False) - - result += list(main_range) - - # Add ... element if there are elements after main_range. - if self.has_trailing: - result.append(False) - return result - - -class DummyPaginator: - is_infinite = True - - def __init__(self, per_page): - self.per_page = per_page - - -def infinite_paginate(queryset, page, page_size, pad_pages, paginator=None): - if page < 1: - raise EmptyPage() - sliced = queryset[(page - 1) * page_size : page * page_size] - if page > 1 and not sliced: - raise EmptyPage() - return InfinitePage(sliced, page, queryset, page_size, pad_pages, paginator) - - -class InfinitePaginationMixin: - pad_pages = 2 - - @property - def use_infinite_pagination(self): - return True - - def paginate_queryset(self, queryset, page_size): - if not self.use_infinite_pagination: - paginator, page, object_list, has_other = super().paginate_queryset( - queryset, page_size - ) - paginator.is_infinite = False - return paginator, page, object_list, has_other - - page_kwarg = self.page_kwarg - page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1 - try: - page_number = int(page) - except ValueError: - raise Http404("Page cannot be converted to an int.") - try: - paginator = DummyPaginator(page_size) - page = infinite_paginate( - queryset, page_number, page_size, self.pad_pages, paginator - ) - return paginator, page, page.object_list, page.has_other_pages() - except InvalidPage as e: - raise Http404( - "Invalid page (%(page_number)s): %(message)s" - % { - "page_number": page_number, - "message": str(e), - } - ) diff --git a/judge/utils/mathoid.py b/judge/utils/mathoid.py index a7ceb24..a28357d 100644 --- a/judge/utils/mathoid.py +++ b/judge/utils/mathoid.py @@ -12,25 +12,25 @@ from mistune import escape from judge.utils.file_cache import HashFileCache from judge.utils.unicode import utf8bytes, utf8text -logger = logging.getLogger("judge.mathoid") -reescape = re.compile(r"(?"), - ("&", "&"), - ("−", "-"), - ("≤", r"\le"), - ("≥", r"\ge"), - ("…", "..."), - (r"\lt", "<"), - (r"\gt", ">"), + ('\u2264', r'\le'), + ('\u2265', r'\ge'), + ('\u2026', '...'), + ('\u2212', '-'), + ('≤', r'\le'), + ('≥', r'\ge'), + ('<', '<'), + ('>', '>'), + ('&', '&'), + ('−', '-'), + ('≤', r'\le'), + ('≥', r'\ge'), + ('…', '...'), + (r'\lt', '<'), + (r'\gt', '>'), ] @@ -41,17 +41,15 @@ def format_math(math): class MathoidMathParser(object): - types = ("svg", "mml", "tex", "jax") + types = ('svg', 'mml', 'tex', 'jax') def __init__(self, type): self.type = type self.mathoid_url = settings.MATHOID_URL - self.cache = HashFileCache( - settings.MATHOID_CACHE_ROOT, - settings.MATHOID_CACHE_URL, - settings.MATHOID_GZIP, - ) + self.cache = HashFileCache(settings.MATHOID_CACHE_ROOT, + settings.MATHOID_CACHE_URL, + settings.MATHOID_GZIP) mml_cache = settings.MATHOID_MML_CACHE self.mml_cache = mml_cache and caches[mml_cache] @@ -63,80 +61,69 @@ class MathoidMathParser(object): self.cache.create(hash) try: - response = requests.post( - self.mathoid_url, - data={ - "q": reescape.sub(lambda m: "\\" + m.group(0), formula).encode( - "utf-8" - ), - "type": "tex" - if formula.startswith(r"\displaystyle") - else "inline-tex", - }, - ) + response = requests.post(self.mathoid_url, data={ + 'q': reescape.sub(lambda m: '\\' + m.group(0), formula).encode('utf-8'), + 'type': 'tex' if formula.startswith(r'\displaystyle') else 'inline-tex', + }) response.raise_for_status() data = response.json() except requests.ConnectionError: - logger.exception("Failed to connect to mathoid for: %s", formula) + logger.exception('Failed to connect to mathoid for: %s', formula) return except requests.HTTPError as e: - logger.error("Mathoid failed to render: %s\n%s", formula, e.response.text) + logger.error('Mathoid failed to render: %s\n%s', formula, e.response.text) return except Exception: - logger.exception("Failed to connect to mathoid for: %s", formula) + logger.exception('Failed to connect to mathoid for: %s', formula) return - if not data["success"]: - logger.error("Mathoid failure for: %s\n%s", formula, data) + if not data['success']: + logger.error('Mathoid failure for: %s\n%s', formula, data) return - if any(i not in data for i in ("mml", "png", "svg", "mathoidStyle")): - logger.error( - "Mathoid did not return required information (mml, png, svg, mathoidStyle needed):\n%s", - data, - ) + if any(i not in data for i in ('mml', 'png', 'svg', 'mathoidStyle')): + logger.error('Mathoid did not return required information (mml, png, svg, mathoidStyle needed):\n%s', data) return - css = data["mathoidStyle"] - mml = data["mml"] + css = data['mathoidStyle'] + mml = data['mml'] result = { - "css": css, - "mml": mml, - "png": self.cache.cache_data(hash, "png", bytearray(data["png"]["data"])), - "svg": self.cache.cache_data(hash, "svg", data["svg"].encode("utf-8")), + 'css': css, 'mml': mml, + 'png': self.cache.cache_data(hash, 'png', bytearray(data['png']['data'])), + 'svg': self.cache.cache_data(hash, 'svg', data['svg'].encode('utf-8')), } - self.cache.cache_data(hash, "mml", mml.encode("utf-8"), url=False, gzip=False) - self.cache.cache_data(hash, "css", css.encode("utf-8"), url=False, gzip=False) + self.cache.cache_data(hash, 'mml', mml.encode('utf-8'), url=False, gzip=False) + self.cache.cache_data(hash, 'css', css.encode('utf-8'), url=False, gzip=False) return result def query_cache(self, hash): result = { - "svg": self.cache.get_url(hash, "svg"), - "png": self.cache.get_url(hash, "png"), + 'svg': self.cache.get_url(hash, 'svg'), + 'png': self.cache.get_url(hash, 'png'), } - key = "mathoid:css:" + hash - css = result["css"] = self.css_cache.get(key) + key = 'mathoid:css:' + hash + css = result['css'] = self.css_cache.get(key) if css is None: - css = result["css"] = self.cache.read_data(hash, "css").decode("utf-8") + css = result['css'] = self.cache.read_data(hash, 'css').decode('utf-8') self.css_cache.set(key, css, self.mml_cache_ttl) mml = None if self.mml_cache: - mml = result["mml"] = self.mml_cache.get("mathoid:mml:" + hash) + mml = result['mml'] = self.mml_cache.get('mathoid:mml:' + hash) if mml is None: - mml = result["mml"] = self.cache.read_data(hash, "mml").decode("utf-8") + mml = result['mml'] = self.cache.read_data(hash, 'mml').decode('utf-8') if self.mml_cache: - self.mml_cache.set("mathoid:mml:" + hash, mml, self.mml_cache_ttl) + self.mml_cache.set('mathoid:mml:' + hash, mml, self.mml_cache_ttl) return result def get_result(self, formula): - if self.type == "tex": + if self.type == 'tex': return hash = hashlib.sha1(utf8bytes(formula)).hexdigest() formula = utf8text(formula) - if self.cache.has_file(hash, "css"): + if self.cache.has_file(hash, 'css'): result = self.query_cache(hash) else: result = self.query_mathoid(formula, hash) @@ -144,76 +131,55 @@ class MathoidMathParser(object): if not result: return None - result["tex"] = formula - result["display"] = formula.startswith(r"\displaystyle") + result['tex'] = formula + result['display'] = formula.startswith(r'\displaystyle') return { - "mml": self.output_mml, - "msp": self.output_msp, - "svg": self.output_svg, - "jax": self.output_jax, - "png": self.output_png, - "raw": lambda x: x, + 'mml': self.output_mml, + 'msp': self.output_msp, + 'svg': self.output_svg, + 'jax': self.output_jax, + 'png': self.output_png, + 'raw': lambda x: x, }[self.type](result) def output_mml(self, result): - return result["mml"] + return result['mml'] def output_msp(self, result): # 100% MediaWiki compatibility. - return format_html( - '' - '' - '', - mark_safe(result["mml"]), - result["svg"], - result["png"], - result["css"], - result["tex"], - ["inline", "display"][result["display"]], - ) + return format_html('' + '' + '', + mark_safe(result['mml']), result['svg'], result['png'], result['css'], result['tex'], + ['inline', 'display'][result['display']]) def output_jax(self, result): - return format_html( - '' - '''{3}""" - """""" - "", - result["svg"], - result["png"], - result["css"], - result["tex"], - ["inline-math", "display-math"][result["display"]], - ["~", "$$"][result["display"]], - ) + return format_html('' + '''{3}''' + '''''' + '', + result['svg'], result['png'], result['css'], result['tex'], + ['inline-math', 'display-math'][result['display']], ['~', '$$'][result['display']]) def output_svg(self, result): - return format_html( - '{3}""", - result["svg"], - result["png"], - result["css"], - result["tex"], - ["inline-math", "display-math"][result["display"]], - ) + return format_html('{3}''', + result['svg'], result['png'], result['css'], result['tex'], + ['inline-math', 'display-math'][result['display']]) def output_png(self, result): - return format_html( - '{2}', - result["png"], - result["css"], - result["tex"], - ["inline-math", "display-math"][result["display"]], - ) + return format_html('{2}', + result['png'], result['css'], result['tex'], + ['inline-math', 'display-math'][result['display']]) def display_math(self, math): math = format_math(math) - return self.get_result(r"\displaystyle " + math) or r"\[%s\]" % escape(math) + return self.get_result(r'\displaystyle ' + math) or r'\[%s\]' % escape(math) def inline_math(self, math): math = format_math(math) - return self.get_result(math) or r"\(%s\)" % escape(math) + return self.get_result(math) or r'\(%s\)' % escape(math) diff --git a/judge/utils/opengraph.py b/judge/utils/opengraph.py index 7454415..df016c2 100644 --- a/judge/utils/opengraph.py +++ b/judge/utils/opengraph.py @@ -5,20 +5,20 @@ from judge.jinja2.markdown import markdown from judge.jinja2.reference import reference -def generate_opengraph(cache_key, data): +def generate_opengraph(cache_key, data, style): metadata = cache.get(cache_key) if metadata is None: description = None - tree = reference(markdown(data)).tree - for p in tree.iterfind(".//p"): + tree = reference(markdown(data, style)).tree + for p in tree.iterfind('.//p'): text = p.text_content().strip() if text: description = text break if description: - for remove in (r"\[", r"\]", r"\(", r"\)"): - description = description.replace(remove, "") - img = tree.xpath(".//img") - metadata = truncatewords(description, 60), img[0].get("src") if img else None + for remove in (r'\[', r'\]', r'\(', r'\)'): + description = description.replace(remove, '') + img = tree.xpath('.//img') + metadata = truncatewords(description, 60), img[0].get('src') if img else None cache.set(cache_key, metadata, 86400) return metadata diff --git a/judge/utils/problem_data.py b/judge/utils/problem_data.py index 5cf9fe7..b0a187b 100644 --- a/judge/utils/problem_data.py +++ b/judge/utils/problem_data.py @@ -2,9 +2,9 @@ import hashlib import json import os import re +import shutil import yaml import zipfile -import shutil from django.conf import settings from django.core.files.base import ContentFile @@ -13,17 +13,13 @@ from django.urls import reverse from django.utils.translation import gettext as _ from django.core.cache import cache -from judge.logging import log_exception +VALIDATOR_TEMPLATE_PATH = 'validator_template/template.py' + if os.altsep: - - def split_path_first( - path, repath=re.compile("[%s]" % re.escape(os.sep + os.altsep)) - ): + def split_path_first(path, repath=re.compile('[%s]' % re.escape(os.sep + os.altsep))): return repath.split(path, 1) - else: - def split_path_first(path): return path.split(os.sep, 1) @@ -35,8 +31,8 @@ class ProblemDataStorage(FileSystemStorage): def url(self, name): path = split_path_first(name) if len(path) != 2: - raise ValueError("This file is not accessible via a URL.") - return reverse("problem_data_file", args=path) + raise ValueError('This file is not accessible via a URL.') + return reverse('problem_data_file', args=path) def _save(self, name, content): if self.exists(name): @@ -49,13 +45,6 @@ class ProblemDataStorage(FileSystemStorage): def rename(self, old, new): return os.rename(self.path(old), self.path(new)) - def delete_directory(self, name): - directory_path = self.path(name) - try: - shutil.rmtree(directory_path) - except FileNotFoundError: - pass - class ProblemDataError(Exception): def __init__(self, message): @@ -77,136 +66,118 @@ class ProblemDataCompiler(object): batch = None def end_batch(): - if not batch["batched"]: - raise ProblemDataError(_("Empty batches not allowed.")) + if not batch['batched']: + raise ProblemDataError(_('Empty batches not allowed.')) cases.append(batch) + def make_checker_for_validator(case): + checker_name = "cppvalidator.py" + validator_path = split_path_first(case.custom_validator.name) + + if len(validator_path) != 2: + raise ProblemDataError(_('How did you corrupt the custom checker path?')) + + checker = os.path.join(settings.DMOJ_PROBLEM_DATA_ROOT, + validator_path[0], + checker_name) + + validator_name = validator_path[1] + shutil.copy(VALIDATOR_TEMPLATE_PATH, checker) + + # replace {{filecpp}} and {{problemid}} in checker file + filedata = open(checker, 'r').read() + filedata = filedata.replace('{{filecpp}}', "\'%s\'" % validator_name) + filedata = filedata.replace('{{problemid}}', "\'%s\'" % validator_path[0]) + open(checker, 'w').write(filedata) + + return checker_name + def make_checker(case): - if case.checker == "custom": + if (case.checker == 'custom'): custom_checker_path = split_path_first(case.custom_checker.name) if len(custom_checker_path) != 2: - raise ProblemDataError( - _("How did you corrupt the custom checker path?") - ) - return custom_checker_path[1] + raise ProblemDataError(_('How did you corrupt the custom checker path?')) + return(custom_checker_path[1]) - if case.checker == "customcpp": - custom_checker_path = split_path_first(case.custom_checker_cpp.name) - if len(custom_checker_path) != 2: - raise ProblemDataError( - _("How did you corrupt the custom checker path?") - ) - return { - "name": "bridged", - "args": { - "files": custom_checker_path[1], - "lang": "CPP14", - "type": "lqdoj", - }, - } - - if case.checker == "testlib": - custom_checker_path = split_path_first(case.custom_checker_cpp.name) - if len(custom_checker_path) != 2: - raise ProblemDataError( - _("How did you corrupt the custom checker path?") - ) - return { - "name": "bridged", - "args": { - "files": custom_checker_path[1], - "lang": "CPP14", - "type": "testlib", - }, - } + if (case.checker == 'customval'): + return make_checker_for_validator(case) if case.checker_args: return { - "name": case.checker, - "args": json.loads(case.checker_args), + 'name': case.checker, + 'args': json.loads(case.checker_args), } return case.checker for i, case in enumerate(self.cases, 1): - if case.type == "C": + if case.type == 'C': data = {} if batch: - if case.points is None: - case.points = 0 - case.is_pretest = batch["is_pretest"] + case.points = None + case.is_pretest = batch['is_pretest'] else: if case.points is None: - raise ProblemDataError( - _("Points must be defined for non-batch case #%d.") % i - ) - data["is_pretest"] = case.is_pretest + raise ProblemDataError(_('Points must be defined for non-batch case #%d.') % i) + data['is_pretest'] = case.is_pretest if not self.generator: if case.input_file not in self.files: - raise ProblemDataError( - _("Input file for case %d does not exist: %s") - % (i, case.input_file) - ) + raise ProblemDataError(_('Input file for case %d does not exist: %s') % + (i, case.input_file)) if case.output_file not in self.files: - raise ProblemDataError( - _("Output file for case %d does not exist: %s") - % (i, case.output_file) - ) + raise ProblemDataError(_('Output file for case %d does not exist: %s') % + (i, case.output_file)) if case.input_file: - data["in"] = case.input_file + data['in'] = case.input_file if case.output_file: - data["out"] = case.output_file + data['out'] = case.output_file if case.points is not None: - data["points"] = case.points + data['points'] = case.points if case.generator_args: - data["generator_args"] = case.generator_args.splitlines() + data['generator_args'] = case.generator_args.splitlines() if case.output_limit is not None: - data["output_limit_length"] = case.output_limit + data['output_limit_length'] = case.output_limit if case.output_prefix is not None: - data["output_prefix_length"] = case.output_prefix + data['output_prefix_length'] = case.output_prefix if case.checker: - data["checker"] = make_checker(case) + data['checker'] = make_checker(case) else: - case.checker_args = "" - case.save(update_fields=("checker_args", "is_pretest")) - (batch["batched"] if batch else cases).append(data) - elif case.type == "S": + case.checker_args = '' + case.save(update_fields=('checker_args', 'is_pretest')) + (batch['batched'] if batch else cases).append(data) + elif case.type == 'S': if batch: end_batch() if case.points is None: - raise ProblemDataError( - _("Batch start case #%d requires points.") % i - ) + raise ProblemDataError(_('Batch start case #%d requires points.') % i) batch = { - "points": case.points, - "batched": [], - "is_pretest": case.is_pretest, + 'points': case.points, + 'batched': [], + 'is_pretest': case.is_pretest, } if case.generator_args: - batch["generator_args"] = case.generator_args.splitlines() + batch['generator_args'] = case.generator_args.splitlines() if case.output_limit is not None: - batch["output_limit_length"] = case.output_limit + batch['output_limit_length'] = case.output_limit if case.output_prefix is not None: - batch["output_prefix_length"] = case.output_prefix + batch['output_prefix_length'] = case.output_prefix if case.checker: - batch["checker"] = make_checker(case) + batch['checker'] = make_checker(case) else: - case.checker_args = "" - case.input_file = "" - case.output_file = "" - case.save(update_fields=("checker_args", "input_file", "output_file")) - elif case.type == "E": + case.checker_args = '' + case.input_file = '' + case.output_file = '' + case.save(update_fields=('checker_args', 'input_file', 'output_file')) + elif case.type == 'E': if not batch: - raise ProblemDataError( - _("Attempt to end batch outside of one in case #%d") % i - ) - case.is_pretest = batch["is_pretest"] - case.input_file = "" - case.output_file = "" - case.generator_args = "" - case.checker = "" - case.checker_args = "" + raise ProblemDataError(_('Attempt to end batch outside of one in case #%d') % i) + case.is_pretest = batch['is_pretest'] + case.input_file = '' + case.output_file = '' + case.generator_args = '' + case.checker = '' + case.checker_args = '' case.save() end_batch() batch = None @@ -218,69 +189,36 @@ class ProblemDataCompiler(object): if self.data.zipfile: zippath = split_path_first(self.data.zipfile.name) if len(zippath) != 2: - raise ProblemDataError(_("How did you corrupt the zip path?")) - init["archive"] = zippath[1] + raise ProblemDataError(_('How did you corrupt the zip path?')) + init['archive'] = zippath[1] if self.generator: generator_path = split_path_first(self.generator.name) if len(generator_path) != 2: - raise ProblemDataError(_("How did you corrupt the generator path?")) - init["generator"] = generator_path[1] + raise ProblemDataError(_('How did you corrupt the generator path?')) + init['generator'] = generator_path[1] - pretests = [case for case in cases if case["is_pretest"]] + pretests = [case for case in cases if case['is_pretest']] for case in cases: - del case["is_pretest"] + del case['is_pretest'] if pretests: - init["pretest_test_cases"] = pretests + init['pretest_test_cases'] = pretests if cases: - init["test_cases"] = cases + init['test_cases'] = cases if self.data.output_limit is not None: - init["output_limit_length"] = self.data.output_limit + init['output_limit_length'] = self.data.output_limit if self.data.output_prefix is not None: - init["output_prefix_length"] = self.data.output_prefix + init['output_prefix_length'] = self.data.output_prefix if self.data.checker: - if self.data.checker == "interact": - interactor_path = split_path_first(self.data.interactive_judge.name) - if len(interactor_path) != 2: - raise ProblemDataError(_("Invalid interactor judge")) - init["interactive"] = { - "files": interactor_path[1], - "feedback": True, - "type": "lqdoj", - } - init["unbuffered"] = True - else: - init["checker"] = make_checker(self.data) + init['checker'] = make_checker(self.data) else: - self.data.checker_args = "" - if self.data.fileio_input: - if "file_io" not in init: - init["file_io"] = {} - init["file_io"]["input"] = self.data.fileio_input - if self.data.fileio_output: - if "file_io" not in init: - init["file_io"] = {} - init["file_io"]["output"] = self.data.fileio_output - if self.data.output_only: - init["output_only"] = True - if self.data.use_ioi_signature: - handler_path = split_path_first(self.data.signature_handler.name) - if len(handler_path) != 2: - raise ProblemDataError(_("Invalid signature handler")) - header_path = split_path_first(self.data.signature_header.name) - if len(header_path) != 2: - raise ProblemDataError(_("Invalid signature header")) - - init["signature_grader"] = { - "entry": handler_path[1], - "header": header_path[1], - } + self.data.checker_args = '' return init def compile(self): from judge.models import problem_data_storage - yml_file = "%s/init.yml" % self.problem.code + yml_file = '%s/init.yml' % self.problem.code try: init = yaml.safe_dump(self.make_init()) except ProblemDataError as e: @@ -288,7 +226,7 @@ class ProblemDataCompiler(object): self.data.save() problem_data_storage.delete(yml_file) else: - self.data.feedback = "" + self.data.feedback = '' self.data.save() problem_data_storage.save(yml_file, ContentFile(init)) @@ -299,27 +237,26 @@ class ProblemDataCompiler(object): def get_visible_content(data): - data = data or b"" - data = data.replace(b"\r\n", b"\r").replace(b"\r", b"\n") + data = data or b'' + data = data.replace(b'\r\n', b'\r').replace(b'\r', b'\n') - data = data.decode("utf-8") + data = data.decode('utf-8') - if len(data) > settings.TESTCASE_VISIBLE_LENGTH: - data = data[: settings.TESTCASE_VISIBLE_LENGTH] - data += "." * 3 + if (len(data) > settings.TESTCASE_VISIBLE_LENGTH): + data = data[:settings.TESTCASE_VISIBLE_LENGTH] + data += '.' * 3 return data def get_file_cachekey(file): return hashlib.sha1(file.encode()).hexdigest() - def get_problem_case(problem, files): result = {} uncached_files = [] for file in files: - cache_key = "problem_archive:%s:%s" % (problem.code, get_file_cachekey(file)) + cache_key = 'problem_archive:%s:%s' % (problem.code, get_file_cachekey(file)) qs = cache.get(cache_key) if qs is None: uncached_files.append(file) @@ -329,37 +266,33 @@ def get_problem_case(problem, files): if not uncached_files: return result - archive_path = os.path.join( - settings.DMOJ_PROBLEM_DATA_ROOT, str(problem.data_files.zipfile) - ) + archive_path = os.path.join(settings.DMOJ_PROBLEM_DATA_ROOT, + str(problem.data_files.zipfile)) if not os.path.exists(archive_path): - log_exception('archive file "%s" does not exist' % archive_path) - return {} + raise Exception( + 'archive file "%s" does not exist' % archive_path) try: - archive = zipfile.ZipFile(archive_path, "r") + archive = zipfile.ZipFile(archive_path, 'r') except zipfile.BadZipfile: - log_exception('bad archive: "%s"' % archive_path) - return {} + raise Exception('bad archive: "%s"' % archive_path) for file in uncached_files: - cache_key = "problem_archive:%s:%s" % (problem.code, get_file_cachekey(file)) + cache_key = 'problem_archive:%s:%s' % (problem.code, get_file_cachekey(file)) with archive.open(file) as f: s = f.read(settings.TESTCASE_VISIBLE_LENGTH + 3) # add this so there are no characters left behind (ex, 'á' = 2 utf-8 chars) while True: try: - s.decode("utf-8") + s.decode('utf-8') break except UnicodeDecodeError: next_char = f.read(1) if next_char: s += next_char else: - s = f"File {file} is not able to decode in utf-8" - s = s.encode("utf-8") - break + raise Exception('File %s is not able to decode in utf-8' % file) qs = get_visible_content(s) cache.set(cache_key, qs, 86400) result[file] = qs - return result + return result \ No newline at end of file diff --git a/judge/utils/problems.py b/judge/utils/problems.py index 6423c70..ba846e6 100644 --- a/judge/utils/problems.py +++ b/judge/utils/problems.py @@ -1,8 +1,6 @@ from collections import defaultdict from math import e -from datetime import datetime, timedelta -import random -from enum import Enum +import os, zipfile from django.conf import settings from django.core.cache import cache @@ -10,137 +8,82 @@ from django.db.models import Case, Count, ExpressionWrapper, F, Max, Q, When from django.db.models.fields import FloatField from django.utils import timezone from django.utils.translation import gettext as _, gettext_noop -from django.http import Http404 from judge.models import Problem, Submission -from judge.ml.collab_filter import CollabFilter -from judge.caching import cache_wrapper -__all__ = [ - "contest_completed_ids", - "get_result_data", - "user_completed_ids", - "user_editable_ids", - "user_tester_ids", -] +__all__ = ['contest_completed_ids', 'get_result_data', 'user_completed_ids', 'user_authored_ids', 'user_editable_ids'] -@cache_wrapper(prefix="user_tester") -def user_tester_ids(profile): - return set( - Problem.testers.through.objects.filter(profile=profile) - .values_list("problem_id", flat=True) - .distinct() - ) +def user_authored_ids(profile): + result = set(Problem.objects.filter(authors=profile).values_list('id', flat=True)) + return result -@cache_wrapper(prefix="user_editable") def user_editable_ids(profile): - result = set( - ( - Problem.objects.filter(authors=profile) - | Problem.objects.filter(curators=profile) - ) - .values_list("id", flat=True) - .distinct() - ) + result = set((Problem.objects.filter(authors=profile) | Problem.objects.filter(curators=profile)) + .values_list('id', flat=True)) return result -@cache_wrapper(prefix="contest_complete") def contest_completed_ids(participation): - result = set( - participation.submissions.filter( - submission__result="AC", points=F("problem__points") - ) - .values_list("problem__problem__id", flat=True) - .distinct() - ) + key = 'contest_complete:%d' % participation.id + result = cache.get(key) + if result is None: + result = set(participation.submissions.filter(submission__result='AC', points=F('problem__points')) + .values_list('problem__problem__id', flat=True).distinct()) + cache.set(key, result, 86400) return result -@cache_wrapper(prefix="user_complete") def user_completed_ids(profile): - result = set( - Submission.objects.filter( - user=profile, result="AC", points=F("problem__points") - ) - .values_list("problem_id", flat=True) - .distinct() - ) + key = 'user_complete:%d' % profile.id + result = cache.get(key) + if result is None: + result = set(Submission.objects.filter(user=profile, result='AC', points=F('problem__points')) + .values_list('problem_id', flat=True).distinct()) + cache.set(key, result, 86400) return result -@cache_wrapper(prefix="contest_attempted") def contest_attempted_ids(participation): - result = { - id: {"achieved_points": points, "max_points": max_points} - for id, max_points, points in ( - participation.submissions.values_list( - "problem__problem__id", "problem__points" - ) - .annotate(points=Max("points")) - .filter(points__lt=F("problem__points")) - ) - } + key = 'contest_attempted:%s' % participation.id + result = cache.get(key) + if result is None: + result = {id: {'achieved_points': points, 'max_points': max_points} + for id, max_points, points in (participation.submissions + .values_list('problem__problem__id', 'problem__points') + .annotate(points=Max('points')) + .filter(points__lt=F('problem__points')))} + cache.set(key, result, 86400) return result -@cache_wrapper(prefix="user_attempted") def user_attempted_ids(profile): - result = { - id: { - "achieved_points": points, - "max_points": max_points, - "last_submission": last_submission, - "code": problem_code, - "name": problem_name, - } - for id, max_points, problem_code, problem_name, points, last_submission in ( - Submission.objects.filter(user=profile) - .values_list( - "problem__id", "problem__points", "problem__code", "problem__name" - ) - .annotate(points=Max("points"), last_submission=Max("id")) - .filter(points__lt=F("problem__points")) - ) - } + key = 'user_attempted:%s' % profile.id + result = cache.get(key) + if result is None: + result = {id: {'achieved_points': points, 'max_points': max_points} + for id, max_points, points in (Submission.objects.filter(user=profile) + .values_list('problem__id', 'problem__points') + .annotate(points=Max('points')) + .filter(points__lt=F('problem__points')))} + cache.set(key, result, 86400) return result def _get_result_data(results): return { - "categories": [ + 'categories': [ # Using gettext_noop here since this will be tacked into the cache, so it must be language neutral. # The caller, SubmissionList.get_result_data will run ugettext on the name. - {"code": "AC", "name": gettext_noop("Accepted"), "count": results["AC"]}, - { - "code": "WA", - "name": gettext_noop("Wrong Answer"), - "count": results["WA"], - }, - { - "code": "CE", - "name": gettext_noop("Compile Error"), - "count": results["CE"], - }, - { - "code": "TLE", - "name": gettext_noop("Time Limit Exceeded"), - "count": results["TLE"], - }, - { - "code": "ERR", - "name": gettext_noop("Error"), - "count": results["MLE"] - + results["OLE"] - + results["IR"] - + results["RTE"] - + results["AB"] - + results["IE"], - }, + {'code': 'AC', 'name': gettext_noop('Accepted'), 'count': results['AC']}, + {'code': 'WA', 'name': gettext_noop('Wrong'), 'count': results['WA']}, + {'code': 'CE', 'name': gettext_noop('Compile Error'), 'count': results['CE']}, + {'code': 'TLE', 'name': gettext_noop('Timeout'), 'count': results['TLE']}, + {'code': 'ERR', 'name': gettext_noop('Error'), + 'count': results['MLE'] + results['OLE'] + results['IR'] + results['RTE'] + results['AB'] + results['IE']}, ], - "total": sum(results.values()), + 'total': sum(results.values()), } @@ -150,16 +93,8 @@ def get_result_data(*args, **kwargs): if kwargs: raise ValueError(_("Can't pass both queryset and keyword filters")) else: - submissions = ( - Submission.objects.filter(**kwargs) - if kwargs is not None - else Submission.objects - ) - raw = ( - submissions.values("result") - .annotate(count=Count("result")) - .values_list("result", "count") - ) + submissions = Submission.objects.filter(**kwargs) if kwargs is not None else Submission.objects + raw = submissions.values('result').annotate(count=Count('result')).values_list('result', 'count') return _get_result_data(defaultdict(int, raw)) @@ -167,163 +102,48 @@ def editable_problems(user, profile=None): subquery = Problem.objects.all() if profile is None: profile = user.profile - if not user.has_perm("judge.edit_all_problem"): + if not user.has_perm('judge.edit_all_problem'): subfilter = Q(authors__id=profile.id) | Q(curators__id=profile.id) - if user.has_perm("judge.edit_public_problem"): + if user.has_perm('judge.edit_public_problem'): subfilter |= Q(is_public=True) subquery = subquery.filter(subfilter) return subquery -@cache_wrapper(prefix="hp", timeout=14400) def hot_problems(duration, limit): - qs = Problem.get_public_problems().filter( - submission__date__gt=timezone.now() - duration - ) - qs0 = ( - qs.annotate(k=Count("submission__user", distinct=True)) - .order_by("-k") - .values_list("k", flat=True) - ) + cache_key = 'hot_problems:%d:%d' % (duration.total_seconds(), limit) + qs = cache.get(cache_key) + if qs is None: + qs = Problem.get_public_problems() \ + .filter(submission__date__gt=timezone.now() - duration, points__gt=3, points__lt=25) + qs0 = qs.annotate(k=Count('submission__user', distinct=True)).order_by('-k').values_list('k', flat=True) - if not qs0: - return [] - # make this an aggregate - mx = float(qs0[0]) + if not qs0: + return [] + # make this an aggregate + mx = float(qs0[0]) - qs = qs.annotate(unique_user_count=Count("submission__user", distinct=True)) - # fix braindamage in excluding CE - qs = qs.annotate( - submission_volume=Count( - Case( - When(submission__result="AC", then=1), - When(submission__result="WA", then=1), - When(submission__result="IR", then=1), - When(submission__result="RTE", then=1), - When(submission__result="TLE", then=1), - When(submission__result="OLE", then=1), - output_field=FloatField(), - ) - ) - ) - qs = qs.annotate( - ac_volume=Count( - Case( - When(submission__result="AC", then=1), - output_field=FloatField(), - ) - ) - ) - qs = qs.filter(unique_user_count__gt=max(mx / 3.0, 1)) + qs = qs.annotate(unique_user_count=Count('submission__user', distinct=True)) + # fix braindamage in excluding CE + qs = qs.annotate(submission_volume=Count(Case( + When(submission__result='AC', then=1), + When(submission__result='WA', then=1), + When(submission__result='IR', then=1), + When(submission__result='RTE', then=1), + When(submission__result='TLE', then=1), + When(submission__result='OLE', then=1), + output_field=FloatField(), + ))) + qs = qs.annotate(ac_volume=Count(Case( + When(submission__result='AC', then=1), + output_field=FloatField(), + ))) + qs = qs.filter(unique_user_count__gt=max(mx / 3.0, 1)) - qs = ( - qs.annotate( - ordering=ExpressionWrapper( - 0.02 - * F("points") - * (0.4 * F("ac_volume") / F("submission_volume") + 0.6 * F("ac_rate")) - + 100 * e ** (F("unique_user_count") / mx), - output_field=FloatField(), - ) - ) - .order_by("-ordering") - .defer("description")[:limit] - ) - return qs + qs = qs.annotate(ordering=ExpressionWrapper( + 0.5 * F('points') * (0.4 * F('ac_volume') / F('submission_volume') + 0.6 * F('ac_rate')) + + 100 * e ** (F('unique_user_count') / mx), output_field=FloatField(), + )).order_by('-ordering').defer('description')[:limit] - -@cache_wrapper(prefix="grp", timeout=14400) -def get_related_problems(profile, problem, limit=8): - if not profile or not settings.ML_OUTPUT_PATH: - return None - problemset = Problem.get_visible_problems(profile.user).values_list("id", flat=True) - problemset = problemset.exclude(id__in=user_completed_ids(profile)) - problemset = problemset.exclude(id=problem.id) - cf_model = CollabFilter("collab_filter") - results = cf_model.problem_neighbors( - problem, problemset, CollabFilter.DOT, limit - ) + cf_model.problem_neighbors(problem, problemset, CollabFilter.COSINE, limit) - results = list(set([i[1] for i in results])) - seed = datetime.now().strftime("%d%m%Y") - random.shuffle(results) - results = results[:limit] - results = [Problem.objects.get(id=i) for i in results] - return results - - -def finished_submission(sub): - keys = ["user_complete:%d" % sub.user_id, "user_attempted:%s" % sub.user_id] - if hasattr(sub, "contest"): - participation = sub.contest.participation - keys += ["contest_complete:%d" % participation.id] - keys += ["contest_attempted:%d" % participation.id] - cache.delete_many(keys) - - -class RecommendationType(Enum): - HOT_PROBLEM = 1 - CF_DOT = 2 - CF_COSINE = 3 - CF_TIME_DOT = 4 - CF_TIME_COSINE = 5 - - -# Return a list of list. Each inner list correspond to each type in types -def get_user_recommended_problems( - user_id, - problem_ids, - recommendation_types, - limits, - shuffle=False, -): - cf_model = CollabFilter("collab_filter") - cf_time_model = CollabFilter("collab_filter_time") - - def get_problem_ids_from_type(rec_type, limit): - if type(rec_type) == int: - try: - rec_type = RecommendationType(rec_type) - except ValueError: - raise Http404() - if rec_type == RecommendationType.HOT_PROBLEM: - return [ - problem.id - for problem in hot_problems(timedelta(days=7), limit) - if problem.id in set(problem_ids) - ] - if rec_type == RecommendationType.CF_DOT: - return cf_model.user_recommendations( - user_id, problem_ids, cf_model.DOT, limit - ) - if rec_type == RecommendationType.CF_COSINE: - return cf_model.user_recommendations( - user_id, problem_ids, cf_model.COSINE, limit - ) - if rec_type == RecommendationType.CF_TIME_DOT: - return cf_time_model.user_recommendations( - user_id, problem_ids, cf_model.DOT, limit - ) - if rec_type == RecommendationType.CF_TIME_COSINE: - return cf_time_model.user_recommendations( - user_id, problem_ids, cf_model.COSINE, limit - ) - return [] - - all_problems = [] - for rec_type, limit in zip(recommendation_types, limits): - all_problems += get_problem_ids_from_type(rec_type, limit) - if shuffle: - seed = datetime.now().strftime("%d%m%Y") - random.Random(seed).shuffle(all_problems) - - # deduplicate problems - res = [] - used_pid = set() - - for obj in all_problems: - if type(obj) == tuple: - obj = obj[1] - if obj not in used_pid: - res.append(obj) - used_pid.add(obj) - return res + cache.set(cache_key, qs, 900) + return qs \ No newline at end of file diff --git a/judge/utils/pwned.py b/judge/utils/pwned.py index bd67acc..fa3df63 100644 --- a/judge/utils/pwned.py +++ b/judge/utils/pwned.py @@ -40,13 +40,14 @@ import requests from django.conf import settings from django.contrib.auth.password_validation import CommonPasswordValidator from django.core.exceptions import ValidationError +from django.utils.six import string_types from django.utils.translation import gettext as _, ungettext from judge.utils.unicode import utf8bytes log = logging.getLogger(__name__) -API_ENDPOINT = "https://api.pwnedpasswords.com/range/{}" +API_ENDPOINT = 'https://api.pwnedpasswords.com/range/{}' REQUEST_TIMEOUT = 2.0 # 2 seconds @@ -60,19 +61,19 @@ def _get_pwned(prefix): url=API_ENDPOINT.format(prefix), timeout=getattr( settings, - "PWNED_PASSWORDS_API_TIMEOUT", + 'PWNED_PASSWORDS_API_TIMEOUT', REQUEST_TIMEOUT, ), ) response.raise_for_status() except requests.RequestException: # Gracefully handle timeouts and HTTP error response codes. - log.warning("Skipped Pwned Passwords check due to error", exc_info=True) + log.warning('Skipped Pwned Passwords check due to error', exc_info=True) return None results = {} for line in response.text.splitlines(): - line_suffix, _, times = line.partition(":") + line_suffix, _, times = line.partition(':') results[line_suffix] = int(times) return results @@ -82,8 +83,8 @@ def pwned_password(password): """ Checks a password against the Pwned Passwords database. """ - if not isinstance(password, str): - raise TypeError("Password values to check must be strings.") + if not isinstance(password, string_types): + raise TypeError('Password values to check must be strings.') password_hash = hashlib.sha1(utf8bytes(password)).hexdigest().upper() prefix, suffix = password_hash[:5], password_hash[5:] results = _get_pwned(prefix) @@ -97,22 +98,21 @@ class PwnedPasswordsValidator(object): """ Password validator which checks the Pwned Passwords database. """ - DEFAULT_HELP_MESSAGE = _("Your password can't be a commonly used password.") - DEFAULT_PWNED_MESSAGE = _("This password is too common.") + DEFAULT_PWNED_MESSAGE = _('This password is too common.') def __init__(self, error_message=None, help_message=None): self.help_message = help_message or self.DEFAULT_HELP_MESSAGE error_message = error_message or self.DEFAULT_PWNED_MESSAGE # If there is no plural, use the same message for both forms. - if isinstance(error_message, str): + if isinstance(error_message, string_types): singular, plural = error_message, error_message else: singular, plural = error_message self.error_message = { - "singular": singular, - "plural": plural, + 'singular': singular, + 'plural': plural, } def validate(self, password, user=None): @@ -125,12 +125,12 @@ class PwnedPasswordsValidator(object): elif amount: raise ValidationError( ungettext( - self.error_message["singular"], - self.error_message["plural"], + self.error_message['singular'], + self.error_message['plural'], amount, ), - params={"amount": amount}, - code="pwned_password", + params={'amount': amount}, + code='pwned_password', ) def get_help_text(self): diff --git a/judge/utils/ranker.py b/judge/utils/ranker.py index 3f15e62..52f0e4c 100644 --- a/judge/utils/ranker.py +++ b/judge/utils/ranker.py @@ -1,7 +1,7 @@ from operator import attrgetter -def ranker(iterable, key=attrgetter("points"), rank=0): +def ranker(iterable, key=attrgetter('points'), rank=0): delta = 1 last = None for item in iterable: @@ -12,3 +12,4 @@ def ranker(iterable, key=attrgetter("points"), rank=0): delta += 1 yield rank, item last = key(item) + diff --git a/judge/utils/raw_sql.py b/judge/utils/raw_sql.py index 736314d..edbca4c 100644 --- a/judge/utils/raw_sql.py +++ b/judge/utils/raw_sql.py @@ -1,43 +1,48 @@ +from copy import copy + from django.db import connections +from django.db.models import Field +from django.db.models.expressions import RawSQL from django.db.models.sql.constants import INNER, LOUTER from django.db.models.sql.datastructures import Join +from django.utils import six from judge.utils.cachedict import CacheDict +def unique_together_left_join(queryset, model, link_field_name, filter_field_name, filter_value, parent_model=None): + link_field = copy(model._meta.get_field(link_field_name).remote_field) + filter_field = model._meta.get_field(filter_field_name) + + def restrictions(where_class, alias, related_alias): + cond = where_class() + cond.add(filter_field.get_lookup('exact')(filter_field.get_col(alias), filter_value), 'AND') + return cond + + link_field.get_extra_restriction = restrictions + + if parent_model is not None: + parent_alias = parent_model._meta.db_table + else: + parent_alias = queryset.query.get_initial_alias() + return queryset.query.join(Join(model._meta.db_table, parent_alias, None, LOUTER, link_field, True)) + + class RawSQLJoin(Join): - def __init__( - self, - subquery, - subquery_params, - parent_alias, - table_alias, - join_type, - join_field, - nullable, - filtered_relation=None, - ): + def __init__(self, subquery, subquery_params, parent_alias, table_alias, join_type, join_field, nullable, + filtered_relation=None): self.subquery_params = subquery_params - super().__init__( - subquery, - parent_alias, - table_alias, - join_type, - join_field, - nullable, - filtered_relation, - ) + super().__init__(subquery, parent_alias, table_alias, join_type, join_field, nullable, filtered_relation) def as_sql(self, compiler, connection): - compiler.quote_cache[self.table_name] = "(%s)" % self.table_name + compiler.quote_cache[self.table_name] = '(%s)' % self.table_name sql, params = super().as_sql(compiler, connection) return sql, self.subquery_params + params class FakeJoinField: - def __init__(self, joining_columns, related_model): + def __init__(self, joining_columns): self.joining_columns = joining_columns - self.related_model = related_model def get_joining_columns(self): return self.joining_columns @@ -46,44 +51,33 @@ class FakeJoinField: pass -def join_sql_subquery( - queryset, - subquery, - params, - join_fields, - alias, - related_model, - join_type=INNER, - parent_model=None, -): +def join_sql_subquery(queryset, subquery, params, join_fields, alias, join_type=INNER, parent_model=None): if parent_model is not None: parent_alias = parent_model._meta.db_table else: parent_alias = queryset.query.get_initial_alias() - if isinstance(queryset.query.external_aliases, dict): # Django 3.x - queryset.query.external_aliases[alias] = True - else: - queryset.query.external_aliases.add(alias) - join = RawSQLJoin( - subquery, - params, - parent_alias, - alias, - join_type, - FakeJoinField(join_fields, related_model), - join_type == LOUTER, - ) + queryset.query.external_aliases.add(alias) + join = RawSQLJoin(subquery, params, parent_alias, alias, join_type, FakeJoinField(join_fields), join_type == LOUTER) queryset.query.join(join) join.table_alias = alias +def RawSQLColumn(model, field=None): + if isinstance(model, Field): + field = model + model = field.model + if isinstance(field, six.string_types): + field = model._meta.get_field(field) + return RawSQL('%s.%s' % (model._meta.db_table, field.get_attname_column()[1]), ()) + + def make_straight_join_query(QueryType): class Query(QueryType): def join(self, join, *args, **kwargs): alias = super().join(join, *args, **kwargs) join = self.alias_map[alias] if join.join_type == INNER: - join.join_type = "STRAIGHT_JOIN" + join.join_type = 'STRAIGHT_JOIN' return alias return Query @@ -93,7 +87,7 @@ straight_join_cache = CacheDict(make_straight_join_query) def use_straight_join(queryset): - if connections[queryset.db].vendor != "mysql": + if connections[queryset.db].vendor != 'mysql': return try: cloner = queryset.query.chain diff --git a/judge/utils/recaptcha.py b/judge/utils/recaptcha.py index 9be7e5f..331a12d 100644 --- a/judge/utils/recaptcha.py +++ b/judge/utils/recaptcha.py @@ -6,7 +6,6 @@ except ImportError: ReCaptchaWidget = None else: from django.conf import settings - - if not hasattr(settings, "RECAPTCHA_PRIVATE_KEY"): + if not hasattr(settings, 'RECAPTCHA_PRIVATE_KEY'): ReCaptchaField = None ReCaptchaWidget = None diff --git a/judge/utils/stats.py b/judge/utils/stats.py index 6c79d8c..ba923aa 100644 --- a/judge/utils/stats.py +++ b/judge/utils/stats.py @@ -1,30 +1,10 @@ from operator import itemgetter -__all__ = ("chart_colors", "highlight_colors", "get_pie_chart", "get_bar_chart") +__all__ = ('chart_colors', 'highlight_colors', 'get_pie_chart', 'get_bar_chart') -chart_colors = [ - 0x3366CC, - 0xDC3912, - 0xFF9900, - 0x109618, - 0x990099, - 0x3B3EAC, - 0x0099C6, - 0xDD4477, - 0x66AA00, - 0xB82E2E, - 0x316395, - 0x994499, - 0x22AA99, - 0xAAAA11, - 0x6633CC, - 0xE67300, - 0x8B0707, - 0x329262, - 0x5574A6, - 0x3B3EAC, -] +chart_colors = [0x3366CC, 0xDC3912, 0xFF9900, 0x109618, 0x990099, 0x3B3EAC, 0x0099C6, 0xDD4477, 0x66AA00, 0xB82E2E, + 0x316395, 0x994499, 0x22AA99, 0xAAAA11, 0x6633CC, 0xE67300, 0x8B0707, 0x329262, 0x5574A6, 0x3B3EAC] highlight_colors = [] @@ -33,26 +13,25 @@ highlight_colors = [] def _highlight_colors(): for color in chart_colors: r, g, b = color >> 16, (color >> 8) & 0xFF, color & 0xFF - highlight_colors.append( - "#%02X%02X%02X" - % (min(int(r * 1.2), 255), min(int(g * 1.2), 255), min(int(b * 1.2), 255)) - ) + highlight_colors.append('#%02X%02X%02X' % (min(int(r * 1.2), 255), + min(int(g * 1.2), 255), + min(int(b * 1.2), 255))) _highlight_colors() -chart_colors = list(map("#%06X".__mod__, chart_colors)) +chart_colors = list(map('#%06X'.__mod__, chart_colors)) def get_pie_chart(data): return { - "labels": list(map(itemgetter(0), data)), - "datasets": [ + 'labels': list(map(itemgetter(0), data)), + 'datasets': [ { - "backgroundColor": chart_colors, - "highlightBackgroundColor": highlight_colors, - "data": list(map(itemgetter(1), data)), + 'backgroundColor': chart_colors, + 'highlightBackgroundColor': highlight_colors, + 'data': list(map(itemgetter(1), data)), }, ], } @@ -60,39 +39,30 @@ def get_pie_chart(data): def get_bar_chart(data, **kwargs): return { - "labels": list(map(itemgetter(0), data)), - "datasets": [ + 'labels': list(map(itemgetter(0), data)), + 'datasets': [ { - "backgroundColor": kwargs.get("fillColor", "rgba(151,187,205,0.5)"), - "borderColor": kwargs.get("strokeColor", "rgba(151,187,205,0.8)"), - "borderWidth": 1, - "hoverBackgroundColor": kwargs.get( - "highlightFill", "rgba(151,187,205,0.75)" - ), - "hoverBorderColor": kwargs.get( - "highlightStroke", "rgba(151,187,205,1)" - ), - "data": list(map(itemgetter(1), data)), + 'backgroundColor': kwargs.get('fillColor', 'rgba(151,187,205,0.5)'), + 'borderColor': kwargs.get('strokeColor', 'rgba(151,187,205,0.8)'), + 'borderWidth': 1, + 'hoverBackgroundColor': kwargs.get('highlightFill', 'rgba(151,187,205,0.75)'), + 'hoverBorderColor': kwargs.get('highlightStroke', 'rgba(151,187,205,1)'), + 'data': list(map(itemgetter(1), data)), }, ], } - def get_histogram(data, **kwargs): return { - "labels": [round(i, 1) for i in list(map(itemgetter(0), data))], - "datasets": [ + 'labels': [round(i, 1) for i in list(map(itemgetter(0), data))], + 'datasets': [ { - "backgroundColor": kwargs.get("fillColor", "rgba(151,187,205,0.5)"), - "borderColor": kwargs.get("strokeColor", "rgba(151,187,205,0.8)"), - "borderWidth": 1, - "hoverBackgroundColor": kwargs.get( - "highlightFill", "rgba(151,187,205,0.75)" - ), - "hoverBorderColor": kwargs.get( - "highlightStroke", "rgba(151,187,205,1)" - ), - "data": list(map(itemgetter(1), data)), + 'backgroundColor': kwargs.get('fillColor', 'rgba(151,187,205,0.5)'), + 'borderColor': kwargs.get('strokeColor', 'rgba(151,187,205,0.8)'), + 'borderWidth': 1, + 'hoverBackgroundColor': kwargs.get('highlightFill', 'rgba(151,187,205,0.75)'), + 'hoverBorderColor': kwargs.get('highlightStroke', 'rgba(151,187,205,1)'), + 'data': list(map(itemgetter(1), data)), }, ], - } + } \ No newline at end of file diff --git a/judge/utils/subscription.py b/judge/utils/subscription.py new file mode 100644 index 0000000..1129b0d --- /dev/null +++ b/judge/utils/subscription.py @@ -0,0 +1,8 @@ +from django.conf import settings + +if 'newsletter' in settings.INSTALLED_APPS: + from newsletter.models import Subscription +else: + Subscription = None + +newsletter_id = None if Subscription is None else settings.DMOJ_NEWSLETTER_ID_ON_REGISTER diff --git a/judge/utils/texoid.py b/judge/utils/texoid.py index cb51169..d668066 100644 --- a/judge/utils/texoid.py +++ b/judge/utils/texoid.py @@ -10,16 +10,16 @@ from django.core.cache import caches from judge.utils.file_cache import HashFileCache from judge.utils.unicode import utf8bytes -logger = logging.getLogger("judge.texoid") +logger = logging.getLogger('judge.texoid') -TEXOID_ENABLED = hasattr(settings, "TEXOID_URL") +TEXOID_ENABLED = hasattr(settings, 'TEXOID_URL') class TexoidRenderer(object): def __init__(self): - self.cache = HashFileCache( - settings.TEXOID_CACHE_ROOT, settings.TEXOID_CACHE_URL, settings.TEXOID_GZIP - ) + self.cache = HashFileCache(settings.TEXOID_CACHE_ROOT, + settings.TEXOID_CACHE_URL, + settings.TEXOID_GZIP) self.meta_cache = caches[settings.TEXOID_META_CACHE] self.meta_cache_ttl = settings.TEXOID_META_CACHE_TTL @@ -27,69 +27,59 @@ class TexoidRenderer(object): self.cache.create(hash) try: - response = requests.post( - settings.TEXOID_URL, - data=utf8bytes(document), - headers={ - "Content-Type": "application/x-tex", - }, - ) + response = requests.post(settings.TEXOID_URL, data=utf8bytes(document), headers={ + 'Content-Type': 'application/x-tex', + }) response.raise_for_status() except requests.HTTPError as e: if e.response.status == 400: - logger.error( - "Texoid failed to render: %s\n%s", document, e.response.text - ) + logger.error('Texoid failed to render: %s\n%s', document, e.response.text) else: - logger.exception("Failed to connect to texoid for: %s", document) + logger.exception('Failed to connect to texoid for: %s', document) return except Exception: - logger.exception("Failed to connect to texoid for: %s", document) + logger.exception('Failed to connect to texoid for: %s', document) return try: data = response.json() except ValueError: - logger.exception( - "Invalid texoid response for: %s\n%s", document, response.text - ) + logger.exception('Invalid texoid response for: %s\n%s', document, response.text) return - if not data["success"]: - logger.error("Texoid failure for: %s\n%s", document, data) - return {"error": data["error"]} + if not data['success']: + logger.error('Texoid failure for: %s\n%s', document, data) + return {'error': data['error']} - meta = data["meta"] - self.cache.cache_data( - hash, "meta", utf8bytes(json.dumps(meta)), url=False, gzip=False - ) + meta = data['meta'] + self.cache.cache_data(hash, 'meta', utf8bytes(json.dumps(meta)), url=False, gzip=False) result = { - "png": self.cache.cache_data(hash, "png", b64decode(data["png"])), - "svg": self.cache.cache_data(hash, "svg", data["svg"].encode("utf-8")), - "meta": meta, + 'png': self.cache.cache_data(hash, 'png', b64decode(data['png'])), + 'svg': self.cache.cache_data(hash, 'svg', data['svg'].encode('utf-8')), + 'meta': meta, } return result def query_cache(self, hash): result = { - "svg": self.cache.get_url(hash, "svg"), - "png": self.cache.get_url(hash, "png"), + 'svg': self.cache.get_url(hash, 'svg'), + 'png': self.cache.get_url(hash, 'png'), } - key = "texoid:meta:" + hash + key = 'texoid:meta:' + hash cached_meta = self.meta_cache.get(key) if cached_meta is None: - cached_meta = json.loads(self.cache.read_data(hash, "meta").decode("utf-8")) + cached_meta = json.loads(self.cache.read_data(hash, 'meta').decode('utf-8')) self.meta_cache.set(key, cached_meta, self.meta_cache_ttl) - result["meta"] = cached_meta + result['meta'] = cached_meta return result def get_result(self, formula): hash = hashlib.sha1(utf8bytes(formula)).hexdigest() - if self.cache.has_file(hash, "svg"): + if self.cache.has_file(hash, 'svg'): return self.query_cache(hash) else: return self.query_texoid(formula, hash) diff --git a/judge/utils/tickets.py b/judge/utils/tickets.py index b630e60..1743db4 100644 --- a/judge/utils/tickets.py +++ b/judge/utils/tickets.py @@ -12,10 +12,6 @@ def own_ticket_filter(profile_id): def filter_visible_tickets(queryset, user, profile=None): if profile is None: profile = user.profile - return queryset.filter( - own_ticket_filter(profile.id) - | Q( - content_type=ContentType.objects.get_for_model(Problem), - object_id__in=editable_problems(user, profile), - ) - ).distinct() + return queryset.filter(own_ticket_filter(profile.id) | + Q(content_type=ContentType.objects.get_for_model(Problem), + object_id__in=editable_problems(user, profile))).distinct() diff --git a/judge/utils/timedelta.py b/judge/utils/timedelta.py index adf3330..292caeb 100644 --- a/judge/utils/timedelta.py +++ b/judge/utils/timedelta.py @@ -3,7 +3,7 @@ import datetime from django.utils.translation import npgettext, pgettext, ungettext -def nice_repr(timedelta, display="long", sep=", "): +def nice_repr(timedelta, display='long', sep=', '): """ Turns a datetime.timedelta object into a nice string repr. @@ -16,9 +16,7 @@ def nice_repr(timedelta, display="long", sep=", "): '1d, 1s' """ - assert isinstance( - timedelta, datetime.timedelta - ), "First argument must be a timedelta." + assert isinstance(timedelta, datetime.timedelta), 'First argument must be a timedelta.' result = [] @@ -28,94 +26,65 @@ def nice_repr(timedelta, display="long", sep=", "): minutes = (timedelta.seconds % 3600) // 60 seconds = timedelta.seconds % 60 - if display == "simple-no-seconds": + if display == 'simple-no-seconds': days += weeks * 7 if days: if hours or minutes: - return "%d day%s %d:%02d" % (days, "s"[days == 1 :], hours, minutes) - return "%d day%s" % (days, "s"[days == 1 :]) + return '%d day%s %d:%02d' % (days, 's'[days == 1:], hours, minutes) + return '%d day%s' % (days, 's'[days == 1:]) else: - return "%d:%02d" % (hours, minutes) - elif display == "sql": + return '%d:%02d' % (hours, minutes) + elif display == 'sql': days += weeks * 7 - return "%d %02d:%02d:%02d" % (days, hours, minutes, seconds) - elif display == "simple": + return '%d %02d:%02d:%02d' % (days, hours, minutes, seconds) + elif display == 'simple': days += weeks * 7 if days: - return "%d day%s %02d:%02d:%02d" % ( - days, - "s"[days == 1 :], - hours, - minutes, - seconds, - ) + return '%d day%s %02d:%02d:%02d' % (days, 's'[days == 1:], hours, minutes, seconds) else: - return "%02d:%02d:%02d" % (hours, minutes, seconds) - elif display == "localized": + return '%02d:%02d:%02d' % (hours, minutes, seconds) + elif display == 'localized': days += weeks * 7 if days: - return ( - npgettext( - "time format with day", "%d day %h:%m:%s", "%d days %h:%m:%s", days - ) - .replace("%d", str(days)) - .replace("%h", "%02d" % hours) - .replace("%m", "%02d" % minutes) - .replace("%s", "%02d" % seconds) - ) + return npgettext('time format with day', '%d day %h:%m:%s', '%d days %h:%m:%s', days) \ + .replace('%d', str(days)).replace('%h', '%02d' % hours).replace('%m', '%02d' % minutes) \ + .replace('%s', '%02d' % seconds) else: - return ( - pgettext("time format without day", "%h:%m:%s") - .replace("%h", "%02d" % hours) - .replace("%m", "%02d" % minutes) - .replace("%s", "%02d" % seconds) - ) - elif display == "localized-no-seconds": + return pgettext('time format without day', '%h:%m:%s') \ + .replace('%h', '%02d' % hours).replace('%m', '%02d' % minutes).replace('%s', '%02d' % seconds) + elif display == 'localized-no-seconds': days += weeks * 7 if days: if hours or minutes: - return ( - npgettext( - "time format no seconds with day", - "%d day %h:%m", - "%d days %h:%m", - days, - ) - .replace("%d", str(days)) - .replace("%h", "%02d" % hours) - .replace("%m", "%02d" % minutes) - ) - return ungettext("%d day", "%d days", days) % days + return npgettext('time format no seconds with day', '%d day %h:%m', '%d days %h:%m', days) \ + .replace('%d', str(days)).replace('%h', '%02d' % hours).replace('%m', '%02d' % minutes) + return ungettext('%d day', '%d days', days) % days else: - return ( - pgettext("hours and minutes", "%h:%m") - .replace("%h", "%02d" % hours) - .replace("%m", "%02d" % minutes) - ) - elif display == "concise": + return pgettext('hours and minutes', '%h:%m').replace('%h', '%02d' % hours).replace('%m', '%02d' % minutes) + elif display == 'concise': days += weeks * 7 if days: - return "%dd %02d:%02d:%02d" % (days, hours, minutes, seconds) + return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds) else: - return "%02d:%02d:%02d" % (hours, minutes, seconds) - elif display == "noday": + return '%02d:%02d:%02d' % (hours, minutes, seconds) + elif display == 'noday': days += weeks * 7 hours += days * 24 - return "%02d:%02d:%02d" % (hours, minutes, seconds) - elif display == "minimal": - words = ["w", "d", "h", "m", "s"] - elif display == "short": - words = [" wks", " days", " hrs", " min", " sec"] + return '%02d:%02d:%02d' % (hours, minutes, seconds) + elif display == 'minimal': + words = ['w', 'd', 'h', 'm', 's'] + elif display == 'short': + words = [' wks', ' days', ' hrs', ' min', ' sec'] else: - words = [" weeks", " days", " hours", " minutes", " seconds"] + words = [' weeks', ' days', ' hours', ' minutes', ' seconds'] values = [weeks, days, hours, minutes, seconds] for i in range(len(values)): if values[i]: if values[i] == 1 and len(words[i]) > 1: - result.append("%i%s" % (values[i], words[i].rstrip("s"))) + result.append('%i%s' % (values[i], words[i].rstrip('s'))) else: - result.append("%i%s" % (values[i], words[i])) + result.append('%i%s' % (values[i], words[i])) return sep.join(result) diff --git a/judge/utils/unicode.py b/judge/utils/unicode.py index 084be4f..78cc176 100644 --- a/judge/utils/unicode.py +++ b/judge/utils/unicode.py @@ -1,37 +1,17 @@ -from typing import AnyStr, Optional, overload - - -@overload -def utf8bytes(maybe_text: AnyStr) -> bytes: - pass - - -@overload -def utf8bytes(maybe_text: None) -> None: - pass +from django.utils import six def utf8bytes(maybe_text): if maybe_text is None: - return None - if isinstance(maybe_text, bytes): + return + if isinstance(maybe_text, six.binary_type): return maybe_text - return maybe_text.encode("utf-8") + return maybe_text.encode('utf-8') -@overload -def utf8text(maybe_bytes: AnyStr, errors="strict") -> str: - pass - - -@overload -def utf8text(maybe_bytes: None, errors="strict") -> None: - pass - - -def utf8text(maybe_bytes, errors="strict") -> Optional[str]: +def utf8text(maybe_bytes, errors='strict'): if maybe_bytes is None: - return None - if isinstance(maybe_bytes, str): + return + if isinstance(maybe_bytes, six.text_type): return maybe_bytes - return maybe_bytes.decode("utf-8", errors) + return maybe_bytes.decode('utf-8', errors) diff --git a/judge/utils/users.py b/judge/utils/users.py deleted file mode 100644 index 9ac071c..0000000 --- a/judge/utils/users.py +++ /dev/null @@ -1,67 +0,0 @@ -from django.urls import reverse -from django.utils.formats import date_format -from django.utils.translation import gettext as _, gettext_lazy - -from judge.caching import cache_wrapper -from judge.models import Profile, Rating, Submission, Friend, ProfileInfo - - -@cache_wrapper(prefix="grr") -def get_rating_rank(profile): - if profile.is_unlisted: - return None - rank = None - if profile.rating: - rank = ( - Profile.objects.filter( - is_unlisted=False, - rating__gt=profile.rating, - ).count() - + 1 - ) - return rank - - -@cache_wrapper(prefix="gpr") -def get_points_rank(profile): - if profile.is_unlisted: - return None - return ( - Profile.objects.filter( - is_unlisted=False, - performance_points__gt=profile.performance_points, - ).count() - + 1 - ) - - -@cache_wrapper(prefix="gcr") -def get_contest_ratings(profile): - return ( - profile.ratings.order_by("-contest__end_time") - .select_related("contest") - .defer("contest__description") - ) - - -def get_awards(profile): - ratings = get_contest_ratings(profile) - - sorted_ratings = sorted( - ratings, key=lambda x: (x.rank, -x.contest.end_time.timestamp()) - ) - - result = [ - { - "label": rating.contest.name, - "ranking": rating.rank, - "link": reverse("contest_ranking", args=(rating.contest.key,)) - + "#!" - + profile.username, - "date": date_format(rating.contest.end_time, _("M j, Y")), - } - for rating in sorted_ratings - if rating.rank <= 3 - ] - - return result diff --git a/judge/utils/views.py b/judge/utils/views.py index 530caba..622f0e6 100644 --- a/judge/utils/views.py +++ b/judge/utils/views.py @@ -6,7 +6,6 @@ from django.views.generic.detail import SingleObjectMixin from judge.utils.diggpaginator import DiggPaginator from django.utils.html import mark_safe - def class_view_decorator(function_decorator): """Convert a function based decorator into a class based decorator usable on class based Views. @@ -23,43 +22,34 @@ def class_view_decorator(function_decorator): def generic_message(request, title, message, status=None): - return render( - request, - "generic-message.html", - { - "message": message, - "title": title, - }, - status=status, - ) + return render(request, 'generic-message.html', { + 'message': message, + 'title': title, + }, status=status) def paginate_query_context(request): query = request.GET.copy() - query.setlist("page", []) + query.setlist('page', []) query = query.urlencode() if query: - return { - "page_prefix": "%s?%s&page=" % (request.path, query), - "first_page_href": "%s?%s" % (request.path, query), - } + return {'page_prefix': '%s?%s&page=' % (request.path, query), + 'first_page_href': '%s?%s' % (request.path, query)} else: - return { - "page_prefix": "%s?page=" % request.path, - "first_page_href": request.path, - } + return {'page_prefix': '%s?page=' % request.path, + 'first_page_href': request.path} class TitleMixin(object): - title = "(untitled)" + title = '(untitled)' content_title = None def get_context_data(self, **kwargs): context = super(TitleMixin, self).get_context_data(**kwargs) - context["title"] = self.get_title() + context['title'] = self.get_title() content_title = self.get_content_title() if content_title is not None: - context["content_title"] = content_title + context['content_title'] = content_title return context def get_content_title(self): @@ -70,18 +60,10 @@ class TitleMixin(object): class DiggPaginatorMixin(object): - def get_paginator( - self, queryset, per_page, orphans=0, allow_empty_first_page=True, **kwargs - ): - return DiggPaginator( - queryset, - per_page, - body=6, - padding=2, - orphans=orphans, - allow_empty_first_page=allow_empty_first_page, - **kwargs - ) + def get_paginator(self, queryset, per_page, orphans=0, + allow_empty_first_page=True, **kwargs): + return DiggPaginator(queryset, per_page, body=6, padding=2, + orphans=orphans, allow_empty_first_page=allow_empty_first_page, **kwargs) class QueryStringSortMixin(object): @@ -93,11 +75,8 @@ class QueryStringSortMixin(object): return self.default_sort def get(self, request, *args, **kwargs): - order = request.GET.get("order", "") - if not ( - (not order.startswith("-") or order.count("-") == 1) - and (order.lstrip("-") in self.all_sorts) - ): + order = request.GET.get('order', '') + if not ((not order.startswith('-') or order.count('-') == 1) and (order.lstrip('-') in self.all_sorts)): order = self.get_default_sort_order(request) self.order = order @@ -105,26 +84,17 @@ class QueryStringSortMixin(object): def get_sort_context(self): query = self.request.GET.copy() - query.setlist("order", []) + query.setlist('order', []) query = query.urlencode() - sort_prefix = ( - "%s?%s&order=" % (self.request.path, query) - if query - else "%s?order=" % self.request.path - ) - current = self.order.lstrip("-") + sort_prefix = '%s?%s&order=' % (self.request.path, query) if query else '%s?order=' % self.request.path + current = self.order.lstrip('-') - links = { - key: sort_prefix + ("-" if key in self.default_desc else "") + key - for key in self.all_sorts - } - links[current] = ( - sort_prefix + ("" if self.order.startswith("-") else "-") + current - ) + links = {key: sort_prefix + ('-' if key in self.default_desc else '') + key for key in self.all_sorts} + links[current] = sort_prefix + ('' if self.order.startswith('-') else '-') + current - order = {key: "" for key in self.all_sorts} - order[current] = " \u25BE" if self.order.startswith("-") else " \u25B4" - return {"sort_links": links, "sort_order": order} + order = {key: '' for key in self.all_sorts} + order[current] = ' \u25BE' if self.order.startswith('-') else u' \u25B4' + return {'sort_links': links, 'sort_order': order} def get_sort_paginate_context(self): return paginate_query_context(self.request) diff --git a/judge/views/__init__.py b/judge/views/__init__.py index 94a01c5..37d173d 100644 --- a/judge/views/__init__.py +++ b/judge/views/__init__.py @@ -5,6 +5,6 @@ class TitledTemplateView(TemplateView): title = None def get_context_data(self, **kwargs): - if "title" not in kwargs and self.title is not None: - kwargs["title"] = self.title + if 'title' not in kwargs and self.title is not None: + kwargs['title'] = self.title return super(TitledTemplateView, self).get_context_data(**kwargs) diff --git a/judge/views/about.py b/judge/views/about.py index 3f6d266..d328066 100644 --- a/judge/views/about.py +++ b/judge/views/about.py @@ -3,252 +3,12 @@ from django.utils.translation import gettext as _ def about(request): - return render( - request, - "about/about.html", - { - "title": _("About"), - }, - ) + return render(request, 'about/about.html', { + 'title': _('About'), + }) def custom_checker_sample(request): - content = """ -1. Trình chấm tự viết (PY) -2. Trình chấm tự viết (CPP) -3. Interactive (CPP) -4. Dùng hàm như IOI (CPP) - ---- - -##1. Trình chấm tự viết (PY) -Đây là checker mặc định của website, cho phép người dùng cập nhật được nhiều thông tin nhất (chi tiết xem ở bên dưới). Chúng ta cần hoàn thành hàm `check` dưới đây: -```py -def check(process_output, judge_output, **kwargs): - # return True/False -``` - -Trong đó, `**kwargs` có thể chứa các biến sau: - -- `process_output`: output -- `judge_output`: đáp án -- `submission_source`: Code bài nộp -- `judge_input`: input -- `point_value`: điểm của test đang chấm -- `case_position`: thứ tự của test -- `submission_language`: ngôn ngữ của bài nộp -- `execution_time`: thời gian chạy - -**Return**: - -- Cách 1: Trả về True/False -- Cách 2: Trả về một object `CheckerResult` có thể được gọi như sau `CheckerResult(case_passed_bool, points_awarded, feedback='')` - -**Ví dụ:** -Dưới đây là ví dụ cho bài toán: Input gồm 1 số nguyên n. In ra 2 số nguyên a, b sao cho a + b = n. - -```py -from dmoj.result import CheckerResult - -def wa(feedback): - return CheckerResult(False, 0, feedback) - -def check(process_output, judge_output, judge_input, **kwargs): - # process the input - input_arr = judge_input.split() - assert(len(input_arr) == 1) - n = int(input_arr[0]) - - # process the contestant's output - output_arr = process_output.split() - - if (len(output_arr) != 2): - return wa('Wrong output format') - - try: - a, b = int(output_arr[0]), int(output_arr[1]) - except: - return wa('Wrong output format') - - if (n == a + b): - return True - return wa('a + b != n') -``` - -## 2. Trình chấm tự viết (CPP) - -Để sử dụng chức năng này, cần viết một chương trình C++ pass vào 3 arguments theo thứ tự `input_file`, `output_file`, `ans_file` tương ứng với các file input, output, đáp án. - -Để test chương trình trên máy tính, có thể dùng lệnh như sau (Windows): - -```bash -main.exe [input_file] [output_file] [ans_file] -``` - -hoặc thay bằng `./main` trên Linux/MacOS. - -**Return:** -Chương trình trả về giá trị: - -- 0 nếu AC (100% điểm) -- 1 nếu WA (0 điểm) -- 2 nếu điểm thành phần. Khi đó cần in ra stderr một số thực trong đoạn [0, 1] thể hiện cho tỷ lệ điểm. Nếu điểm < 1 thì hiển thị WA, điểm = 1 thì hiển thị AC. -Những thông tin được viết ra stdout (bằng cout) sẽ được in ra màn hình cho người nộp bài(feedback) - -**Ví dụ:** -Chương trình sau dùng để chấm bài toán: Cho n là một số nguyên dương. In ra hai số tự nhiên a, b sao cho a + b = n. - -Nếu in ra a + b = n và a, b >= 0 thì được 100% số điểm, nếu a + b = n nhưng một trong 2 số a, b âm thì được 50% số điểm. - -```cpp -#include -using namespace std; - -int main(int argc, char** argv) { - ifstream inp(argv[1]); - ifstream out(argv[2]); - ifstream ans(argv[3]); - - int n, a, b, c, d; - - inp >> n; - out >> a >> b; - ans >> c >> d; - - if (a + b == c + d) { - cout << a << " + " << b << " = " << c << " + " << d << endl; - - if (a >= 0 && b >= 0) { - return 0; // AC - } - else { - cerr << 0.5; - return 2; // PARTIAL - } - } - else { - cout << "a + b = " << a + b << " != " << n << endl; - return 1; // WA - } -} -``` - -## 3. Interactive (CPP) -Để sử dụng chức năng này, cần viết một chương trình C++ pass vào 2 arguments `input_file` `answer_file` tương ứng file input và đáp án (nếu cần thiết). - -Để test chương trình trên máy tính với tư cách thí sinh, có thể dùng lệnh như sau (Windows): - -```bash -main.exe [input_file] [answer_file] -``` - -hoặc thay bằng `./main` trên Linux/MacOS. - -**Return:** -Chương trình trả về giá trị: - -- 0 nếu AC (100% điểm) -- 1 nếu WA (0 điểm) -- 2 nếu điểm thành phần. Khi đó cần in ra stderr một số thực trong đoạn [0, 1] thể hiện cho tỷ lệ điểm. Nếu điểm < 1 thì hiển thị WA, điểm = 1 thì hiển thị AC. -Thông tin được in ra trong stderr (bằng cerr) sẽ là feedback hiển thị cho người dùng. - -**Ví dụ:** -Chương trình sau dùng để chấm bài toán guessgame: Người chơi phải tìm 1 số bí mật n (n chứa trong file input). Mỗi lần họ được hỏi một số x, và chương trình sẽ trả về "SMALLER", "BIGGER" hoặc "HOLA" dựa trên giá trị của n và x. Cần tìm ra n sau không quá 31 câu hỏi. - -```cpp -#include -using namespace std; - -void quit(string reason) { - cerr << reason << endl; - exit(1); -} - -void read(long long& guess) { - if (!(cin >> guess)) exit(1); // Nếu không có dòng này, chương trình sẽ chờ vô hạn - if (guess < 1 || guess > 2e9) exit(1); -} - -int main(int argc, char *argv[]) { - ifstream inp(argv[1]); - int N, guesses = 0; - long long guess; - inp >> N; - - while (guess != N && guesses <= 31) { - read(guess); - if (guess == N) { - cout << "HOLA" << endl; - } else if (guess > N) { - cout << "SMALLER" << endl; - } else { - cout << "BIGGER" << endl; - } - guesses++; - } - - cerr << "Number of used guesses: " << guesses << endl; - - if (guesses <= 31) - return 0; // AC - else { - cerr << "Used too many guesses" << endl; - return 1; // WA - } -} -``` -## 4. IOI Signature (CPP) -Đây là chức năng để sử dụng hàm như trong IOI. Thí sinh được cho một định nghĩa hàm và cần cài đặt hàm đó trả về giá trị đúng. -Để sử dụng chức năng này, cần viết 2 chương trình: -- Header: Đây là file định nghĩa hàm (đuôi phải là `.h`) -- Handler: Đây là chương trình xử lý input và xuất ra output dựa trên hàm (đuôi phải là `.cpp`) - -**Ví dụ:** -Cho bài toán: nhập vào số n. Viết hàm `solve(int n)` trả về `n * 2`. Giả sử input là multitest có dạng: -- Dòng đầu chứa `t` là số test -- Mỗi dòng chứa một số nguyên `n` - -**Header (header.h):** -```cpp -#ifndef _HEADER_INCLUDED -#define _HEADER_INCLUDED -long long solve(long long n); -#endif -``` - -**Handler (handler.cpp):** -```cpp -#include -#include "header.h" -using namespace std; - - -int main() { - int t; - cin >> t; - for (int z = 1; z <= t; z++) { - long long n; - cin >> n; - cout << solve(n) << "\\n"; - } - - return 0; -} -``` - -**Bài nộp thí sinh:** -```cpp -int solve(int n) { - return n * 2; -} -``` - -""" - return render( - request, - "about/custom-checker-sample.html", - { - "title": _("Custom Checker Sample"), - "content": content, - }, - ) + return render(request, 'about/custom-checker-sample.html', { + 'title': _('Custom Checker Sample'), + }) diff --git a/judge/views/api/api_v1.py b/judge/views/api/api_v1.py index 80f18f0..bad7486 100644 --- a/judge/views/api/api_v1.py +++ b/judge/views/api/api_v1.py @@ -5,40 +5,27 @@ from django.http import Http404, JsonResponse from django.shortcuts import get_object_or_404 from dmoj import settings -from judge.models import ( - Contest, - ContestParticipation, - ContestTag, - Problem, - Profile, - Submission, -) +from judge.models import Contest, ContestParticipation, ContestTag, Problem, Profile, Submission def sane_time_repr(delta): days = delta.days hours = delta.seconds / 3600 minutes = (delta.seconds % 3600) / 60 - return "%02d:%02d:%02d" % (days, hours, minutes) + return '%02d:%02d:%02d' % (days, hours, minutes) def api_v1_contest_list(request): queryset = Contest.get_visible_contests(request.user).prefetch_related( - Prefetch("tags", queryset=ContestTag.objects.only("name"), to_attr="tag_list") - ) - - return JsonResponse( - { - c.key: { - "name": c.name, - "start_time": c.start_time.isoformat(), - "end_time": c.end_time.isoformat(), - "time_limit": c.time_limit and sane_time_repr(c.time_limit), - "labels": list(map(attrgetter("name"), c.tag_list)), - } - for c in queryset - } - ) + Prefetch('tags', queryset=ContestTag.objects.only('name'), to_attr='tag_list')) + + return JsonResponse({c.key: { + 'name': c.name, + 'start_time': c.start_time.isoformat(), + 'end_time': c.end_time.isoformat(), + 'time_limit': c.time_limit and sane_time_repr(c.time_limit), + 'labels': list(map(attrgetter('name'), c.tag_list)), + } for c in queryset}) def api_v1_contest_detail(request, contest): @@ -46,87 +33,61 @@ def api_v1_contest_detail(request, contest): in_contest = contest.is_in_contest(request.user) can_see_rankings = contest.can_see_full_scoreboard(request.user) - problems = list( - contest.contest_problems.select_related("problem") - .defer("problem__description") - .order_by("order") - ) - participations = ( - contest.users.filter(virtual=0) - .prefetch_related("user__organizations") - .annotate(username=F("user__user__username")) - .order_by("-score", "cumtime") - if can_see_rankings - else [] - ) + problems = list(contest.contest_problems.select_related('problem') + .defer('problem__description').order_by('order')) + participations = (contest.users.filter(virtual=0) + .prefetch_related('user__organizations') + .annotate(username=F('user__user__username')) + .order_by('-score', 'cumtime') if can_see_rankings else []) - can_see_problems = ( - in_contest or contest.ended or contest.is_editable_by(request.user) - ) + can_see_problems = (in_contest or contest.ended or contest.is_editable_by(request.user)) - return JsonResponse( - { - "time_limit": contest.time_limit and contest.time_limit.total_seconds(), - "start_time": contest.start_time.isoformat(), - "end_time": contest.end_time.isoformat(), - "tags": list(contest.tags.values_list("name", flat=True)), - "is_rated": contest.is_rated, - "rate_all": contest.is_rated and contest.rate_all, - "has_rating": contest.ratings.exists(), - "rating_floor": contest.rating_floor, - "rating_ceiling": contest.rating_ceiling, - "format": { - "name": contest.format_name, - "config": contest.format_config, - }, - "problems": [ - { - "points": int(problem.points), - "partial": problem.partial, - "name": problem.problem.name, - "code": problem.problem.code, - } - for problem in problems - ] - if can_see_problems - else [], - "rankings": [ - { - "user": participation.username, - "points": participation.score, - "cumtime": participation.cumtime, - "is_disqualified": participation.is_disqualified, - "solutions": contest.format.get_problem_breakdown( - participation, problems - ), - } - for participation in participations - ], - } - ) + return JsonResponse({ + 'time_limit': contest.time_limit and contest.time_limit.total_seconds(), + 'start_time': contest.start_time.isoformat(), + 'end_time': contest.end_time.isoformat(), + 'tags': list(contest.tags.values_list('name', flat=True)), + 'is_rated': contest.is_rated, + 'rate_all': contest.is_rated and contest.rate_all, + 'has_rating': contest.ratings.exists(), + 'rating_floor': contest.rating_floor, + 'rating_ceiling': contest.rating_ceiling, + 'format': { + 'name': contest.format_name, + 'config': contest.format_config, + }, + 'problems': [ + { + 'points': int(problem.points), + 'partial': problem.partial, + 'name': problem.problem.name, + 'code': problem.problem.code, + } for problem in problems] if can_see_problems else [], + 'rankings': [ + { + 'user': participation.username, + 'points': participation.score, + 'cumtime': participation.cumtime, + 'is_disqualified': participation.is_disqualified, + 'solutions': contest.format.get_problem_breakdown(participation, problems), + } for participation in participations], + }) def api_v1_problem_list(request): queryset = Problem.objects.filter(is_public=True, is_organization_private=False) - if settings.ENABLE_FTS and "search" in request.GET: - query = " ".join(request.GET.getlist("search")).strip() + if settings.ENABLE_FTS and 'search' in request.GET: + query = ' '.join(request.GET.getlist('search')).strip() if query: queryset = queryset.search(query) - queryset = queryset.values_list( - "code", "points", "partial", "name", "group__full_name" - ) + queryset = queryset.values_list('code', 'points', 'partial', 'name', 'group__full_name') - return JsonResponse( - { - code: { - "points": points, - "partial": partial, - "name": name, - "group": group, - } - for code, points, partial, name, group in queryset - } - ) + return JsonResponse({code: { + 'points': points, + 'partial': partial, + 'name': name, + 'group': group, + } for code, points, partial, name, group in queryset}) def api_v1_problem_info(request, problem): @@ -134,83 +95,60 @@ def api_v1_problem_info(request, problem): if not p.is_accessible_by(request.user): raise Http404() - return JsonResponse( - { - "name": p.name, - "authors": list(p.authors.values_list("user__username", flat=True)), - "types": list(p.types.values_list("full_name", flat=True)), - "group": p.group.full_name, - "time_limit": p.time_limit, - "memory_limit": p.memory_limit, - "points": p.points, - "partial": p.partial, - "languages": list(p.allowed_languages.values_list("key", flat=True)), - } - ) + return JsonResponse({ + 'name': p.name, + 'authors': list(p.authors.values_list('user__username', flat=True)), + 'types': list(p.types.values_list('full_name', flat=True)), + 'group': p.group.full_name, + 'time_limit': p.time_limit, + 'memory_limit': p.memory_limit, + 'points': p.points, + 'partial': p.partial, + 'languages': list(p.allowed_languages.values_list('key', flat=True)), + }) def api_v1_user_list(request): - queryset = Profile.objects.filter(is_unlisted=False).values_list( - "user__username", "points", "performance_points", "display_rank" - ) - return JsonResponse( - { - username: { - "points": points, - "performance_points": performance_points, - "rank": rank, - } - for username, points, performance_points, rank in queryset - } - ) + queryset = Profile.objects.filter(is_unlisted=False).values_list('user__username', 'points', 'performance_points', + 'display_rank') + return JsonResponse({username: { + 'points': points, + 'performance_points': performance_points, + 'rank': rank, + } for username, points, performance_points, rank in queryset}) def api_v1_user_info(request, user): profile = get_object_or_404(Profile, user__username=user) - submissions = list( - Submission.objects.filter( - case_points=F("case_total"), - user=profile, - problem__is_public=True, - problem__is_organization_private=False, - ) - .values("problem") - .distinct() - .values_list("problem__code", flat=True) - ) + submissions = list(Submission.objects.filter(case_points=F('case_total'), user=profile, problem__is_public=True, + problem__is_organization_private=False) + .values('problem').distinct().values_list('problem__code', flat=True)) resp = { - "points": profile.points, - "performance_points": profile.performance_points, - "rank": profile.display_rank, - "solved_problems": submissions, - "organizations": list(profile.organizations.values_list("id", flat=True)), + 'points': profile.points, + 'performance_points': profile.performance_points, + 'rank': profile.display_rank, + 'solved_problems': submissions, + 'organizations': list(profile.organizations.values_list('id', flat=True)), } last_rating = profile.ratings.last() contest_history = {} - participations = ContestParticipation.objects.filter( - user=profile, - virtual=0, - contest__is_visible=True, - contest__is_private=False, - contest__is_organization_private=False, - ) + participations = ContestParticipation.objects.filter(user=profile, virtual=0, contest__is_visible=True, + contest__is_private=False, + contest__is_organization_private=False) for contest_key, rating, mean, performance in participations.values_list( - "contest__key", - "rating__rating", - "rating__mean", - "rating__performance", + 'contest__key', 'rating__rating', 'rating__mean', 'rating__performance', ): contest_history[contest_key] = { - "rating": rating, - "raw_rating": mean, - "performance": performance, + 'rating': rating, + 'raw_rating': mean, + 'performance': performance, } - resp["contests"] = { - "current_rating": last_rating.rating if last_rating else None, - "history": contest_history, + resp['contests'] = { + 'current_rating': last_rating.rating if last_rating else None, + 'history': contest_history, } return JsonResponse(resp) @@ -218,30 +156,14 @@ def api_v1_user_info(request, user): def api_v1_user_submissions(request, user): profile = get_object_or_404(Profile, user__username=user) - subs = Submission.objects.filter( - user=profile, problem__is_public=True, problem__is_organization_private=False - ) + subs = Submission.objects.filter(user=profile, problem__is_public=True, problem__is_organization_private=False) - return JsonResponse( - { - sub["id"]: { - "problem": sub["problem__code"], - "time": sub["time"], - "memory": sub["memory"], - "points": sub["points"], - "language": sub["language__key"], - "status": sub["status"], - "result": sub["result"], - } - for sub in subs.values( - "id", - "problem__code", - "time", - "memory", - "points", - "language__key", - "status", - "result", - ) - } - ) + return JsonResponse({sub['id']: { + 'problem': sub['problem__code'], + 'time': sub['time'], + 'memory': sub['memory'], + 'points': sub['points'], + 'language': sub['language__key'], + 'status': sub['status'], + 'result': sub['result'], + } for sub in subs.values('id', 'problem__code', 'time', 'memory', 'points', 'language__key', 'status', 'result')}) diff --git a/judge/views/api/api_v2.py b/judge/views/api/api_v2.py index a750d98..8e512b3 100644 --- a/judge/views/api/api_v2.py +++ b/judge/views/api/api_v2.py @@ -9,7 +9,7 @@ from judge.views.contests import contest_ranking_list def error(message): - return JsonResponse({"error": message}, status=422) + return JsonResponse({'error': message}, status=422) def api_v2_user_info(request): @@ -44,9 +44,9 @@ def api_v2_user_info(request): // ... ] } - """ + """ try: - username = request.GET["username"] + username = request.GET['username'] except KeyError: return error("no username passed") if not username: @@ -56,90 +56,66 @@ def api_v2_user_info(request): except Profile.DoesNotExist: return error("no such user") - last_rating = list(profile.ratings.order_by("-contest__end_time")) + last_rating = list(profile.ratings.order_by('-contest__end_time')) resp = { "rank": profile.display_rank, - "organizations": list(profile.organizations.values_list("key", flat=True)), + "organizations": list(profile.organizations.values_list('key', flat=True)), } contest_history = [] - for participation in ContestParticipation.objects.filter( - user=profile, virtual=0, contest__is_visible=True - ).order_by("-contest__end_time"): + for participation in (ContestParticipation.objects.filter(user=profile, virtual=0, contest__is_visible=True) + .order_by('-contest__end_time')): contest = participation.contest - problems = list( - contest.contest_problems.select_related("problem") - .defer("problem__description") - .order_by("order") - ) - rank, result = next( - filter( - lambda data: data[1].user == profile.user, - ranker( - contest_ranking_list(contest, problems), - key=attrgetter("points", "cumtime"), - ), - ) - ) + problems = list(contest.contest_problems.select_related('problem').defer('problem__description') + .order_by('order')) + rank, result = next(filter(lambda data: data[1].user == profile.user, + ranker(contest_ranking_list(contest, problems), + key=attrgetter('points', 'cumtime')))) - contest_history.append( - { - "contest": { - "code": contest.key, - "name": contest.name, - "tags": list(contest.tags.values_list("name", flat=True)), - "time_limit": contest.time_limit - and contest.time_limit.total_seconds(), - "start_time": contest.start_time.isoformat(), - "end_time": contest.end_time.isoformat(), - }, - "rank": rank, - "rating": result.participation_rating, - } - ) + contest_history.append({ + 'contest': { + 'code': contest.key, + 'name': contest.name, + 'tags': list(contest.tags.values_list('name', flat=True)), + 'time_limit': contest.time_limit and contest.time_limit.total_seconds(), + 'start_time': contest.start_time.isoformat(), + 'end_time': contest.end_time.isoformat(), + }, + 'rank': rank, + 'rating': result.participation_rating, + }) - resp["contests"] = { + resp['contests'] = { "current_rating": last_rating[0].rating if last_rating else None, - "history": contest_history, + 'history': contest_history, } solved_problems = [] attempted_problems = [] - problem_data = ( - Submission.objects.filter( - points__gt=0, - user=profile, - problem__is_public=True, - problem__is_organization_private=False, - ) - .annotate(max_pts=Max("points")) - .values_list("max_pts", "problem__points", "problem__code") - .distinct() - ) + problem_data = (Submission.objects.filter(points__gt=0, user=profile, problem__is_public=True, + problem__is_organization_private=False) + .annotate(max_pts=Max('points')) + .values_list('max_pts', 'problem__points', 'problem__code') + .distinct()) for awarded_pts, max_pts, problem in problem_data: if awarded_pts == max_pts: solved_problems.append(problem) else: - attempted_problems.append( - { - "awarded": awarded_pts, - "max": max_pts, - "problem": problem, - } - ) + attempted_problems.append({ + 'awarded': awarded_pts, + 'max': max_pts, + 'problem': problem, + }) - resp["problems"] = { - "points": profile.points, - "solved": solved_problems, - "attempted": attempted_problems, - "authored": list( - Problem.objects.filter( - is_public=True, is_organization_private=False, authors=profile - ).values_list("code", flat=True) - ), + resp['problems'] = { + 'points': profile.points, + 'solved': solved_problems, + 'attempted': attempted_problems, + 'authored': list(Problem.objects.filter(is_public=True, is_organization_private=False, authors=profile) + .values_list('code', flat=True)), } return JsonResponse(resp) diff --git a/judge/views/blog.py b/judge/views/blog.py index 93b3c35..c16aa6e 100644 --- a/judge/views/blog.py +++ b/judge/views/blog.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.db.models import Count, Max, Q from django.http import Http404 from django.urls import reverse @@ -6,215 +7,127 @@ from django.utils.functional import lazy from django.utils.translation import ugettext as _ from django.views.generic import ListView -from judge.views.comment import CommentedDetailView -from judge.views.pagevote import PageVoteDetailView -from judge.views.bookmark import BookMarkDetailView -from judge.models import ( - BlogPost, - Comment, - Contest, - Language, - Problem, - ContestProblemClarification, - Profile, - Submission, - Ticket, -) -from judge.models.profile import Organization, OrganizationProfile +from judge.comments import CommentedDetailView +from judge.models import BlogPost, Comment, Contest, Language, Problem, ProblemClarification, Profile, Submission, \ + Ticket from judge.utils.cachedict import CacheDict from judge.utils.diggpaginator import DiggPaginator +from judge.utils.problems import user_completed_ids from judge.utils.tickets import filter_visible_tickets from judge.utils.views import TitleMixin -from judge.utils.users import get_rating_rank, get_points_rank, get_awards -from judge.views.feed import FeedView -# General view for all content list on home feed -class HomeFeedView(FeedView): - template_name = "blog/list.html" +class PostList(ListView): + model = BlogPost + paginate_by = 10 + context_object_name = 'posts' + template_name = 'blog/list.html' title = None - def get_context_data(self, **kwargs): - context = super(HomeFeedView, self).get_context_data(**kwargs) - context["has_clarifications"] = False - if self.request.user.is_authenticated: - participation = self.request.profile.current_contest - if participation: - clarifications = ContestProblemClarification.objects.filter( - problem__in=participation.contest.contest_problems.all() - ) - context["has_clarifications"] = clarifications.count() > 0 - context["clarifications"] = clarifications.order_by("-date") - if participation.contest.is_editable_by(self.request.user): - context["can_edit_contest"] = True - - now = timezone.now() - visible_contests = ( - Contest.get_visible_contests(self.request.user, show_own_contests_only=True) - .filter(is_visible=True, official__isnull=True) - .order_by("start_time") - ) - if self.request.organization: - visible_contests = visible_contests.filter( - is_organization_private=True, organizations=self.request.organization - ) - context["current_contests"] = visible_contests.filter( - start_time__lte=now, end_time__gt=now - ) - context["future_contests"] = visible_contests.filter(start_time__gt=now) - context[ - "recent_organizations" - ] = OrganizationProfile.get_most_recent_organizations(self.request.profile) - - profile_queryset = Profile.objects - if self.request.organization: - profile_queryset = self.request.organization.members - context["top_rated"] = ( - profile_queryset.filter(is_unlisted=False) - .order_by("-rating") - .only("id", "rating")[:10] - ) - context["top_scorer"] = ( - profile_queryset.filter(is_unlisted=False) - .order_by("-performance_points") - .only("id", "performance_points")[:10] - ) - Profile.prefetch_profile_cache([p.id for p in context["top_rated"]]) - Profile.prefetch_profile_cache([p.id for p in context["top_scorer"]]) - - if self.request.user.is_authenticated: - context["rating_rank"] = get_rating_rank(self.request.profile) - context["points_rank"] = get_points_rank(self.request.profile) - - medals_list = get_awards(self.request.profile) - context["awards"] = { - "medals": medals_list, - "gold_count": 0, - "silver_count": 0, - "bronze_count": 0, - } - for medal in medals_list: - if medal["ranking"] == 1: - context["awards"]["gold_count"] += 1 - elif medal["ranking"] == 2: - context["awards"]["silver_count"] += 1 - elif medal["ranking"] == 3: - context["awards"]["bronze_count"] += 1 - - return context - - -class PostList(HomeFeedView): - model = BlogPost - paginate_by = 4 - context_object_name = "posts" - feed_content_template_name = "blog/content.html" - url_name = "blog_post_list" + def get_paginator(self, queryset, per_page, orphans=0, + allow_empty_first_page=True, **kwargs): + return DiggPaginator(queryset, per_page, body=6, padding=2, + orphans=orphans, allow_empty_first_page=allow_empty_first_page, **kwargs) def get_queryset(self): - queryset = ( - BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()) - .order_by("-sticky", "-publish_on") - .prefetch_related("organizations") - ) - filter = Q(is_organization_private=False) - if self.request.user.is_authenticated: - filter |= Q(organizations__in=self.request.profile.organizations.all()) - if self.request.organization: - filter &= Q(organizations=self.request.organization) - queryset = queryset.filter(filter) + queryset = BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()) \ + .order_by('-sticky', '-publish_on') \ + .prefetch_related('authors__user', 'organizations') + if not self.request.user.has_perm('judge.edit_all_post'): + filter = Q(is_organization_private=False) + if self.request.user.is_authenticated: + filter |= Q(organizations__in=self.request.profile.organizations.all()) + queryset = queryset.filter(filter) return queryset def get_context_data(self, **kwargs): context = super(PostList, self).get_context_data(**kwargs) - context["title"] = ( - self.title or _("Page %d of Posts") % context["page_obj"].number - ) - context["page_type"] = "blog" - return context + context['title'] = self.title or _('Page %d of Posts') % context['page_obj'].number + context['first_page_href'] = reverse('home') + context['page_prefix'] = reverse('blog_post_list') + context['comments'] = Comment.most_recent(self.request.user, 25) + context['new_problems'] = Problem.objects.filter(is_public=True, is_organization_private=False) \ + .order_by('-date', '-id')[:settings.DMOJ_BLOG_NEW_PROBLEM_COUNT] + context['page_titles'] = CacheDict(lambda page: Comment.get_page_title(page)) + context['has_clarifications'] = False + if self.request.user.is_authenticated: + participation = self.request.profile.current_contest + if participation: + clarifications = ProblemClarification.objects.filter(problem__in=participation.contest.problems.all()) + context['has_clarifications'] = clarifications.count() > 0 + context['clarifications'] = clarifications.order_by('-date') + if participation.contest.is_editable_by(self.request.user): + context['can_edit_contest'] = True + context['user_count'] = lazy(Profile.objects.count, int, int) + context['problem_count'] = lazy(Problem.objects.filter(is_public=True).count, int, int) + context['submission_count'] = lazy(Submission.objects.count, int, int) + context['language_count'] = lazy(Language.objects.count, int, int) -class TicketFeed(HomeFeedView): - model = Ticket - context_object_name = "tickets" - paginate_by = 8 - feed_content_template_name = "ticket/feed.html" + context['post_comment_counts'] = { + int(page[2:]): count for page, count in + Comment.objects + .filter(page__in=['b:%d' % post.id for post in context['posts']], hidden=False) + .values_list('page').annotate(count=Count('page')).order_by() + } - def get_queryset(self, is_own=True): - profile = self.request.profile - if is_own: - if self.request.user.is_authenticated: - return ( - Ticket.objects.filter( - Q(user=profile) | Q(assignees__in=[profile]), is_open=True - ) - .order_by("-id") - .prefetch_related("linked_item") - ) - else: - return [] + now = timezone.now() + + # Dashboard stuff + if self.request.user.is_authenticated: + user = self.request.profile + context['recently_attempted_problems'] = (Submission.objects.filter(user=user) + .exclude(problem__in=user_completed_ids(user)) + .values_list('problem__code', 'problem__name', 'problem__points') + .annotate(points=Max('points'), latest=Max('date')) + .order_by('-latest') + [:settings.DMOJ_BLOG_RECENTLY_ATTEMPTED_PROBLEMS_COUNT]) + + visible_contests = Contest.get_visible_contests(self.request.user).filter(is_visible=True) \ + .order_by('start_time') + + context['current_contests'] = visible_contests.filter(start_time__lte=now, end_time__gt=now) + context['future_contests'] = visible_contests.filter(start_time__gt=now) + + visible_contests = Contest.get_visible_contests(self.request.user).filter(is_visible=True) + if self.request.user.is_authenticated: + profile = self.request.profile + context['own_open_tickets'] = (Ticket.objects.filter(Q(user=profile) | Q(assignees__in=[profile]), is_open=True).order_by('-id') + .prefetch_related('linked_item').select_related('user__user')) else: - # Superusers better be staffs, not the spell-casting kind either. - if self.request.user.is_staff: - tickets = ( - Ticket.objects.order_by("-id") - .filter(is_open=True) - .prefetch_related("linked_item") - ) - return filter_visible_tickets(tickets, self.request.user, profile) - else: - return [] + profile = None + context['own_open_tickets'] = [] + + # Superusers better be staffs, not the spell-casting kind either. + if self.request.user.is_staff: + tickets = (Ticket.objects.order_by('-id').filter(is_open=True).prefetch_related('linked_item') + .select_related('user__user')) + context['open_tickets'] = filter_visible_tickets(tickets, self.request.user, profile)[:10] + else: + context['open_tickets'] = [] - def get_context_data(self, **kwargs): - context = super(TicketFeed, self).get_context_data(**kwargs) - context["page_type"] = "ticket" - context["title"] = _("Ticket feed") return context -class CommentFeed(HomeFeedView): - model = Comment - context_object_name = "comments" - paginate_by = 15 - feed_content_template_name = "comments/feed.html" - - def get_queryset(self): - return Comment.most_recent( - self.request.user, 100, organization=self.request.organization - ) - - def get_context_data(self, **kwargs): - context = super(CommentFeed, self).get_context_data(**kwargs) - context["title"] = _("Comment feed") - context["page_type"] = "comment" - return context - - -class PostView(TitleMixin, CommentedDetailView, PageVoteDetailView, BookMarkDetailView): +class PostView(TitleMixin, CommentedDetailView): model = BlogPost - pk_url_kwarg = "id" - context_object_name = "post" - template_name = "blog/blog.html" + pk_url_kwarg = 'id' + context_object_name = 'post' + template_name = 'blog/blog.html' def get_title(self): return self.object.title + def get_comment_page(self): + return 'b:%s' % self.object.id + def get_context_data(self, **kwargs): context = super(PostView, self).get_context_data(**kwargs) - context["og_image"] = self.object.og_image - context["editable_orgs"] = [] - - orgs = list(self.object.organizations.all()) - - if self.request.profile: - for org in orgs: - if self.request.profile.can_edit_organization(org): - context["editable_orgs"].append(org) - + context['og_image'] = self.object.og_image return context def get_object(self, queryset=None): post = super(PostView, self).get_object(queryset) - if not post.is_accessible_by(self.request.user): + if not post.can_see(self.request.user): raise Http404() return post diff --git a/judge/views/bookmark.py b/judge/views/bookmark.py deleted file mode 100644 index a05f715..0000000 --- a/judge/views/bookmark.py +++ /dev/null @@ -1,75 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.db import IntegrityError -from django.db.models import F -from django.http import ( - Http404, - HttpResponse, - HttpResponseBadRequest, - HttpResponseForbidden, -) -from django.utils.translation import gettext as _ -from django.views.generic.base import TemplateResponseMixin -from django.views.generic.detail import SingleObjectMixin - -from django.views.generic import View, ListView - -from judge.models.bookmark import BookMark, MakeBookMark, dirty_bookmark - -__all__ = [ - "dobookmark_page", - "undobookmark_page", - "BookMarkDetailView", -] - - -@login_required -def bookmark_page(request, delta): - if request.method != "POST": - return HttpResponseForbidden() - - if "id" not in request.POST: - return HttpResponseBadRequest() - - try: - bookmark_id = int(request.POST["id"]) - bookmark = BookMark.objects.get(id=bookmark_id) - except ValueError: - return HttpResponseBadRequest() - except BookMark.DoesNotExist: - raise Http404() - - if delta == 0: - bookmarklist = MakeBookMark.objects.filter( - bookmark=bookmark, user=request.profile - ) - if not bookmarklist.exists(): - newbookmark = MakeBookMark( - bookmark=bookmark, - user=request.profile, - ) - newbookmark.save() - else: - bookmarklist = MakeBookMark.objects.filter( - bookmark=bookmark, user=request.profile - ) - if bookmarklist.exists(): - bookmarklist.delete() - - dirty_bookmark(bookmark, request.profile) - - return HttpResponse("success", content_type="text/plain") - - -def dobookmark_page(request): - return bookmark_page(request, 0) - - -def undobookmark_page(request): - return bookmark_page(request, 1) - - -class BookMarkDetailView(TemplateResponseMixin, SingleObjectMixin, View): - def get_context_data(self, **kwargs): - context = super(BookMarkDetailView, self).get_context_data(**kwargs) - context["bookmark"] = self.object.get_or_create_bookmark() - return context diff --git a/judge/views/comment.py b/judge/views/comment.py index 8ac62fa..7550d97 100644 --- a/judge/views/comment.py +++ b/judge/views/comment.py @@ -1,95 +1,44 @@ -import json - -from django import forms -from django.conf import settings -from django.contrib.auth.context_processors import PermWrapper from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import PermissionDenied, ValidationError -from django.db import IntegrityError -from django.db.models import Count, F, FilteredRelation, Q -from django.db.models.expressions import Value -from django.db.models.functions import Coalesce -from django.forms import ModelForm -from django.http import ( - Http404, - HttpResponse, - HttpResponseBadRequest, - HttpResponseForbidden, - HttpResponseNotFound, - HttpResponseRedirect, -) -from django.shortcuts import get_object_or_404, render -from django.urls import reverse_lazy -from django.utils.decorators import method_decorator +from django.core.exceptions import PermissionDenied +from django.db import IntegrityError, transaction +from django.db.models import F +from django.forms.models import ModelForm +from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden +from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _ -from django.utils.datastructures import MultiValueDictKeyError from django.views.decorators.http import require_POST -from django.views.generic import DetailView, UpdateView, View -from django.views.generic.base import TemplateResponseMixin -from django.views.generic.detail import SingleObjectMixin -from django_ratelimit.decorators import ratelimit -from django.contrib.contenttypes.models import ContentType - +from django.views.generic import DetailView, UpdateView from reversion import revisions -from reversion.models import Revision, Version +from reversion.models import Version -from judge.jinja2.reference import get_user_from_text -from judge.models import BlogPost, Comment, CommentVote, Notification -from judge.models.notification import make_notification -from judge.models.comment import get_visible_comment_count +from judge.dblock import LockModel +from judge.models import Comment, CommentVote, Notification from judge.utils.views import TitleMixin -from judge.widgets import HeavyPreviewPageDownWidget +from judge.widgets import MathJaxPagedownWidget +from judge.comments import add_mention_notifications, del_mention_notifications -__all__ = [ - "upvote_comment", - "downvote_comment", - "CommentEditAjax", - "CommentContent", - "CommentEdit", -] - -DEFAULT_OFFSET = 10 +__all__ = ['upvote_comment', 'downvote_comment', 'CommentEditAjax', 'CommentContent', + 'CommentEdit'] -def _get_html_link_notification(comment): - return f'{comment.page_title}' - - -def add_mention_notifications(comment): - users_mentioned = get_user_from_text(comment.body).exclude(id=comment.author.id) - link = _get_html_link_notification(comment) - make_notification(users_mentioned, "Mention", link, comment.author) - - -@ratelimit(key="user", rate=settings.RL_VOTE) @login_required def vote_comment(request, delta): if abs(delta) != 1: - return HttpResponseBadRequest( - _("Messing around, are we?"), content_type="text/plain" - ) + return HttpResponseBadRequest(_('Messing around, are we?'), content_type='text/plain') - if request.method != "POST": + if request.method != 'POST': return HttpResponseForbidden() - if "id" not in request.POST: + if 'id' not in request.POST: return HttpResponseBadRequest() - if ( - not request.user.is_staff - and not request.profile.submission_set.filter( - points=F("problem__points") - ).exists() - ): - return HttpResponseBadRequest( - _("You must solve at least one problem before you can vote."), - content_type="text/plain", - ) + if not request.user.is_staff and not request.profile.submission_set.filter(points=F('problem__points')).exists(): + return HttpResponseBadRequest(_('You must solve at least one problem before you can vote.'), + content_type='text/plain') try: - comment_id = int(request.POST["id"]) + comment_id = int(request.POST['id']) except ValueError: return HttpResponseBadRequest() else: @@ -101,22 +50,24 @@ def vote_comment(request, delta): vote.voter = request.profile vote.score = delta - try: - vote.save() - except IntegrityError: + while True: try: - vote = CommentVote.objects.get(comment_id=comment_id, voter=request.profile) - except CommentVote.DoesNotExist: - raise Http404() - if -vote.score != delta: - return HttpResponseBadRequest( - _("You already voted."), content_type="text/plain" - ) - vote.delete() - Comment.objects.filter(id=comment_id).update(score=F("score") - vote.score) - else: - Comment.objects.filter(id=comment_id).update(score=F("score") + delta) - return HttpResponse("success", content_type="text/plain") + vote.save() + except IntegrityError: + with LockModel(write=(CommentVote,)): + try: + vote = CommentVote.objects.get(comment_id=comment_id, voter=request.profile) + except CommentVote.DoesNotExist: + # We must continue racing in case this is exploited to manipulate votes. + continue + if -vote.score != delta: + return HttpResponseBadRequest(_('You already voted.'), content_type='text/plain') + vote.delete() + Comment.objects.filter(id=comment_id).update(score=F('score') - vote.score) + else: + Comment.objects.filter(id=comment_id).update(score=F('score') + delta) + break + return HttpResponse('success', content_type='text/plain') def upvote_comment(request): @@ -127,119 +78,28 @@ def downvote_comment(request): return vote_comment(request, -1) -def get_comments(request, limit=10): - try: - comment_id = int(request.GET["id"]) - parent_none = int(request.GET["parent_none"]) - except (ValueError, MultiValueDictKeyError): - return HttpResponseBadRequest() - else: - if comment_id and not Comment.objects.filter(id=comment_id).exists(): - raise Http404() - - offset = 0 - if "offset" in request.GET: - try: - offset = int(request.GET["offset"]) - except ValueError: - return HttpResponseBadRequest() - - target_comment = -1 - if "target_comment" in request.GET: - target_comment = int(request.GET["target_comment"]) - - comment_root_id = 0 - - if comment_id: - comment_obj = Comment.objects.get(pk=comment_id) - comment_root_id = comment_obj.id - else: - comment_obj = None - - queryset = comment_obj.linked_object.comments - if parent_none: - queryset = queryset.filter(parent=None, hidden=False) - queryset = queryset.exclude(pk=target_comment) - else: - queryset = queryset.filter(parent=comment_obj, hidden=False) - comment_count = len(queryset) - queryset = ( - queryset.select_related("author__user") - .defer("author__about") - .annotate( - count_replies=Count("replies", distinct=True), - )[offset : offset + limit] - ) - profile = None - if request.user.is_authenticated: - profile = request.profile - queryset = queryset.annotate( - my_vote=FilteredRelation("votes", condition=Q(votes__voter_id=profile.id)), - ).annotate(vote_score=Coalesce(F("my_vote__score"), Value(0))) - - new_offset = offset + min(len(queryset), limit) - - return render( - request, - "comments/content-list.html", - { - "profile": profile, - "comment_root_id": comment_root_id, - "comment_list": queryset, - "vote_hide_threshold": settings.DMOJ_COMMENT_VOTE_HIDE_THRESHOLD, - "perms": PermWrapper(request.user), - "offset": new_offset, - "limit": limit, - "comment_count": comment_count, - "comment_parent_none": parent_none, - "target_comment": target_comment, - "comment_more": comment_count - new_offset, - }, - ) - - -def get_show_more(request): - return get_comments(request) - - -def get_replies(request): - return get_comments(request) - - class CommentMixin(object): model = Comment - pk_url_kwarg = "id" - context_object_name = "comment" + pk_url_kwarg = 'id' + context_object_name = 'comment' class CommentRevisionAjax(CommentMixin, DetailView): - template_name = "comments/revision-ajax.html" + template_name = 'comments/revision-ajax.html' def get_context_data(self, **kwargs): context = super(CommentRevisionAjax, self).get_context_data(**kwargs) - revisions = Version.objects.get_for_object(self.object).order_by("-revision") - - if len(revisions) == 0: - raise Http404 - + revisions = Version.objects.get_for_object(self.object).order_by('-revision') try: - wanted = min( - max(int(self.request.GET.get("revision", 0)), 0), len(revisions) - 1 - ) - revision = revisions[wanted] - except (ValueError, IndexError): + wanted = min(max(int(self.request.GET.get('revision', 0)), 0), len(revisions) - 1) + except ValueError: raise Http404 - - data = json.loads(revision.serialized_data) - try: - context["body"] = data[0]["fields"]["body"] - except Exception: - context["body"] = "" + context['revision'] = revisions[wanted] return context def get_object(self, queryset=None): comment = super(CommentRevisionAjax, self).get_object(queryset) - if comment.hidden and not self.request.user.has_perm("judge.change_comment"): + if comment.hidden and not self.request.user.has_perm('judge.change_comment'): raise Http404() return comment @@ -247,29 +107,23 @@ class CommentRevisionAjax(CommentMixin, DetailView): class CommentEditForm(ModelForm): class Meta: model = Comment - fields = ["body"] - widgets = { - "body": HeavyPreviewPageDownWidget( - id="id-edit-comment-body", - preview=reverse_lazy("comment_preview"), - preview_timeout=1000, - hide_preview_button=True, - ), - } + fields = ['body'] + if MathJaxPagedownWidget is not None: + widgets = {'body': MathJaxPagedownWidget(attrs={'id': 'id-edit-comment-body'})} class CommentEditAjax(LoginRequiredMixin, CommentMixin, UpdateView): - template_name = "comments/edit-ajax.html" + template_name = 'comments/edit-ajax.html' form_class = CommentEditForm def form_valid(self, form): # update notifications comment = form.instance + del_mention_notifications(comment) add_mention_notifications(comment) - comment.revision_count = comment.versions.count() + 1 - comment.save(update_fields=["revision_count"]) - with revisions.create_revision(): - revisions.set_comment(_("Edited from site")) + + with transaction.atomic(), revisions.create_revision(): + revisions.set_comment(_('Edited from site')) revisions.set_user(self.request.user) return super(CommentEditAjax, self).form_valid(form) @@ -278,7 +132,7 @@ class CommentEditAjax(LoginRequiredMixin, CommentMixin, UpdateView): def get_object(self, queryset=None): comment = super(CommentEditAjax, self).get_object(queryset) - if self.request.user.has_perm("judge.change_comment"): + if self.request.user.has_perm('judge.change_comment'): return comment profile = self.request.profile if profile != comment.author or profile.mute or comment.hidden: @@ -287,228 +141,36 @@ class CommentEditAjax(LoginRequiredMixin, CommentMixin, UpdateView): class CommentEdit(TitleMixin, CommentEditAjax): - template_name = "comments/edit.html" + template_name = 'comments/edit.html' def get_title(self): - return _("Editing comment") + return _('Editing comment') class CommentContent(CommentMixin, DetailView): - template_name = "comments/content.html" + template_name = 'comments/content.html' class CommentVotesAjax(PermissionRequiredMixin, CommentMixin, DetailView): - template_name = "comments/votes.html" - permission_required = "judge.change_commentvote" + template_name = 'comments/votes.html' + permission_required = 'judge.change_commentvote' def get_context_data(self, **kwargs): context = super(CommentVotesAjax, self).get_context_data(**kwargs) - context["votes"] = self.object.votes.select_related("voter__user").only( - "id", "voter__display_rank", "voter__user__username", "score" - ) + context['votes'] = (self.object.votes.select_related('voter__user') + .only('id', 'voter__display_rank', 'voter__user__username', 'score')) return context @require_POST def comment_hide(request): - if not request.user.has_perm("judge.change_comment"): + if not request.user.has_perm('judge.change_comment'): raise PermissionDenied() try: - comment_id = int(request.POST["id"]) + comment_id = int(request.POST['id']) except ValueError: return HttpResponseBadRequest() comment = get_object_or_404(Comment, id=comment_id) comment.get_descendants(include_self=True).update(hidden=True) - get_visible_comment_count.dirty(comment.content_type, comment.object_id) - return HttpResponse("ok") - - -class CommentForm(ModelForm): - class Meta: - model = Comment - fields = ["body", "parent"] - widgets = { - "parent": forms.HiddenInput(), - } - - if HeavyPreviewPageDownWidget is not None: - widgets["body"] = HeavyPreviewPageDownWidget( - preview=reverse_lazy("comment_preview"), - preview_timeout=1000, - hide_preview_button=True, - ) - - def __init__(self, request, *args, **kwargs): - self.request = request - super(CommentForm, self).__init__(*args, **kwargs) - self.fields["body"].widget.attrs.update({"placeholder": _("Comment body")}) - - def clean(self): - if self.request is not None and self.request.user.is_authenticated: - profile = self.request.profile - if profile.mute: - raise ValidationError(_("Your part is silent, little toad.")) - elif ( - not self.request.user.is_staff - and not profile.submission_set.filter( - points=F("problem__points") - ).exists() - ): - raise ValidationError( - _( - "You need to have solved at least one problem " - "before your voice can be heard." - ) - ) - return super(CommentForm, self).clean() - - -class CommentedDetailView(TemplateResponseMixin, SingleObjectMixin, View): - comment_page = None - - def is_comment_locked(self): - if self.request.user.has_perm("judge.override_comment_lock"): - return False - return ( - self.request.in_contest - and self.request.participation.contest.use_clarifications - ) - - @method_decorator(ratelimit(key="user", rate=settings.RL_COMMENT)) - @method_decorator(login_required) - def post(self, request, *args, **kwargs): - self.object = self.get_object() - if self.is_comment_locked(): - return HttpResponseForbidden() - - parent = request.POST.get("parent") - if parent: - try: - parent = int(parent) - except ValueError: - return HttpResponseNotFound() - else: - if not self.object.comments.filter(hidden=False, id=parent).exists(): - return HttpResponseNotFound() - - form = CommentForm(request, request.POST) - if form.is_valid(): - comment = form.save(commit=False) - comment.author = request.profile - comment.linked_object = self.object - - with revisions.create_revision(): - revisions.set_user(request.user) - revisions.set_comment(_("Posted comment")) - comment.save() - - # add notification for reply - comment_notif_link = _get_html_link_notification(comment) - if comment.parent and comment.parent.author != comment.author: - make_notification( - [comment.parent.author], "Reply", comment_notif_link, comment.author - ) - - # add notification for page authors - page_authors = comment.linked_object.authors.all() - make_notification( - page_authors, "Comment", comment_notif_link, comment.author - ) - - add_mention_notifications(comment) - get_visible_comment_count.dirty(comment.content_type, comment.object_id) - - return HttpResponseRedirect(comment.get_absolute_url()) - - context = self.get_context_data(object=self.object, comment_form=form) - return self.render_to_response(context) - - def get(self, request, *args, **kwargs): - target_comment = None - self.object = self.get_object() - if "comment-id" in request.GET: - try: - comment_id = int(request.GET["comment-id"]) - comment_obj = Comment.objects.get(id=comment_id) - except (Comment.DoesNotExist, ValueError): - raise Http404 - if comment_obj.linked_object != self.object: - raise Http404 - target_comment = comment_obj.get_root() - return self.render_to_response( - self.get_context_data( - object=self.object, - target_comment=target_comment, - comment_form=CommentForm(request, initial={"parent": None}), - ) - ) - - def _get_queryset(self, target_comment): - if target_comment: - queryset = target_comment.get_descendants(include_self=True) - queryset = queryset.filter(hidden=False) - else: - queryset = self.object.comments - queryset = queryset.filter(parent=None, hidden=False) - queryset = queryset.filter(hidden=False).annotate( - count_replies=Count("replies", distinct=True), - )[:DEFAULT_OFFSET] - - if self.request.user.is_authenticated: - profile = self.request.profile - queryset = queryset.annotate( - my_vote=FilteredRelation( - "votes", condition=Q(votes__voter_id=profile.id) - ), - ).annotate(vote_score=Coalesce(F("my_vote__score"), Value(0))) - - return queryset - - def get_context_data(self, target_comment=None, **kwargs): - context = super(CommentedDetailView, self).get_context_data(**kwargs) - queryset = self._get_queryset(target_comment) - comment_count = self.object.comments.filter(parent=None, hidden=False).count() - - content_type = ContentType.objects.get_for_model(self.object) - all_comment_count = get_visible_comment_count(content_type, self.object.pk) - - if target_comment != None: - context["target_comment"] = target_comment.id - else: - context["target_comment"] = -1 - - if self.request.user.is_authenticated: - context["is_new_user"] = ( - not self.request.user.is_staff - and not self.request.profile.submission_set.filter( - points=F("problem__points") - ).exists() - ) - - context["comment_lock"] = self.is_comment_locked() - context["comment_list"] = list(queryset) - context["has_comments"] = len(context["comment_list"]) > 0 - - context["vote_hide_threshold"] = settings.DMOJ_COMMENT_VOTE_HIDE_THRESHOLD - - if queryset.exists(): - context["comment_root_id"] = context["comment_list"][0].id - else: - context["comment_root_id"] = 0 - - context["comment_parent_none"] = 1 - - if target_comment != None: - context["offset"] = 0 - context["comment_more"] = comment_count - 1 - else: - context["offset"] = DEFAULT_OFFSET - context["comment_more"] = comment_count - DEFAULT_OFFSET - - context["limit"] = DEFAULT_OFFSET - context["comment_count"] = comment_count - context["profile"] = self.request.profile - context["all_comment_count"] = all_comment_count - - return context + return HttpResponse('ok') diff --git a/judge/views/contests.py b/judge/views/contests.py index 3dc27f2..8dcd43b 100644 --- a/judge/views/contests.py +++ b/judge/views/contests.py @@ -1,4 +1,3 @@ -from copy import deepcopy import json import math from calendar import Calendar, SUNDAY @@ -14,29 +13,9 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.db import IntegrityError -from django.db.models import ( - Case, - Count, - F, - FloatField, - IntegerField, - Max, - Min, - Q, - Sum, - Value, - When, -) -from django.dispatch import receiver +from django.db.models import Case, Count, F, FloatField, IntegerField, Max, Min, Q, Sum, Value, When from django.db.models.expressions import CombinedExpression -from django.http import ( - Http404, - HttpResponse, - HttpResponseBadRequest, - HttpResponseRedirect, - JsonResponse, - HttpResponseNotAllowed, -) +from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, JsonResponse, HttpResponseNotAllowed from django.shortcuts import get_object_or_404, render from django.template.defaultfilters import date as date_filter from django.urls import reverse, reverse_lazy @@ -47,294 +26,118 @@ from django.utils.safestring import mark_safe from django.utils.timezone import make_aware from django.utils.translation import gettext as _, gettext_lazy from django.views.generic import ListView, TemplateView -from django.views.generic.detail import ( - BaseDetailView, - DetailView, - SingleObjectMixin, - View, -) +from django.views.generic.detail import BaseDetailView, DetailView, SingleObjectMixin, View from judge import event_poster as event -from judge.views.comment import CommentedDetailView +from judge.comments import CommentedDetailView from judge.forms import ContestCloneForm -from judge.models import ( - Contest, - ContestMoss, - ContestParticipation, - ContestProblem, - ContestTag, - Organization, - Problem, - Profile, - Submission, - ContestProblemClarification, - ContestsSummary, - OfficialContestCategory, - OfficialContestLocation, -) +from judge.models import Contest, ContestMoss, ContestParticipation, ContestProblem, ContestTag, \ + Organization, Problem, Profile, Submission, ProblemClarification from judge.tasks import run_moss from judge.utils.celery import redirect_to_task_status from judge.utils.opengraph import generate_opengraph from judge.utils.problems import _get_result_data from judge.utils.ranker import ranker from judge.utils.stats import get_bar_chart, get_pie_chart, get_histogram -from judge.utils.views import ( - DiggPaginatorMixin, - QueryStringSortMixin, - SingleObjectFormView, - TitleMixin, - generic_message, -) +from judge.utils.views import DiggPaginatorMixin, QueryStringSortMixin, SingleObjectFormView, TitleMixin, \ + generic_message from judge.widgets import HeavyPreviewPageDownWidget -from judge.views.pagevote import PageVoteDetailView -from judge.views.bookmark import BookMarkDetailView + +__all__ = ['ContestList', 'ContestDetail', 'ContestRanking', 'ContestJoin', 'ContestLeave', 'ContestCalendar', + 'ContestClone', 'ContestStats', 'ContestMossView', 'ContestMossDelete', 'contest_ranking_ajax', + 'ContestParticipationList', 'ContestParticipationDisqualify', 'get_contest_ranking_list', + 'base_contest_ranking_list', 'ContestClarificationView', 'update_contest_mode'] -__all__ = [ - "ContestList", - "ContestDetail", - "ContestRanking", - "ContestJoin", - "ContestLeave", - "ContestCalendar", - "ContestClone", - "ContestStats", - "ContestMossView", - "ContestMossDelete", - "ContestParticipationList", - "ContestParticipationDisqualify", - "get_contest_ranking_list", - "base_contest_ranking_list", - "ContestClarificationView", - "update_contest_mode", - "OfficialContestList", -] - - -def _find_contest(request, key): +def _find_contest(request, key, private_check=True): try: contest = Contest.objects.get(key=key) - private_check = not contest.public_scoreboard if private_check and not contest.is_accessible_by(request.user): raise ObjectDoesNotExist() except ObjectDoesNotExist: - return ( - generic_message( - request, - _("No such contest"), - _('Could not find a contest with the key "%s".') % key, - status=404, - ), - False, - ) + return generic_message(request, _('No such contest'), + _('Could not find a contest with the key "%s".') % key, status=404), False return contest, True class ContestListMixin(object): - official = False - def get_queryset(self): - q = Contest.get_visible_contests(self.request.user) - if self.official: - q = q.filter(official__isnull=False).select_related( - "official", "official__category", "official__location" - ) - else: - q = q.filter(official__isnull=True) - return q + return Contest.get_visible_contests(self.request.user) -class ContestList( - QueryStringSortMixin, DiggPaginatorMixin, TitleMixin, ContestListMixin, ListView -): +class ContestList(QueryStringSortMixin, DiggPaginatorMixin, TitleMixin, ContestListMixin, ListView): model = Contest - paginate_by = 10 - template_name = "contest/list.html" - title = gettext_lazy("Contests") - all_sorts = frozenset(("name", "user_count", "start_time")) - default_desc = frozenset(("name", "user_count")) - context_object_name = "contests" - - def get_default_sort_order(self, request): - if request.GET.get("contest") and settings.ENABLE_FTS: - return "-relevance" - if self.current_tab == "future": - return "start_time" - return "-start_time" - + paginate_by = 20 + template_name = 'contest/list.html' + title = gettext_lazy('Contests') + context_object_name = 'past_contests' + all_sorts = frozenset(('name', 'user_count', 'start_time')) + default_desc = frozenset(('name', 'user_count')) + default_sort = '-start_time' + @cached_property def _now(self): return timezone.now() - def GET_with_session(self, request, key): - if not request.GET.get(key): - return request.session.get(key, False) - return request.GET.get(key, None) == "1" - - def setup_contest_list(self, request): - self.contest_query = request.GET.get("contest", "") - - self.hide_organization_contests = 0 - if self.GET_with_session(request, "hide_organization_contests"): - self.hide_organization_contests = 1 - + def get(self, request, *args, **kwargs): + self.contest_query = None self.org_query = [] - if request.GET.get("orgs") and request.profile: + + if 'orgs' in self.request.GET: try: - self.org_query = list(map(int, request.GET.getlist("orgs"))) - if not request.user.is_superuser: - self.org_query = [ - i - for i in self.org_query - if i - in set( - request.profile.organizations.values_list("id", flat=True) - ) - ] + self.org_query = list(map(int, request.GET.getlist('orgs'))) except ValueError: pass - def get(self, request, *args, **kwargs): - default_tab = "active" - if not self.request.user.is_authenticated: - default_tab = "current" - self.current_tab = self.request.GET.get("tab", default_tab) - - self.setup_contest_list(request) - return super(ContestList, self).get(request, *args, **kwargs) - def post(self, request, *args, **kwargs): - to_update = ("hide_organization_contests",) - for key in to_update: - if key in request.GET: - val = request.GET.get(key) == "1" - request.session[key] = val - else: - request.session[key] = False - return HttpResponseRedirect(request.get_full_path()) - - def extra_queryset_filters(self, queryset): - return queryset - def _get_queryset(self): - queryset = ( - super(ContestList, self) - .get_queryset() - .prefetch_related("tags", "organizations") - ) - - if self.contest_query: - substr_queryset = queryset.filter( - Q(key__icontains=self.contest_query) - | Q(name__icontains=self.contest_query) - ) - if settings.ENABLE_FTS: - queryset = ( - queryset.search(self.contest_query).extra(order_by=["-relevance"]) - | substr_queryset - ) - else: - queryset = substr_queryset - if not self.org_query and self.request.organization: - self.org_query = [self.request.organization.id] - if self.hide_organization_contests: - queryset = queryset.filter(organizations=None) + queryset = super(ContestList, self).get_queryset() \ + .prefetch_related('tags', 'organizations', 'authors', 'curators', 'testers') + + if 'contest' in self.request.GET: + self.contest_query = query = ' '.join(self.request.GET.getlist('contest')).strip() + if query: + queryset = queryset.filter( + Q(key__icontains=query) | Q(name__icontains=query)) if self.org_query: queryset = queryset.filter(organizations__in=self.org_query) - queryset = self.extra_queryset_filters(queryset) + return queryset - def _get_past_contests_queryset(self): - return ( - self._get_queryset() - .filter(end_time__lt=self._now) - .order_by(self.order, "key") - ) - - def _active_participations(self): - return ContestParticipation.objects.filter( - virtual=0, - user=self.request.profile, - contest__start_time__lte=self._now, - contest__end_time__gte=self._now, - ) - - @cached_property - def _active_contests_ids(self): - return [ - participation.contest_id - for participation in self._active_participations().select_related("contest") - if not participation.ended - ] - - def _get_current_contests_queryset(self): - return ( - self._get_queryset() - .exclude(id__in=self._active_contests_ids) - .filter(start_time__lte=self._now, end_time__gte=self._now) - .order_by(self.order, "key") - ) - - def _get_future_contests_queryset(self): - return ( - self._get_queryset() - .filter(start_time__gt=self._now) - .order_by(self.order, "key") - ) - - def _get_active_participations_queryset(self): - active_contests = ( - self._get_queryset() - .filter(id__in=self._active_contests_ids) - .order_by(self.order, "key") - ) - ordered_ids = list(active_contests.values_list("id", flat=True)) - - participations = self._active_participations().filter( - contest_id__in=ordered_ids - ) - participations = sorted( - participations, key=lambda p: ordered_ids.index(p.contest_id) - ) - return participations - def get_queryset(self): - if self.current_tab == "past": - return self._get_past_contests_queryset() - elif self.current_tab == "current": - return self._get_current_contests_queryset() - elif self.current_tab == "future": - return self._get_future_contests_queryset() - else: # Default to active - return self._get_active_participations_queryset() + return self._get_queryset().order_by(self.order, 'key').filter(end_time__lt=self._now) def get_context_data(self, **kwargs): context = super(ContestList, self).get_context_data(**kwargs) + present, active, future = [], [], [] + for contest in self._get_queryset().exclude(end_time__lt=self._now): + if contest.start_time > self._now: + future.append(contest) + else: + present.append(contest) - context["current_tab"] = self.current_tab + if self.request.user.is_authenticated: + for participation in ContestParticipation.objects.filter(virtual=0, user=self.request.profile, + contest_id__in=present) \ + .select_related('contest') \ + .prefetch_related('contest__authors', 'contest__curators', 'contest__testers')\ + .annotate(key=F('contest__key')): + if not participation.ended: + active.append(participation) + present.remove(participation.contest) - context["current_count"] = self._get_current_contests_queryset().count() - context["future_count"] = self._get_future_contests_queryset().count() - context["active_count"] = len(self._get_active_participations_queryset()) - - context["now"] = self._now - context["first_page_href"] = "." - context["contest_query"] = self.contest_query - context["org_query"] = self.org_query - context["hide_organization_contests"] = int(self.hide_organization_contests) - if self.request.profile: - context["organizations"] = self.request.profile.organizations.all() - context["page_type"] = "list" - context["selected_order"] = self.request.GET.get("order") - context["all_sort_options"] = [ - ("start_time", _("Start time (asc.)")), - ("-start_time", _("Start time (desc.)")), - ("name", _("Name (asc.)")), - ("-name", _("Name (desc.)")), - ("user_count", _("User count (asc.)")), - ("-user_count", _("User count (desc.)")), - ] + active.sort(key=attrgetter('end_time', 'key')) + present.sort(key=attrgetter('end_time', 'key')) + future.sort(key=attrgetter('start_time')) + context['active_participations'] = active + context['current_contests'] = present + context['future_contests'] = future + context['now'] = self._now + context['first_page_href'] = '.' + context['contest_query'] = self.contest_query + context['org_query'] = self.org_query + context['organizations'] = Organization.objects.all() context.update(self.get_sort_context()) context.update(self.get_sort_paginate_context()) return context @@ -349,10 +152,10 @@ class PrivateContestError(Exception): class ContestMixin(object): - context_object_name = "contest" + context_object_name = 'contest' model = Contest - slug_field = "key" - slug_url_kwarg = "contest" + slug_field = 'key' + slug_url_kwarg = 'contest' @cached_property def is_editor(self): @@ -365,98 +168,76 @@ class ContestMixin(object): if not self.request.user.is_authenticated: return False return self.request.profile.id in self.object.tester_ids - + @cached_property def can_edit(self): return self.object.is_editable_by(self.request.user) - @cached_property - def can_access(self): - return self.object.is_accessible_by(self.request.user) - - def should_bypass_access_check(self, contest): - return False - def get_context_data(self, **kwargs): context = super(ContestMixin, self).get_context_data(**kwargs) if self.request.user.is_authenticated: try: - context[ - "live_participation" - ] = self.request.profile.contest_history.get( - contest=self.object, - virtual=ContestParticipation.LIVE, + context['live_participation'] = ( + self.request.profile.contest_history.get( + contest=self.object, + virtual=ContestParticipation.LIVE, + ) ) except ContestParticipation.DoesNotExist: - context["live_participation"] = None - context["has_joined"] = False + context['live_participation'] = None + context['has_joined'] = False else: - context["has_joined"] = True + context['has_joined'] = True else: - context["live_participation"] = None - context["has_joined"] = False - - context["now"] = timezone.now() - context["is_editor"] = self.is_editor - context["is_tester"] = self.is_tester - context["can_edit"] = self.can_edit - context["can_access"] = self.can_access + context['live_participation'] = None + context['has_joined'] = False + + context['now'] = timezone.now() + context['is_editor'] = self.is_editor + context['is_tester'] = self.is_tester + context['can_edit'] = self.can_edit if not self.object.og_image or not self.object.summary: - metadata = generate_opengraph( - "generated-meta-contest:%d" % self.object.id, - self.object.description, - ) - context["meta_description"] = self.object.summary or metadata[0] - context["og_image"] = self.object.og_image or metadata[1] - context["has_moss_api_key"] = settings.MOSS_API_KEY is not None - context["contest_has_hidden_subtasks"] = self.object.format.has_hidden_subtasks - context[ - "show_final_ranking" - ] = self.object.format.has_hidden_subtasks and self.object.is_editable_by( - self.request.user - ) - context["logo_override_image"] = self.object.logo_override_image - - if ( - not context["logo_override_image"] - and self.object.organizations.count() == 1 - ): - org_image = self.object.organizations.first().organization_image - if org_image: - context["logo_override_image"] = org_image.url + metadata = generate_opengraph('generated-meta-contest:%d' % self.object.id, + self.object.description, 'contest') + context['meta_description'] = self.object.summary or metadata[0] + context['og_image'] = self.object.og_image or metadata[1] + context['has_moss_api_key'] = settings.MOSS_API_KEY is not None + context['logo_override_image'] = self.object.logo_override_image + if not context['logo_override_image'] and self.object.organizations.count() == 1: + context['logo_override_image'] = self.object.organizations.first().logo_override_image return context - def contest_access_check(self, contest): - try: - contest.access_check(self.request.user) - except Contest.PrivateContest: - raise PrivateContestError( - contest.name, - contest.is_private, - contest.is_organization_private, - contest.organizations.all(), - ) - except Contest.Inaccessible: - raise Http404() - def get_object(self, queryset=None): contest = super(ContestMixin, self).get_object(queryset) profile = self.request.profile - if ( - profile is not None - and ContestParticipation.objects.filter( - id=profile.current_contest_id, contest_id=contest.id - ).exists() - ): + if (profile is not None and + ContestParticipation.objects.filter(id=profile.current_contest_id, contest_id=contest.id).exists()): return contest - if self.should_bypass_access_check(contest): + try: + contest.access_check(self.request.user) + except Contest.PrivateContest: + raise PrivateContestError(contest.name, contest.is_private, contest.is_organization_private, + contest.organizations.all()) + except Contest.Inaccessible: + raise Http404() + else: return contest - - self.contest_access_check(contest) + + if contest.is_private or contest.is_organization_private: + private_contest_error = PrivateContestError(contest.name, contest.is_private, + contest.is_organization_private, contest.organizations.all()) + if profile is None: + raise private_contest_error + if user.has_perm('judge.edit_all_contest'): + return contest + if not (contest.is_organization_private and + contest.organizations.filter(id__in=profile.organizations.all()).exists()) and \ + not (contest.is_private and contest.private_contestants.filter(id=profile.id).exists()): + raise private_contest_error return contest @@ -466,145 +247,59 @@ class ContestMixin(object): except Http404: key = kwargs.get(self.slug_url_kwarg, None) if key: - return generic_message( - request, - _("No such contest"), - _('Could not find a contest with the key "%s".') % key, - ) + return generic_message(request, _('No such contest'), + _('Could not find a contest with the key "%s".') % key) else: - return generic_message( - request, _("No such contest"), _("Could not find such contest.") - ) + return generic_message(request, _('No such contest'), + _('Could not find such contest.')) except PrivateContestError as e: - return render( - request, - "contest/private.html", - { - "error": e, - "title": _('Access to contest "%s" denied') % e.name, - }, - status=403, - ) + return render(request, 'contest/private.html', { + 'error': e, 'title': _('Access to contest "%s" denied') % e.name, + }, status=403) -class ContestDetail( - ContestMixin, - TitleMixin, - CommentedDetailView, - PageVoteDetailView, - BookMarkDetailView, -): - template_name = "contest/contest.html" +class ContestDetail(ContestMixin, TitleMixin, CommentedDetailView): + template_name = 'contest/contest.html' + + def get_comment_page(self): + return 'c:%s' % self.object.key def get_title(self): return self.object.name - def get_editable_organizations(self): - if not self.request.profile: - return [] - res = [] - for organization in self.object.organizations.all(): - can_edit = False - if self.request.profile.can_edit_organization(organization): - can_edit = True - if self.request.profile in organization and self.object.is_editable_by( - self.request.user - ): - can_edit = True - if can_edit: - res.append(organization) - return res - def get_context_data(self, **kwargs): context = super(ContestDetail, self).get_context_data(**kwargs) - context["contest_problems"] = ( - Problem.objects.filter(contests__contest=self.object) - .order_by("contests__order") - .defer("description") - .annotate( - has_public_editorial=Sum( - Case( - When(solution__is_public=True, then=1), - default=0, - output_field=IntegerField(), - ) - ) - ) + context['contest_problems'] = Problem.objects.filter(contests__contest=self.object) \ + .order_by('contests__order').defer('description') \ + .annotate(has_public_editorial=Sum(Case(When(solution__is_public=True, then=1), + default=0, output_field=IntegerField()))) \ .add_i18n_name(self.request.LANGUAGE_CODE) - ) - context["editable_organizations"] = self.get_editable_organizations() - context["is_clonable"] = is_contest_clonable(self.request, self.object) - - if self.object.is_in_course: - from judge.models import Course, CourseContest - - course = CourseContest.get_course_of_contest(self.object) - if Course.is_editable_by(course, self.request.profile): - context["editable_course"] = course - - if self.request.in_contest: - context["current_contest"] = self.request.participation.contest - else: - context["current_contest"] = None return context -def is_contest_clonable(request, contest): - if not request.profile: - return False - if not Organization.objects.filter(admins=request.profile).exists(): - return False - if request.user.has_perm("judge.clone_contest"): - return True - if contest.access_code and not contest.is_editable_by(request.user): - return False - if ( - contest.end_time is not None - and contest.end_time + timedelta(days=1) < contest._now - ): - return True - return False - - -class ContestClone(ContestMixin, TitleMixin, SingleObjectFormView): - title = _("Clone Contest") - template_name = "contest/clone.html" +class ContestClone(ContestMixin, PermissionRequiredMixin, TitleMixin, SingleObjectFormView): + title = _('Clone Contest') + template_name = 'contest/clone.html' form_class = ContestCloneForm - - def get_object(self, queryset=None): - contest = super().get_object(queryset) - if not is_contest_clonable(self.request, contest): - raise Http404() - return contest - - def get_form_kwargs(self): - kwargs = super().get_form_kwargs() - kwargs["org_choices"] = tuple( - Organization.objects.filter(admins=self.request.profile).values_list( - "id", "name" - ) - ) - kwargs["profile"] = self.request.profile - return kwargs + permission_required = 'judge.clone_contest' def form_valid(self, form): - tags = self.object.tags.all() - organization = form.cleaned_data["organization"] - private_contestants = self.object.private_contestants.all() - view_contest_scoreboard = self.object.view_contest_scoreboard.all() - contest_problems = self.object.contest_problems.all() + contest = self.object - contest = deepcopy(self.object) + tags = contest.tags.all() + organizations = contest.organizations.all() + private_contestants = contest.private_contestants.all() + view_contest_scoreboard = contest.view_contest_scoreboard.all() + contest_problems = contest.contest_problems.all() contest.pk = None contest.is_visible = False contest.user_count = 0 - contest.key = form.cleaned_data["key"] - contest.is_rated = False + contest.key = form.cleaned_data['key'] contest.save() contest.tags.set(tags) - contest.organizations.set([organization]) + contest.organizations.set(organizations) contest.private_contestants.set(private_contestants) contest.view_contest_scoreboard.set(view_contest_scoreboard) contest.authors.add(self.request.profile) @@ -614,16 +309,7 @@ class ContestClone(ContestMixin, TitleMixin, SingleObjectFormView): problem.pk = None ContestProblem.objects.bulk_create(contest_problems) - return HttpResponseRedirect( - reverse( - "organization_contest_edit", - args=( - organization.id, - organization.slug, - contest.key, - ), - ) - ) + return HttpResponseRedirect(reverse('admin:judge_contest_change', args=(contest.id,))) class ContestAccessDenied(Exception): @@ -635,7 +321,7 @@ class ContestAccessCodeForm(forms.Form): def __init__(self, *args, **kwargs): super(ContestAccessCodeForm, self).__init__(*args, **kwargs) - self.fields["access_code"].widget.attrs.update({"autocomplete": "off"}) + self.fields['access_code'].widget.attrs.update({'autocomplete': 'off'}) class ContestJoin(LoginRequiredMixin, ContestMixin, BaseDetailView): @@ -648,7 +334,7 @@ class ContestJoin(LoginRequiredMixin, ContestMixin, BaseDetailView): try: return self.join_contest(request) except ContestAccessDenied: - if request.POST.get("access_code"): + if request.POST.get('access_code'): return self.ask_for_access_code(ContestAccessCodeForm(request.POST)) else: return HttpResponseRedirect(request.path) @@ -657,54 +343,30 @@ class ContestJoin(LoginRequiredMixin, ContestMixin, BaseDetailView): contest = self.object if not contest.can_join and not (self.is_editor or self.is_tester): - return generic_message( - request, - _("Contest not ongoing"), - _('"%s" is not currently ongoing.') % contest.name, - ) + return generic_message(request, _('Contest not ongoing'), + _('"%s" is not currently ongoing.') % contest.name) profile = request.profile if profile.current_contest is not None: - profile.remove_contest() + return generic_message(request, _('Already in contest'), + _('You are already in a contest: "%s".') % profile.current_contest.contest.name) - if ( - not request.user.is_superuser - and contest.banned_users.filter(id=profile.id).exists() - ): - return generic_message( - request, - _("Banned from joining"), - _( - "You have been declared persona non grata for this contest. " - "You are permanently barred from joining this contest." - ), - ) + if not request.user.is_superuser and contest.banned_users.filter(id=profile.id).exists(): + return generic_message(request, _('Banned from joining'), + _('You have been declared persona non grata for this contest. ' + 'You are permanently barred from joining this contest.')) - requires_access_code = ( - not self.can_edit - and contest.access_code - and access_code != contest.access_code - ) + requires_access_code = (not self.can_edit and contest.access_code and access_code != contest.access_code) if contest.ended: if requires_access_code: raise ContestAccessDenied() while True: - virtual_id = max( - ( - ContestParticipation.objects.filter( - contest=contest, user=profile - ).aggregate(virtual_id=Max("virtual"))["virtual_id"] - or 0 - ) - + 1, - 1, - ) + virtual_id = max((ContestParticipation.objects.filter(contest=contest, user=profile) + .aggregate(virtual_id=Max('virtual'))['virtual_id'] or 0) + 1, 1) try: participation = ContestParticipation.objects.create( - contest=contest, - user=profile, - virtual=virtual_id, + contest=contest, user=profile, virtual=virtual_id, real_start=timezone.now(), ) # There is obviously a race condition here, so we keep trying until we win the race. @@ -717,57 +379,43 @@ class ContestJoin(LoginRequiredMixin, ContestMixin, BaseDetailView): LIVE = ContestParticipation.LIVE try: participation = ContestParticipation.objects.get( - contest=contest, - user=profile, - virtual=(SPECTATE if self.is_editor or self.is_tester else LIVE), + contest=contest, user=profile, virtual=(SPECTATE if self.is_editor or self.is_tester else LIVE), ) except ContestParticipation.DoesNotExist: if requires_access_code: raise ContestAccessDenied() participation = ContestParticipation.objects.create( - contest=contest, - user=profile, - virtual=(SPECTATE if self.is_editor or self.is_tester else LIVE), + contest=contest, user=profile, virtual=(SPECTATE if self.is_editor or self.is_tester else LIVE), real_start=timezone.now(), ) else: if participation.ended: participation = ContestParticipation.objects.get_or_create( - contest=contest, - user=profile, - virtual=SPECTATE, - defaults={"real_start": timezone.now()}, + contest=contest, user=profile, virtual=SPECTATE, + defaults={'real_start': timezone.now()}, )[0] profile.current_contest = participation profile.save() contest._updating_stats_only = True contest.update_user_count() - request.session["contest_mode"] = True - return HttpResponseRedirect(reverse("problem_list")) + return HttpResponseRedirect(reverse('problem_list')) def ask_for_access_code(self, form=None): contest = self.object wrong_code = False if form: if form.is_valid(): - if form.cleaned_data["access_code"] == contest.access_code: - return self.join_contest( - self.request, form.cleaned_data["access_code"] - ) + if form.cleaned_data['access_code'] == contest.access_code: + return self.join_contest(self.request, form.cleaned_data['access_code']) wrong_code = True else: form = ContestAccessCodeForm() - return render( - self.request, - "contest/access_code.html", - { - "form": form, - "wrong_code": wrong_code, - "title": _('Enter access code for "%s"') % contest.name, - }, - ) + return render(self.request, 'contest/access_code.html', { + 'form': form, 'wrong_code': wrong_code, + 'title': _('Enter access code for "%s"') % contest.name, + }) class ContestLeave(LoginRequiredMixin, ContestMixin, BaseDetailView): @@ -775,38 +423,29 @@ class ContestLeave(LoginRequiredMixin, ContestMixin, BaseDetailView): contest = self.get_object() profile = request.profile - if ( - profile.current_contest is None - or profile.current_contest.contest_id != contest.id - ): - return generic_message( - request, - _("No such contest"), - _('You are not in contest "%s".') % contest.key, - 404, - ) + if profile.current_contest is None or profile.current_contest.contest_id != contest.id: + return generic_message(request, _('No such contest'), + _('You are not in contest "%s".') % contest.key, 404) profile.remove_contest() - request.session["contest_mode"] = True # reset contest_mode - return HttpResponseRedirect(reverse("contest_view", args=(contest.key,))) + request.session['contest_mode'] = True # reset contest_mode + return HttpResponseRedirect(reverse('contest_view', args=(contest.key,))) -ContestDay = namedtuple("ContestDay", "date weekday is_pad is_today starts ends oneday") +ContestDay = namedtuple('ContestDay', 'date weekday is_pad is_today starts ends oneday') class ContestCalendar(TitleMixin, ContestListMixin, TemplateView): firstweekday = SUNDAY - weekday_classes = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"] - template_name = "contest/calendar.html" + weekday_classes = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'] + template_name = 'contest/calendar.html' def get(self, request, *args, **kwargs): try: - self.year = int(kwargs["year"]) - self.month = int(kwargs["month"]) + self.year = int(kwargs['year']) + self.month = int(kwargs['month']) except (KeyError, ValueError): - raise ImproperlyConfigured( - _("ContestCalendar requires integer year and month") - ) + raise ImproperlyConfigured(_('ContestCalendar requires integer year and month')) self.today = timezone.now().date() return self.render() @@ -816,16 +455,12 @@ class ContestCalendar(TitleMixin, ContestListMixin, TemplateView): def get_contest_data(self, start, end): end += timedelta(days=1) - contests = self.get_queryset().filter( - Q(start_time__gte=start, start_time__lt=end) - | Q(end_time__gte=start, end_time__lt=end) - ) + contests = self.get_queryset().filter(Q(start_time__gte=start, start_time__lt=end) | + Q(end_time__gte=start, end_time__lt=end)) starts, ends, oneday = (defaultdict(list) for i in range(3)) for contest in contests: start_date = timezone.localtime(contest.start_time).date() - end_date = timezone.localtime( - contest.end_time - timedelta(seconds=1) - ).date() + end_date = timezone.localtime(contest.end_time - timedelta(seconds=1)).date() if start_date == end_date: oneday[start_date].append(contest) else: @@ -835,25 +470,12 @@ class ContestCalendar(TitleMixin, ContestListMixin, TemplateView): def get_table(self): calendar = Calendar(self.firstweekday).monthdatescalendar(self.year, self.month) - starts, ends, oneday = self.get_contest_data( - make_aware(datetime.combine(calendar[0][0], time.min)), - make_aware(datetime.combine(calendar[-1][-1], time.min)), - ) - return [ - [ - ContestDay( - date=date, - weekday=self.weekday_classes[weekday], - is_pad=date.month != self.month, - is_today=date == self.today, - starts=starts[date], - ends=ends[date], - oneday=oneday[date], - ) - for weekday, date in enumerate(week) - ] - for week in calendar - ] + starts, ends, oneday = self.get_contest_data(make_aware(datetime.combine(calendar[0][0], time.min)), + make_aware(datetime.combine(calendar[-1][-1], time.min))) + return [[ContestDay( + date=date, weekday=self.weekday_classes[weekday], is_pad=date.month != self.month, + is_today=date == self.today, starts=starts[date], ends=ends[date], oneday=oneday[date], + ) for weekday, date in enumerate(week)] for week in calendar] def get_context_data(self, **kwargs): context = super(ContestCalendar, self).get_context_data(**kwargs) @@ -863,53 +485,40 @@ class ContestCalendar(TitleMixin, ContestListMixin, TemplateView): except ValueError: raise Http404() else: - context["title"] = _("Contests in %(month)s") % { - "month": date_filter(month, _("F Y")) - } + context['title'] = _('Contests in %(month)s') % {'month': date_filter(month, _("F Y"))} - dates = Contest.objects.aggregate(min=Min("start_time"), max=Max("end_time")) + dates = Contest.objects.aggregate(min=Min('start_time'), max=Max('end_time')) min_month = (self.today.year, self.today.month) - if dates["min"] is not None: - min_month = dates["min"].year, dates["min"].month + if dates['min'] is not None: + min_month = dates['min'].year, dates['min'].month max_month = (self.today.year, self.today.month) - if dates["max"] is not None: - max_month = max( - (dates["max"].year, dates["max"].month), - (self.today.year, self.today.month), - ) + if dates['max'] is not None: + max_month = max((dates['max'].year, dates['max'].month), (self.today.year, self.today.month)) month = (self.year, self.month) if month < min_month or month > max_month: # 404 is valid because it merely declares the lack of existence, without any reason raise Http404() - context["now"] = timezone.now() - context["calendar"] = self.get_table() - context["curr_month"] = date(self.year, self.month, 1) + context['now'] = timezone.now() + context['calendar'] = self.get_table() + context['curr_month'] = date(self.year, self.month, 1) if month > min_month: - context["prev_month"] = date( - self.year - (self.month == 1), - 12 if self.month == 1 else self.month - 1, - 1, - ) + context['prev_month'] = date(self.year - (self.month == 1), 12 if self.month == 1 else self.month - 1, 1) else: - context["prev_month"] = None + context['prev_month'] = None if month < max_month: - context["next_month"] = date( - self.year + (self.month == 12), - 1 if self.month == 12 else self.month + 1, - 1, - ) + context['next_month'] = date(self.year + (self.month == 12), 1 if self.month == 12 else self.month + 1, 1) else: - context["next_month"] = None + context['next_month'] = None return context class CachedContestCalendar(ContestCalendar): def render(self): - key = "contest_cal:%d:%d" % (self.year, self.month) + key = 'contest_cal:%d:%d' % (self.year, self.month) cached = cache.get(key) if cached is not None: return HttpResponse(cached) @@ -920,11 +529,11 @@ class CachedContestCalendar(ContestCalendar): class ContestStats(TitleMixin, ContestMixin, DetailView): - template_name = "contest/stats.html" - POINT_BIN = 10 # in point distribution + template_name = 'contest/stats.html' + POINT_BIN = 10 # in point distribution def get_title(self): - return _("%s Statistics") % self.object.name + return _('%s Statistics') % self.object.name def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -934,22 +543,15 @@ class ContestStats(TitleMixin, ContestMixin, DetailView): queryset = Submission.objects.filter(contest_object=self.object) - ac_count = Count( - Case(When(result="AC", then=Value(1)), output_field=IntegerField()) - ) - ac_rate = CombinedExpression( - ac_count / Count("problem"), "*", Value(100.0), output_field=FloatField() - ) + ac_count = Count(Case(When(result='AC', then=Value(1)), output_field=IntegerField())) + ac_rate = CombinedExpression(ac_count / Count('problem'), '*', Value(100.0), output_field=FloatField()) status_count_queryset = list( - queryset.values("problem__code", "result") - .annotate(count=Count("result")) - .values_list("problem__code", "result", "count"), + queryset.values('problem__code', 'result').annotate(count=Count('result')) + .values_list('problem__code', 'result', 'count'), ) labels, codes = [], [] - contest_problems = self.object.contest_problems.order_by("order").values_list( - "problem__name", "problem__code" - ) + contest_problems = self.object.contest_problems.order_by('order').values_list('problem__name', 'problem__code') if contest_problems: labels, codes = zip(*contest_problems) num_problems = len(labels) @@ -960,218 +562,145 @@ class ContestStats(TitleMixin, ContestMixin, DetailView): result_data = defaultdict(partial(list, [0] * num_problems)) for i in range(num_problems): - for category in _get_result_data(defaultdict(int, status_counts[i]))[ - "categories" - ]: - result_data[category["code"]][i] = category["count"] + for category in _get_result_data(defaultdict(int, status_counts[i]))['categories']: + result_data[category['code']][i] = category['count'] problem_points = [[] for _ in range(num_problems)] - point_count_queryset = list( - queryset.values( - "problem__code", "contest__points", "contest__problem__points" - ) - .annotate(count=Count("contest__points")) - .order_by("problem__code", "contest__points") - .values_list( - "problem__code", "contest__points", "contest__problem__points", "count" - ) - ) + point_count_queryset = list(queryset.values('problem__code', 'contest__points', 'contest__problem__points') + .annotate(count=Count('contest__points')) + .order_by('problem__code', 'contest__points') + .values_list('problem__code', 'contest__points', 'contest__problem__points', 'count')) counter = [[0 for _ in range(self.POINT_BIN + 1)] for _ in range(num_problems)] for problem_code, point, max_point, count in point_count_queryset: - if (point == None) or (problem_code not in codes): - continue + if (point == None) or (problem_code not in codes): continue problem_idx = codes.index(problem_code) - if max_point > 0: - bin_idx = math.floor(point * self.POINT_BIN / max_point) - else: - bin_idx = 0 - bin_idx = max(min(bin_idx, self.POINT_BIN), 0) + bin_idx = math.floor(point * self.POINT_BIN / max_point) counter[problem_idx][bin_idx] += count for i in range(num_problems): - problem_points[i] = [ - (j * 100 / self.POINT_BIN, counter[i][j]) - for j in range(len(counter[i])) - ] - + problem_points[i] = [(j * 100 / self.POINT_BIN, counter[i][j]) + for j in range(len(counter[i]))] + stats = { - "problem_status_count": { - "labels": labels, - "datasets": [ + 'problem_status_count': { + 'labels': labels, + 'datasets': [ { - "label": name, - "backgroundColor": settings.DMOJ_STATS_SUBMISSION_RESULT_COLORS[ - name - ], - "data": data, + 'label': name, + 'backgroundColor': settings.DMOJ_STATS_SUBMISSION_RESULT_COLORS[name], + 'data': data, } for name, data in result_data.items() ], }, - "problem_ac_rate": get_bar_chart( - queryset.values("contest__problem__order", "problem__name") - .annotate(ac_rate=ac_rate) - .order_by("contest__problem__order") - .values_list("problem__name", "ac_rate"), + 'problem_ac_rate': get_bar_chart( + queryset.values('contest__problem__order', 'problem__name').annotate(ac_rate=ac_rate) + .order_by('contest__problem__order').values_list('problem__name', 'ac_rate'), ), - "problem_point": [ - get_histogram(problem_points[i]) for i in range(num_problems) + 'problem_point': [get_histogram(problem_points[i]) + for i in range(num_problems) ], - "language_count": get_pie_chart( - queryset.values("language__name") - .annotate(count=Count("language__name")) - .filter(count__gt=0) - .order_by("-count") - .values_list("language__name", "count"), + 'language_count': get_pie_chart( + queryset.values('language__name').annotate(count=Count('language__name')) + .filter(count__gt=0).order_by('-count').values_list('language__name', 'count'), ), - "language_ac_rate": get_bar_chart( - queryset.values("language__name") - .annotate(ac_rate=ac_rate) - .filter(ac_rate__gt=0) - .values_list("language__name", "ac_rate"), + 'language_ac_rate': get_bar_chart( + queryset.values('language__name').annotate(ac_rate=ac_rate) + .filter(ac_rate__gt=0).values_list('language__name', 'ac_rate'), ), } - context["stats"] = mark_safe(json.dumps(stats)) - context["problems"] = labels + context['stats'] = mark_safe(json.dumps(stats)) + context['problems'] = labels return context ContestRankingProfile = namedtuple( - "ContestRankingProfile", - "id user username points cumtime tiebreaker participation " - "participation_rating problem_cells result_cell", + 'ContestRankingProfile', + 'id user css_class username points cumtime tiebreaker organization participation ' + 'participation_rating problem_cells result_cell', ) -BestSolutionData = namedtuple("BestSolutionData", "code points time state is_pretested") +BestSolutionData = namedtuple('BestSolutionData', 'code points time state is_pretested') -def make_contest_ranking_profile( - contest, participation, contest_problems, show_final=False -): - if not show_final: - points = participation.score - cumtime = participation.cumtime - else: - points = participation.score_final - cumtime = participation.cumtime_final - +def make_contest_ranking_profile(contest, participation, contest_problems): user = participation.user return ContestRankingProfile( id=user.id, - user=user, + user=user.user, + css_class=user.css_class, username=user.username, - points=points, - cumtime=cumtime, + points=participation.score, + cumtime=participation.cumtime, tiebreaker=participation.tiebreaker, - participation_rating=participation.rating.rating - if hasattr(participation, "rating") - else None, - problem_cells=[ - contest.format.display_user_problem( - participation, contest_problem, show_final - ) - for contest_problem in contest_problems - ], - result_cell=contest.format.display_participation_result( - participation, show_final - ), + organization=user.organization, + participation_rating=participation.rating.rating if hasattr(participation, 'rating') else None, + problem_cells=[contest.format.display_user_problem(participation, contest_problem) + for contest_problem in contest_problems], + result_cell=contest.format.display_participation_result(participation), participation=participation, ) -def base_contest_ranking_list( - contest, problems, queryset, show_final=False, extra_participation=None -): - participation_fields = [ - field.name - for field in ContestParticipation._meta.get_fields() - if field.concrete and not field.many_to_many - ] - fields_to_fetch = participation_fields + [ - "user__id", - "rating__rating", - ] - - res = [ - make_contest_ranking_profile(contest, participation, problems, show_final) - for participation in queryset.select_related("user", "rating").only( - *fields_to_fetch - ) - ] - Profile.prefetch_profile_cache([p.id for p in res]) - return res +def base_contest_ranking_list(contest, problems, queryset): + return [make_contest_ranking_profile(contest, participation, problems) for participation in + queryset.select_related('user__user', 'rating').defer('user__about', 'user__organizations__about')] -def contest_ranking_list( - contest, problems, queryset=None, show_final=False, extra_participation=None -): - if queryset is None: +def contest_ranking_list(contest, problems, queryset=None): + if not queryset: queryset = contest.users.filter(virtual=0) - - if extra_participation and extra_participation.virtual: - queryset = queryset | contest.users.filter(id=extra_participation.id) - - if show_final: - queryset = queryset.order_by( - "is_disqualified", "-score_final", "cumtime_final", "tiebreaker" - ) - else: - queryset = queryset.order_by( - "is_disqualified", "-score", "cumtime", "tiebreaker" - ) - - return base_contest_ranking_list( - contest, - problems, - queryset, - show_final, - ) + return base_contest_ranking_list(contest, problems, queryset + .prefetch_related('user__organizations') + .extra(select={'round_score': 'round(score, 6)'}) + .order_by('is_disqualified', '-round_score', 'cumtime', 'tiebreaker')) -def get_contest_ranking_list( - request, - contest, - participation=None, - ranking_list=contest_ranking_list, - ranker=ranker, - show_final=False, -): - problems = list( - contest.contest_problems.select_related("problem") - .defer("problem__description") - .order_by("order") - ) +def get_contest_ranking_list(request, contest, participation=None, ranking_list=contest_ranking_list, + show_current_virtual=False, ranker=ranker): + problems = list(contest.contest_problems.select_related('problem').defer('problem__description').order_by('order')) - if participation is None: - participation = _get_current_virtual_participation(request, contest) + users = ranker(ranking_list(contest, problems), key=attrgetter('points', 'cumtime', 'tiebreaker')) - ranking_list_result = ranking_list( - contest, problems, show_final=show_final, extra_participation=participation - ) - - users = ranker( - ranking_list_result, - key=attrgetter("points", "cumtime", "tiebreaker"), - ) + if show_current_virtual: + if participation is None and request.user.is_authenticated: + participation = request.profile.current_contest + if participation is None or participation.contest_id != contest.id: + participation = None + if participation is not None and participation.virtual: + users = chain([('-', make_contest_ranking_profile(contest, participation, problems))], users) return users, problems -def _get_current_virtual_participation(request, contest): - # Return None if not eligible - if not request.user.is_authenticated: - return None +def contest_ranking_ajax(request, contest, participation=None): + contest, exists = _find_contest(request, contest) + if not exists: + return HttpResponseBadRequest('Invalid contest', content_type='text/plain') - participation = request.profile.current_contest + if not contest.can_see_full_scoreboard(request.user): + raise Http404() - if participation is None or participation.contest_id != contest.id: - return None + queryset = contest.users.filter(virtual__gte=0) + if request.GET.get('friend') == 'true' and request.profile: + friends = list(request.profile.get_friends()) + queryset = queryset.filter(user__user__username__in=friends) + if request.GET.get('virtual') != 'true': + queryset = queryset.filter(virtual=0) - return participation + users, problems = get_contest_ranking_list(request, contest, participation, + ranking_list=partial(contest_ranking_list, queryset=queryset)) + return render(request, 'contest/ranking-table.html', { + 'users': users, + 'problems': problems, + 'contest': contest, + 'has_rating': contest.ratings.exists(), + 'can_edit': contest.is_editable_by(request.user) + }) class ContestRankingBase(ContestMixin, TitleMixin, DetailView): - template_name = "contest/ranking.html" - page_type = None + template_name = 'contest/ranking.html' + tab = None def get_title(self): raise NotImplementedError() @@ -1189,135 +718,67 @@ class ContestRankingBase(ContestMixin, TitleMixin, DetailView): raise Http404() users, problems = self.get_ranking_list() - context["users"] = users - context["problems"] = problems - context["page_type"] = self.page_type + context['users'] = users + context['problems'] = problems + context['tab'] = self.tab return context class ContestRanking(ContestRankingBase): - page_type = "ranking" - show_final = False - - def should_bypass_access_check(self, contest): - return contest.public_scoreboard + tab = 'ranking' def get_title(self): - return _("%s Rankings") % self.object.name + return _('%s Rankings') % self.object.name def get_ranking_list(self): if not self.object.can_see_full_scoreboard(self.request.user): - queryset = self.object.users.filter( - user=self.request.profile, virtual=ContestParticipation.LIVE - ) + queryset = self.object.users.filter(user=self.request.profile, virtual=ContestParticipation.LIVE) return get_contest_ranking_list( - self.request, - self.object, + self.request, self.object, ranking_list=partial(base_contest_ranking_list, queryset=queryset), - ranker=lambda users, key: ((_("???"), user) for user in users), + ranker=lambda users, key: ((_('???'), user) for user in users), ) - queryset = self.object.users - if self.friend_only: - friends = self.request.profile.get_friends() - queryset = queryset.filter(user_id__in=friends) - if not self.include_virtual: - queryset = queryset.filter(virtual=0) - else: - queryset = queryset.filter(virtual__gte=0) - - return get_contest_ranking_list( - self.request, - self.object, - ranking_list=partial(contest_ranking_list, queryset=queryset), - show_final=self.show_final, - ) - - def _get_default_include_virtual(self): - if hasattr(self.object, "official"): - return "1" - return "0" - - def setup_filters(self): - if self.request.profile: - self.friend_only = bool(self.request.GET.get("friend") == "1") - else: - self.friend_only = False - self.include_virtual = bool( - self.request.GET.get("virtual", self._get_default_include_virtual()) == "1" - ) - self.ajax_only = bool(self.request.GET.get("ajax") == "1") - - if self.ajax_only: - self.template_name = "contest/ranking-table.html" + return get_contest_ranking_list(self.request, self.object) def get_context_data(self, **kwargs): - self.setup_filters() context = super().get_context_data(**kwargs) - context["has_rating"] = self.object.ratings.exists() - if not self.ajax_only: - context["include_virtual"] = self.include_virtual - context["friend_only"] = self.friend_only + context['has_rating'] = self.object.ratings.exists() return context -class ContestFinalRanking(LoginRequiredMixin, ContestRanking): - page_type = "final_ranking" - show_final = True - - def get_ranking_list(self): - if not self.object.is_editable_by(self.request.user): - raise Http404() - if not self.object.format.has_hidden_subtasks: - raise Http404() - return super().get_ranking_list() - - class ContestParticipationList(LoginRequiredMixin, ContestRankingBase): - page_type = "participation" + tab = 'participation' def get_title(self): if self.profile == self.request.profile: - return _("Your participation in %s") % self.object.name + return _('Your participation in %s') % self.object.name return _("%s's participation in %s") % (self.profile.username, self.object.name) def get_ranking_list(self): - if ( - not self.object.can_see_full_scoreboard(self.request.user) - and self.profile != self.request.profile - ): + if not self.object.can_see_full_scoreboard(self.request.user) and self.profile != self.request.profile: raise Http404() - - queryset = self.object.users.filter(user=self.profile, virtual__gte=0).order_by( - "-virtual" - ) - live_link = format_html( - '{0}', - _("Live"), - self.profile.username, - reverse("contest_ranking", args=[self.object.key]), - ) + + queryset = self.object.users.filter(user=self.profile, virtual__gte=0).order_by('-virtual') + live_link = format_html('{0}', _('Live'), self.profile.username, + reverse('contest_ranking', args=[self.object.key])) return get_contest_ranking_list( - self.request, - self.object, + self.request, self.object, show_current_virtual=False, ranking_list=partial(base_contest_ranking_list, queryset=queryset), - ranker=lambda users, key: ( - (user.participation.virtual or live_link, user) for user in users - ), - ) + ranker=lambda users, key: ((user.participation.virtual or live_link, user) for user in users)) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["has_rating"] = False - context["now"] = timezone.now() - context["rank_header"] = _("Participation") - context["participation_tab"] = True + context['has_rating'] = False + context['now'] = timezone.now() + context['rank_header'] = _('Participation') + context['participation_tab'] = True return context def get(self, request, *args, **kwargs): - if "user" in kwargs: - self.profile = get_object_or_404(Profile, user__username=kwargs["user"]) + if 'user' in kwargs: + self.profile = get_object_or_404(Profile, user__username=kwargs['user']) else: self.profile = self.request.profile return super().get(request, *args, **kwargs) @@ -1334,22 +795,20 @@ class ContestParticipationDisqualify(ContestMixin, SingleObjectMixin, View): self.object = self.get_object() try: - participation = self.object.users.get(pk=request.POST.get("participation")) + participation = self.object.users.get(pk=request.POST.get('participation')) except ObjectDoesNotExist: pass else: participation.set_disqualified(not participation.is_disqualified) - return HttpResponseRedirect(reverse("contest_ranking", args=(self.object.key,))) + return HttpResponseRedirect(reverse('contest_ranking', args=(self.object.key,))) class ContestMossMixin(ContestMixin, PermissionRequiredMixin): - permission_required = "judge.moss_contest" + permission_required = 'judge.moss_contest' def get_object(self, queryset=None): contest = super().get_object(queryset) - if settings.MOSS_API_KEY is None or not contest.is_editable_by( - self.request.user - ): + if settings.MOSS_API_KEY is None or not contest.is_editable_by(self.request.user): raise Http404() if not contest.is_editable_by(self.request.user): raise Http404() @@ -1357,22 +816,16 @@ class ContestMossMixin(ContestMixin, PermissionRequiredMixin): class ContestMossView(ContestMossMixin, TitleMixin, DetailView): - template_name = "contest/moss.html" + template_name = 'contest/moss.html' def get_title(self): - return _("%s MOSS Results") % self.object.name + return _('%s MOSS Results') % self.object.name def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - problems = list( - map( - attrgetter("problem"), - self.object.contest_problems.order_by("order").select_related( - "problem" - ), - ) - ) + problems = list(map(attrgetter('problem'), self.object.contest_problems.order_by('order') + .select_related('problem'))) languages = list(map(itemgetter(0), ContestMoss.LANG_MAPPING)) results = ContestMoss.objects.filter(contest=self.object) @@ -1383,11 +836,9 @@ class ContestMossView(ContestMossMixin, TitleMixin, DetailView): for result_list in moss_results.values(): result_list.sort(key=lambda x: languages.index(x.language)) - context["languages"] = languages - context["has_results"] = results.exists() - context["moss_results"] = [ - (problem, moss_results[problem]) for problem in problems - ] + context['languages'] = languages + context['has_results'] = results.exists() + context['moss_results'] = [(problem, moss_results[problem]) for problem in problems] return context @@ -1395,9 +846,8 @@ class ContestMossView(ContestMossMixin, TitleMixin, DetailView): self.object = self.get_object() status = run_moss.delay(self.object.key) return redirect_to_task_status( - status, - message=_("Running MOSS for %s...") % (self.object.name,), - redirect=reverse("contest_moss", args=(self.object.key,)), + status, message=_('Running MOSS for %s...') % (self.object.name,), + redirect=reverse('contest_moss', args=(self.object.key,)), ) @@ -1405,45 +855,40 @@ class ContestMossDelete(ContestMossMixin, SingleObjectMixin, View): def post(self, request, *args, **kwargs): self.object = self.get_object() ContestMoss.objects.filter(contest=self.object).delete() - return HttpResponseRedirect(reverse("contest_moss", args=(self.object.key,))) + return HttpResponseRedirect(reverse('contest_moss', args=(self.object.key,))) class ContestTagDetailAjax(DetailView): model = ContestTag - slug_field = slug_url_kwarg = "name" - context_object_name = "tag" - template_name = "contest/tag-ajax.html" + slug_field = slug_url_kwarg = 'name' + context_object_name = 'tag' + template_name = 'contest/tag-ajax.html' class ContestTagDetail(TitleMixin, ContestTagDetailAjax): - template_name = "contest/tag.html" + template_name = 'contest/tag.html' def get_title(self): - return _("Contest tag: %s") % self.object.name + return _('Contest tag: %s') % self.object.name -class ContestProblemClarificationForm(forms.Form): - body = forms.CharField( - widget=HeavyPreviewPageDownWidget( - preview=reverse_lazy("comment_preview"), - preview_timeout=1000, - hide_preview_button=True, - ) - ) +class ProblemClarificationForm(forms.Form): + body = forms.CharField(widget=HeavyPreviewPageDownWidget(preview=reverse_lazy('comment_preview'), + preview_timeout=1000, hide_preview_button=True)) def __init__(self, request, *args, **kwargs): self.request = request - super(ContestProblemClarificationForm, self).__init__(*args, **kwargs) - self.fields["body"].widget.attrs.update({"placeholder": _("Issue description")}) + super(ProblemClarificationForm, self).__init__(*args, **kwargs) + self.fields['body'].widget.attrs.update({'placeholder': _('Issue description')}) class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView): - form_class = ContestProblemClarificationForm - template_name = "contest/clarification.html" + form_class = ProblemClarificationForm + template_name = 'contest/clarification.html' def get_form_kwargs(self): kwargs = super(NewContestClarificationView, self).get_form_kwargs() - kwargs["request"] = self.request + kwargs['request'] = self.request return kwargs def is_accessible(self): @@ -1453,7 +898,9 @@ class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView return False if not self.request.participation.contest == self.get_object(): return False - return self.get_object().is_editable_by(self.request.user) + return self.request.user.is_superuser or \ + self.request.profile in self.request.participation.contest.authors.all() or \ + self.request.profile in self.request.participation.contest.curators.all() def get(self, request, *args, **kwargs): if not self.is_accessible(): @@ -1461,35 +908,28 @@ class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView return super().get(self, request, *args, **kwargs) def form_valid(self, form): - problem_code = self.request.POST["problem"] - description = form.cleaned_data["body"] + problem_code = self.request.POST['problem'] + description = form.cleaned_data['body'] - clarification = ContestProblemClarification(description=description) - clarification.problem = get_object_or_404( - ContestProblem, contest=self.get_object(), problem__code=problem_code - ) + clarification = ProblemClarification(description=description) + clarification.problem = Problem.objects.get(code=problem_code) clarification.save() - - return HttpResponseRedirect(reverse("problem_list")) + + link = reverse('home') + return HttpResponseRedirect(link) def get_title(self): return "New clarification for %s" % self.object.name def get_content_title(self): - return mark_safe( - escape(_("New clarification for %s")) - % format_html( - '{1}', - reverse("problem_detail", args=[self.object.key]), - self.object.name, - ) - ) + return mark_safe(escape(_('New clarification for %s')) % + format_html('{1}', reverse('problem_detail', args=[self.object.key]), + self.object.name)) def get_context_data(self, **kwargs): context = super(NewContestClarificationView, self).get_context_data(**kwargs) - context["problems"] = ContestProblem.objects.filter( - contest=self.object - ).order_by("order") + context['problems'] = ContestProblem.objects.filter(contest=self.object)\ + .order_by('order') return context @@ -1499,164 +939,27 @@ class ContestClarificationAjax(ContestMixin, DetailView): if not self.object.is_accessible_by(request.user): raise Http404() - polling_time = 1 # minute - last_one_minute = timezone.now() - timezone.timedelta(minutes=polling_time) + polling_time = 1 # minute + last_one_minute = last_five_minutes = timezone.now()-timezone.timedelta(minutes=polling_time) + + queryset = list(ProblemClarification.objects.filter( + problem__in=self.object.problems.all(), + date__gte=last_one_minute + ).values('problem', 'problem__name', 'description')) + + problems = list(ContestProblem.objects.filter(contest=self.object)\ + .order_by('order').values('problem')) + problems = [i['problem'] for i in problems] + for cla in queryset: + cla['order'] = self.object.get_label_for_problem(problems.index(cla['problem'])) - queryset = ContestProblemClarification.objects.filter( - problem__in=self.object.contest_problems.all(), date__gte=last_one_minute - ) - - problems = list( - ContestProblem.objects.filter(contest=self.object) - .order_by("order") - .values_list("problem__code", flat=True) - ) - res = [] - for clarification in queryset: - value = { - "order": self.object.get_label_for_problem( - problems.index(clarification.problem.problem.code) - ), - "problem__name": clarification.problem.problem.name, - "description": clarification.description, - } - res.append(value) - - return JsonResponse(res, safe=False, json_dumps_params={"ensure_ascii": False}) + return JsonResponse(queryset, safe=False, json_dumps_params={'ensure_ascii': False}) def update_contest_mode(request): - if not request.is_ajax() or not request.method == "POST": - return HttpResponseNotAllowed(["POST"]) + if not request.is_ajax() or not request.method=='POST': + return HttpResponseNotAllowed(['POST']) - old_mode = request.session.get("contest_mode", True) - request.session["contest_mode"] = not old_mode - return HttpResponse() - - -ContestsSummaryData = namedtuple( - "ContestsSummaryData", - "username first_name last_name points point_contests css_class", -) - - -class ContestsSummaryView(DiggPaginatorMixin, ListView): - paginate_by = 50 - template_name = "contest/contests_summary.html" - - def get(self, *args, **kwargs): - try: - self.contests_summary = ContestsSummary.objects.get(key=kwargs["key"]) - except: - raise Http404() - return super().get(*args, **kwargs) - - def get_queryset(self): - total_rank = self.contests_summary.results - return total_rank - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["contests"] = self.contests_summary.contests.all() - context["title"] = _("Contests") - context["first_page_href"] = "." - return context - - -def recalculate_contest_summary_result(request, contest_summary): - scores_system = contest_summary.scores - contests = contest_summary.contests.all() - total_points = defaultdict(int) - result_per_contest = defaultdict(lambda: [(0, 0)] * len(contests)) - user_css_class = {} - - for i in range(len(contests)): - contest = contests[i] - users, problems = get_contest_ranking_list(request, contest) - for rank, user in users: - curr_score = 0 - if rank - 1 < len(scores_system): - curr_score = scores_system[rank - 1] - total_points[user.user] += curr_score - result_per_contest[user.user][i] = (curr_score, rank) - user_css_class[user.user] = user.user.css_class - - sorted_total_points = [ - ContestsSummaryData( - username=user.username, - first_name=user.first_name, - last_name=user.last_name, - points=total_points[user], - point_contests=result_per_contest[user], - css_class=user_css_class[user], - ) - for user in total_points - ] - - sorted_total_points.sort(key=lambda x: x.points, reverse=True) - total_rank = ranker(sorted_total_points) - return [(rank, item._asdict()) for rank, item in total_rank] - - -class OfficialContestList(ContestList): - official = True - template_name = "contest/official_list.html" - - def setup_contest_list(self, request): - self.contest_query = request.GET.get("contest", "") - self.org_query = [] - self.hide_organization_contests = False - - self.selected_categories = [] - self.selected_locations = [] - self.year_from = None - self.year_to = None - - if "category" in request.GET: - try: - self.selected_categories = list( - map(int, request.GET.getlist("category")) - ) - except ValueError: - pass - if "location" in request.GET: - try: - self.selected_locations = list( - map(int, request.GET.getlist("location")) - ) - except ValueError: - pass - if "year_from" in request.GET: - try: - self.year_from = int(request.GET.get("year_from")) - except ValueError: - pass - if "year_to" in request.GET: - try: - self.year_to = int(request.GET.get("year_to")) - except ValueError: - pass - - def extra_queryset_filters(self, queryset): - if self.selected_categories: - queryset = queryset.filter(official__category__in=self.selected_categories) - if self.selected_locations: - queryset = queryset.filter(official__location__in=self.selected_locations) - if self.year_from: - queryset = queryset.filter(official__year__gte=self.year_from) - if self.year_to: - queryset = queryset.filter(official__year__lte=self.year_to) - return queryset - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["page_type"] = "official" - context["is_official"] = True - context["categories"] = OfficialContestCategory.objects.all() - context["locations"] = OfficialContestLocation.objects.all() - context["selected_categories"] = self.selected_categories - context["selected_locations"] = self.selected_locations - context["year_from"] = self.year_from - context["year_to"] = self.year_to - - return context + old_mode = request.session.get('contest_mode', True) + request.session['contest_mode'] = not old_mode + return HttpResponse() \ No newline at end of file diff --git a/judge/views/course.py b/judge/views/course.py deleted file mode 100644 index 7514d6f..0000000 --- a/judge/views/course.py +++ /dev/null @@ -1,781 +0,0 @@ -from django.utils.html import mark_safe -from django.db import models -from django.views.generic import ListView, DetailView, View -from django.utils.translation import gettext, gettext_lazy as _ -from django.http import Http404 -from django import forms -from django.forms import ( - inlineformset_factory, - ModelForm, - modelformset_factory, - BaseModelFormSet, -) -from django.views.generic.edit import FormView -from django.shortcuts import get_object_or_404 -from django.urls import reverse_lazy, reverse -from django.db.models import Max, F, Sum -from django.core.exceptions import ObjectDoesNotExist - -from judge.models import ( - Course, - Contest, - CourseLesson, - Submission, - Profile, - CourseRole, - CourseLessonProblem, - CourseContest, - ContestProblem, - ContestParticipation, -) -from judge.models.course import RoleInCourse -from judge.widgets import ( - HeavyPreviewPageDownWidget, - HeavySelect2MultipleWidget, - HeavySelect2Widget, - DateTimePickerWidget, - Select2MultipleWidget, - Select2Widget, -) -from judge.forms import ( - ContestProblemFormSet, -) -from judge.utils.problems import ( - user_attempted_ids, - user_completed_ids, -) -from judge.utils.contest import ( - maybe_trigger_contest_rescore, -) -from reversion import revisions - - -def max_case_points_per_problem(profile, problems): - # return a dict {problem_id: {case_points, case_total}} - q = ( - Submission.objects.filter(user=profile, problem__in=problems) - .values("problem") - .annotate(case_points=Max("case_points"), case_total=F("case_total")) - .order_by("problem") - ) - res = {} - for problem in q: - res[problem["problem"]] = problem - return res - - -def calculate_lessons_progress(profile, lessons): - res = {} - total_achieved_points = total_lesson_points = 0 - for lesson in lessons: - problems = lesson.lesson_problems.values_list("problem", flat=True) - problem_points = max_case_points_per_problem(profile, problems) - achieved_points = total_points = 0 - - for lesson_problem in lesson.lesson_problems.all(): - val = problem_points.get(lesson_problem.problem.id) - if val and val["case_total"]: - achieved_points += ( - val["case_points"] / val["case_total"] * lesson_problem.score - ) - total_points += lesson_problem.score - - res[lesson.id] = { - "achieved_points": achieved_points, - "total_points": total_points, - "percentage": achieved_points / total_points * 100 if total_points else 0, - } - if total_points: - total_achieved_points += achieved_points / total_points * lesson.points - total_lesson_points += lesson.points - - res["total"] = { - "achieved_points": total_achieved_points, - "total_points": total_lesson_points, - "percentage": total_achieved_points / total_lesson_points * 100 - if total_lesson_points - else 0, - } - return res - - -def calculate_contests_progress(profile, course_contests): - res = {} - total_achieved_points = total_contest_points = 0 - for course_contest in course_contests: - contest = course_contest.contest - - achieved_points = 0 - participation = ContestParticipation.objects.filter( - contest=contest, user=profile, virtual=0 - ).first() - - if participation: - achieved_points = participation.score - - total_points = ( - ContestProblem.objects.filter(contest=contest).aggregate(Sum("points"))[ - "points__sum" - ] - or 0 - ) - - res[course_contest.id] = { - "achieved_points": achieved_points, - "total_points": total_points, - "percentage": achieved_points / total_points * 100 if total_points else 0, - } - - if total_points: - total_achieved_points += ( - achieved_points / total_points * course_contest.points - ) - total_contest_points += course_contest.points - - res["total"] = { - "achieved_points": total_achieved_points, - "total_points": total_contest_points, - "percentage": total_achieved_points / total_contest_points * 100 - if total_contest_points - else 0, - } - return res - - -def calculate_total_progress(profile, lesson_progress, contest_progress): - lesson_total = lesson_progress["total"] - contest_total = contest_progress["total"] - total_achieved_points = ( - lesson_total["achieved_points"] + contest_total["achieved_points"] - ) - total_points = lesson_total["total_points"] + contest_total["total_points"] - - res = { - "achieved_points": total_achieved_points, - "total_points": total_points, - "percentage": total_achieved_points / total_points * 100 if total_points else 0, - } - return res - - -class CourseList(ListView): - model = Course - template_name = "course/list.html" - queryset = Course.objects.filter(is_public=True).filter(is_open=True) - - def get_context_data(self, **kwargs): - context = super(CourseList, self).get_context_data(**kwargs) - context["courses"] = Course.get_accessible_courses(self.request.profile) - context["title"] = _("Courses") - context["page_type"] = "list" - return context - - -class CourseDetailMixin(object): - def dispatch(self, request, *args, **kwargs): - self.course = get_object_or_404(Course, slug=self.kwargs["slug"]) - if not Course.is_accessible_by(self.course, self.request.profile): - raise Http404() - self.is_editable = Course.is_editable_by(self.course, self.request.profile) - return super(CourseDetailMixin, self).dispatch(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super(CourseDetailMixin, self).get_context_data(**kwargs) - context["course"] = self.course - context["is_editable"] = self.is_editable - return context - - -class CourseEditableMixin(CourseDetailMixin): - def dispatch(self, request, *args, **kwargs): - res = super(CourseEditableMixin, self).dispatch(request, *args, **kwargs) - if not self.is_editable: - raise Http404() - return res - - -class CourseDetail(CourseDetailMixin, DetailView): - model = Course - template_name = "course/course.html" - - def get_object(self): - return self.course - - def get_context_data(self, **kwargs): - context = super(CourseDetail, self).get_context_data(**kwargs) - lessons = ( - self.course.lessons.filter(is_visible=True) - .order_by("order") - .prefetch_related("lesson_problems") - .all() - ) - course_contests = ( - self.course.contests.select_related("contest") - .filter(contest__is_visible=True) - .order_by("order") - ) - context["title"] = self.course.name - context["page_type"] = "home" - context["lessons"] = lessons - context["lesson_progress"] = calculate_lessons_progress( - self.request.profile, lessons - ) - context["course_contests"] = course_contests - context["contest_progress"] = calculate_contests_progress( - self.request.profile, course_contests - ) - - context["total_progress"] = calculate_total_progress( - self.request.profile, - context["lesson_progress"], - context["contest_progress"], - ) - - return context - - -class CourseLessonDetail(CourseDetailMixin, DetailView): - model = CourseLesson - template_name = "course/lesson.html" - - def get_object(self): - try: - self.lesson = CourseLesson.objects.get( - course=self.course, id=self.kwargs["id"] - ) - - is_editable = Course.is_editable_by(self.course, self.request.profile) - if not self.lesson.is_visible and not is_editable: - raise Http404() - - return self.lesson - except ObjectDoesNotExist: - raise Http404() - - def get_profile(self): - username = self.request.GET.get("user") - if not username: - return self.request.profile - - is_editable = Course.is_editable_by(self.course, self.request.profile) - if not is_editable: - raise Http404() - - try: - profile = Profile.objects.get(user__username=username) - is_student = profile.course_roles.filter( - role=RoleInCourse.STUDENT, course=self.course - ).exists() - if not is_student: - raise Http404() - return profile - except ObjectDoesNotExist: - raise Http404() - - def get_context_data(self, **kwargs): - context = super(CourseLessonDetail, self).get_context_data(**kwargs) - profile = self.get_profile() - context["profile"] = profile - context["title"] = self.lesson.title - context["lesson"] = self.lesson - context["completed_problem_ids"] = user_completed_ids(profile) - context["attempted_problems"] = user_attempted_ids(profile) - context["problem_points"] = max_case_points_per_problem( - profile, self.lesson.lesson_problems.values_list("problem", flat=True) - ) - return context - - -class CourseLessonForm(forms.ModelForm): - class Meta: - model = CourseLesson - fields = ["order", "title", "is_visible", "points", "content"] - widgets = { - "title": forms.TextInput(), - "content": HeavyPreviewPageDownWidget(preview=reverse_lazy("blog_preview")), - "problems": HeavySelect2MultipleWidget(data_view="problem_select2"), - } - - -CourseLessonFormSet = inlineformset_factory( - Course, CourseLesson, form=CourseLessonForm, extra=1, can_delete=True -) - - -class CourseLessonProblemForm(ModelForm): - class Meta: - model = CourseLessonProblem - fields = ["order", "problem", "score", "lesson"] - widgets = { - "problem": HeavySelect2Widget( - data_view="problem_select2", attrs={"style": "width: 100%"} - ), - "lesson": forms.HiddenInput(), - } - - -CourseLessonProblemFormSet = modelformset_factory( - CourseLessonProblem, form=CourseLessonProblemForm, extra=5, can_delete=True -) - - -class EditCourseLessonsView(CourseEditableMixin, FormView): - template_name = "course/edit_lesson.html" - form_class = CourseLessonFormSet - - def get_problem_formset(self, post=False, lesson=None): - formset = CourseLessonProblemFormSet( - data=self.request.POST if post else None, - prefix=f"problems_{lesson.id}" if lesson else "problems", - queryset=CourseLessonProblem.objects.filter(lesson=lesson).order_by( - "order" - ), - ) - if lesson: - for form in formset: - form.fields["lesson"].initial = lesson - return formset - - def get_context_data(self, **kwargs): - context = super(EditCourseLessonsView, self).get_context_data(**kwargs) - if self.request.method == "POST": - context["formset"] = self.form_class( - self.request.POST, self.request.FILES, instance=self.course - ) - context["problem_formsets"] = { - lesson.instance.id: self.get_problem_formset( - post=True, lesson=lesson.instance - ) - for lesson in context["formset"].forms - if lesson.instance.id - } - else: - context["formset"] = self.form_class( - instance=self.course, queryset=self.course.lessons.order_by("order") - ) - context["problem_formsets"] = { - lesson.instance.id: self.get_problem_formset( - post=False, lesson=lesson.instance - ) - for lesson in context["formset"].forms - if lesson.instance.id - } - - context["title"] = _("Edit lessons for %(course_name)s") % { - "course_name": self.course.name - } - context["content_title"] = mark_safe( - _("Edit lessons for %(course_name)s") - % { - "course_name": self.course.name, - "url": self.course.get_absolute_url(), - } - ) - context["page_type"] = "edit_lesson" - - return context - - def post(self, request, *args, **kwargs): - formset = self.form_class(request.POST, instance=self.course) - problem_formsets = [ - self.get_problem_formset(post=True, lesson=lesson.instance) - for lesson in formset.forms - if lesson.instance.id - ] - for pf in problem_formsets: - if not pf.is_valid(): - return self.form_invalid(pf) - - if formset.is_valid(): - formset.save() - for problem_formset in problem_formsets: - problem_formset.save() - for obj in problem_formset.deleted_objects: - if obj.pk is not None: - obj.delete() - return self.form_valid(formset) - else: - return self.form_invalid(formset) - - def get_success_url(self): - return self.request.path - - -class CourseStudentResults(CourseEditableMixin, DetailView): - model = Course - template_name = "course/grades.html" - - def get_object(self): - return self.course - - def get_grades(self): - students = self.course.get_students() - students.sort(key=lambda u: u.username.lower()) - lessons = ( - self.course.lessons.filter(is_visible=True) - .prefetch_related("lesson_problems") - .all() - ) - course_contests = ( - self.course.contests.select_related("contest") - .filter(contest__is_visible=True) - .order_by("order") - ) - - grade_lessons = {} - grade_contests = {} - grade_total = {} - for s in students: - grade_lessons[s] = lesson_progress = calculate_lessons_progress(s, lessons) - grade_contests[s] = contest_progress = calculate_contests_progress( - s, course_contests - ) - grade_total[s] = calculate_total_progress( - s, lesson_progress, contest_progress - ) - return grade_lessons, grade_contests, grade_total - - def get_context_data(self, **kwargs): - context = super(CourseStudentResults, self).get_context_data(**kwargs) - context["title"] = _("Grades in %(course_name)s") % { - "course_name": self.course.name, - } - context["content_title"] = mark_safe( - _("Grades in %(course_name)s") - % { - "course_name": self.course.name, - "url": self.course.get_absolute_url(), - } - ) - context["page_type"] = "grades" - ( - context["grade_lessons"], - context["grade_contests"], - context["grade_total"], - ) = self.get_grades() - context["lessons"] = self.course.lessons.filter(is_visible=True).order_by( - "order" - ) - context["course_contests"] = ( - self.course.contests.select_related("contest") - .filter(contest__is_visible=True) - .order_by("order") - ) - return context - - -class CourseStudentResultsLesson(CourseEditableMixin, DetailView): - model = CourseLesson - template_name = "course/grades_lesson.html" - - def get_object(self): - try: - self.lesson = CourseLesson.objects.get( - course=self.course, id=self.kwargs["id"] - ) - return self.lesson - except ObjectDoesNotExist: - raise Http404() - - def get_lesson_grades(self): - students = self.course.get_students() - students.sort(key=lambda u: u.username.lower()) - problems = self.lesson.lesson_problems.values_list("problem", flat=True) - lesson_problems = self.lesson.lesson_problems.all() - grades = {} - for s in students: - grades[s] = problem_points = max_case_points_per_problem(s, problems) - achieved_points = total_points = 0 - for lesson_problem in lesson_problems: - val = problem_points.get(lesson_problem.problem.id) - if val and val["case_total"]: - achieved_points += ( - val["case_points"] / val["case_total"] * lesson_problem.score - ) - total_points += lesson_problem.score - grades[s]["total"] = { - "achieved_points": achieved_points, - "total_points": total_points, - "percentage": achieved_points / total_points * 100 - if total_points - else 0, - } - return grades - - def get_context_data(self, **kwargs): - context = super(CourseStudentResultsLesson, self).get_context_data(**kwargs) - context["lesson"] = self.lesson - context["title"] = _("Grades of %(lesson_name)s in %(course_name)s") % { - "course_name": self.course.name, - "lesson_name": self.lesson.title, - } - context["content_title"] = mark_safe( - _( - "Grades of %(lesson_name)s in %(course_name)s" - ) - % { - "course_name": self.course.name, - "lesson_name": self.lesson.title, - "url_course": self.course.get_absolute_url(), - "url_lesson": self.lesson.get_absolute_url(), - } - ) - context["page_type"] = "grades" - context["grades"] = self.get_lesson_grades() - return context - - -class AddCourseContestForm(forms.ModelForm): - order = forms.IntegerField(label=_("Order")) - points = forms.IntegerField(label=_("Points")) - - class Meta: - model = Contest - fields = [ - "order", - "points", - "key", - "name", - "start_time", - "end_time", - "problems", - ] - widgets = { - "start_time": DateTimePickerWidget(), - "end_time": DateTimePickerWidget(), - "problems": HeavySelect2MultipleWidget(data_view="problem_select2"), - } - - def save(self, course, profile, commit=True): - contest = super().save(commit=False) - contest.is_in_course = True - - old_save_m2m = self.save_m2m - - def save_m2m(): - for i, problem in enumerate(self.cleaned_data["problems"]): - contest_problem = ContestProblem( - contest=contest, problem=problem, points=100, order=i + 1 - ) - contest_problem.save() - contest.contest_problems.add(contest_problem) - contest.authors.add(profile) - old_save_m2m() - - self.save_m2m = save_m2m - contest.save() - self.save_m2m() - - CourseContest.objects.create( - course=course, - contest=contest, - order=self.cleaned_data["order"], - points=self.cleaned_data["points"], - ) - - return contest - - -class AddCourseContest(CourseEditableMixin, FormView): - template_name = "course/add_contest.html" - form_class = AddCourseContestForm - - def get_title(self): - return _("Add contest") - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["title"] = self.get_title() - return context - - def form_valid(self, form): - with revisions.create_revision(): - revisions.set_comment(_("Added from course") + " " + self.course.name) - revisions.set_user(self.request.user) - - self.contest = form.save(course=self.course, profile=self.request.profile) - - return super().form_valid(form) - - def get_success_url(self): - return reverse( - "edit_course_contest", - args=[self.course.slug, self.contest.key], - ) - - -class CourseContestList(CourseEditableMixin, ListView): - template_name = "course/contest_list.html" - context_object_name = "course_contests" - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["title"] = _("Contest list") - context["page_type"] = "contests" - return context - - def get_queryset(self): - return self.course.contests.select_related("contest").all().order_by("order") - - -class EditCourseContestForm(ModelForm): - order = forms.IntegerField(label=_("Order")) - points = forms.IntegerField(label=_("Points")) - - class Meta: - model = Contest - fields = ( - "order", - "points", - "is_visible", - "key", - "name", - "start_time", - "end_time", - "format_name", - "authors", - "curators", - "testers", - "time_limit", - "freeze_after", - "use_clarifications", - "hide_problem_tags", - "public_scoreboard", - "scoreboard_visibility", - "points_precision", - "rate_limit", - "description", - "access_code", - "private_contestants", - "view_contest_scoreboard", - "banned_users", - ) - widgets = { - "authors": HeavySelect2MultipleWidget(data_view="profile_select2"), - "curators": HeavySelect2MultipleWidget(data_view="profile_select2"), - "testers": HeavySelect2MultipleWidget(data_view="profile_select2"), - "private_contestants": HeavySelect2MultipleWidget( - data_view="profile_select2" - ), - "banned_users": HeavySelect2MultipleWidget(data_view="profile_select2"), - "view_contest_scoreboard": HeavySelect2MultipleWidget( - data_view="profile_select2" - ), - "tags": Select2MultipleWidget, - "description": HeavyPreviewPageDownWidget( - preview=reverse_lazy("contest_preview") - ), - "start_time": DateTimePickerWidget(), - "end_time": DateTimePickerWidget(), - "format_name": Select2Widget(), - "scoreboard_visibility": Select2Widget(), - } - - def __init__(self, *args, **kwargs): - self.course_contest_instance = kwargs.pop("course_contest_instance", None) - super().__init__(*args, **kwargs) - - if self.course_contest_instance: - self.fields["order"].initial = self.course_contest_instance.order - self.fields["points"].initial = self.course_contest_instance.points - - def save(self, commit=True): - contest = super().save(commit=commit) - - if self.course_contest_instance: - self.course_contest_instance.order = self.cleaned_data["order"] - self.course_contest_instance.points = self.cleaned_data["points"] - if commit: - self.course_contest_instance.save() - - return contest - - -class EditCourseContest(CourseEditableMixin, FormView): - template_name = "course/edit_contest.html" - form_class = EditCourseContestForm - - def dispatch(self, request, *args, **kwargs): - self.contest = get_object_or_404(Contest, key=self.kwargs["contest"]) - res = super().dispatch(request, *args, **kwargs) - if not self.contest.is_in_course: - raise Http404() - return res - - def get_form_kwargs(self): - self.course_contest = get_object_or_404( - CourseContest, course=self.course, contest=self.contest - ) - - kwargs = super().get_form_kwargs() - kwargs["instance"] = self.contest - kwargs["course_contest_instance"] = self.course_contest - return kwargs - - def post(self, request, *args, **kwargs): - problem_formset = self.get_problem_formset(True) - if problem_formset.is_valid(): - self.problem_form_changes = False - for problem_form in problem_formset: - if problem_form.has_changed(): - self.problem_form_changes = True - if problem_form.cleaned_data.get("DELETE") and problem_form.instance.pk: - problem_form.instance.delete() - - for problem_form in problem_formset.save(commit=False): - if problem_form: - problem_form.contest = self.contest - problem_form.save() - - return super().post(request, *args, **kwargs) - - self.object = self.contest - return self.render_to_response( - self.get_context_data( - problems_form=problem_formset, - ) - ) - - def get_title(self): - return _("Edit contest") - - def form_valid(self, form): - with revisions.create_revision(): - revisions.set_comment(_("Edited from course") + " " + self.course.name) - revisions.set_user(self.request.user) - - if self.problem_form_changes: - maybe_trigger_contest_rescore(form, self.contest, True) - - form.save() - - return super().form_valid(form) - - def get_problem_formset(self, post=False): - return ContestProblemFormSet( - data=self.request.POST if post else None, - prefix="problems", - queryset=ContestProblem.objects.filter(contest=self.contest).order_by( - "order" - ), - ) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["title"] = self.get_title() - context["content_title"] = mark_safe( - _("Edit %(contest_name)s") - % { - "contest_name": self.contest.name, - "url": self.contest.get_absolute_url(), - } - ) - if "problems_form" not in context: - context["problems_form"] = self.get_problem_formset() - return context - - def get_success_url(self): - return reverse( - "edit_course_contest", - args=[self.course.slug, self.contest.key], - ) diff --git a/judge/views/custom_file_upload.py b/judge/views/custom_file_upload.py deleted file mode 100644 index cdc4f8e..0000000 --- a/judge/views/custom_file_upload.py +++ /dev/null @@ -1,43 +0,0 @@ -from django.shortcuts import render -from django.core.files.storage import FileSystemStorage -from django import forms -from django.utils.translation import gettext as _ -from django.conf import settings -from django.http import Http404 - -import os -import secrets -from urllib.parse import urljoin - -MEDIA_PATH = "uploads" - - -class FileUploadForm(forms.Form): - file = forms.FileField() - - -def file_upload(request): - if not request.user.is_superuser: - raise Http404() - file_url = None - if request.method == "POST": - form = FileUploadForm(request.POST, request.FILES) - if form.is_valid(): - file = request.FILES["file"] - random_str = secrets.token_urlsafe(5) - file_name, file_extension = os.path.splitext(file.name) - new_filename = f"{file_name}_{random_str}{file_extension}" - - fs = FileSystemStorage( - location=os.path.join(settings.MEDIA_ROOT, MEDIA_PATH) - ) - filename = fs.save(new_filename, file) - file_url = urljoin(settings.MEDIA_URL, os.path.join(MEDIA_PATH, filename)) - else: - form = FileUploadForm() - - return render( - request, - "custom_file_upload.html", - {"form": form, "file_url": file_url, "title": _("File Upload")}, - ) diff --git a/judge/views/email.py b/judge/views/email.py deleted file mode 100644 index 5544daf..0000000 --- a/judge/views/email.py +++ /dev/null @@ -1,121 +0,0 @@ -from django.contrib.auth.tokens import default_token_generator -from django.core.mail import send_mail -from django.shortcuts import render, redirect -from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode -from django.utils.encoding import force_bytes, force_text -from django.conf import settings -from django import forms -from django.utils.translation import gettext_lazy as _ -from django.urls import reverse -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.contrib.auth.hashers import check_password - -from urllib.parse import urlencode, urlunparse, urlparse - -from judge.models import Profile -from judge.utils.email_render import render_email_message - - -class EmailChangeForm(forms.Form): - new_email = forms.EmailField(label=_("New Email")) - password = forms.CharField(label=_("Password"), widget=forms.PasswordInput) - - def __init__(self, *args, **kwargs): - self.user = kwargs.pop("user", None) - super().__init__(*args, **kwargs) - - def clean_new_email(self): - new_email = self.cleaned_data.get("new_email") - if User.objects.filter(email=new_email).exists(): - raise forms.ValidationError(_("An account with this email already exists.")) - return new_email - - def clean_password(self): - password = self.cleaned_data.get("password") - if not self.user.check_password(password): - raise forms.ValidationError("Invalid password") - return password - - -@login_required -def email_change_view(request): - form = EmailChangeForm(request.POST or None, user=request.user) - - if request.method == "POST" and form.is_valid(): - new_email = request.POST.get("new_email") - user = request.user - profile = request.profile - - # Generate a token for email verification - token = default_token_generator.make_token(user) - uid = urlsafe_base64_encode(force_bytes(user.pk)) - - # Send the email to the user - subject = settings.SITE_NAME + " - " + _("Email Change Request") - email_contexts = { - "message": _( - "We have received a request to change your email to this email. Click the button below to change your email:" - ), - "title": _("Email Change"), - "button_text": _("Change Email"), - "url_path": reverse( - "email_change_verify", kwargs={"uidb64": uid, "token": token} - ), - } - message = render_email_message(request, email_contexts) - send_mail( - subject, - message, - settings.EMAIL_HOST_USER, - [new_email], - html_message=message, - ) - profile.email_change_pending = new_email - profile.save() - return redirect("email_change_pending") - - return render( - request, - "email_change/email_change.html", - { - "form": form, - "title": _("Change email"), - }, - ) - - -def verify_email_view(request, uidb64, token): - try: - uid = force_text(urlsafe_base64_decode(uidb64)) - user = User.objects.get(pk=uid) - except (TypeError, ValueError, OverflowError, User.DoesNotExist): - user = None - if user is not None and default_token_generator.check_token(user, token): - profile = Profile.objects.get(user=user) - new_email = profile.email_change_pending - if new_email and not User.objects.filter(email=new_email).exists(): - user.email = new_email - profile.email_change_pending = None - user.save() - profile.save() - - return render( - request, - "email_change/email_change_success.html", - {"title": _("Success"), "user": user}, - ) - - return render( - request, "email_change/email_change_failure.html", {"title": _("Invalid")} - ) - - -def email_change_pending_view(request): - return render( - request, - "email_change/email_change_pending.html", - { - "title": _("Email change pending"), - }, - ) diff --git a/judge/views/error.py b/judge/views/error.py index cffed7e..113422d 100644 --- a/judge/views/error.py +++ b/judge/views/error.py @@ -5,42 +5,29 @@ from django.utils.translation import gettext as _ def error(request, context, status): - return render(request, "error.html", context=context, status=status) + return render(request, 'error.html', context=context, status=status) def error404(request, exception=None): # TODO: "panic: go back" - return render( - request, - "generic-message.html", - { - "title": _("404 error"), - "message": _('Could not find page "%s"') % request.path, - }, - status=404, - ) + return render(request, 'generic-message.html', { + 'title': _('404 error'), + 'message': _('Could not find page "%s"') % request.path, + }, status=404) def error403(request, exception=None): - return error( - request, - { - "id": "unauthorized_access", - "description": _("no permission for %s") % request.path, - "code": 403, - }, - 403, - ) + return error(request, { + 'id': 'unauthorized_access', + 'description': _('no permission for %s') % request.path, + 'code': 403, + }, 403) def error500(request): - return error( - request, - { - "id": "invalid_state", - "description": _("corrupt page %s") % request.path, - "traceback": traceback.format_exc(), - "code": 500, - }, - 500, - ) + return error(request, { + 'id': 'invalid_state', + 'description': _('corrupt page %s') % request.path, + 'traceback': traceback.format_exc(), + 'code': 500, + }, 500) diff --git a/judge/views/feed.py b/judge/views/feed.py deleted file mode 100644 index cd809d5..0000000 --- a/judge/views/feed.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.views.generic import ListView -from django.shortcuts import render -from django.urls import reverse - -from judge.utils.infinite_paginator import InfinitePaginationMixin - - -class FeedView(InfinitePaginationMixin, ListView): - def get_feed_context(selfl, object_list): - return {} - - def get(self, request, *args, **kwargs): - only_content = request.GET.get("only_content", None) - if only_content and self.feed_content_template_name: - queryset = self.get_queryset() - paginator, page, object_list, _ = self.paginate_queryset( - queryset, self.paginate_by - ) - context = { - self.context_object_name: object_list, - "has_next_page": page.has_next(), - } - context.update(self.get_feed_context(object_list)) - return render(request, self.feed_content_template_name, context) - - return super(FeedView, self).get(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["has_next_page"] = context["page_obj"].has_next() - try: - context["feed_content_url"] = reverse(self.url_name) - except Exception as e: - context["feed_content_url"] = self.request.path - return context diff --git a/judge/views/ide.py b/judge/views/ide.py new file mode 100644 index 0000000..cbdba47 --- /dev/null +++ b/judge/views/ide.py @@ -0,0 +1,29 @@ +from django.utils.translation import gettext as _ +from django.contrib.auth.decorators import login_required +from django.http import HttpResponse, Http404, JsonResponse +from django.views.decorators.csrf import csrf_exempt + +import requests, json, http + +PREFIX_URL = 'ide/api' + +@login_required +@csrf_exempt +def api(request): + url = 'http://localhost:2358' + request.get_full_path()[len(PREFIX_URL) + 1:] + headers = {'X-Judge0-Token': 'cuom1999'} + r = None + if request.method == 'POST': + r = requests.post(url, data=json.loads(request.body.decode('utf-8')), headers=headers) + elif request.method == 'GET': + r = requests.get(url, headers=headers) + else: + return Http404() + + res = r.content.decode('utf-8') + try: + res = json.loads(r.content.decode('utf-8')) + return JsonResponse(res, status=r.status_code, safe=False) + except Exception: + return HttpResponse(res) + \ No newline at end of file diff --git a/judge/views/internal.py b/judge/views/internal.py deleted file mode 100644 index 060a2c3..0000000 --- a/judge/views/internal.py +++ /dev/null @@ -1,170 +0,0 @@ -import logging -import json - -from django.views.generic import ListView -from django.utils.translation import gettext as _, gettext_lazy -from django.db.models import Count, Q -from django.http import HttpResponseForbidden -from django.urls import reverse -from django.shortcuts import render - -from judge.utils.diggpaginator import DiggPaginator -from judge.models import VolunteerProblemVote, Problem - - -class InternalView(object): - def get(self, request, *args, **kwargs): - if request.user.is_superuser: - return super(InternalView, self).get(request, *args, **kwargs) - return HttpResponseForbidden() - - -class InternalProblem(InternalView, ListView): - model = Problem - title = _("Internal problems") - template_name = "internal/problem/problem.html" - paginate_by = 100 - context_object_name = "problems" - - def get_paginator( - self, queryset, per_page, orphans=0, allow_empty_first_page=True, **kwargs - ): - return DiggPaginator( - queryset, - per_page, - body=6, - padding=2, - orphans=orphans, - allow_empty_first_page=allow_empty_first_page, - **kwargs - ) - - def get_search_query(self): - return self.request.GET.get("q") or self.request.POST.get("q") - - def get_queryset(self): - queryset = Problem.objects.annotate( - vote_count=Count("volunteer_user_votes") - ).filter(vote_count__gte=1) - query = self.get_search_query() - if query: - queryset = queryset.filter( - Q(code__icontains=query) | Q(name__icontains=query) - ) - return queryset.order_by("-vote_count") - - def get_context_data(self, **kwargs): - context = super(InternalProblem, self).get_context_data(**kwargs) - context["page_type"] = "problem" - context["title"] = self.title - context["page_prefix"] = self.request.path + "?page=" - context["first_page_href"] = self.request.path - context["query"] = self.get_search_query() - - return context - - -def get_problem_votes(request): - if not request.user.is_superuser: - return HttpResponseForbidden() - try: - problem = Problem.objects.get(id=request.GET.get("id")) - except: - return HttpResponseForbidden() - votes = ( - problem.volunteer_user_votes.select_related("voter") - .prefetch_related("types") - .order_by("id") - ) - return render( - request, - "internal/problem/votes.html", - { - "problem": problem, - "votes": votes, - }, - ) - - -class RequestTimeMixin(object): - def get_requests_data(self): - logger = logging.getLogger(self.log_name) - log_filename = logger.handlers[0].baseFilename - requests = [] - - with open(log_filename, "r") as f: - for line in f: - try: - info = json.loads(line) - requests.append(info) - except: - continue - return requests - - -class InternalRequestTime(InternalView, ListView, RequestTimeMixin): - title = _("Request times") - template_name = "internal/request_time.html" - context_object_name = "pages" - log_name = "judge.request_time" - detail_url_name = "internal_request_time_detail" - page_type = "request_time" - - def get_queryset(self): - requests = self.get_requests_data() - table = {} - for r in requests: - url_name = r["url_name"] - if url_name not in table: - table[url_name] = { - "time": 0, - "count": 0, - "url_name": url_name, - } - old_sum = table[url_name]["time"] * table[url_name]["count"] - table[url_name]["count"] += 1 - table[url_name]["time"] = (old_sum + float(r["response_time"])) / table[ - url_name - ]["count"] - order = self.request.GET.get("order", "time") - return sorted(table.values(), key=lambda x: x[order], reverse=True) - - def get_context_data(self, **kwargs): - context = super(InternalRequestTime, self).get_context_data(**kwargs) - context["page_type"] = self.page_type - context["title"] = self.title - context["current_path"] = self.request.path - context["detail_path"] = reverse(self.detail_url_name) - return context - - -class InternalRequestTimeDetail(InternalRequestTime): - template_name = "internal/request_time_detail.html" - context_object_name = "requests" - - def get_queryset(self): - url_name = self.request.GET.get("url_name", None) - if not url_name: - return HttpResponseForbidden() - if url_name == "None": - url_name = None - self.title = url_name - requests = self.get_requests_data() - filtered_requests = [r for r in requests if r["url_name"] == url_name] - order = self.request.GET.get("order", "response_time") - return sorted(filtered_requests, key=lambda x: x[order], reverse=True)[:200] - - def get_context_data(self, **kwargs): - context = super(InternalRequestTimeDetail, self).get_context_data(**kwargs) - context["url_name"] = self.request.GET.get("url_name", None) - return context - - -class InternalSlowRequest(InternalRequestTime): - log_name = "judge.slow_request" - detail_url_name = "internal_slow_request_detail" - page_type = "slow_request" - - -class InternalSlowRequestDetail(InternalRequestTimeDetail): - log_name = "judge.slow_request" diff --git a/judge/views/language.py b/judge/views/language.py index bbdc4bd..87ab8cb 100644 --- a/judge/views/language.py +++ b/judge/views/language.py @@ -7,12 +7,12 @@ from judge.utils.views import TitleMixin class LanguageList(TitleMixin, ListView): model = Language - context_object_name = "languages" - template_name = "status/language-list.html" - title = gettext_lazy("Runtimes") + context_object_name = 'languages' + template_name = 'status/language-list.html' + title = gettext_lazy('Runtimes') def get_queryset(self): - queryset = super().get_queryset().prefetch_related("runtimeversion_set") + queryset = super().get_queryset().prefetch_related('runtimeversion_set') if not self.request.user.is_superuser and not self.request.user.is_staff: queryset = queryset.filter(judges__online=True).distinct() return queryset diff --git a/judge/views/license.py b/judge/views/license.py index 0e208b7..77208e6 100644 --- a/judge/views/license.py +++ b/judge/views/license.py @@ -6,9 +6,9 @@ from judge.utils.views import TitleMixin class LicenseDetail(TitleMixin, DetailView): model = License - slug_field = slug_url_kwarg = "key" - context_object_name = "license" - template_name = "license.html" + slug_field = slug_url_kwarg = 'key' + context_object_name = 'license' + template_name = 'license.html' def get_title(self): return self.object.name diff --git a/judge/views/mailgun.py b/judge/views/mailgun.py index f28589a..3bd8321 100644 --- a/judge/views/mailgun.py +++ b/judge/views/mailgun.py @@ -15,77 +15,49 @@ from registration.models import RegistrationProfile from judge.utils.unicode import utf8bytes -logger = logging.getLogger("judge.mail.activate") +logger = logging.getLogger('judge.mail.activate') class MailgunActivationView(View): - if hasattr(settings, "MAILGUN_ACCESS_KEY"): - + if hasattr(settings, 'MAILGUN_ACCESS_KEY'): def post(self, request, *args, **kwargs): params = request.POST - timestamp = params.get("timestamp", "") - token = params.get("token", "") - signature = params.get("signature", "") + timestamp = params.get('timestamp', '') + token = params.get('token', '') + signature = params.get('signature', '') - logger.debug("Received request: %s", params) + logger.debug('Received request: %s', params) - if ( - signature - != hmac.new( - key=utf8bytes(settings.MAILGUN_ACCESS_KEY), - msg=utf8bytes("%s%s" % (timestamp, token)), - digestmod=hashlib.sha256, - ).hexdigest() - ): - logger.info( - "Rejected request: signature: %s, timestamp: %s, token: %s", - signature, - timestamp, - token, - ) + if signature != hmac.new(key=utf8bytes(settings.MAILGUN_ACCESS_KEY), + msg=utf8bytes('%s%s' % (timestamp, token)), digestmod=hashlib.sha256).hexdigest(): + logger.info('Rejected request: signature: %s, timestamp: %s, token: %s', signature, timestamp, token) raise PermissionDenied() - _, sender = parseaddr(params.get("from")) + _, sender = parseaddr(params.get('from')) if not sender: - logger.info("Rejected invalid sender: %s", params.get("from")) + logger.info('Rejected invalid sender: %s', params.get('from')) return HttpResponse(status=406) try: user = User.objects.get(email__iexact=sender) except (User.DoesNotExist, User.MultipleObjectsReturned): - logger.info( - "Rejected unknown sender: %s: %s", sender, params.get("from") - ) + logger.info('Rejected unknown sender: %s: %s', sender, params.get('from')) return HttpResponse(status=406) try: registration = RegistrationProfile.objects.get(user=user) except RegistrationProfile.DoesNotExist: - logger.info( - "Rejected sender without RegistrationProfile: %s: %s", - sender, - params.get("from"), - ) + logger.info('Rejected sender without RegistrationProfile: %s: %s', sender, params.get('from')) return HttpResponse(status=406) if registration.activated: - logger.info( - "Rejected activated sender: %s: %s", sender, params.get("from") - ) + logger.info('Rejected activated sender: %s: %s', sender, params.get('from')) return HttpResponse(status=406) key = registration.activation_key - if key in params.get("body-plain", "") or key in params.get( - "body-html", "" - ): - if RegistrationProfile.objects.activate_user( - key, get_current_site(request) - ): - logger.info("Activated sender: %s: %s", sender, params.get("from")) - return HttpResponse("Activated", status=200) - logger.info( - "Failed to activate sender: %s: %s", sender, params.get("from") - ) + if key in params.get('body-plain', '') or key in params.get('body-html', ''): + if RegistrationProfile.objects.activate_user(key, get_current_site(request)): + logger.info('Activated sender: %s: %s', sender, params.get('from')) + return HttpResponse('Activated', status=200) + logger.info('Failed to activate sender: %s: %s', sender, params.get('from')) else: - logger.info( - "Activation key not found: %s: %s", sender, params.get("from") - ) + logger.info('Activation key not found: %s: %s', sender, params.get('from')) return HttpResponse(status=406) @method_decorator(csrf_exempt) diff --git a/judge/views/markdown_editor.py b/judge/views/markdown_editor.py deleted file mode 100644 index a014ba6..0000000 --- a/judge/views/markdown_editor.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.views import View -from django.shortcuts import render -from django.utils.translation import gettext_lazy as _ - - -class MarkdownEditor(View): - def get(self, request): - return render( - request, - "markdown_editor/markdown_editor.html", - { - "title": _("Markdown Editor"), - }, - ) diff --git a/judge/views/notification.py b/judge/views/notification.py index ee720c1..b0d350f 100644 --- a/judge/views/notification.py +++ b/judge/views/notification.py @@ -2,41 +2,51 @@ from django.contrib.auth.decorators import login_required from django.views.generic import ListView from django.utils.translation import ugettext as _ from django.utils.timezone import now -from django.http import Http404 +from django.db.models import BooleanField, Value -from judge.models import Profile, Notification, NotificationProfile -from judge.models.notification import unseen_notifications_count -from judge.utils.infinite_paginator import InfinitePaginationMixin +from judge.utils.cachedict import CacheDict +from judge.models import Profile, Comment, Notification -__all__ = ["NotificationList"] +__all__ = ['NotificationList'] - -class NotificationList(InfinitePaginationMixin, ListView): +class NotificationList(ListView): model = Notification - context_object_name = "notifications" - template_name = "notification/list.html" - paginate_by = 50 - + context_object_name = 'notifications' + template_name = 'notification/list.html' + def get_queryset(self): - self.unseen_cnt = unseen_notifications_count(self.request.profile) - - self.queryset = Notification.objects.filter( - owner=self.request.profile - ).order_by("-id") + self.unseen_cnt = self.request.profile.count_unseen_notifications + + query = { + 'owner': self.request.profile, + } + self.queryset = Notification.objects.filter(**query)\ + .order_by('-time')[:100] \ + .annotate(seen=Value(True, output_field=BooleanField())) + + # Mark the several first unseen + for cnt, q in enumerate(self.queryset): + if (cnt < self.unseen_cnt): + q.seen = False + else: + break + return self.queryset - + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["unseen_count"] = self.unseen_cnt - context["title"] = _("Notifications (%d unseen)") % context["unseen_count"] - context["first_page_href"] = "." + context['unseen_count'] = self.unseen_cnt + context['title'] = _('Notifications (%d unseen)' % context['unseen_count']) + context['has_notifications'] = self.queryset.exists() + context['page_titles'] = CacheDict(lambda page: Comment.get_page_title(page)) return context def get(self, request, *args, **kwargs): ret = super().get(request, *args, **kwargs) - if not request.user.is_authenticated: - raise Http404() - NotificationProfile.objects.filter(user=request.profile).update(unread_count=0) - unseen_notifications_count.dirty(self.request.profile) + + # update after rendering + Notification.objects.filter(owner=self.request.profile).update(read=True) + return ret + diff --git a/judge/views/organization.py b/judge/views/organization.py index 2a5fea1..057e617 100644 --- a/judge/views/organization.py +++ b/judge/views/organization.py @@ -7,542 +7,141 @@ from django.core.cache.utils import make_template_fragment_key from django.core.exceptions import PermissionDenied from django.db import transaction from django.db.models import Count, Q, Value, BooleanField -from django.db.utils import ProgrammingError from django.forms import Form, modelformset_factory -from django.http import ( - Http404, - HttpResponsePermanentRedirect, - HttpResponseRedirect, - HttpResponseBadRequest, -) -from django.shortcuts import get_object_or_404 +from django.http import Http404, HttpResponsePermanentRedirect, HttpResponseRedirect from django.urls import reverse from django.utils import timezone -from django.utils.html import format_html -from django.utils.functional import cached_property from django.utils.safestring import mark_safe from django.utils.translation import gettext as _, gettext_lazy, ungettext -from django.views.generic import ( - DetailView, - FormView, - ListView, - UpdateView, - View, - CreateView, -) -from django.views.generic.detail import ( - SingleObjectMixin, - SingleObjectTemplateResponseMixin, -) -from django.core.paginator import Paginator -from django.contrib.sites.shortcuts import get_current_site +from django.views.generic import DetailView, FormView, ListView, UpdateView, View +from django.views.generic.detail import SingleObjectMixin, SingleObjectTemplateResponseMixin from reversion import revisions -from judge.forms import ( - EditOrganizationForm, - AddOrganizationForm, - AddOrganizationMemberForm, - OrganizationBlogForm, - OrganizationAdminBlogForm, - EditOrganizationContestForm, - ContestProblemFormSet, - AddOrganizationContestForm, -) -from judge.models import ( - BlogPost, - Comment, - Organization, - OrganizationRequest, - Problem, - Profile, - Contest, - ContestProblem, - OrganizationProfile, -) -from judge.models.notification import make_notification -from judge import event_poster as event +from judge.forms import EditOrganizationForm +from judge.models import BlogPost, Comment, Organization, OrganizationRequest, Problem, Profile, Contest from judge.utils.ranker import ranker -from judge.utils.views import ( - TitleMixin, - generic_message, - QueryStringSortMixin, - DiggPaginatorMixin, -) -from judge.utils.problems import user_attempted_ids, user_completed_ids -from judge.utils.contest import maybe_trigger_contest_rescore -from judge.views.problem import ProblemList -from judge.views.contests import ContestList -from judge.views.submission import SubmissionsListBase -from judge.views.feed import FeedView -from judge.tasks import rescore_contest - -__all__ = [ - "OrganizationList", - "OrganizationHome", - "OrganizationUsers", - "OrganizationProblems", - "OrganizationContests", - "OrganizationMembershipChange", - "JoinOrganization", - "LeaveOrganization", - "EditOrganization", - "RequestJoinOrganization", - "OrganizationRequestDetail", - "OrganizationRequestView", - "OrganizationRequestLog", - "KickUserWidgetView", -] +from judge.utils.views import TitleMixin, generic_message, QueryStringSortMixin, DiggPaginatorMixin +__all__ = ['OrganizationList', 'OrganizationHome', 'OrganizationUsers', 'OrganizationMembershipChange', + 'JoinOrganization', 'LeaveOrganization', 'EditOrganization', 'RequestJoinOrganization', + 'OrganizationRequestDetail', 'OrganizationRequestView', 'OrganizationRequestLog', + 'KickUserWidgetView'] class OrganizationBase(object): def can_edit_organization(self, org=None): if org is None: org = self.object - if self.request.profile: - return self.request.profile.can_edit_organization(org) - return False + if not self.request.user.is_authenticated: + return False + profile_id = self.request.profile.id + return org.admins.filter(id=profile_id).exists() or org.registrant_id == profile_id def is_member(self, org=None): if org is None: org = self.object - if self.request.profile: - return org.is_member(self.request.profile) - return False - - def is_admin(self, org=None): - if org is None: - org = self.object - if self.request.profile: - return org.is_admin(self.request.profile) - return False - - def can_access(self, org): - if self.request.user.is_superuser: - return True - if org is None: - org = self.object - return self.is_member(org) or self.can_edit_organization(org) - + return self.request.profile in org if self.request.user.is_authenticated else False class OrganizationMixin(OrganizationBase): + context_object_name = 'organization' + model = Organization + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["is_member"] = self.is_member(self.organization) - context["is_admin"] = self.is_admin(self.organization) - context["can_edit"] = self.can_edit_organization(self.organization) - context["organization"] = self.organization - context["organization_image"] = self.organization.organization_image - context["organization_subdomain"] = ( - ("http" if settings.DMOJ_SSL == 0 else "https") - + "://" - + self.organization.slug - + "." - + get_current_site(self.request).domain - ) - if "organizations" in context: - context.pop("organizations") + context['logo_override_image'] = self.object.logo_override_image return context def dispatch(self, request, *args, **kwargs): try: - self.organization_id = int(kwargs["pk"]) - self.organization = get_object_or_404(Organization, id=self.organization_id) + return super(OrganizationMixin, self).dispatch(request, *args, **kwargs) except Http404: - key = None - if hasattr(self, "slug_url_kwarg"): - key = kwargs.get(self.slug_url_kwarg, None) + key = kwargs.get(self.slug_url_kwarg, None) if key: - return generic_message( - request, - _("No such organization"), - _('Could not find an organization with the key "%s".') % key, - status=403, - ) + return generic_message(request, _('No such organization'), + _('Could not find an organization with the key "%s".') % key) else: - return generic_message( - request, - _("No such organization"), - _("Could not find such organization."), - status=403, - ) - if self.organization.slug != kwargs["slug"]: - return HttpResponsePermanentRedirect( - request.get_full_path().replace(kwargs["slug"], self.organization.slug) - ) - if self.request.user.is_authenticated: - OrganizationProfile.add_organization( - self.request.profile, self.organization - ) - - return super(OrganizationMixin, self).dispatch(request, *args, **kwargs) - - -class AdminOrganizationMixin(OrganizationMixin): - def dispatch(self, request, *args, **kwargs): - res = super(AdminOrganizationMixin, self).dispatch(request, *args, **kwargs) - if not hasattr(self, "organization") or self.can_edit_organization( - self.organization - ): - return res - return generic_message( - request, - _("Can't edit organization"), - _("You are not allowed to edit this organization."), - status=403, - ) - - -class MemberOrganizationMixin(OrganizationMixin): - def dispatch(self, request, *args, **kwargs): - res = super(MemberOrganizationMixin, self).dispatch(request, *args, **kwargs) - if not hasattr(self, "organization") or self.can_access(self.organization): - return res - return generic_message( - request, - _("Can't access organization"), - _("You are not allowed to access this organization."), - status=403, - ) - - -class OrganizationHomeView(OrganizationMixin): - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - if not hasattr(self, "organization"): - self.organization = self.object - if self.can_edit_organization(self.organization): - context["pending_count"] = OrganizationRequest.objects.filter( - state="P", organization=self.organization - ).count() - context["pending_blog_count"] = BlogPost.objects.filter( - visible=False, organizations=self.organization - ).count() - else: - context["pending_blog_count"] = BlogPost.objects.filter( - visible=False, - organizations=self.organization, - authors=self.request.profile, - ).count() - context["top_rated"] = ( - self.organization.members.filter(is_unlisted=False) - .order_by("-rating") - .only("id", "rating")[:10] - ) - context["top_scorer"] = ( - self.organization.members.filter(is_unlisted=False) - .order_by("-performance_points") - .only("id", "performance_points")[:10] - ) - Profile.prefetch_profile_cache([p.id for p in context["top_rated"]]) - Profile.prefetch_profile_cache([p.id for p in context["top_scorer"]]) - - return context - - -class OrganizationList( - QueryStringSortMixin, DiggPaginatorMixin, TitleMixin, ListView, OrganizationBase -): - model = Organization - context_object_name = "organizations" - template_name = "organization/list.html" - title = gettext_lazy("Groups") - paginate_by = 12 - all_sorts = frozenset(("name", "member_count")) - default_desc = frozenset(("name", "member_count")) - - def get_default_sort_order(self, request): - return "-member_count" + return generic_message(request, _('No such organization'), + _('Could not find such organization.')) + +class OrganizationDetailView(OrganizationMixin, DetailView): def get(self, request, *args, **kwargs): - default_tab = "mine" - if not self.request.user.is_authenticated: - default_tab = "public" - self.current_tab = self.request.GET.get("tab", default_tab) - self.organization_query = request.GET.get("organization", "") + self.object = self.get_object() + if self.object.slug != kwargs['slug']: + return HttpResponsePermanentRedirect(request.get_full_path().replace(kwargs['slug'], self.object.slug)) + context = self.get_context_data(object=self.object) + return self.render_to_response(context) - return super(OrganizationList, self).get(request, *args, **kwargs) - def _get_queryset(self): - queryset = ( - super(OrganizationList, self) - .get_queryset() - .annotate(member_count=Count("member")) - .defer("about") - ) - - if self.organization_query: - queryset = queryset.filter( - Q(slug__icontains=self.organization_query) - | Q(name__icontains=self.organization_query) - | Q(short_name__icontains=self.organization_query) - ) - return queryset +class OrganizationList(TitleMixin, ListView, OrganizationBase): + model = Organization + context_object_name = 'organizations' + template_name = 'organization/list.html' + title = gettext_lazy('Organizations') def get_queryset(self): - organization_list = self._get_queryset() - - my_organizations = [] - if self.request.profile: - my_organizations = organization_list.filter( - id__in=self.request.profile.organizations.values("id") - ) - - if self.current_tab == "public": - queryset = organization_list.exclude(id__in=my_organizations).filter( - is_open=True - ) - elif self.current_tab == "private": - queryset = organization_list.exclude(id__in=my_organizations).filter( - is_open=False - ) - else: - queryset = my_organizations - - if queryset: - queryset = queryset.order_by(self.order) - return queryset + return super(OrganizationList, self).get_queryset().annotate(member_count=Count('member')) def get_context_data(self, **kwargs): context = super(OrganizationList, self).get_context_data(**kwargs) + context['my_organizations'] = set() - context["first_page_href"] = "." - context["current_tab"] = self.current_tab - context["page_type"] = self.current_tab - context["organization_query"] = self.organization_query - context["selected_order"] = self.request.GET.get("order") - context["all_sort_options"] = [ - ("name", _("Name (asc.)")), - ("-name", _("Name (desc.)")), - ("member_count", _("Member count (asc.)")), - ("-member_count", _("Member count (desc.)")), - ] - - context.update(self.get_sort_context()) - context.update(self.get_sort_paginate_context()) + for organization in context['organizations']: + if self.can_edit_organization(organization) or self.is_member(organization): + context['my_organizations'].add(organization) return context - -class OrganizationHome(OrganizationHomeView, FeedView): - template_name = "organization/home.html" - paginate_by = 4 - context_object_name = "posts" - feed_content_template_name = "blog/content.html" - - def get_queryset(self): - return ( - BlogPost.objects.filter( - visible=True, - publish_on__lte=timezone.now(), - is_organization_private=True, - organizations=self.organization, - ) - .order_by("-sticky", "-publish_on") - .prefetch_related("authors__user", "organizations") - ) +class OrganizationHome(OrganizationDetailView): + template_name = 'organization/home.html' def get_context_data(self, **kwargs): context = super(OrganizationHome, self).get_context_data(**kwargs) - context["title"] = self.organization.name - - now = timezone.now() - visible_contests = ( - Contest.get_visible_contests(self.request.user) - .filter( - is_visible=True, - is_organization_private=True, - organizations=self.organization, - ) - .order_by("start_time") - ) - context["current_contests"] = visible_contests.filter( - start_time__lte=now, end_time__gt=now - ) - context["future_contests"] = visible_contests.filter(start_time__gt=now) - context["page_type"] = "home" + context['title'] = self.object.name + context['can_edit'] = self.can_edit_organization() + context['is_member'] = self.is_member() + context['new_problems'] = Problem.objects.filter(is_public=True, is_organization_private=True, + organizations=self.object) \ + .order_by('-date', '-id')[:settings.DMOJ_BLOG_NEW_PROBLEM_COUNT] + context['new_contests'] = Contest.objects.filter(is_visible=True, is_organization_private=True, + organizations=self.object) \ + .order_by('-end_time', '-id')[:settings.DMOJ_BLOG_NEW_CONTEST_COUNT] + context['posts'] = BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now(), + is_organization_private=True, organizations=self.object) \ + .order_by('-sticky', '-publish_on') \ + .prefetch_related('authors__user', 'organizations') + context['post_comment_counts'] = { + int(page[2:]): count for page, count in + Comment.objects.filter(page__in=['b:%d' % post.id for post in context['posts']], hidden=False) + .values_list('page').annotate(count=Count('page')).order_by() + } + context['pending_count'] = OrganizationRequest.objects.filter(state='P', organization=self.object).count() return context -class OrganizationUsers( - DiggPaginatorMixin, QueryStringSortMixin, OrganizationMixin, ListView -): - template_name = "organization/users.html" - all_sorts = frozenset(("points", "problem_count", "rating", "performance_points")) +class OrganizationUsers(QueryStringSortMixin, OrganizationDetailView): + template_name = 'organization/users.html' + all_sorts = frozenset(('points', 'problem_count', 'rating', 'performance_points')) default_desc = all_sorts - default_sort = "-performance_points" - paginate_by = 100 - context_object_name = "users" - - def get_queryset(self): - return ( - self.organization.members.filter(is_unlisted=False) - .order_by(self.order, "id") - .select_related("user") - .only( - "display_rank", - "user__username", - "points", - "rating", - "performance_points", - "problem_count", - ) - ) - - def dispatch(self, request, *args, **kwargs): - res = super(OrganizationUsers, self).dispatch(request, *args, **kwargs) - if res.status_code != 200: - return res - if self.can_access(self.organization) or self.organization.is_open: - return res - return generic_message( - request, - _("Can't access organization"), - _("You are not allowed to access this organization."), - status=403, - ) - + default_sort = '-performance_points' + def get_context_data(self, **kwargs): context = super(OrganizationUsers, self).get_context_data(**kwargs) - context["title"] = _("%s Members") % self.organization.name - context["partial"] = True - context["kick_url"] = reverse( - "organization_user_kick", - args=[self.organization.id, self.organization.slug], - ) - context["users"] = ranker( - context["users"], rank=self.paginate_by * (context["page_obj"].number - 1) - ) + context['title'] = _('%s Members') % self.object.name + context['partial'] = True + context['is_admin'] = self.can_edit_organization() + context['kick_url'] = reverse('organization_user_kick', args=[self.object.id, self.object.slug]) - context["first_page_href"] = "." - context["page_type"] = "users" + context['users'] = ranker( + self.get_object().members.filter(is_unlisted=False).order_by(self.order, 'id').select_related('user') \ + .only('display_rank', 'user__username', 'points', 'rating', 'performance_points', 'problem_count') + ) + context['first_page_href'] = '.' context.update(self.get_sort_context()) return context -class OrganizationProblems(LoginRequiredMixin, MemberOrganizationMixin, ProblemList): - template_name = "organization/problems.html" - filter_organization = True - - def get_queryset(self): - self.org_query = [self.organization_id] - return super().get_normal_queryset() - - def get(self, request, *args, **kwargs): - self.setup_problem_list(request) - return super().get(request, *args, **kwargs) - - def get_completed_problems(self): - return user_completed_ids(self.profile) if self.profile is not None else () - - def get_attempted_problems(self): - return user_attempted_ids(self.profile) if self.profile is not None else () - - @cached_property - def in_contest(self): - return False - - def get_context_data(self, **kwargs): - context = super(OrganizationProblems, self).get_context_data(**kwargs) - context["page_type"] = "problems" - context["show_contest_mode"] = False - return context - - -class OrganizationContestMixin( - LoginRequiredMixin, - TitleMixin, - OrganizationHomeView, -): - model = Contest - - def is_contest_editable(self, request, contest): - return contest.is_editable_by(request.user) or self.can_edit_organization( - self.organization - ) - - -class OrganizationContests( - OrganizationContestMixin, MemberOrganizationMixin, ContestList -): - template_name = "organization/contests.html" - - def get_queryset(self): - self.org_query = [self.organization_id] - self.hide_organization_contests = False - return super().get_queryset() - - def set_editable_contest(self, contest): - if not contest: - return False - contest.is_editable = self.is_contest_editable(self.request, contest) - - def get_context_data(self, **kwargs): - context = super(OrganizationContests, self).get_context_data(**kwargs) - context["page_type"] = "contests" - context.pop("organizations") - - if self.can_edit_organization(self.organization): - context["create_url"] = reverse( - "organization_contest_add", - args=[self.organization.id, self.organization.slug], - ) - - if self.current_tab == "active": - for participation in context["contests"]: - self.set_editable_contest(participation.contest) - else: - for contest in context["contests"]: - self.set_editable_contest(contest) - return context - - -class OrganizationSubmissions( - LoginRequiredMixin, MemberOrganizationMixin, SubmissionsListBase -): - template_name = "organization/submissions.html" - - @cached_property - def in_contest(self): - return False - - @cached_property - def contest(self): - return None - - def get_context_data(self, **kwargs): - context = super(OrganizationSubmissions, self).get_context_data(**kwargs) - # context["dynamic_update"] = context["page_obj"].number == 1 - # context["last_msg"] = event.last() - context["stats_update_interval"] = 3600 - context["page_type"] = "submissions" - context["page_prefix"] = None - context["page_suffix"] = suffix = ( - ("?" + self.request.GET.urlencode()) if self.request.GET else "" - ) - context["first_page_href"] = (self.first_page_href or ".") + suffix - - return context - - def get_content_title(self): - return format_html( - _('All submissions in {0}'), - self.organization, - reverse( - "organization_home", args=[self.organization.id, self.organization.slug] - ), - ) - - def get_title(self): - return _("Submissions in") + f" {self.organization}" - - -class OrganizationMembershipChange( - LoginRequiredMixin, OrganizationMixin, SingleObjectMixin, View -): - model = Organization - context_object_name = "organization" - +class OrganizationMembershipChange(LoginRequiredMixin, OrganizationMixin, SingleObjectMixin, View): def post(self, request, *args, **kwargs): org = self.get_object() response = self.handle(request, org, request.profile) @@ -557,42 +156,29 @@ class OrganizationMembershipChange( class JoinOrganization(OrganizationMembershipChange): def handle(self, request, org, profile): if profile.organizations.filter(id=org.id).exists(): - return generic_message( - request, - _("Joining group"), - _("You are already in the group."), - ) + return generic_message(request, _('Joining organization'), _('You are already in the organization.')) if not org.is_open: - return generic_message( - request, _("Joining group"), _("This group is not open.") - ) + return generic_message(request, _('Joining organization'), _('This organization is not open.')) max_orgs = settings.DMOJ_USER_MAX_ORGANIZATION_COUNT if profile.organizations.filter(is_open=True).count() >= max_orgs: return generic_message( - request, - _("Joining group"), - _("You may not be part of more than {count} public groups.").format( - count=max_orgs - ), + request, _('Joining organization'), + _('You may not be part of more than {count} public organizations.').format(count=max_orgs), ) profile.organizations.add(org) profile.save() - cache.delete(make_template_fragment_key("org_member_count", (org.id,))) + cache.delete(make_template_fragment_key('org_member_count', (org.id,))) class LeaveOrganization(OrganizationMembershipChange): def handle(self, request, org, profile): if not profile.organizations.filter(id=org.id).exists(): - return generic_message( - request, - _("Leaving group"), - _('You are not in "%s".') % org.short_name, - ) + return generic_message(request, _('Leaving organization'), _('You are not in "%s".') % org.short_name) profile.organizations.remove(org) - cache.delete(make_template_fragment_key("org_member_count", (org.id,))) + cache.delete(make_template_fragment_key('org_member_count', (org.id,))) class OrganizationRequestForm(Form): @@ -601,9 +187,9 @@ class OrganizationRequestForm(Form): class RequestJoinOrganization(LoginRequiredMixin, SingleObjectMixin, FormView): model = Organization - slug_field = "key" - slug_url_kwarg = "key" - template_name = "organization/requests/request.html" + slug_field = 'key' + slug_url_kwarg = 'key' + template_name = 'organization/requests/request.html' form_class = OrganizationRequestForm def dispatch(self, request, *args, **kwargs): @@ -614,94 +200,75 @@ class RequestJoinOrganization(LoginRequiredMixin, SingleObjectMixin, FormView): context = super(RequestJoinOrganization, self).get_context_data(**kwargs) if self.object.is_open: raise Http404() - context["title"] = _("Request to join %s") % self.object.name + context['title'] = _('Request to join %s') % self.object.name return context def form_valid(self, form): request = OrganizationRequest() request.organization = self.get_object() request.user = self.request.profile - request.reason = form.cleaned_data["reason"] - request.state = "P" + request.reason = form.cleaned_data['reason'] + request.state = 'P' request.save() - return HttpResponseRedirect( - reverse( - "request_organization_detail", - args=( - request.organization.id, - request.organization.slug, - request.id, - ), - ) - ) + return HttpResponseRedirect(reverse('request_organization_detail', args=( + request.organization.id, request.organization.slug, request.id, + ))) -class OrganizationRequestDetail( - LoginRequiredMixin, - TitleMixin, - OrganizationHomeView, - DetailView, -): +class OrganizationRequestDetail(LoginRequiredMixin, TitleMixin, DetailView): model = OrganizationRequest - template_name = "organization/requests/detail.html" - title = gettext_lazy("Join request detail") - pk_url_kwarg = "rpk" + template_name = 'organization/requests/detail.html' + title = gettext_lazy('Join request detail') + pk_url_kwarg = 'rpk' def get_object(self, queryset=None): object = super(OrganizationRequestDetail, self).get_object(queryset) profile = self.request.profile - if ( - object.user_id != profile.id - and not object.organization.admins.filter(id=profile.id).exists() - ): + if object.user_id != profile.id and not object.organization.admins.filter(id=profile.id).exists(): raise PermissionDenied() return object -OrganizationRequestFormSet = modelformset_factory( - OrganizationRequest, extra=0, fields=("state",), can_delete=True -) +OrganizationRequestFormSet = modelformset_factory(OrganizationRequest, extra=0, fields=('state',), can_delete=True) -class OrganizationRequestBaseView( - AdminOrganizationMixin, - DetailView, - OrganizationHomeView, - TitleMixin, - LoginRequiredMixin, - SingleObjectTemplateResponseMixin, - SingleObjectMixin, -): +class OrganizationRequestBaseView(TitleMixin, LoginRequiredMixin, SingleObjectTemplateResponseMixin, SingleObjectMixin, View): model = Organization - slug_field = "key" - slug_url_kwarg = "key" + slug_field = 'key' + slug_url_kwarg = 'key' tab = None - def get_content_title(self): - return _("Manage join requests") + def get_object(self, queryset=None): + organization = super(OrganizationRequestBaseView, self).get_object(queryset) + if not (organization.admins.filter(id=self.request.profile.id).exists() or + organization.registrant_id == self.request.profile.id): + raise PermissionDenied() + return organization + def get_content_title(self): + href = reverse('organization_home', args=[self.object.id, self.object.slug]) + return mark_safe(f'Manage join requests for {self.object.name}') + def get_context_data(self, **kwargs): context = super(OrganizationRequestBaseView, self).get_context_data(**kwargs) - context["title"] = _("Managing join requests for %s") % self.object.name - context["tab"] = self.tab + context['title'] = _('Managing join requests for %s') % self.object.name + context['tab'] = self.tab return context class OrganizationRequestView(OrganizationRequestBaseView): - template_name = "organization/requests/pending.html" - tab = "pending" + template_name = 'organization/requests/pending.html' + tab = 'pending' def get_context_data(self, **kwargs): context = super(OrganizationRequestView, self).get_context_data(**kwargs) - context["formset"] = self.formset + context['formset'] = self.formset return context def get(self, request, *args, **kwargs): self.object = self.get_object() self.formset = OrganizationRequestFormSet( - queryset=OrganizationRequest.objects.filter( - state="P", organization=self.object - ), + queryset=OrganizationRequest.objects.filter(state='P', organization=self.object), ) context = self.get_context_data(object=self.object) return self.render_to_response(context) @@ -712,43 +279,24 @@ class OrganizationRequestView(OrganizationRequestBaseView): if formset.is_valid(): if organization.slots is not None: deleted_set = set(formset.deleted_forms) - to_approve = sum( - form.cleaned_data["state"] == "A" - for form in formset.forms - if form not in deleted_set - ) + to_approve = sum(form.cleaned_data['state'] == 'A' for form in formset.forms if form not in deleted_set) can_add = organization.slots - organization.members.count() if to_approve > can_add: - messages.error( - request, - _( - "Your organization can only receive %d more members. " - "You cannot approve %d users." - ) - % (can_add, to_approve), - ) - return self.render_to_response( - self.get_context_data(object=organization) - ) + messages.error(request, _('Your organization can only receive %d more members. ' + 'You cannot approve %d users.') % (can_add, to_approve)) + return self.render_to_response(self.get_context_data(object=organization)) approved, rejected = 0, 0 for obj in formset.save(): - if obj.state == "A": + if obj.state == 'A': obj.user.organizations.add(obj.organization) approved += 1 - elif obj.state == "R": + elif obj.state == 'R': rejected += 1 - messages.success( - request, - ungettext("Approved %d user.", "Approved %d users.", approved) - % approved - + "\n" - + ungettext("Rejected %d user.", "Rejected %d users.", rejected) - % rejected, - ) - cache.delete( - make_template_fragment_key("org_member_count", (organization.id,)) - ) + messages.success(request, + ungettext('Approved %d user.', 'Approved %d users.', approved) % approved + '\n' + + ungettext('Rejected %d user.', 'Rejected %d users.', rejected) % rejected) + cache.delete(make_template_fragment_key('org_member_count', (organization.id,))) return HttpResponseRedirect(request.get_full_path()) return self.render_to_response(self.get_context_data(object=organization)) @@ -756,9 +304,9 @@ class OrganizationRequestView(OrganizationRequestBaseView): class OrganizationRequestLog(OrganizationRequestBaseView): - states = ("A", "R") - tab = "log" - template_name = "organization/requests/log.html" + states = ('A', 'R') + tab = 'log' + template_name = 'organization/requests/log.html' def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -767,102 +315,17 @@ class OrganizationRequestLog(OrganizationRequestBaseView): def get_context_data(self, **kwargs): context = super(OrganizationRequestLog, self).get_context_data(**kwargs) - context["requests"] = self.object.requests.filter(state__in=self.states) + context['requests'] = self.object.requests.filter(state__in=self.states) return context -class AddOrganizationMember( - LoginRequiredMixin, - TitleMixin, - AdminOrganizationMixin, - OrganizationHomeView, - UpdateView, -): - template_name = "organization/add-member.html" - model = Organization - form_class = AddOrganizationMemberForm - - def get_title(self): - return _("Add member for %s") % self.object.name - - def get_object(self, queryset=None): - object = super(AddOrganizationMember, self).get_object() - if not self.can_edit_organization(object): - raise PermissionDenied() - return object - - def form_valid(self, form): - new_users = form.cleaned_data["new_users"] - self.object.members.add(*new_users) - link = reverse("organization_home", args=[self.object.id, self.object.slug]) - html = f'{self.object.name}' - make_notification(new_users, "Added to group", html, self.request.profile) - with revisions.create_revision(): - usernames = ", ".join([u.username for u in new_users]) - revisions.set_comment(_("Added members from site") + ": " + usernames) - revisions.set_user(self.request.user) - return super(AddOrganizationMember, self).form_valid(form) - - def get_success_url(self): - return reverse("organization_users", args=[self.object.id, self.object.slug]) - - -class KickUserWidgetView( - LoginRequiredMixin, AdminOrganizationMixin, SingleObjectMixin, View -): - model = Organization - - def post(self, request, *args, **kwargs): - organization = self.get_object() - try: - user = Profile.objects.get(id=request.POST.get("user", None)) - except Profile.DoesNotExist: - return generic_message( - request, - _("Can't kick user"), - _("The user you are trying to kick does not exist!"), - status=400, - ) - - if not organization.is_member(user): - return generic_message( - request, - _("Can't kick user"), - _("The user you are trying to kick is not in organization: %s.") - % organization.name, - status=400, - ) - - if organization.is_admin(user): - return generic_message( - request, - _("Can't kick user"), - _("The user you are trying to kick is an organization admin."), - status=400, - ) - - with revisions.create_revision(): - revisions.set_comment(_("Kicked member") + " " + user.username) - revisions.set_user(self.request.user) - organization.members.remove(user) - organization.save() - - return HttpResponseRedirect(organization.get_users_url()) - - -class EditOrganization( - LoginRequiredMixin, - TitleMixin, - AdminOrganizationMixin, - OrganizationHomeView, - UpdateView, -): - template_name = "organization/edit.html" +class EditOrganization(LoginRequiredMixin, TitleMixin, OrganizationMixin, UpdateView): + template_name = 'organization/edit.html' model = Organization form_class = EditOrganizationForm def get_title(self): - return _("Edit %s") % self.object.name + return _('Editing %s') % self.object.name def get_object(self, queryset=None): object = super(EditOrganization, self).get_object() @@ -872,373 +335,41 @@ class EditOrganization( def get_form(self, form_class=None): form = super(EditOrganization, self).get_form(form_class) - form.fields["admins"].queryset = Profile.objects.filter( - Q(organizations=self.object) | Q(admin_of=self.object) - ).distinct() + form.fields['admins'].queryset = \ + Profile.objects.filter(Q(organizations=self.object) | Q(admin_of=self.object)).distinct() return form def form_valid(self, form): - with revisions.create_revision(): - revisions.set_comment(_("Edited from site")) + with transaction.atomic(), revisions.create_revision(): + revisions.set_comment(_('Edited from site')) revisions.set_user(self.request.user) return super(EditOrganization, self).form_valid(form) - -class AddOrganization(LoginRequiredMixin, TitleMixin, CreateView): - template_name = "organization/add.html" - model = Organization - form_class = AddOrganizationForm - - def get_title(self): - return _("Create group") - - def get_form_kwargs(self): - kwargs = super(AddOrganization, self).get_form_kwargs() - kwargs["request"] = self.request - return kwargs - - def form_valid(self, form): - if ( - not self.request.user.is_staff - and Organization.objects.filter(registrant=self.request.profile).count() - >= settings.DMOJ_USER_MAX_ORGANIZATION_ADD - ): - return generic_message( - self.request, - _("Exceeded limit"), - _("You created too many groups. You can only create at most %d groups") - % settings.DMOJ_USER_MAX_ORGANIZATION_ADD, - status=400, - ) - with revisions.create_revision(): - revisions.set_comment(_("Added from site")) - revisions.set_user(self.request.user) - res = super(AddOrganization, self).form_valid(form) - self.object.admins.add(self.request.profile) - self.object.members.add(self.request.profile) - self.object.save() - return res - - -class AddOrganizationContest( - AdminOrganizationMixin, OrganizationContestMixin, CreateView -): - template_name = "organization/contest/add.html" - form_class = AddOrganizationContestForm - - def get_title(self): - return _("Add contest") - - def get_form_kwargs(self): - kwargs = super(AddOrganizationContest, self).get_form_kwargs() - kwargs["request"] = self.request - return kwargs - - def form_valid(self, form): - with revisions.create_revision(): - revisions.set_comment(_("Added from site")) - revisions.set_user(self.request.user) - - res = super(AddOrganizationContest, self).form_valid(form) - - self.object.organizations.add(self.organization) - self.object.is_organization_private = True - self.object.authors.add(self.request.profile) - self.object.save() - return res - - def get_success_url(self): - return reverse( - "organization_contest_edit", - args=[self.organization.id, self.organization.slug, self.object.key], - ) - - -class EditOrganizationContest( - OrganizationContestMixin, MemberOrganizationMixin, UpdateView -): - template_name = "organization/contest/edit.html" - form_class = EditOrganizationContestForm - - def setup_contest(self, request, *args, **kwargs): - contest_key = kwargs.get("contest", None) - if not contest_key: - raise Http404() - self.contest = get_object_or_404(Contest, key=contest_key) - if self.organization not in self.contest.organizations.all(): - raise Http404() - if not self.is_contest_editable(request, self.contest): - return generic_message( - self.request, - _("Permission denied"), - _("You are not allowed to edit this contest"), - status=400, - ) - - def get_form_kwargs(self): - kwargs = super(EditOrganizationContest, self).get_form_kwargs() - kwargs["org_id"] = self.organization.id - return kwargs - - def get(self, request, *args, **kwargs): - res = self.setup_contest(request, *args, **kwargs) - if res: - return res - return super().get(request, *args, **kwargs) - - def post(self, request, *args, **kwargs): - res = self.setup_contest(request, *args, **kwargs) - if res: - return res - problem_formset = self.get_problem_formset(True) - if problem_formset.is_valid(): - for problem_form in problem_formset: - if problem_form.cleaned_data.get("DELETE") and problem_form.instance.pk: - problem_form.instance.delete() - - for problem_form in problem_formset.save(commit=False): - if problem_form: - problem_form.contest = self.contest - problem_form.save() - - super().post(request, *args, **kwargs) - return HttpResponseRedirect( - reverse( - "organization_contest_edit", - args=( - self.organization_id, - self.organization.slug, - self.contest.key, - ), - ) - ) - - self.object = self.contest - return self.render_to_response( - self.get_context_data( - problems_form=problem_formset, - ) - ) - - def get_title(self): - return _("Edit %s") % self.contest.key - - def get_content_title(self): - href = reverse("contest_view", args=[self.contest.key]) - return mark_safe(_("Edit") + f' {self.contest.key}') - - def get_object(self): - return self.contest - - def form_valid(self, form): - with revisions.create_revision(): - revisions.set_comment(_("Edited from site")) - revisions.set_user(self.request.user) - res = super(EditOrganizationContest, self).form_valid(form) - self.object.organizations.add(self.organization) - self.object.is_organization_private = True - self.object.save() - - maybe_trigger_contest_rescore(form, self.object, True) - - return res - - def get_problem_formset(self, post=False): - return ContestProblemFormSet( - data=self.request.POST if post else None, - prefix="problems", - queryset=ContestProblem.objects.filter(contest=self.contest).order_by( - "order" - ), - ) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - if "problems_form" not in context: - context["problems_form"] = self.get_problem_formset() - return context - - def get_success_url(self): - return self.request.path - - -class AddOrganizationBlog( - LoginRequiredMixin, - TitleMixin, - OrganizationHomeView, - MemberOrganizationMixin, - CreateView, -): - template_name = "organization/blog/add.html" - model = BlogPost - form_class = OrganizationBlogForm - - def get_form_class(self): - if self.can_edit_organization(self.organization): - return OrganizationAdminBlogForm - return OrganizationBlogForm - - def get_title(self): - return _("Add blog for %s") % self.organization.name - - def form_valid(self, form): - with revisions.create_revision(): - res = super(AddOrganizationBlog, self).form_valid(form) - self.object.is_organization_private = True - self.object.authors.add(self.request.profile) - self.object.slug = self.organization.slug + "-" + self.request.user.username - self.object.organizations.add(self.organization) - self.object.save() - - revisions.set_comment(_("Added from site")) - revisions.set_user(self.request.user) - - link = reverse( - "edit_organization_blog", - args=[self.organization.id, self.organization.slug, self.object.id], - ) - html = ( - f'{self.object.title} - {self.organization.name}' - ) - make_notification( - self.organization.admins.all(), "Add blog", html, self.request.profile - ) - return res - - def get_success_url(self): - return reverse( - "organization_home", args=[self.organization.id, self.organization.slug] - ) - - -class EditOrganizationBlog( - LoginRequiredMixin, - TitleMixin, - OrganizationHomeView, - AdminOrganizationMixin, - UpdateView, -): - template_name = "organization/blog/edit.html" - model = BlogPost - - def get_form_class(self): - if self.can_edit_organization(self.organization): - return OrganizationAdminBlogForm - return OrganizationBlogForm - - def setup_blog(self, request, *args, **kwargs): + def dispatch(self, request, *args, **kwargs): try: - self.blog_id = kwargs["blog_pk"] - self.blog = BlogPost.objects.get(id=self.blog_id) - if self.organization not in self.blog.organizations.all(): - raise Exception(_("This blog does not belong to this organization")) - if not self.request.profile.can_edit_organization(self.organization): - raise Exception(_("Not allowed to edit this blog")) - except Exception as e: - return generic_message( - request, - _("Permission denied"), - e, - ) + return super(EditOrganization, self).dispatch(request, *args, **kwargs) + except PermissionDenied: + return generic_message(request, _("Can't edit organization"), + _('You are not allowed to edit this organization.'), status=403) - def publish_blog(self, request, *args, **kwargs): - self.blog_id = kwargs["blog_pk"] - BlogPost.objects.filter(pk=self.blog_id).update(visible=True) - - def delete_blog(self, request, *args, **kwargs): - self.blog_id = kwargs["blog_pk"] - BlogPost.objects.get(pk=self.blog_id).delete() - - def get(self, request, *args, **kwargs): - res = self.setup_blog(request, *args, **kwargs) - if res: - return res - return super().get(request, *args, **kwargs) +class KickUserWidgetView(LoginRequiredMixin, OrganizationMixin, SingleObjectMixin, View): def post(self, request, *args, **kwargs): - res = self.setup_blog(request, *args, **kwargs) - if res: - return res - if request.POST["action"] == "Delete": - self.create_notification("Delete blog") - self.delete_blog(request, *args, **kwargs) - cur_url = reverse( - "organization_home", - args=(self.organization_id, self.organization.slug), - ) - return HttpResponseRedirect(cur_url) - elif request.POST["action"] == "Reject": - self.create_notification("Reject blog") - self.delete_blog(request, *args, **kwargs) - cur_url = reverse( - "organization_pending_blogs", - args=(self.organization_id, self.organization.slug), - ) - return HttpResponseRedirect(cur_url) - elif request.POST["action"] == "Approve": - self.create_notification("Approve blog") - self.publish_blog(request, *args, **kwargs) - cur_url = reverse( - "organization_pending_blogs", - args=(self.organization_id, self.organization.slug), - ) - return HttpResponseRedirect(cur_url) - else: - return super().post(request, *args, **kwargs) + organization = self.get_object() + if not self.can_edit_organization(organization): + return generic_message(request, _("Can't edit organization"), + _('You are not allowed to kick people from this organization.'), status=403) - def get_object(self): - return self.blog + try: + user = Profile.objects.get(id=request.POST.get('user', None)) + except Profile.DoesNotExist: + return generic_message(request, _("Can't kick user"), + _('The user you are trying to kick does not exist!'), status=400) - def get_title(self): - return _("Edit blog %s") % self.object.title + if not organization.members.filter(id=user.id).exists(): + return generic_message(request, _("Can't kick user"), + _('The user you are trying to kick is not in organization: %s.') % + organization.name, status=400) - def create_notification(self, action): - blog = BlogPost.objects.get(pk=self.blog_id) - link = reverse( - "edit_organization_blog", - args=[self.organization.id, self.organization.slug, self.blog_id], - ) - html = f'{blog.title} - {self.organization.name}' - to_users = (self.organization.admins.all() | blog.get_authors()).distinct() - make_notification(to_users, action, html, self.request.profile) - - def form_valid(self, form): - with revisions.create_revision(): - res = super(EditOrganizationBlog, self).form_valid(form) - revisions.set_comment(_("Edited from site")) - revisions.set_user(self.request.user) - self.create_notification("Edit blog") - return res - - def get_success_url(self): - return reverse( - "organization_home", args=[self.organization.id, self.organization.slug] - ) - - -class PendingBlogs( - LoginRequiredMixin, - TitleMixin, - MemberOrganizationMixin, - OrganizationHomeView, - ListView, -): - model = BlogPost - template_name = "organization/blog/pending.html" - context_object_name = "blogs" - - def get_queryset(self): - queryset = BlogPost.objects.filter( - organizations=self.organization, visible=False - ) - if not self.can_edit_organization(self.organization): - queryset = queryset.filter(authors=self.request.profile) - return queryset.order_by("publish_on") - - def get_title(self): - return _("Pending blogs in %s") % self.organization.name - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["org"] = self.organization - return context + organization.members.remove(user) + return HttpResponseRedirect(organization.get_users_url()) diff --git a/judge/views/pagevote.py b/judge/views/pagevote.py deleted file mode 100644 index a24680c..0000000 --- a/judge/views/pagevote.py +++ /dev/null @@ -1,105 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.db import IntegrityError -from django.db.models import F -from django.http import ( - Http404, - HttpResponse, - HttpResponseBadRequest, - HttpResponseForbidden, -) -from django.utils.translation import gettext as _ -from django.views.generic.base import TemplateResponseMixin -from django.views.generic.detail import SingleObjectMixin -from django.views.generic import View, ListView -from django_ratelimit.decorators import ratelimit -from django.conf import settings - -from judge.models.pagevote import PageVote, PageVoteVoter, dirty_pagevote - -__all__ = [ - "upvote_page", - "downvote_page", - "PageVoteDetailView", - "PageVoteListView", -] - - -@ratelimit(key="user", rate=settings.RL_VOTE) -@login_required -def vote_page(request, delta): - if abs(delta) != 1: - return HttpResponseBadRequest( - _("Messing around, are we?"), content_type="text/plain" - ) - - if request.method != "POST": - return HttpResponseForbidden() - - if "id" not in request.POST: - return HttpResponseBadRequest() - - if ( - not request.user.is_staff - and not request.profile.submission_set.filter( - points=F("problem__points") - ).exists() - ): - return HttpResponseBadRequest( - _("You must solve at least one problem before you can vote."), - content_type="text/plain", - ) - - try: - pagevote_id = int(request.POST["id"]) - except ValueError: - return HttpResponseBadRequest() - - try: - pagevote = PageVote.objects.get(id=pagevote_id) - except PageVote.DoesNotExist: - raise Http404() - - vote = PageVoteVoter() - vote.pagevote_id = pagevote_id - vote.voter = request.profile - vote.score = delta - - try: - vote.save() - except IntegrityError: - try: - vote = PageVoteVoter.objects.get( - pagevote_id=pagevote_id, voter=request.profile - ) - except PageVoteVoter.DoesNotExist: - raise Http404() - vote.delete() - PageVote.objects.filter(id=pagevote_id).update(score=F("score") - vote.score) - else: - PageVote.objects.filter(id=pagevote_id).update(score=F("score") + delta) - - dirty_pagevote(pagevote, request.profile) - - return HttpResponse("success", content_type="text/plain") - - -def upvote_page(request): - return vote_page(request, 1) - - -def downvote_page(request): - return vote_page(request, -1) - - -class PageVoteDetailView(TemplateResponseMixin, SingleObjectMixin, View): - pagevote_page = None - - def get_pagevote_page(self): - if self.pagevote_page is None: - raise NotImplementedError() - return self.pagevote_page - - def get_context_data(self, **kwargs): - context = super(PageVoteDetailView, self).get_context_data(**kwargs) - context["pagevote"] = self.object.get_or_create_pagevote() - return context diff --git a/judge/views/preview.py b/judge/views/preview.py index e4581d4..5fbd2e8 100644 --- a/judge/views/preview.py +++ b/judge/views/preview.py @@ -5,48 +5,46 @@ from django.views.generic.base import ContextMixin, TemplateResponseMixin, View class MarkdownPreviewView(TemplateResponseMixin, ContextMixin, View): def post(self, request, *args, **kwargs): try: - self.preview_data = data = request.POST["preview"] + self.preview_data = data = request.POST['preview'] except KeyError: - return HttpResponseBadRequest("No preview data specified.") + return HttpResponseBadRequest('No preview data specified.') - return self.render_to_response( - self.get_context_data( - preview_data=data, - ) - ) + return self.render_to_response(self.get_context_data( + preview_data=data, + )) class ProblemMarkdownPreviewView(MarkdownPreviewView): - template_name = "problem/preview.html" + template_name = 'problem/preview.html' class BlogMarkdownPreviewView(MarkdownPreviewView): - template_name = "blog/preview.html" + template_name = 'blog/preview.html' class ContestMarkdownPreviewView(MarkdownPreviewView): - template_name = "contest/preview.html" + template_name = 'contest/preview.html' class CommentMarkdownPreviewView(MarkdownPreviewView): - template_name = "comments/preview.html" + template_name = 'comments/preview.html' class ProfileMarkdownPreviewView(MarkdownPreviewView): - template_name = "user/preview.html" + template_name = 'user/preview.html' class OrganizationMarkdownPreviewView(MarkdownPreviewView): - template_name = "organization/preview.html" + template_name = 'organization/preview.html' class SolutionMarkdownPreviewView(MarkdownPreviewView): - template_name = "solution-preview.html" + template_name = 'solution-preview.html' class LicenseMarkdownPreviewView(MarkdownPreviewView): - template_name = "license-preview.html" + template_name = 'license-preview.html' class TicketMarkdownPreviewView(MarkdownPreviewView): - template_name = "ticket/preview.html" + template_name = 'ticket/preview.html' diff --git a/judge/views/problem.py b/judge/views/problem.py index ad57692..e7f2763 100644 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -1,38 +1,18 @@ import logging import os import shutil +from datetime import timedelta from operator import itemgetter from random import randrange -from copy import deepcopy -from django.core.cache import cache from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.db import transaction -from django.db.models import ( - BooleanField, - Case, - CharField, - Count, - F, - FilteredRelation, - Prefetch, - Q, - When, - IntegerField, - Sum, -) -from django.db.models.functions import Coalesce +from django.db.models import Count, F, Prefetch, Q, Sum, Case, When, IntegerField from django.db.utils import ProgrammingError -from django.http import ( - Http404, - HttpResponse, - HttpResponseForbidden, - HttpResponseRedirect, - JsonResponse, -) +from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.template.loader import get_template from django.urls import reverse @@ -41,57 +21,23 @@ from django.utils.functional import cached_property from django.utils.html import escape, format_html from django.utils.safestring import mark_safe from django.utils.translation import gettext as _, gettext_lazy -from django.views.generic import ListView, View +from django.views.generic import DetailView, ListView, View from django.views.generic.base import TemplateResponseMixin from django.views.generic.detail import SingleObjectMixin -from judge.views.comment import CommentedDetailView -from judge.forms import ProblemCloneForm, ProblemSubmitForm, ProblemPointsVoteForm -from judge.models import ( - ContestProblem, - ContestSubmission, - Judge, - Language, - Problem, - ContestProblemClarification, - ProblemGroup, - ProblemTranslation, - ProblemType, - ProblemPointsVote, - RuntimeVersion, - Solution, - Submission, - SubmissionSource, - Organization, - Profile, - LanguageTemplate, - Contest, -) +from judge.comments import CommentedDetailView +from judge.forms import ProblemCloneForm, ProblemSubmitForm +from judge.models import ContestProblem, ContestSubmission, Judge, Language, Problem, ProblemClarification, \ + ProblemGroup, ProblemTranslation, ProblemType, RuntimeVersion, Solution, Submission, SubmissionSource, \ + TranslatedProblemForeignKeyQuerySet, Organization from judge.pdf_problems import DefaultPdfMaker, HAS_PDF from judge.utils.diggpaginator import DiggPaginator from judge.utils.opengraph import generate_opengraph -from judge.utils.problems import ( - contest_attempted_ids, - contest_completed_ids, - hot_problems, - user_attempted_ids, - user_completed_ids, - get_related_problems, - get_user_recommended_problems, - RecommendationType, -) +from judge.utils.problems import contest_attempted_ids, contest_completed_ids, hot_problems, user_attempted_ids, \ + user_completed_ids from judge.utils.strings import safe_float_or_none, safe_int_or_none from judge.utils.tickets import own_ticket_filter -from judge.utils.views import ( - QueryStringSortMixin, - SingleObjectFormView, - TitleMixin, - generic_message, -) -from judge.ml.collab_filter import CollabFilter -from judge.views.pagevote import PageVoteDetailView -from judge.views.bookmark import BookMarkDetailView -from judge.views.feed import FeedView +from judge.utils.views import QueryStringSortMixin, SingleObjectFormView, TitleMixin, generic_message def get_contest_problem(problem, profile): @@ -102,25 +48,14 @@ def get_contest_problem(problem, profile): def get_contest_submission_count(problem, profile, virtual): - return ( - profile.current_contest.submissions.exclude(submission__status__in=["IE"]) - .filter(problem__problem=problem, participation__virtual=virtual) - .count() - ) - - -def get_problems_in_organization(request, organization): - problem_list = ProblemList(request=request) - problem_list.setup_problem_list(request) - problem_list.org_query = [organization.id] - problems = problem_list.get_normal_queryset() - return problems + return profile.current_contest.submissions.exclude(submission__status__in=['IE']) \ + .filter(problem__problem=problem, participation__virtual=virtual).count() class ProblemMixin(object): model = Problem - slug_url_kwarg = "problem" - slug_field = "code" + slug_url_kwarg = 'problem' + slug_field = 'code' def get_object(self, queryset=None): problem = super(ProblemMixin, self).get_object(queryset) @@ -130,17 +65,14 @@ class ProblemMixin(object): def no_such_problem(self): code = self.kwargs.get(self.slug_url_kwarg, None) - return generic_message( - self.request, - _("No such problem"), - _('Could not find a problem with the code "%s".') % code, - status=404, - ) + return generic_message(self.request, _('No such problem'), + _('Could not find a problem with the code "%s".') % code, status=404) def get(self, request, *args, **kwargs): try: return super(ProblemMixin, self).get(request, *args, **kwargs) except Http404 as e: + print(e) return self.no_such_problem() @@ -157,25 +89,10 @@ class SolvedProblemMixin(object): else: return user_attempted_ids(self.profile) if self.profile is not None else () - def get_latest_attempted_problems(self, limit=None, queryset=None): - if self.in_contest or not self.profile: - return () - result = list(user_attempted_ids(self.profile).values()) - if queryset: - queryset_ids = set([i.code for i in queryset]) - result = filter(lambda i: i["code"] in queryset_ids, result) - result = sorted(result, key=lambda d: -d["last_submission"]) - if limit: - result = result[:limit] - return result - @cached_property def in_contest(self): - return ( - self.profile is not None - and self.profile.current_contest is not None + return self.profile is not None and self.profile.current_contest is not None \ and self.request.in_contest_mode - ) @cached_property def contest(self): @@ -188,192 +105,148 @@ class SolvedProblemMixin(object): return self.request.profile -class ProblemSolution( - SolvedProblemMixin, - ProblemMixin, - TitleMixin, - CommentedDetailView, - PageVoteDetailView, - BookMarkDetailView, -): - context_object_name = "solution" - template_name = "problem/editorial.html" +class ProblemSolution(SolvedProblemMixin, ProblemMixin, TitleMixin, CommentedDetailView): + context_object_name = 'problem' + template_name = 'problem/editorial.html' def get_title(self): - return _("Editorial for {0}").format(self.problem.name) + return _('Editorial for {0}').format(self.object.name) def get_content_title(self): - return format_html( - _('Editorial for {0}'), - self.problem.name, - reverse("problem_detail", args=[self.problem.code]), - ) - - def get_object(self): - self.problem = super().get_object() - solution = get_object_or_404(Solution, problem=self.problem) - return solution + return format_html(_(u'Editorial for {0}'), self.object.name, + reverse('problem_detail', args=[self.object.code])) def get_context_data(self, **kwargs): context = super(ProblemSolution, self).get_context_data(**kwargs) - solution = self.get_object() - if ( - not solution.is_public or solution.publish_on > timezone.now() - ) and not self.request.user.has_perm("judge.see_private_solution"): + + solution = get_object_or_404(Solution, problem=self.object) + + if (not solution.is_public or solution.publish_on > timezone.now()) and \ + not self.request.user.has_perm('judge.see_private_solution'): raise Http404() - context["problem"] = self.problem - context["has_solved_problem"] = self.problem.id in self.get_completed_problems() + context['solution'] = solution + context['has_solved_problem'] = self.object.id in self.get_completed_problems() return context + def get_comment_page(self): + return 's:' + self.object.code -class ProblemRaw( - ProblemMixin, TitleMixin, TemplateResponseMixin, SingleObjectMixin, View -): - context_object_name = "problem" - template_name = "problem/raw.html" + +class ProblemRaw(ProblemMixin, TitleMixin, TemplateResponseMixin, SingleObjectMixin, View): + context_object_name = 'problem' + template_name = 'problem/raw.html' def get_title(self): return self.object.name def get_context_data(self, **kwargs): context = super(ProblemRaw, self).get_context_data(**kwargs) - context["problem_name"] = self.object.name - context["url"] = self.request.build_absolute_uri() - context["description"] = self.object.description - if hasattr(self.object, "data_files"): - context["fileio_input"] = self.object.data_files.fileio_input - context["fileio_output"] = self.object.data_files.fileio_output - else: - context["fileio_input"] = None - context["fileio_output"] = None + context['problem_name'] = self.object.name + context['url'] = self.request.build_absolute_uri() + context['description'] = self.object.description return context def get(self, request, *args, **kwargs): self.object = self.get_object() with translation.override(settings.LANGUAGE_CODE): - return self.render_to_response( - self.get_context_data( - object=self.object, - ) - ) + return self.render_to_response(self.get_context_data( + object=self.object, + )) -class ProblemDetail( - ProblemMixin, - SolvedProblemMixin, - CommentedDetailView, - PageVoteDetailView, - BookMarkDetailView, -): - context_object_name = "problem" - template_name = "problem/problem.html" +class ProblemDetail(ProblemMixin, SolvedProblemMixin, DetailView): + context_object_name = 'problem' + template_name = 'problem/problem.html' def get_context_data(self, **kwargs): context = super(ProblemDetail, self).get_context_data(**kwargs) user = self.request.user authed = user.is_authenticated - context["has_submissions"] = ( - authed - and Submission.objects.filter( - user=user.profile, problem=self.object - ).exists() - ) - contest_problem = ( - None - if not authed or user.profile.current_contest is None - else get_contest_problem(self.object, user.profile) - ) - context["contest_problem"] = contest_problem + context['has_submissions'] = authed and Submission.objects.filter(user=user.profile, + problem=self.object).exists() + contest_problem = (None if not authed or user.profile.current_contest is None else + get_contest_problem(self.object, user.profile)) + context['contest_problem'] = contest_problem if contest_problem: - clarifications = contest_problem.clarifications - context["has_clarifications"] = clarifications.count() > 0 - context["clarifications"] = clarifications.order_by("-date") - context["submission_limit"] = contest_problem.max_submissions + clarifications = self.object.clarifications + context['has_clarifications'] = clarifications.count() > 0 + context['clarifications'] = clarifications.order_by('-date') + context['submission_limit'] = contest_problem.max_submissions if contest_problem.max_submissions: - context["submissions_left"] = max( - contest_problem.max_submissions - - get_contest_submission_count( - self.object, user.profile, user.profile.current_contest.virtual - ), - 0, - ) + context['submissions_left'] = max(contest_problem.max_submissions - + get_contest_submission_count(self.object, user.profile, + user.profile.current_contest.virtual), 0) - context["available_judges"] = Judge.objects.filter( - online=True, problems=self.object - ) - context["show_languages"] = ( - self.object.allowed_languages.count() != Language.objects.count() - ) - context["has_pdf_render"] = HAS_PDF - context["completed_problem_ids"] = self.get_completed_problems() - context["attempted_problems"] = self.get_attempted_problems() + context['available_judges'] = Judge.objects.filter(online=True, problems=self.object) + context['show_languages'] = self.object.allowed_languages.count() != Language.objects.count() + context['has_pdf_render'] = HAS_PDF + context['completed_problem_ids'] = self.get_completed_problems() + context['attempted_problems'] = self.get_attempted_problems() can_edit = self.object.is_editable_by(user) - context["can_edit_problem"] = can_edit + context['can_edit_problem'] = can_edit if user.is_authenticated: tickets = self.object.tickets if not can_edit: tickets = tickets.filter(own_ticket_filter(user.profile.id)) - context["has_tickets"] = tickets.exists() - context["num_open_tickets"] = ( - tickets.filter(is_open=True).values("id").distinct().count() - ) + context['has_tickets'] = tickets.exists() + context['num_open_tickets'] = tickets.filter(is_open=True).values('id').distinct().count() try: - context["editorial"] = Solution.objects.get(problem=self.object) + context['editorial'] = Solution.objects.get(problem=self.object) except ObjectDoesNotExist: pass try: - translation = self.object.translations.get( - language=self.request.LANGUAGE_CODE - ) + translation = self.object.translations.get(language=self.request.LANGUAGE_CODE) except ProblemTranslation.DoesNotExist: - context["title"] = self.object.name - context["language"] = settings.LANGUAGE_CODE - context["description"] = self.object.description - context["translated"] = False + context['title'] = self.object.name + context['language'] = settings.LANGUAGE_CODE + context['description'] = self.object.description + context['translated'] = False else: - context["title"] = translation.name - context["language"] = self.request.LANGUAGE_CODE - context["description"] = translation.description - context["translated"] = True + context['title'] = translation.name + context['language'] = self.request.LANGUAGE_CODE + context['description'] = translation.description + context['translated'] = True if not self.object.og_image or not self.object.summary: - metadata = generate_opengraph( - "generated-meta-problem:%s:%d" % (context["language"], self.object.id), - context["description"], - ) - context["meta_description"] = self.object.summary or metadata[0] - context["og_image"] = self.object.og_image or metadata[1] - if hasattr(self.object, "data_files"): - context["fileio_input"] = self.object.data_files.fileio_input - context["fileio_output"] = self.object.data_files.fileio_output - else: - context["fileio_input"] = None - context["fileio_output"] = None - if not self.in_contest and settings.ML_OUTPUT_PATH: - context["related_problems"] = get_related_problems( - self.profile, self.object - ) - + metadata = generate_opengraph('generated-meta-problem:%s:%d' % (context['language'], self.object.id), + context['description'], 'problem') + context['meta_description'] = self.object.summary or metadata[0] + context['og_image'] = self.object.og_image or metadata[1] return context +class ProblemComments(ProblemMixin, TitleMixin, CommentedDetailView): + context_object_name = 'problem' + template_name = 'problem/comments.html' + + def get_title(self): + return _('Disscuss {0}').format(self.object.name) + + def get_content_title(self): + return format_html(_(u'Discuss {0}'), self.object.name, + reverse('problem_detail', args=[self.object.code])) + + def get_comment_page(self): + return 'p:%s' % self.object.code + + class LatexError(Exception): pass class ProblemPdfView(ProblemMixin, SingleObjectMixin, View): - logger = logging.getLogger("judge.problem.pdf") + logger = logging.getLogger('judge.problem.pdf') languages = set(map(itemgetter(0), settings.LANGUAGES)) def get(self, request, *args, **kwargs): if not HAS_PDF: raise Http404() - language = kwargs.get("language", self.request.LANGUAGE_CODE) + language = kwargs.get('language', self.request.LANGUAGE_CODE) if language not in self.languages: raise Http404() @@ -383,151 +256,95 @@ class ProblemPdfView(ProblemMixin, SingleObjectMixin, View): except ProblemTranslation.DoesNotExist: trans = None - cache = os.path.join( - settings.DMOJ_PDF_PROBLEM_CACHE, "%s.%s.pdf" % (problem.code, language) - ) + cache = os.path.join(settings.DMOJ_PDF_PROBLEM_CACHE, '%s.%s.pdf' % (problem.code, language)) if not os.path.exists(cache): - self.logger.info("Rendering: %s.%s.pdf", problem.code, language) + self.logger.info('Rendering: %s.%s.pdf', problem.code, language) with DefaultPdfMaker() as maker, translation.override(language): problem_name = problem.name if trans is None else trans.name - maker.html = ( - get_template("problem/raw.html") - .render( - { - "problem": problem, - "problem_name": problem_name, - "description": problem.description - if trans is None - else trans.description, - "url": request.build_absolute_uri(), - } - ) - .replace('"//', '"https://') - .replace("'//", "'https://") - ) + maker.html = get_template('problem/raw.html').render({ + 'problem': problem, + 'problem_name': problem_name, + 'description': problem.description if trans is None else trans.description, + 'url': request.build_absolute_uri(), + 'math_engine': maker.math_engine, + }).replace('"//', '"https://').replace("'//", "'https://") maker.title = problem_name - assets = ["style.css"] + assets = ['style.css', 'pygment-github.css'] + if maker.math_engine == 'jax': + assets.append('mathjax_config.js') for file in assets: maker.load(file, os.path.join(settings.DMOJ_RESOURCES, file)) maker.make() if not maker.success: - self.logger.error("Failed to render PDF for %s", problem.code) - return HttpResponse( - maker.log, status=500, content_type="text/plain" - ) + self.logger.error('Failed to render PDF for %s', problem.code) + return HttpResponse(maker.log, status=500, content_type='text/plain') shutil.move(maker.pdffile, cache) response = HttpResponse() - if hasattr(settings, "DMOJ_PDF_PROBLEM_INTERNAL") and request.META.get( - "SERVER_SOFTWARE", "" - ).startswith("nginx/"): - response["X-Accel-Redirect"] = "%s/%s.%s.pdf" % ( - settings.DMOJ_PDF_PROBLEM_INTERNAL, - problem.code, - language, - ) + if hasattr(settings, 'DMOJ_PDF_PROBLEM_INTERNAL') and \ + request.META.get('SERVER_SOFTWARE', '').startswith('nginx/'): + response['X-Accel-Redirect'] = '%s/%s.%s.pdf' % (settings.DMOJ_PDF_PROBLEM_INTERNAL, problem.code, language) else: - with open(cache, "rb") as f: + with open(cache, 'rb') as f: response.content = f.read() - response["Content-Type"] = "application/pdf" - response["Content-Disposition"] = "inline; filename=%s.%s.pdf" % ( - problem.code, - language, - ) - return response - - -class ProblemPdfDescriptionView(ProblemMixin, SingleObjectMixin, View): - def get(self, request, *args, **kwargs): - problem = self.get_object() - if not problem.pdf_description: - raise Http404() - response = HttpResponse() - # if request.META.get("SERVER_SOFTWARE", "").startswith("nginx/"): - # response["X-Accel-Redirect"] = problem.pdf_description.path - # else: - with open(problem.pdf_description.path, "rb") as f: - response.content = f.read() - - response["Content-Type"] = "application/pdf" - response["Content-Disposition"] = "inline; filename=%s.pdf" % (problem.code,) + response['Content-Type'] = 'application/pdf' + response['Content-Disposition'] = 'inline; filename=%s.%s.pdf' % (problem.code, language) return response class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView): model = Problem - title = gettext_lazy("Problems") - context_object_name = "problems" - template_name = "problem/list.html" + title = gettext_lazy('Problems') + context_object_name = 'problems' + template_name = 'problem/list.html' paginate_by = 50 - sql_sort = frozenset(("date", "points", "ac_rate", "user_count", "code")) - manual_sort = frozenset(("name", "group", "solved", "type")) + sql_sort = frozenset(('date', 'points', 'ac_rate', 'user_count', 'code')) + manual_sort = frozenset(('name', 'group', 'solved', 'type')) all_sorts = sql_sort | manual_sort - default_desc = frozenset(("date", "points", "ac_rate", "user_count")) - first_page_href = None - filter_organization = False + default_desc = frozenset(('date', 'points', 'ac_rate', 'user_count')) + default_sort = '-date' - def get_default_sort_order(self, request): - if "search" in request.GET and settings.ENABLE_FTS: - return "-relevance" - return "-date" - - def get_paginator( - self, queryset, per_page, orphans=0, allow_empty_first_page=True, **kwargs - ): - paginator = DiggPaginator( - queryset, - per_page, - body=6, - padding=2, - orphans=orphans, - allow_empty_first_page=allow_empty_first_page, - count=queryset.values("pk").count() if not self.in_contest else None, - **kwargs, - ) + def get_paginator(self, queryset, per_page, orphans=0, + allow_empty_first_page=True, **kwargs): + paginator = DiggPaginator(queryset, per_page, body=6, padding=2, orphans=orphans, + allow_empty_first_page=allow_empty_first_page, **kwargs) if not self.in_contest: + # Get the number of pages and then add in this magic. + # noinspection PyStatementEffect + paginator.num_pages + queryset = queryset.add_i18n_name(self.request.LANGUAGE_CODE) - queryset = self.sort_queryset(queryset) + sort_key = self.order.lstrip('-') + if sort_key in self.sql_sort: + queryset = queryset.order_by(self.order) + elif sort_key == 'name': + queryset = queryset.order_by(self.order.replace('name', 'i18n_name')) + elif sort_key == 'group': + queryset = queryset.order_by(self.order + '__name') + elif sort_key == 'solved': + if self.request.user.is_authenticated: + profile = self.request.profile + solved = user_completed_ids(profile) + attempted = user_attempted_ids(profile) + + def _solved_sort_order(problem): + if problem.id in solved: + return 1 + if problem.id in attempted: + return 0 + return -1 + + queryset = list(queryset) + queryset.sort(key=_solved_sort_order, reverse=self.order.startswith('-')) + elif sort_key == 'type': + if self.show_types: + queryset = list(queryset) + queryset.sort(key=lambda problem: problem.types_list[0] if problem.types_list else '', + reverse=self.order.startswith('-')) paginator.object_list = queryset return paginator - def sort_queryset(self, queryset): - sort_key = self.order.lstrip("-") - if sort_key in self.sql_sort: - queryset = queryset.order_by(self.order) - elif sort_key == "name": - queryset = queryset.order_by(self.order.replace("name", "i18n_name")) - elif sort_key == "group": - queryset = queryset.order_by(self.order + "__name") - elif sort_key == "solved": - if self.request.user.is_authenticated: - profile = self.request.profile - solved = user_completed_ids(profile) - attempted = user_attempted_ids(profile) - - def _solved_sort_order(problem): - if problem.id in solved: - return 1 - if problem.id in attempted: - return 0 - return -1 - - queryset = list(queryset) - queryset.sort( - key=_solved_sort_order, reverse=self.order.startswith("-") - ) - elif sort_key == "type": - if self.show_types: - queryset = list(queryset) - queryset.sort( - key=lambda problem: problem.types_list[0] - if problem.types_list - else "", - reverse=self.order.startswith("-"), - ) - return queryset - @cached_property def profile(self): if not self.request.user.is_authenticated: @@ -535,126 +352,69 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView return self.request.profile def get_contest_queryset(self): - queryset = ( - self.profile.current_contest.contest.contest_problems.select_related( - "problem__group" - ) - .defer("problem__description") - .order_by("problem__code") - .annotate(user_count=Count("submission__participation", distinct=True)) - .annotate( - i18n_translation=FilteredRelation( - "problem__translations", - condition=Q( - problem__translations__language=self.request.LANGUAGE_CODE - ), - ) - ) - .annotate( - i18n_name=Coalesce( - F("i18n_translation__name"), - F("problem__name"), - output_field=CharField(), - ) - ) - .order_by("order") - ) - return [ - { - "id": p["problem_id"], - "code": p["problem__code"], - "name": p["problem__name"], - "i18n_name": p["i18n_name"], - "group": {"full_name": p["problem__group__full_name"]}, - "points": p["points"], - "partial": p["partial"], - "user_count": p["user_count"], - } - for p in queryset.values( - "problem_id", - "problem__code", - "problem__name", - "i18n_name", - "problem__group__full_name", - "points", - "partial", - "user_count", - ) - ] - - def get_org_query(self, query): - if not self.profile: - return [] - return [ - i - for i in query - if i in self.profile.organizations.values_list("id", flat=True) - ][:3] + queryset = self.profile.current_contest.contest.contest_problems.select_related('problem__group') \ + .defer('problem__description').order_by('problem__code') \ + .annotate(user_count=Count('submission__participation', distinct=True)) \ + .order_by('order') + queryset = TranslatedProblemForeignKeyQuerySet.add_problem_i18n_name(queryset, 'i18n_name', + self.request.LANGUAGE_CODE, + 'problem__name') + return [{ + 'id': p['problem_id'], + 'code': p['problem__code'], + 'name': p['problem__name'], + 'i18n_name': p['i18n_name'], + 'group': {'full_name': p['problem__group__full_name']}, + 'points': p['points'], + 'partial': p['partial'], + 'user_count': p['user_count'], + } for p in queryset.values('problem_id', 'problem__code', 'problem__name', 'i18n_name', + 'problem__group__full_name', 'points', 'partial', 'user_count')] def get_normal_queryset(self): - queryset = Problem.get_visible_problems(self.request.user) - queryset = queryset.select_related("group") + filter = Q(is_public=True) + if self.profile is not None: + filter |= Q(authors=self.profile) + filter |= Q(curators=self.profile) + filter |= Q(testers=self.profile) + queryset = Problem.objects.filter(filter).select_related('group').defer('description') + if not self.request.user.has_perm('see_organization_problem'): + filter = Q(is_organization_private=False) + if self.profile is not None: + filter |= Q(organizations__in=self.profile.organizations.all()) + queryset = queryset.filter(filter) if self.profile is not None and self.hide_solved: - solved_problems = self.get_completed_problems() - queryset = queryset.exclude(id__in=solved_problems) - if not self.org_query and self.request.organization: - self.org_query = [self.request.organization.id] + queryset = queryset.exclude(id__in=Submission.objects.filter(user=self.profile, points=F('problem__points')) + .values_list('problem__id', flat=True)) if self.org_query: - self.org_query = self.get_org_query(self.org_query) - contest_problems = ( - Contest.objects.filter(organizations__in=self.org_query) - .select_related("problems") - .values_list("contest_problems__problem__id") - .distinct() - ) queryset = queryset.filter( - Q(organizations__in=self.org_query) | Q(id__in=contest_problems) - ) - if self.author_query: - queryset = queryset.filter(authors__in=self.author_query) + Q(organizations__in=self.org_query) | + Q(contests__contest__organizations__in=self.org_query)) if self.show_types: - queryset = queryset.prefetch_related("types") + queryset = queryset.prefetch_related('types') if self.category is not None: queryset = queryset.filter(group__id=self.category) if self.selected_types: queryset = queryset.filter(types__in=self.selected_types) - if "search" in self.request.GET: - self.search_query = query = " ".join( - self.request.GET.getlist("search") - ).strip() + if 'search' in self.request.GET: + self.search_query = query = ' '.join(self.request.GET.getlist('search')).strip() if query: - substr_queryset = queryset.filter( - Q(code__icontains=query) - | Q(name__icontains=query) - | Q( - translations__name__icontains=query, - translations__language=self.request.LANGUAGE_CODE, - ) - ) - if settings.ENABLE_FTS: - queryset = ( - queryset.search(query, queryset.BOOLEAN).extra( - order_by=["-relevance"] - ) - | substr_queryset - ) + if settings.ENABLE_FTS and self.full_text: + queryset = queryset.search(query, queryset.BOOLEAN).extra(order_by=['-relevance']) else: - queryset = substr_queryset + queryset = queryset.filter( + Q(code__icontains=query) | Q(name__icontains=query) | + Q(translations__name__icontains=query, translations__language=self.request.LANGUAGE_CODE)) self.prepoint_queryset = queryset if self.point_start is not None: queryset = queryset.filter(points__gte=self.point_start) if self.point_end is not None: queryset = queryset.filter(points__lte=self.point_end) + if self.show_editorial: + queryset = queryset.annotate( + has_public_editorial=Sum(Case(When(solution__is_public=True, then=1), + default=0, output_field=IntegerField()))) - queryset = queryset.annotate( - has_public_editorial=Sum( - Case( - When(solution__is_public=True, then=1), - default=0, - output_field=IntegerField(), - ) - ) - ) return queryset.distinct() def get_queryset(self): @@ -665,148 +425,99 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView def get_context_data(self, **kwargs): context = super(ProblemList, self).get_context_data(**kwargs) + context['hide_solved'] = 0 if self.in_contest else int(self.hide_solved) + context['show_types'] = 0 if self.in_contest else int(self.show_types) + context['full_text'] = 0 if self.in_contest else int(self.full_text) + context['show_editorial'] = 0 if self.in_contest else int(self.show_editorial) - if self.request.organization: - self.filter_organization = True - context["hide_solved"] = 0 if self.in_contest else int(self.hide_solved) - context["show_types"] = 0 if self.in_contest else int(self.show_types) - context["full_text"] = 0 if self.in_contest else int(self.full_text) - context["show_editorial"] = 0 if self.in_contest else int(self.show_editorial) - context["show_solved_only"] = ( - 0 if self.in_contest else int(self.show_solved_only) - ) - - if self.request.profile: - context["organizations"] = self.request.profile.organizations.all() - context["category"] = self.category - context["categories"] = ProblemGroup.objects.all() + context['organizations'] = Organization.objects.all() + context['category'] = self.category + context['categories'] = ProblemGroup.objects.all() if self.show_types: - context["selected_types"] = self.selected_types - context["problem_types"] = ProblemType.objects.all() - context["has_fts"] = settings.ENABLE_FTS - context["org_query"] = self.org_query - context["author_query"] = Profile.objects.filter(id__in=self.author_query) - context["search_query"] = self.search_query - context["completed_problem_ids"] = self.get_completed_problems() - context["attempted_problems"] = self.get_attempted_problems() - context["last_attempted_problems"] = self.get_latest_attempted_problems( - 15, context["problems"] if self.filter_organization else None - ) - context["page_type"] = "list" + context['selected_types'] = self.selected_types + context['problem_types'] = ProblemType.objects.all() + context['has_fts'] = settings.ENABLE_FTS + context['org_query'] = self.org_query + context['search_query'] = self.search_query + context['completed_problem_ids'] = self.get_completed_problems() + context['attempted_problems'] = self.get_attempted_problems() + context.update(self.get_sort_paginate_context()) if not self.in_contest: context.update(self.get_sort_context()) - ( - context["point_start"], - context["point_end"], - context["point_values"], - ) = self.get_noui_slider_points() + context['hot_problems'] = hot_problems(timedelta(days=1), 7) + context['point_start'], context['point_end'], context['point_values'] = self.get_noui_slider_points() else: - context["point_start"], context["point_end"], context["point_values"] = ( - 0, - 0, - {}, - ) - context["hide_contest_scoreboard"] = self.contest.scoreboard_visibility in ( - self.contest.SCOREBOARD_AFTER_CONTEST, - self.contest.SCOREBOARD_AFTER_PARTICIPATION, - ) - context["has_clarifications"] = False - + context['hot_problems'] = None + context['point_start'], context['point_end'], context['point_values'] = 0, 0, {} + context['hide_contest_scoreboard'] = self.contest.scoreboard_visibility in \ + (self.contest.SCOREBOARD_AFTER_CONTEST, self.contest.SCOREBOARD_AFTER_PARTICIPATION) + context['has_clarifications'] = False if self.request.user.is_authenticated: participation = self.request.profile.current_contest if participation: - clarifications = ContestProblemClarification.objects.filter( - problem__in=participation.contest.contest_problems.all() - ) - context["has_clarifications"] = clarifications.count() > 0 - context["clarifications"] = clarifications.order_by("-date") + clarifications = ProblemClarification.objects.filter(problem__in=participation.contest.problems.all()) + context['has_clarifications'] = clarifications.count() > 0 + context['clarifications'] = clarifications.order_by('-date') if participation.contest.is_editable_by(self.request.user): - context["can_edit_contest"] = True - - context["page_prefix"] = None - context["page_suffix"] = suffix = ( - ("?" + self.request.GET.urlencode()) if self.request.GET else "" - ) - context["first_page_href"] = (self.first_page_href or ".") + suffix - context["has_show_editorial_option"] = True - context["show_contest_mode"] = self.request.in_contest_mode + context['can_edit_contest'] = True return context def get_noui_slider_points(self): - points = sorted( - self.prepoint_queryset.values_list("points", flat=True).distinct() - ) + points = sorted(self.prepoint_queryset.values_list('points', flat=True).distinct()) if not points: return 0, 0, {} if len(points) == 1: - return ( - points[0], - points[0], - { - "min": points[0] - 1, - "max": points[0] + 1, - }, - ) + return points[0], points[0], { + 'min': points[0] - 1, + 'max': points[0] + 1, + } start, end = points[0], points[-1] if self.point_start is not None: start = self.point_start if self.point_end is not None: end = self.point_end - points_map = {0.0: "min", 1.0: "max"} + points_map = {0.0: 'min', 1.0: 'max'} size = len(points) - 1 - return ( - start, - end, - { - points_map.get(i / size, "%.2f%%" % (100 * i / size,)): j - for i, j in enumerate(points) - }, - ) + return start, end, {points_map.get(i / size, '%.2f%%' % (100 * i / size,)): j for i, j in enumerate(points)} def GET_with_session(self, request, key): - if not request.GET.get(key): + if not request.GET: return request.session.get(key, False) - return request.GET.get(key, None) == "1" + return request.GET.get(key, None) == '1' def setup_problem_list(self, request): - self.hide_solved = self.GET_with_session(request, "hide_solved") - self.show_types = self.GET_with_session(request, "show_types") - self.full_text = self.GET_with_session(request, "full_text") - self.show_editorial = self.GET_with_session(request, "show_editorial") - self.show_solved_only = self.GET_with_session(request, "show_solved_only") - + self.hide_solved = self.GET_with_session(request, 'hide_solved') + self.show_types = self.GET_with_session(request, 'show_types') + self.full_text = self.GET_with_session(request, 'full_text') + self.show_editorial = self.GET_with_session(request, 'show_editorial') + self.search_query = None self.category = None self.org_query = [] - self.author_query = [] self.selected_types = [] # This actually copies into the instance dictionary... self.all_sorts = set(self.all_sorts) if not self.show_types: - self.all_sorts.discard("type") + self.all_sorts.discard('type') - self.category = safe_int_or_none(request.GET.get("category")) - if "type" in request.GET: + self.category = safe_int_or_none(request.GET.get('category')) + if 'type' in request.GET: try: - self.selected_types = list(map(int, request.GET.getlist("type"))) - except ValueError: - pass - if "orgs" in request.GET: - try: - self.org_query = list(map(int, request.GET.getlist("orgs"))) - except ValueError: - pass - if "authors" in request.GET: - try: - self.author_query = list(map(int, request.GET.getlist("authors"))) + self.selected_types = list(map(int, request.GET.getlist('type'))) except ValueError: pass - self.point_start = safe_float_or_none(request.GET.get("point_start")) - self.point_end = safe_float_or_none(request.GET.get("point_end")) + if 'orgs' in request.GET: + try: + self.org_query = list(map(int, request.GET.getlist('orgs'))) + except ValueError: + pass + + self.point_start = safe_float_or_none(request.GET.get('point_start')) + self.point_end = safe_float_or_none(request.GET.get('point_end')) def get(self, request, *args, **kwargs): self.setup_problem_list(request) @@ -814,344 +525,134 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView try: return super(ProblemList, self).get(request, *args, **kwargs) except ProgrammingError as e: - return generic_message(request, "FTS syntax error", e.args[1], status=400) + return generic_message(request, 'FTS syntax error', e.args[1], status=400) def post(self, request, *args, **kwargs): - to_update = ( - "hide_solved", - "show_types", - "full_text", - "show_editorial", - "show_solved_only", - ) + to_update = ('hide_solved', 'show_types', 'full_text') for key in to_update: if key in request.GET: - val = request.GET.get(key) == "1" + val = request.GET.get(key) == '1' request.session[key] = val else: - request.session[key] = False + request.session.pop(key, None) return HttpResponseRedirect(request.get_full_path()) -class ProblemFeed(ProblemList, FeedView): - model = Problem - context_object_name = "problems" - template_name = "problem/feed.html" - feed_content_template_name = "problem/feed/items.html" - paginate_by = 4 - title = _("Problem feed") - feed_type = None - - def get_recommended_problem_ids(self, queryset): - user_id = self.request.profile.id - problem_ids = queryset.values_list("id", flat=True) - rec_types = [ - RecommendationType.CF_DOT, - RecommendationType.CF_COSINE, - RecommendationType.CF_TIME_DOT, - RecommendationType.CF_TIME_COSINE, - RecommendationType.HOT_PROBLEM, - ] - limits = [100, 100, 100, 100, 20] - shuffle = True - - allow_debug_type = ( - self.request.user.is_impersonate or self.request.user.is_superuser - ) - if allow_debug_type and "debug_type" in self.request.GET: - try: - debug_type = int(self.request.GET.get("debug_type")) - except ValueError: - raise Http404() - rec_types = [debug_type] - limits = [100] - shuffle = False - - return get_user_recommended_problems( - user_id, problem_ids, rec_types, limits, shuffle - ) - - def get_queryset(self): - if self.feed_type == "volunteer": - self.hide_solved = 0 - self.show_types = 1 - queryset = super(ProblemFeed, self).get_queryset() - - user = self.request.profile - - if self.feed_type == "new": - return queryset.order_by("-date").add_i18n_name(self.request.LANGUAGE_CODE) - elif user and self.feed_type == "volunteer": - voted_problems = ( - user.volunteer_problem_votes.values_list("problem", flat=True) - if not bool(self.search_query) - else [] - ) - if self.show_solved_only: - queryset = queryset.filter( - id__in=Submission.objects.filter( - user=self.profile, points=F("problem__points") - ).values_list("problem__id", flat=True) - ) - return ( - queryset.exclude(id__in=voted_problems) - .order_by("?") - .add_i18n_name(self.request.LANGUAGE_CODE) - ) - if "search" in self.request.GET: - return queryset.add_i18n_name(self.request.LANGUAGE_CODE) - if not settings.ML_OUTPUT_PATH or not user: - return queryset.order_by("?").add_i18n_name(self.request.LANGUAGE_CODE) - - q = self.get_recommended_problem_ids(queryset) - - queryset = Problem.objects.filter(id__in=q) - queryset = queryset.add_i18n_name(self.request.LANGUAGE_CODE) - - # Reorder results from database to correct positions - res = [None for _ in range(len(q))] - position_in_q = {i: idx for idx, i in enumerate(q)} - for problem in queryset: - res[position_in_q[problem.id]] = problem - return res - - def get_feed_context(self, object_list): - return { - "completed_problem_ids": self.get_completed_problems(), - "attempted_problems": self.get_attempted_problems(), - "show_types": self.show_types, - } - - def get_context_data(self, **kwargs): - context = super(ProblemFeed, self).get_context_data(**kwargs) - context["page_type"] = "feed" - context["title"] = self.title - context["feed_type"] = self.feed_type - context["has_show_editorial_option"] = False - - return context - - def get(self, request, *args, **kwargs): - if request.in_contest_mode: - return HttpResponseRedirect(reverse("problem_list")) - return super(ProblemFeed, self).get(request, *args, **kwargs) - - class LanguageTemplateAjax(View): def get(self, request, *args, **kwargs): try: - problem = request.GET.get("problem", None) - lang_id = int(request.GET.get("id", 0)) - res = None - if problem: - try: - res = LanguageTemplate.objects.get( - language__id=lang_id, problem__id=problem - ).source - except ObjectDoesNotExist: - pass - if not res: - res = get_object_or_404(Language, id=lang_id).template + language = get_object_or_404(Language, id=int(request.GET.get('id', 0))) except ValueError: raise Http404() - return HttpResponse(res, content_type="text/plain") + return HttpResponse(language.template, content_type='text/plain') class RandomProblem(ProblemList): def get(self, request, *args, **kwargs): self.setup_problem_list(request) - - try: - return super().get(request, *args, **kwargs) - except ProgrammingError as e: - return generic_message(request, "FTS syntax error", e.args[1], status=400) - if self.in_contest: raise Http404() queryset = self.get_normal_queryset() count = queryset.count() if not count: - return HttpResponseRedirect( - "%s%s%s" - % ( - reverse("problem_list"), - request.META["QUERY_STRING"] and "?", - request.META["QUERY_STRING"], - ) - ) + return HttpResponseRedirect('%s%s%s' % (reverse('problem_list'), request.META['QUERY_STRING'] and '?', + request.META['QUERY_STRING'])) return HttpResponseRedirect(queryset[randrange(count)].get_absolute_url()) -user_logger = logging.getLogger("judge.user") - - -def last_nth_submitted_date_in_contest(profile, contest, n): - submissions = Submission.objects.filter( - user=profile, contest_object=contest - ).order_by("-id")[:n] - if submissions.count() >= n: - return submissions[n - 1].date - return None +user_logger = logging.getLogger('judge.user') @login_required def problem_submit(request, problem, submission=None): - if ( - submission is not None - and not request.user.has_perm("judge.resubmit_other") - and get_object_or_404(Submission, id=int(submission)).user.user != request.user - ): + if submission is not None and not request.user.has_perm('judge.resubmit_other') and \ + get_object_or_404(Submission, id=int(submission)).user.user != request.user: raise PermissionDenied() profile = request.profile problem = get_object_or_404(Problem, code=problem) if not problem.is_accessible_by(request.user): - if request.method == "POST": - user_logger.info( - "Naughty user %s wants to submit to %s without permission", - request.user.username, - problem.code, - ) - return HttpResponseForbidden("

Not allowed to submit. Try later.

") + if request.method == 'POST': + user_logger.info('Naughty user %s wants to submit to %s without permission', + request.user.username, problem.code) + return HttpResponseForbidden('

Not allowed to submit. Try later.

') raise Http404() if problem.is_editable_by(request.user): - judge_choices = tuple( - Judge.objects.filter(online=True, problems=problem).values_list( - "name", "name" - ) - ) + judge_choices = tuple(Judge.objects.filter(online=True, problems=problem).values_list('name', 'name')) else: judge_choices = () - if request.method == "POST": - form = ProblemSubmitForm( - request.POST, - request.FILES, - judge_choices=judge_choices, - instance=Submission(user=profile, problem=problem), - request=request, - problem=problem, - ) + if request.method == 'POST': + form = ProblemSubmitForm(request.POST, judge_choices=judge_choices, + instance=Submission(user=profile, problem=problem)) if form.is_valid(): - if ( - not request.user.has_perm("judge.spam_submission") - and Submission.objects.filter(user=profile, was_rejudged=False) - .exclude(status__in=["D", "IE", "CE", "AB"]) - .count() - >= settings.DMOJ_SUBMISSION_LIMIT - ): - return HttpResponse( - _("

You have submitted too many submissions.

"), status=429 - ) - if not problem.allowed_languages.filter( - id=form.cleaned_data["language"].id - ).exists(): + if (not request.user.has_perm('judge.spam_submission') and + Submission.objects.filter(user=profile, was_rejudged=False) + .exclude(status__in=['D', 'IE', 'CE', 'AB']).count() >= settings.DMOJ_SUBMISSION_LIMIT): + return HttpResponse('

You submitted too many submissions.

', status=429) + if not problem.allowed_languages.filter(id=form.cleaned_data['language'].id).exists(): raise PermissionDenied() - if ( - not request.user.is_superuser - and problem.banned_users.filter(id=profile.id).exists() - ): - return generic_message( - request, - _("Banned from submitting"), - _( - "You have been declared persona non grata for this problem. " - "You are permanently barred from submitting this problem." - ), - ) + if not request.user.is_superuser and problem.banned_users.filter(id=profile.id).exists(): + return generic_message(request, _('Banned from submitting'), + _('You have been declared persona non grata for this problem. ' + 'You are permanently barred from submitting this problem.')) with transaction.atomic(): if profile.current_contest is not None: - contest = profile.current_contest.contest contest_id = profile.current_contest.contest_id - rate_limit = contest.rate_limit - - if rate_limit: - t = last_nth_submitted_date_in_contest( - profile, contest, rate_limit - ) - if t is not None and timezone.now() - t < timezone.timedelta( - minutes=1 - ): - return HttpResponse( - _("

You have submitted too many submissions.

"), - status=429, - ) - try: contest_problem = problem.contests.get(contest_id=contest_id) except ContestProblem.DoesNotExist: model = form.save() else: max_subs = contest_problem.max_submissions - if ( - max_subs - and get_contest_submission_count( - problem, profile, profile.current_contest.virtual - ) - >= max_subs - ): - return generic_message( - request, - _("Too many submissions"), - _( - "You have exceeded the submission limit for this problem." - ), - ) + if max_subs and get_contest_submission_count(problem, profile, + profile.current_contest.virtual) >= max_subs: + return generic_message(request, _('Too many submissions'), + _('You have exceeded the submission limit for this problem.')) model = form.save() model.contest_object_id = contest_id - contest = ContestSubmission( - submission=model, - problem=contest_problem, - participation=profile.current_contest, - ) + contest = ContestSubmission(submission=model, problem=contest_problem, + participation=profile.current_contest) contest.save() else: model = form.save() # Create the SubmissionSource object - source = SubmissionSource( - submission=model, source=form.cleaned_data["source"] - ) + source = SubmissionSource(submission=model, source=form.cleaned_data['source']) source.save() profile.update_contest() # Save a query model.source = source - model.judge(rejudge=False, judge_id=form.cleaned_data["judge"]) + model.judge(rejudge=False, judge_id=form.cleaned_data['judge']) - return HttpResponseRedirect( - reverse("submission_status", args=[str(model.id)]) - ) + return HttpResponseRedirect(reverse('submission_status', args=[str(model.id)])) else: form_data = form.cleaned_data if submission is not None: sub = get_object_or_404(Submission, id=int(submission)) else: - initial = {"language": profile.language} + initial = {'language': profile.language} if submission is not None: try: - sub = get_object_or_404( - Submission.objects.select_related("source", "language"), - id=int(submission), - ) - initial["source"] = sub.source.source - initial["language"] = sub.language + sub = get_object_or_404(Submission.objects.select_related('source', 'language'), id=int(submission)) + initial['source'] = sub.source.source + initial['language'] = sub.language except ValueError: raise Http404() form = ProblemSubmitForm(judge_choices=judge_choices, initial=initial) form_data = initial - form.fields["language"].queryset = problem.usable_languages.order_by( - "name", "key" - ).prefetch_related( - Prefetch("runtimeversion_set", RuntimeVersion.objects.order_by("priority")) + form.fields['language'].queryset = ( + problem.usable_languages.order_by('name', 'key') + .prefetch_related(Prefetch('runtimeversion_set', RuntimeVersion.objects.order_by('priority'))) ) - if "language" in form_data: - form.fields["source"].widget.mode = form_data["language"].ace - form.fields["source"].widget.theme = profile.ace_theme + if 'language' in form_data: + form.fields['source'].widget.mode = form_data['language'].ace + form.fields['source'].widget.theme = profile.ace_theme if submission is not None: default_lang = sub.language @@ -1159,84 +660,55 @@ def problem_submit(request, problem, submission=None): default_lang = request.profile.language submission_limit = submissions_left = None - next_valid_submit_time = None if profile.current_contest is not None: - contest = profile.current_contest.contest try: - submission_limit = problem.contests.get(contest=contest).max_submissions + submission_limit = problem.contests.get(contest=profile.current_contest.contest).max_submissions except ContestProblem.DoesNotExist: pass else: if submission_limit: - submissions_left = submission_limit - get_contest_submission_count( - problem, profile, profile.current_contest.virtual - ) - if contest.rate_limit: - t = last_nth_submitted_date_in_contest(profile, contest, contest.rate_limit) - if t is not None: - next_valid_submit_time = t + timezone.timedelta(minutes=1) - next_valid_submit_time = next_valid_submit_time.isoformat() - - return render( - request, - "problem/submit.html", - { - "form": form, - "title": _("Submit to %(problem)s") - % { - "problem": problem.translated_name(request.LANGUAGE_CODE), - }, - "content_title": mark_safe( - escape(_("Submit to %(problem)s")) - % { - "problem": format_html( - '{1}', - reverse("problem_detail", args=[problem.code]), - problem.translated_name(request.LANGUAGE_CODE), - ), - } - ), - "langs": Language.objects.all(), - "no_judges": not form.fields["language"].queryset, - "submission_limit": submission_limit, - "submissions_left": submissions_left, - "ACE_URL": settings.ACE_URL, - "default_lang": default_lang, - "problem_id": problem.id, - "output_only": problem.data_files.output_only - if hasattr(problem, "data_files") - else False, - "next_valid_submit_time": next_valid_submit_time, + submissions_left = submission_limit - get_contest_submission_count(problem, profile, + profile.current_contest.virtual) + return render(request, 'problem/submit.html', { + 'form': form, + 'title': _('Submit to %(problem)s') % { + 'problem': problem.translated_name(request.LANGUAGE_CODE), }, - ) + 'content_title': mark_safe(escape(_('Submit to %(problem)s')) % { + 'problem': format_html('{1}', + reverse('problem_detail', args=[problem.code]), + problem.translated_name(request.LANGUAGE_CODE)), + }), + 'langs': Language.objects.all(), + 'no_judges': not form.fields['language'].queryset, + 'submission_limit': submission_limit, + 'submissions_left': submissions_left, + 'ACE_URL': settings.ACE_URL, + 'default_lang': default_lang, + }) -class ProblemClone( - ProblemMixin, PermissionRequiredMixin, TitleMixin, SingleObjectFormView -): - title = _("Clone Problem") - template_name = "problem/clone.html" +class ProblemClone(ProblemMixin, PermissionRequiredMixin, TitleMixin, SingleObjectFormView): + title = _('Clone Problem') + template_name = 'problem/clone.html' form_class = ProblemCloneForm - permission_required = "judge.clone_problem" + permission_required = 'judge.clone_problem' def form_valid(self, form): - languages = self.object.allowed_languages.all() - language_limits = self.object.language_limits.all() - types = self.object.types.all() - - problem = deepcopy(self.object) + problem = self.object + languages = problem.allowed_languages.all() + language_limits = problem.language_limits.all() + types = problem.types.all() problem.pk = None problem.is_public = False problem.ac_rate = 0 problem.user_count = 0 - problem.code = form.cleaned_data["code"] - problem.save(should_move_data=False) + problem.code = form.cleaned_data['code'] + problem.save() problem.authors.add(self.request.profile) problem.allowed_languages.set(languages) problem.language_limits.set(language_limits) problem.types.set(types) - return HttpResponseRedirect( - reverse("admin:judge_problem_change", args=(problem.id,)) - ) + return HttpResponseRedirect(reverse('admin:judge_problem_change', args=(problem.id,))) \ No newline at end of file diff --git a/judge/views/problem_data.py b/judge/views/problem_data.py index 006a304..3908b8f 100644 --- a/judge/views/problem_data.py +++ b/judge/views/problem_data.py @@ -18,17 +18,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.core.files import File from django.core.exceptions import ValidationError -from django.forms import ( - BaseModelFormSet, - HiddenInput, - ModelForm, - NumberInput, - Select, - formset_factory, - FileInput, - TextInput, - CheckboxInput, -) +from django.forms import BaseModelFormSet, HiddenInput, ModelForm, NumberInput, Select, formset_factory, FileInput from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404, render from django.urls import reverse @@ -38,76 +28,46 @@ from django.utils.translation import gettext as _ from django.views.generic import DetailView from judge.highlight_code import highlight_code -from judge.models import ( - Problem, - ProblemData, - ProblemTestCase, - Submission, - problem_data_storage, -) +from judge.models import Problem, ProblemData, ProblemTestCase, Submission, problem_data_storage from judge.utils.problem_data import ProblemDataCompiler from judge.utils.unicode import utf8text from judge.utils.views import TitleMixin -from judge.utils.fine_uploader import ( - combine_chunks, - save_upload, - handle_upload, - FineUploadFileInput, - FineUploadForm, -) +from judge.utils.fine_uploader import combine_chunks, save_upload, handle_upload, FineUploadFileInput, FineUploadForm from judge.views.problem import ProblemMixin -from judge.logging import log_exception mimetypes.init() -mimetypes.add_type("application/x-yaml", ".yml") +mimetypes.add_type('application/x-yaml', '.yml') def checker_args_cleaner(self): - data = self.cleaned_data["checker_args"] + data = self.cleaned_data['checker_args'] if not data or data.isspace(): - return "" + return '' try: if not isinstance(json.loads(data), dict): - raise ValidationError(_("Checker arguments must be a JSON object")) + raise ValidationError(_('Checker arguments must be a JSON object')) except ValueError: - raise ValidationError(_("Checker arguments is invalid JSON")) + raise ValidationError(_('Checker arguments is invalid JSON')) return data class ProblemDataForm(ModelForm): def clean_zipfile(self): - if hasattr(self, "zip_valid") and not self.zip_valid: - raise ValidationError(_("Your zip file is invalid!")) - return self.cleaned_data["zipfile"] + if hasattr(self, 'zip_valid') and not self.zip_valid: + raise ValidationError(_('Your zip file is invalid!')) + return self.cleaned_data['zipfile'] clean_checker_args = checker_args_cleaner class Meta: model = ProblemData - fields = [ - "zipfile", - "checker", - "checker_args", - "custom_checker", - "custom_checker_cpp", - "interactive_judge", - "fileio_input", - "fileio_output", - "output_only", - "use_ioi_signature", - "signature_handler", - "signature_header", - ] + fields = ['zipfile', 'checker', 'checker_args', 'custom_checker', 'custom_validator'] widgets = { - "zipfile": FineUploadFileInput, - "checker_args": HiddenInput, - "generator": HiddenInput, - "output_limit": HiddenInput, - "output_prefix": HiddenInput, - "fileio_input": TextInput, - "fileio_output": TextInput, - "output_only": CheckboxInput, - "use_ioi_signature": CheckboxInput, + 'zipfile': FineUploadFileInput, + 'checker_args': HiddenInput, + 'generator': HiddenInput, + 'output_limit': HiddenInput, + 'output_prefix': HiddenInput, } @@ -116,35 +76,25 @@ class ProblemCaseForm(ModelForm): class Meta: model = ProblemTestCase - fields = ( - "order", - "type", - "input_file", - "output_file", - "points", - "is_pretest", - "checker", - "checker_args", - ) # , 'output_limit', 'output_prefix', 'generator_args') + fields = ('order', 'type', 'input_file', 'output_file', 'points', + 'is_pretest', 'checker', 'checker_args') #, 'output_limit', 'output_prefix', 'generator_args') widgets = { # 'generator_args': HiddenInput, - "type": Select(attrs={"style": "width: 100%"}), - "points": NumberInput(attrs={"style": "width: 4em"}), + 'type': Select(attrs={'style': 'width: 100%'}), + 'points': NumberInput(attrs={'style': 'width: 4em'}), # 'output_prefix': NumberInput(attrs={'style': 'width: 4.5em'}), # 'output_limit': NumberInput(attrs={'style': 'width: 6em'}), # 'checker_args': HiddenInput, } -class ProblemCaseFormSet( - formset_factory( - ProblemCaseForm, formset=BaseModelFormSet, extra=1, max_num=1, can_delete=True - ) -): + +class ProblemCaseFormSet(formset_factory(ProblemCaseForm, formset=BaseModelFormSet, extra=1, max_num=1, + can_delete=True)): model = ProblemTestCase def __init__(self, *args, **kwargs): - self.valid_files = kwargs.pop("valid_files", None) + self.valid_files = kwargs.pop('valid_files', None) super(ProblemCaseFormSet, self).__init__(*args, **kwargs) def _construct_form(self, i, **kwargs): @@ -164,17 +114,14 @@ class ProblemManagerMixin(LoginRequiredMixin, ProblemMixin, DetailView): class ProblemSubmissionDiff(TitleMixin, ProblemMixin, DetailView): - template_name = "problem/submission-diff.html" + template_name = 'problem/submission-diff.html' def get_title(self): - return _("Comparing submissions for {0}").format(self.object.name) + return _('Comparing submissions for {0}').format(self.object.name) def get_content_title(self): - return format_html( - _('Comparing submissions for {0}'), - self.object.name, - reverse("problem_detail", args=[self.object.code]), - ) + return format_html(_('Comparing submissions for {0}'), self.object.name, + reverse('problem_detail', args=[self.object.code])) def get_object(self, queryset=None): problem = super(ProblemSubmissionDiff, self).get_object(queryset) @@ -185,90 +132,67 @@ class ProblemSubmissionDiff(TitleMixin, ProblemMixin, DetailView): def get_context_data(self, **kwargs): context = super(ProblemSubmissionDiff, self).get_context_data(**kwargs) try: - ids = self.request.GET.getlist("id") + ids = self.request.GET.getlist('id') subs = Submission.objects.filter(id__in=ids) except ValueError: raise Http404 if not subs: raise Http404 - context["submissions"] = subs + context['submissions'] = subs # If we have associated data we can do better than just guess - data = ProblemTestCase.objects.filter(dataset=self.object, type="C") + data = ProblemTestCase.objects.filter(dataset=self.object, type='C') if data: num_cases = data.count() else: num_cases = subs.first().test_cases.count() - context["num_cases"] = num_cases + context['num_cases'] = num_cases return context class ProblemDataView(TitleMixin, ProblemManagerMixin): - template_name = "problem/data.html" + template_name = 'problem/data.html' def get_title(self): - return _("Editing data for {0}").format(self.object.name) + return _('Editing data for {0}').format(self.object.name) def get_content_title(self): - return mark_safe( - escape(_("Editing data for %s")) - % ( - format_html( - '{0}', - self.object.name, - reverse("problem_detail", args=[self.object.code]), - ) - ) - ) + return mark_safe(escape(_('Editing data for %s')) % ( + format_html('{0}', self.object.name, + reverse('problem_detail', args=[self.object.code])))) def get_data_form(self, post=False): - return ProblemDataForm( - data=self.request.POST if post else None, - prefix="problem-data", - files=self.request.FILES if post else None, - instance=ProblemData.objects.get_or_create(problem=self.object)[0], - ) + return ProblemDataForm(data=self.request.POST if post else None, prefix='problem-data', + files=self.request.FILES if post else None, + instance=ProblemData.objects.get_or_create(problem=self.object)[0]) def get_case_formset(self, files, post=False): - return ProblemCaseFormSet( - data=self.request.POST if post else None, - prefix="cases", - valid_files=files, - queryset=ProblemTestCase.objects.filter(dataset_id=self.object.pk).order_by( - "order" - ), - ) + return ProblemCaseFormSet(data=self.request.POST if post else None, prefix='cases', valid_files=files, + queryset=ProblemTestCase.objects.filter(dataset_id=self.object.pk).order_by('order')) def get_valid_files(self, data, post=False): try: - if post and "problem-data-zipfile-clear" in self.request.POST: + if post and 'problem-data-zipfile-clear' in self.request.POST: return [] - elif post and "problem-data-zipfile" in self.request.FILES: - return ZipFile(self.request.FILES["problem-data-zipfile"]).namelist() + elif post and 'problem-data-zipfile' in self.request.FILES: + return ZipFile(self.request.FILES['problem-data-zipfile']).namelist() elif data.zipfile: return ZipFile(data.zipfile.path).namelist() except BadZipfile: return [] - except FileNotFoundError as e: - log_exception(e) - return [] return [] def get_context_data(self, **kwargs): context = super(ProblemDataView, self).get_context_data(**kwargs) - if "data_form" not in context: - context["data_form"] = self.get_data_form() - valid_files = context["valid_files"] = self.get_valid_files( - context["data_form"].instance - ) - context["data_form"].zip_valid = valid_files is not False - context["cases_formset"] = self.get_case_formset(valid_files) - context["valid_files_json"] = mark_safe(json.dumps(context["valid_files"])) - context["valid_files"] = set(context["valid_files"]) - context["all_case_forms"] = chain( - context["cases_formset"], [context["cases_formset"].empty_form] - ) + if 'data_form' not in context: + context['data_form'] = self.get_data_form() + valid_files = context['valid_files'] = self.get_valid_files(context['data_form'].instance) + context['data_form'].zip_valid = valid_files is not False + context['cases_formset'] = self.get_case_formset(valid_files) + context['valid_files_json'] = mark_safe(json.dumps(context['valid_files'])) + context['valid_files'] = set(context['valid_files']) + context['all_case_forms'] = chain(context['cases_formset'], [context['cases_formset'].empty_form]) return context def post(self, request, *args, **kwargs): @@ -284,17 +208,10 @@ class ProblemDataView(TitleMixin, ProblemManagerMixin): case.save() for case in cases_formset.deleted_objects: case.delete() - ProblemDataCompiler.generate( - problem, data, problem.cases.order_by("order"), valid_files - ) + ProblemDataCompiler.generate(problem, data, problem.cases.order_by('order'), valid_files) return HttpResponseRedirect(request.get_full_path()) - return self.render_to_response( - self.get_context_data( - data_form=data_form, - cases_formset=cases_formset, - valid_files=valid_files, - ) - ) + return self.render_to_response(self.get_context_data(data_form=data_form, cases_formset=cases_formset, + valid_files=valid_files)) put = post @@ -306,22 +223,16 @@ def problem_data_file(request, problem, path): raise Http404() response = HttpResponse() - if hasattr(settings, "DMOJ_PROBLEM_DATA_INTERNAL") and request.META.get( - "SERVER_SOFTWARE", "" - ).startswith("nginx/"): - response["X-Accel-Redirect"] = "%s/%s/%s" % ( - settings.DMOJ_PROBLEM_DATA_INTERNAL, - problem, - path, - ) + if hasattr(settings, 'DMOJ_PROBLEM_DATA_INTERNAL') and request.META.get('SERVER_SOFTWARE', '').startswith('nginx/'): + response['X-Accel-Redirect'] = '%s/%s/%s' % (settings.DMOJ_PROBLEM_DATA_INTERNAL, problem, path) else: try: - with problem_data_storage.open(os.path.join(problem, path), "rb") as f: + with problem_data_storage.open(os.path.join(problem, path), 'rb') as f: response.content = f.read() except IOError: raise Http404() - response["Content-Type"] = "application/octet-stream" + response['Content-Type'] = 'application/octet-stream' return response @@ -332,53 +243,39 @@ def problem_init_view(request, problem): raise Http404() try: - with problem_data_storage.open( - os.path.join(problem.code, "init.yml"), "rb" - ) as f: - data = utf8text(f.read()).rstrip("\n") + with problem_data_storage.open(os.path.join(problem.code, 'init.yml'), 'rb') as f: + data = utf8text(f.read()).rstrip('\n') except IOError: raise Http404() - return render( - request, - "problem/yaml.html", - { - "raw_source": data, - "highlighted_source": highlight_code(data, "yaml", linenos=True), - "title": _("Generated init.yml for %s") % problem.name, - "content_title": mark_safe( - escape(_("Generated init.yml for %s")) - % ( - format_html( - '{0}', - problem.name, - reverse("problem_detail", args=[problem.code]), - ) - ) - ), - }, - ) + return render(request, 'problem/yaml.html', { + 'raw_source': data, 'highlighted_source': highlight_code(data, 'yaml', linenos=False), + 'title': _('Generated init.yml for %s') % problem.name, + 'content_title': mark_safe(escape(_('Generated init.yml for %s')) % ( + format_html('{0}', problem.name, + reverse('problem_detail', args=[problem.code])))), + }) class ProblemZipUploadView(ProblemManagerMixin, View): def dispatch(self, *args, **kwargs): return super(ProblemZipUploadView, self).dispatch(*args, **kwargs) - + def post(self, request, *args, **kwargs): self.object = problem = self.get_object() problem_data = get_object_or_404(ProblemData, problem=self.object) form = FineUploadForm(request.POST, request.FILES) - + if form.is_valid(): - fileuid = form.cleaned_data["qquuid"] - filename = form.cleaned_data["qqfilename"] + fileuid = form.cleaned_data['qquuid'] + filename = form.cleaned_data['qqfilename'] dest = os.path.join(gettempdir(), fileuid) def post_upload(): zip_dest = os.path.join(dest, filename) try: - ZipFile(zip_dest).namelist() # check if this file is valid - with open(zip_dest, "rb") as f: + ZipFile(zip_dest).namelist() # check if this file is valid + with open(zip_dest, 'rb') as f: problem_data.zipfile.delete() problem_data.zipfile.save(filename, File(f)) f.close() @@ -386,16 +283,11 @@ class ProblemZipUploadView(ProblemManagerMixin, View): raise Exception(e) finally: shutil.rmtree(dest) - + try: - handle_upload( - request.FILES["qqfile"], - form.cleaned_data, - dest, - post_upload=post_upload, - ) + handle_upload(request.FILES['qqfile'], form.cleaned_data, dest, post_upload=post_upload) except Exception as e: - return JsonResponse({"success": False, "error": str(e)}) - return JsonResponse({"success": True}) + return JsonResponse({'success': False, 'error': str(e)}) + return JsonResponse({'success': True}) else: - return HttpResponse(status_code=400) + return HttpResponse(status_code=400) \ No newline at end of file diff --git a/judge/views/problem_manage.py b/judge/views/problem_manage.py index dfce739..07e94ec 100644 --- a/judge/views/problem_manage.py +++ b/judge/views/problem_manage.py @@ -5,12 +5,7 @@ import zipfile, tempfile from celery.result import AsyncResult from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin -from django.http import ( - Http404, - HttpResponse, - HttpResponseBadRequest, - HttpResponseRedirect, -) +from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect from django.urls import reverse from django.utils.html import escape, format_html from django.utils.safestring import mark_safe @@ -51,53 +46,33 @@ class ManageProblemSubmissionActionMixin(ManageProblemSubmissionMixin): class ManageProblemSubmissionView(TitleMixin, ManageProblemSubmissionMixin, DetailView): - template_name = "problem/manage_submission.html" + template_name = 'problem/manage_submission.html' def get_title(self): - return _("Managing submissions for %s") % (self.object.name,) + return _('Managing submissions for %s') % (self.object.name,) def get_content_title(self): - return mark_safe( - escape(_("Managing submissions for %s")) - % ( - format_html( - '{0}', - self.object.name, - reverse("problem_detail", args=[self.object.code]), - ) - ) - ) + return mark_safe(escape(_('Managing submissions for %s')) % ( + format_html('{0}', self.object.name, + reverse('problem_detail', args=[self.object.code])))) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["submission_count"] = self.object.submission_set.count() - context["languages"] = [ - (lang_id, short_name or key) - for lang_id, key, short_name in Language.objects.values_list( - "id", "key", "short_name" - ) - ] - context["results"] = sorted(map(itemgetter(0), Submission.RESULT)) - context["current_contest"] = None - if ( - self.request.in_contest_mode - and self.object in self.request.participation.contest.problems.all() - ): - context["current_contest"] = self.request.participation.contest - + context['submission_count'] = self.object.submission_set.count() + context['languages'] = [(lang_id, short_name or key) for lang_id, key, short_name in + Language.objects.values_list('id', 'key', 'short_name')] + context['results'] = sorted(map(itemgetter(0), Submission.RESULT)) return context -class BaseActionSubmissionsView( - PermissionRequiredMixin, ManageProblemSubmissionActionMixin, BaseDetailView -): - permission_required = "judge.rejudge_submission_lot" +class BaseActionSubmissionsView(PermissionRequiredMixin, ManageProblemSubmissionActionMixin, BaseDetailView): + permission_required = 'judge.rejudge_submission_lot' def perform_action(self): - if self.request.POST.get("use_range", "off") == "on": + if self.request.POST.get('use_range', 'off') == 'on': try: - start = int(self.request.POST.get("start")) - end = int(self.request.POST.get("end")) + start = int(self.request.POST.get('start')) + end = int(self.request.POST.get('end')) except (KeyError, ValueError): return HttpResponseBadRequest() id_range = (start, end) @@ -105,77 +80,57 @@ class BaseActionSubmissionsView( id_range = None try: - languages = list(map(int, self.request.POST.getlist("language"))) - results = list(map(str, self.request.POST.getlist("result"))) - contests = list(map(int, self.request.POST.getlist("contest"))) + languages = list(map(int, self.request.POST.getlist('language'))) except ValueError: return HttpResponseBadRequest() - return self.generate_response(id_range, languages, results, contests) + return self.generate_response(id_range, languages, self.request.POST.getlist('result')) - def generate_response(self, id_range, languages, results, contest): + def generate_response(self, id_range, languages, results): raise NotImplementedError() class ActionSubmissionsView(BaseActionSubmissionsView): - def rejudge_response(self, id_range, languages, results, contest): - status = rejudge_problem_filter.delay( - self.object.id, id_range, languages, results, contest - ) + def rejudge_response(self, id_range, languages, results): + status = rejudge_problem_filter.delay(self.object.id, id_range, languages, results) return redirect_to_task_status( - status, - message=_("Rejudging selected submissions for %s...") % (self.object.name,), - redirect=reverse( - "problem_submissions_rejudge_success", - args=[self.object.code, status.id], - ), + status, message=_('Rejudging selected submissions for %s...') % (self.object.name,), + redirect=reverse('problem_submissions_rejudge_success', args=[self.object.code, status.id]), ) - def download_response(self, id_range, languages, results, contest): + def download_response(self, id_range, languages, results): + if not self.request.user.is_superuser: + raise Http404 + queryset = Submission.objects.filter(problem_id=self.object.id) - submissions = apply_submission_filter( - queryset, id_range, languages, results, contest - ) + submissions = apply_submission_filter(queryset, id_range, languages, results) with tempfile.SpooledTemporaryFile() as tmp: - with zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as archive: + with zipfile.ZipFile(tmp, 'w', zipfile.ZIP_DEFLATED) as archive: for submission in submissions: - file_name = ( - str(submission.id) - + "_" - + str(submission.user) - + "." - + str(submission.language.key) - ) + file_name = str(submission.id) + '_' + str(submission.user) + '.' + str(submission.language.key) archive.writestr(file_name, submission.source.source) - + # Reset file pointer tmp.seek(0) # Write file data to response - response = HttpResponse( - tmp.read(), content_type="application/x-zip-compressed" - ) - response["Content-Disposition"] = 'attachment; filename="%s"' % ( - str(self.object.code) + "_submissions.zip" - ) + response = HttpResponse(tmp.read(), content_type='application/x-zip-compressed') + response['Content-Disposition'] = 'attachment; filename="%s"' % (str(self.object.code) + '_submissions.zip') return response - def generate_response(self, id_range, languages, results, contest): - action = self.request.POST.get("action") - if action == "rejudge": - return self.rejudge_response(id_range, languages, results, contest) - elif action == "download": - return self.download_response(id_range, languages, results, contest) + def generate_response(self, id_range, languages, results): + action = self.request.POST.get('action') + if (action == 'rejudge'): + return self.rejudge_response(id_range, languages, results) + elif (action == 'download'): + return self.download_response(id_range, languages, results) else: return Http404() - class PreviewActionSubmissionsView(BaseActionSubmissionsView): - def generate_response(self, id_range, languages, results, contest): - queryset = apply_submission_filter( - self.object.submission_set.all(), id_range, languages, results, contest - ) + def generate_response(self, id_range, languages, results): + queryset = apply_submission_filter(self.object.submission_set.all(), id_range, languages, results) return HttpResponse(str(queryset.count())) @@ -183,12 +138,8 @@ class RescoreAllSubmissionsView(ManageProblemSubmissionActionMixin, BaseDetailVi def perform_action(self): status = rescore_problem.delay(self.object.id) return redirect_to_task_status( - status, - message=_("Rescoring all submissions for %s...") % (self.object.name,), - redirect=reverse( - "problem_submissions_rescore_success", - args=[self.object.code, status.id], - ), + status, message=_('Rescoring all submissions for %s...') % (self.object.name,), + redirect=reverse('problem_submissions_rescore_success', args=[self.object.code, status.id]), ) @@ -196,29 +147,15 @@ def rejudge_success(request, problem, task_id): count = AsyncResult(task_id).result if not isinstance(count, int): raise Http404() - messages.success( - request, - ngettext( - "Successfully scheduled %d submission for rejudging.", - "Successfully scheduled %d submissions for rejudging.", - count, - ) - % (count,), - ) - return HttpResponseRedirect(reverse("problem_manage_submissions", args=[problem])) + messages.success(request, ngettext('Successfully scheduled %d submission for rejudging.', + 'Successfully scheduled %d submissions for rejudging.', count) % (count,)) + return HttpResponseRedirect(reverse('problem_manage_submissions', args=[problem])) def rescore_success(request, problem, task_id): count = AsyncResult(task_id).result if not isinstance(count, int): raise Http404() - messages.success( - request, - ngettext( - "%d submission were successfully rescored.", - "%d submissions were successfully rescored.", - count, - ) - % (count,), - ) - return HttpResponseRedirect(reverse("problem_manage_submissions", args=[problem])) + messages.success(request, ngettext('%d submission were successfully rescored.', + '%d submissions were successfully rescored.', count) % (count,)) + return HttpResponseRedirect(reverse('problem_manage_submissions', args=[problem])) diff --git a/judge/views/ranked_submission.py b/judge/views/ranked_submission.py index fcf13f2..f368d41 100644 --- a/judge/views/ranked_submission.py +++ b/judge/views/ranked_submission.py @@ -7,31 +7,27 @@ from judge.utils.problems import get_result_data from judge.utils.raw_sql import join_sql_subquery from judge.views.submission import ForceContestMixin, ProblemSubmissions -__all__ = ["RankedSubmissions"] +__all__ = ['RankedSubmissions', 'ContestRankedSubmission'] class RankedSubmissions(ProblemSubmissions): - tab = "best_submissions_list" - page_type = "best_submissions_list" + tab = 'best_submissions_list' dynamic_update = False def get_queryset(self): if self.in_contest: - contest_join = "INNER JOIN judge_contestsubmission AS cs ON (sub.id = cs.submission_id)" - points = "cs.points" - constraint = " AND sub.contest_object_id = %s" + contest_join = '''INNER JOIN judge_contestsubmission AS cs ON (sub.id = cs.submission_id) + INNER JOIN judge_contestparticipation AS cp ON (cs.participation_id = cp.id)''' + points = 'cs.points' + constraint = 'AND cp.contest_id = %s' else: - contest_join = "" - points = "sub.points" - constraint = "" - queryset = ( - super(RankedSubmissions, self) - .get_queryset() - .filter(user__is_unlisted=False) - ) + contest_join = '' + points = 'sub.points' + constraint = '' + queryset = super(RankedSubmissions, self).get_queryset().filter(user__is_unlisted=False) join_sql_subquery( queryset, - subquery=""" + subquery=''' SELECT sub.id AS id FROM ( SELECT sub.user_id AS uid, MAX(sub.points) AS points @@ -45,34 +41,49 @@ class RankedSubmissions(ProblemSubmissions): GROUP BY sub.user_id, {points} ) AS fastest ON (highscore.uid = fastest.uid AND highscore.points = fastest.points) STRAIGHT_JOIN judge_submission AS sub - ON (sub.user_id = fastest.uid AND sub.time = fastest.time) - WHERE sub.problem_id = %s {constraint} + ON (sub.user_id = fastest.uid AND sub.time = fastest.time) {contest_join} + WHERE sub.problem_id = %s AND {points} > 0 {constraint} GROUP BY sub.user_id - """.format( - points=points, contest_join=contest_join, constraint=constraint - ), - params=[self.problem.id, self.contest.id] * 3 - if self.in_contest - else [self.problem.id] * 3, - alias="best_subs", - join_fields=[("id", "id")], - related_model=Submission, + '''.format(points=points, contest_join=contest_join, constraint=constraint), + params=[self.problem.id, self.contest.id] * 3 if self.in_contest else [self.problem.id] * 3, + alias='best_subs', join_fields=[('id', 'id')], ) if self.in_contest: - return queryset.order_by("-contest__points", "time") + return queryset.order_by('-contest__points', 'time') else: - return queryset.order_by("-points", "time") + return queryset.order_by('-points', 'time') def get_title(self): - return _("Best solutions for %s") % self.problem_name + return _('Best solutions for %s') % self.problem_name def get_content_title(self): - return format_html( - _('Best solutions for {0}'), - self.problem_name, - reverse("problem_detail", args=[self.problem.code]), - ) + return format_html(_('Best solutions for {0}'), self.problem_name, + reverse('problem_detail', args=[self.problem.code])) def _get_result_data(self): return get_result_data(super(RankedSubmissions, self).get_queryset().order_by()) + + +class ContestRankedSubmission(ForceContestMixin, RankedSubmissions): + def get_title(self): + if self.problem.is_accessible_by(self.request.user): + return _('Best solutions for %(problem)s in %(contest)s') % { + 'problem': self.problem_name, 'contest': self.contest.name, + } + return _('Best solutions for problem %(number)s in %(contest)s') % { + 'number': self.get_problem_number(self.problem), 'contest': self.contest.name, + } + + def get_content_title(self): + if self.problem.is_accessible_by(self.request.user): + return format_html(_('Best solutions for {0} in {2}'), + self.problem_name, reverse('problem_detail', args=[self.problem.code]), + self.contest.name, reverse('contest_view', args=[self.contest.key])) + return format_html(_('Best solutions for problem {0} in {1}'), + self.get_problem_number(self.problem), self.contest.name, + reverse('contest_view', args=[self.contest.key])) + + def _get_result_data(self): + return get_result_data(Submission.objects.filter( + problem_id=self.problem.id, contest__participation__contest_id=self.contest.id)) diff --git a/judge/views/register.py b/judge/views/register.py index 58d1d6f..e7ba8c9 100644 --- a/judge/views/register.py +++ b/judge/views/register.py @@ -8,121 +8,111 @@ from django.contrib.auth.password_validation import get_default_password_validat from django.forms import ChoiceField, ModelChoiceField from django.shortcuts import render from django.utils.translation import gettext, gettext_lazy as _ -from registration.backends.default.views import ( - ActivationView as OldActivationView, - RegistrationView as OldRegistrationView, -) +from registration.backends.default.views import (ActivationView as OldActivationView, + RegistrationView as OldRegistrationView) from registration.forms import RegistrationForm from sortedm2m.forms import SortedMultipleChoiceField -from judge.models import Language, Profile, TIMEZONE +from judge.models import Language, Organization, Profile, TIMEZONE from judge.utils.recaptcha import ReCaptchaField, ReCaptchaWidget +from judge.utils.subscription import Subscription, newsletter_id from judge.widgets import Select2MultipleWidget, Select2Widget -valid_id = re.compile(r"^\w+$") +valid_id = re.compile(r'^\w+$') bad_mail_regex = list(map(re.compile, settings.BAD_MAIL_PROVIDER_REGEX)) class CustomRegistrationForm(RegistrationForm): - username = forms.RegexField( - regex=r"^\w+$", - max_length=30, - label=_("Username"), - error_messages={ - "invalid": _("A username must contain letters, " "numbers, or underscores") - }, - ) - timezone = ChoiceField( - label=_("Timezone"), - choices=TIMEZONE, - widget=Select2Widget(attrs={"style": "width:100%"}), - ) - language = ModelChoiceField( - queryset=Language.objects.all(), - label=_("Preferred language"), - empty_label=None, - widget=Select2Widget(attrs={"style": "width:100%"}), - ) + username = forms.RegexField(regex=r'^\w+$', max_length=30, label=_('Username'), + error_messages={'invalid': _('A username must contain letters, ' + 'numbers, or underscores')}) + timezone = ChoiceField(label=_('Timezone'), choices=TIMEZONE, + widget=Select2Widget(attrs={'style': 'width:100%'})) + language = ModelChoiceField(queryset=Language.objects.all(), label=_('Preferred language'), empty_label=None, + widget=Select2Widget(attrs={'style': 'width:100%'})) + organizations = SortedMultipleChoiceField(queryset=Organization.objects.filter(is_open=True), + label=_('Organizations'), required=False, + widget=Select2MultipleWidget(attrs={'style': 'width:100%'})) + + if newsletter_id is not None: + newsletter = forms.BooleanField(label=_('Subscribe to newsletter?'), initial=True, required=False) if ReCaptchaField is not None: captcha = ReCaptchaField(widget=ReCaptchaWidget()) - def clean_email(self): - if User.objects.filter(email=self.cleaned_data["email"]).exists(): + def clean_organizations(self): + organizations = self.cleaned_data.get('organizations') or [] + max_orgs = settings.DMOJ_USER_MAX_ORGANIZATION_COUNT + + if sum(org.is_open for org in organizations) > max_orgs: raise forms.ValidationError( - gettext( - 'The email address "%s" is already taken. Only one registration ' - "is allowed per address." - ) - % self.cleaned_data["email"] - ) - if "@" in self.cleaned_data["email"]: - domain = self.cleaned_data["email"].split("@")[-1].lower() - if domain in settings.BAD_MAIL_PROVIDERS or any( - regex.match(domain) for regex in bad_mail_regex - ): - raise forms.ValidationError( - gettext( - "Your email provider is not allowed due to history of abuse. " - "Please use a reputable email provider." - ) - ) - return self.cleaned_data["email"] + _('You may not be part of more than {count} public organizations.').format(count=max_orgs)) + + return self.cleaned_data['organizations'] + + def clean_email(self): + if User.objects.filter(email=self.cleaned_data['email']).exists(): + raise forms.ValidationError(gettext('The email address "%s" is already taken. Only one registration ' + 'is allowed per address.') % self.cleaned_data['email']) + if '@' in self.cleaned_data['email']: + domain = self.cleaned_data['email'].split('@')[-1].lower() + if (domain in settings.BAD_MAIL_PROVIDERS or + any(regex.match(domain) for regex in bad_mail_regex)): + raise forms.ValidationError(gettext('Your email provider is not allowed due to history of abuse. ' + 'Please use a reputable email provider.')) + return self.cleaned_data['email'] class RegistrationView(OldRegistrationView): - title = _("Registration") + title = _('Registration') form_class = CustomRegistrationForm - template_name = "registration/registration_form.html" + template_name = 'registration/registration_form.html' def get_context_data(self, **kwargs): - if "title" not in kwargs: - kwargs["title"] = self.title + if 'title' not in kwargs: + kwargs['title'] = self.title tzmap = settings.TIMEZONE_MAP - kwargs["TIMEZONE_MAP"] = tzmap or "http://momentjs.com/static/img/world.png" - kwargs["TIMEZONE_BG"] = settings.TIMEZONE_BG if tzmap else "#4E7CAD" - kwargs["password_validators"] = get_default_password_validators() - kwargs["tos_url"] = settings.TERMS_OF_SERVICE_URL + kwargs['TIMEZONE_MAP'] = tzmap or 'http://momentjs.com/static/img/world.png' + kwargs['TIMEZONE_BG'] = settings.TIMEZONE_BG if tzmap else '#4E7CAD' + kwargs['password_validators'] = get_default_password_validators() + kwargs['tos_url'] = settings.TERMS_OF_SERVICE_URL return super(RegistrationView, self).get_context_data(**kwargs) def register(self, form): user = super(RegistrationView, self).register(form) - profile, _ = Profile.objects.get_or_create( - user=user, - defaults={ - "language": Language.get_default_language(), - }, - ) + profile, _ = Profile.objects.get_or_create(user=user, defaults={ + 'language': Language.get_default_language(), + }) cleaned_data = form.cleaned_data - profile.timezone = cleaned_data["timezone"] - profile.language = cleaned_data["language"] + profile.timezone = cleaned_data['timezone'] + profile.language = cleaned_data['language'] + profile.organizations.add(*cleaned_data['organizations']) profile.save() + + if newsletter_id is not None and cleaned_data['newsletter']: + Subscription(user=user, newsletter_id=newsletter_id, subscribed=True).save() return user def get_initial(self, *args, **kwargs): initial = super(RegistrationView, self).get_initial(*args, **kwargs) - initial["timezone"] = settings.DEFAULT_USER_TIME_ZONE - initial["language"] = Language.objects.get(key=settings.DEFAULT_USER_LANGUAGE) + initial['timezone'] = settings.DEFAULT_USER_TIME_ZONE + initial['language'] = Language.objects.get(key=settings.DEFAULT_USER_LANGUAGE) return initial class ActivationView(OldActivationView): - title = _("Registration") - template_name = "registration/activate.html" + title = _('Registration') + template_name = 'registration/activate.html' def get_context_data(self, **kwargs): - if "title" not in kwargs: - kwargs["title"] = self.title + if 'title' not in kwargs: + kwargs['title'] = self.title return super(ActivationView, self).get_context_data(**kwargs) def social_auth_error(request): - return render( - request, - "generic-message.html", - { - "title": gettext("Authentication failure"), - "message": request.GET.get("message"), - }, - ) + return render(request, 'generic-message.html', { + 'title': gettext('Authentication failure'), + 'message': request.GET.get('message'), + }) diff --git a/judge/views/resolver.py b/judge/views/resolver.py deleted file mode 100644 index 84e5dff..0000000 --- a/judge/views/resolver.py +++ /dev/null @@ -1,155 +0,0 @@ -from django.views.generic import TemplateView -from django.utils.translation import gettext as _ -from django.http import HttpResponseForbidden, JsonResponse -from judge.models import Contest -from django.utils.safestring import mark_safe - -import json - - -class Resolver(TemplateView): - title = _("Resolver") - template_name = "resolver/resolver.html" - - def get_contest_json(self): - problems = self.contest.contest_problems.values_list("order", "id") - order_to_id = {} - id_to_order = {} - for order, problem_id in problems: - id_to_order[str(problem_id)] = order - - hidden_subtasks = self.contest.format.get_hidden_subtasks() - num_problems = len(problems) - problem_sub = [0] * num_problems - sub_frozen = [[] for _ in range(num_problems)] - problems_json = {str(i): {} for i in range(1, num_problems + 1)} - - users = {} - cnt_user = 0 - total_subtask_points_map = {} - - for participation in self.contest.users.filter(virtual=0): - cnt_user += 1 - users[str(cnt_user)] = { - "username": participation.user.username, - "name": participation.user.first_name or participation.user.username, - "school": participation.user.last_name, - "last_submission": participation.cumtime_final, - "problems": {}, - } - for ( - problem_id, - problem_points, - time, - subtask_points, - total_subtask_points, - subtask, - sub_id, - ) in self.contest.format.get_results_by_subtask(participation, True): - subtask = subtask or 1 - problem_id = str(problem_id) - order = id_to_order[problem_id] - problem_sub[order - 1] = max(problem_sub[order - 1], subtask) - if total_subtask_points: - total_subtask_points_map[(order, subtask)] = total_subtask_points - - cnt_user = 0 - for participation in self.contest.users.filter(virtual=0): - cnt_user += 1 - total_points = {} - points_map = {} - frozen_points_map = {} - problem_points_map = {} - for ( - problem_id, - problem_points, - time, - subtask_points, - total_subtask_points, - subtask, - sub_id, - ) in self.contest.format.get_results_by_subtask(participation, True): - subtask = subtask or 1 - problem_id = str(problem_id) - order = id_to_order[problem_id] - points_map[(order, subtask)] = subtask_points - if order not in total_points: - total_points[order] = 0 - total_points[order] += total_subtask_points - problem_points_map[order] = problem_points - - for ( - problem_id, - problem_points, - time, - subtask_points, - total_subtask_points, - subtask, - sub_id, - ) in self.contest.format.get_results_by_subtask(participation, False): - subtask = subtask or 1 - problem_id = str(problem_id) - order = id_to_order[problem_id] - frozen_points_map[(order, subtask)] = subtask_points - - for order in range(1, num_problems + 1): - for subtask in range(1, problem_sub[order - 1] + 1): - if not total_points.get(order, 0): - continue - if str(order) not in users[str(cnt_user)]["problems"]: - users[str(cnt_user)]["problems"][str(order)] = { - "points": {}, - "frozen_points": {}, - } - problems_json[str(order)][str(subtask)] = round( - total_subtask_points_map[(order, subtask)] - / total_points[order] - * problem_points_map[order], - self.contest.points_precision, - ) - users[str(cnt_user)]["problems"][str(order)]["points"][ - str(subtask) - ] = round( - points_map.get((order, subtask), 0) - / total_points[order] - * problem_points_map[order], - self.contest.points_precision, - ) - users[str(cnt_user)]["problems"][str(order)]["frozen_points"][ - str(subtask) - ] = round( - frozen_points_map.get((order, subtask), 0) - / total_points[order] - * problem_points_map[order], - self.contest.points_precision, - ) - - for i in hidden_subtasks: - order = id_to_order[i] - sub_frozen[order - 1] = list(hidden_subtasks[i]) - - return { - "problem_sub": problem_sub, - "sub_frozen": sub_frozen, - "problems": problems_json, - "users": users, - } - - def get_context_data(self, **kwargs): - context = super(Resolver, self).get_context_data(**kwargs) - context["contest_json"] = mark_safe(json.dumps(self.get_contest_json())) - return context - - def get(self, request, *args, **kwargs): - if not request.user.is_superuser: - return HttpResponseForbidden() - self.contest = Contest.objects.get(key=kwargs.get("contest")) - if not self.contest.format.has_hidden_subtasks: - return HttpResponseForbidden() - - if self.request.GET.get("json"): - json_dumps_params = {"ensure_ascii": False} - return JsonResponse( - self.get_contest_json(), json_dumps_params=json_dumps_params - ) - return super(Resolver, self).get(request, *args, **kwargs) diff --git a/judge/views/select2.py b/judge/views/select2.py index 62a850c..1a0bece 100644 --- a/judge/views/select2.py +++ b/judge/views/select2.py @@ -1,11 +1,8 @@ -from urllib.parse import urljoin - from django.db.models import F, Q from django.http import Http404, JsonResponse from django.shortcuts import get_object_or_404 from django.utils.encoding import smart_text from django.views.generic.list import BaseListView -from django.conf import settings from chat_box.utils import encrypt_url @@ -13,15 +10,9 @@ from judge.jinja2.gravatar import gravatar from judge.models import Comment, Contest, Organization, Problem, Profile -def _get_user_queryset(term, org_id=None): - if org_id: - try: - qs = Organization.objects.get(id=org_id).members.all() - except Exception: - raise Http404() - else: - qs = Profile.objects - if term.endswith(" "): +def _get_user_queryset(term): + qs = Profile.objects + if term.endswith(' '): qs = qs.filter(user__username=term.strip()) else: qs = qs.filter(user__username__icontains=term) @@ -33,38 +24,26 @@ class Select2View(BaseListView): def get(self, request, *args, **kwargs): self.request = request - self.term = kwargs.get("term", request.GET.get("term", "")) + self.term = kwargs.get('term', request.GET.get('term', '')) self.object_list = self.get_queryset() context = self.get_context_data() - return JsonResponse( - { - "results": [ - { - "text": smart_text(self.get_name(obj)), - "id": obj.pk, - } - for obj in context["object_list"] - ], - "more": context["page_obj"].has_next(), - } - ) + return JsonResponse({ + 'results': [ + { + 'text': smart_text(self.get_name(obj)), + 'id': obj.pk, + } for obj in context['object_list']], + 'more': context['page_obj'].has_next(), + }) def get_name(self, obj): return str(obj) class UserSelect2View(Select2View): - def get(self, request, *args, **kwargs): - self.org_id = kwargs.get("org_id", request.GET.get("org_id", "")) - return super(UserSelect2View, self).get(request, *args, **kwargs) - def get_queryset(self): - return ( - _get_user_queryset(self.term, self.org_id) - .annotate(username=F("user__username")) - .only("id") - ) + return _get_user_queryset(self.term).annotate(username=F('user__username')).only('id') def get_name(self, obj): return obj.username @@ -77,25 +56,19 @@ class OrganizationSelect2View(Select2View): class ProblemSelect2View(Select2View): def get_queryset(self): - return ( - Problem.get_visible_problems(self.request.user) - .filter(Q(code__icontains=self.term) | Q(name__icontains=self.term)) - .distinct() - ) + return Problem.get_visible_problems(self.request.user) \ + .filter(Q(code__icontains=self.term) | Q(name__icontains=self.term)).distinct() class ContestSelect2View(Select2View): - def get(self, request, *args, **kwargs): - self.problem_id = kwargs.get("problem_id", request.GET.get("problem_id", "")) - return super(ContestSelect2View, self).get(request, *args, **kwargs) - def get_queryset(self): - q = Contest.get_visible_contests(self.request.user).filter( - Q(key__icontains=self.term) | Q(name__icontains=self.term) - ) - if self.problem_id: - q = q.filter(problems=self.problem_id) - return q + return Contest.get_visible_contests(self.request.user) \ + .filter(Q(key__icontains=self.term) | Q(name__icontains=self.term)) + + +class CommentSelect2View(Select2View): + def get_queryset(self): + return Comment.objects.filter(page__icontains=self.term) class UserSearchSelect2View(BaseListView): @@ -104,108 +77,81 @@ class UserSearchSelect2View(BaseListView): def get_queryset(self): return _get_user_queryset(self.term) - def get_json_result_from_object(self, user_tuple): - pk, username, email, display_rank, profile_image = user_tuple - return { - "text": username, - "id": username, - "gravatar_url": gravatar( - None, - self.gravatar_size, - self.gravatar_default, - self.get_profile_image_url(profile_image), - email, - ), - "display_rank": display_rank, - } - def get(self, request, *args, **kwargs): self.request = request self.kwargs = kwargs - self.term = kwargs.get("term", request.GET.get("term", "")) - self.gravatar_size = request.GET.get("gravatar_size", 128) - self.gravatar_default = request.GET.get("gravatar_default", None) + self.term = kwargs.get('term', request.GET.get('term', '')) + self.gravatar_size = request.GET.get('gravatar_size', 128) + self.gravatar_default = request.GET.get('gravatar_default', None) - self.object_list = self.get_queryset().values_list( - "pk", "user__username", "user__email", "display_rank", "profile_image" - ) + self.object_list = self.get_queryset().values_list('pk', 'user__username', 'user__email', 'display_rank') context = self.get_context_data() - return JsonResponse( - { - "results": [ - self.get_json_result_from_object(user_tuple) - for user_tuple in context["object_list"] - ], - "more": context["page_obj"].has_next(), - } - ) + return JsonResponse({ + 'results': [ + { + 'text': username, + 'id': username, + 'gravatar_url': gravatar(email, self.gravatar_size, self.gravatar_default), + 'display_rank': display_rank, + } for pk, username, email, display_rank in context['object_list']], + 'more': context['page_obj'].has_next(), + }) def get_name(self, obj): return str(obj) - def get_profile_image_url(self, profile_image): - if profile_image: - return urljoin(settings.MEDIA_URL, profile_image) - return None - class ContestUserSearchSelect2View(UserSearchSelect2View): def get_queryset(self): - contest = get_object_or_404(Contest, key=self.kwargs["contest"]) - if not contest.is_accessible_by( - self.request.user - ) or not contest.can_see_full_scoreboard(self.request.user): + contest = get_object_or_404(Contest, key=self.kwargs['contest']) + if not contest.is_accessible_by(self.request.user) or not contest.can_see_full_scoreboard(self.request.user): raise Http404() - return Profile.objects.filter( - contest_history__contest=contest, user__username__icontains=self.term - ).distinct() + return Profile.objects.filter(contest_history__contest=contest, + user__username__icontains=self.term).distinct() class TicketUserSelect2View(UserSearchSelect2View): def get_queryset(self): - return Profile.objects.filter( - tickets__isnull=False, user__username__icontains=self.term - ).distinct() + return Profile.objects.filter(tickets__isnull=False, + user__username__icontains=self.term).distinct() class AssigneeSelect2View(UserSearchSelect2View): def get_queryset(self): - return Profile.objects.filter( - assigned_tickets__isnull=False, user__username__icontains=self.term - ).distinct() + return Profile.objects.filter(assigned_tickets__isnull=False, + user__username__icontains=self.term).distinct() -class ChatUserSearchSelect2View(UserSearchSelect2View): - def get_json_result_from_object(self, user_tuple): - if not self.request.user.is_authenticated: - raise Http404() - pk, username, email, display_rank, profile_image = user_tuple - return { - "text": username, - "id": encrypt_url(self.request.profile.id, pk), - "gravatar_url": gravatar( - None, - self.gravatar_size, - self.gravatar_default, - self.get_profile_image_url(profile_image), - email, - ), - "display_rank": display_rank, - } +class ChatUserSearchSelect2View(BaseListView): + paginate_by = 20 + def get_queryset(self): # TODO: add block + return _get_user_queryset(self.term) -class ProblemAuthorSearchSelect2View(UserSearchSelect2View): - def get_queryset(self): - return Profile.objects.filter( - authored_problems__isnull=False, user__username__icontains=self.term - ).distinct() + def get(self, request, *args, **kwargs): + self.request = request + self.kwargs = kwargs + self.term = kwargs.get('term', request.GET.get('term', '')) + self.gravatar_size = request.GET.get('gravatar_size', 128) + self.gravatar_default = request.GET.get('gravatar_default', None) - def get_json_result_from_object(self, user_tuple): - pk, username, email, display_rank, profile_image = user_tuple - return { - "text": username, - "id": pk, - } + self.object_list = self.get_queryset().values_list('pk', 'user__username', 'user__email', 'display_rank') + + context = self.get_context_data() + + return JsonResponse({ + 'results': [ + { + 'text': username, + 'id': encrypt_url(request.profile.id, pk), + 'gravatar_url': gravatar(email, self.gravatar_size, self.gravatar_default), + 'display_rank': display_rank, + } for pk, username, email, display_rank in context['object_list']], + 'more': context['page_obj'].has_next(), + }) + + def get_name(self, obj): + return str(obj) \ No newline at end of file diff --git a/judge/views/stats.py b/judge/views/stats.py index 5071dd5..41a852b 100644 --- a/judge/views/stats.py +++ b/judge/views/stats.py @@ -1,7 +1,5 @@ from itertools import chain, repeat from operator import itemgetter -import json -import pandas as pd from django.conf import settings from django.db.models import Case, Count, FloatField, IntegerField, Value, When @@ -9,192 +7,62 @@ from django.db.models.expressions import CombinedExpression from django.http import JsonResponse from django.shortcuts import render from django.utils.translation import gettext as _ -from django.http import Http404 -from django.views.generic import TemplateView -from django.utils.safestring import mark_safe -from django.db import connection from judge.models import Language, Submission -from judge.utils.stats import ( - chart_colors, - get_bar_chart, - get_pie_chart, - highlight_colors, -) +from judge.utils.stats import chart_colors, get_bar_chart, get_pie_chart, highlight_colors -class StatViewBase(TemplateView): - def get(self, request, *args, **kwargs): - if not request.user.is_superuser: - raise Http404 - return super().get(request, *args, **kwargs) +ac_count = Count(Case(When(submission__result='AC', then=Value(1)), output_field=IntegerField())) -class StatLanguage(StatViewBase): - template_name = "stats/language.html" - ac_count = Count( - Case(When(submission__result="AC", then=Value(1)), output_field=IntegerField()) - ) - - def repeat_chain(iterable): - return chain.from_iterable(repeat(iterable)) - - def language_data( - self, language_count=Language.objects.annotate(count=Count("submission")) - ): - languages = ( - language_count.filter(count__gt=0) - .values("key", "name", "count") - .order_by("-count") - ) - num_languages = min(len(languages), settings.DMOJ_STATS_LANGUAGE_THRESHOLD) - other_count = sum(map(itemgetter("count"), languages[num_languages:])) - - return { - "labels": list(map(itemgetter("name"), languages[:num_languages])) - + ["Other"], - "datasets": [ - { - "backgroundColor": chart_colors[:num_languages] + ["#FDB45C"], - "highlightBackgroundColor": highlight_colors[:num_languages] - + ["#FFC870"], - "data": list(map(itemgetter("count"), languages[:num_languages])) - + [other_count], - }, - ], - } - - def ac_language_data(self): - return self.language_data(Language.objects.annotate(count=self.ac_count)) - - def status_data(self, statuses=None): - if not statuses: - statuses = ( - Submission.objects.values("result") - .annotate(count=Count("result")) - .values("result", "count") - .order_by("-count") - ) - data = [] - for status in statuses: - res = status["result"] - if not res: - continue - count = status["count"] - data.append((str(Submission.USER_DISPLAY_CODES[res]), count)) - - return get_pie_chart(data) - - def ac_rate(self): - rate = CombinedExpression( - self.ac_count / Count("submission"), - "*", - Value(100.0), - output_field=FloatField(), - ) - data = ( - Language.objects.annotate(total=Count("submission"), ac_rate=rate) - .filter(total__gt=0) - .order_by("total") - .values_list("name", "ac_rate") - ) - return get_bar_chart(list(data)) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["title"] = _("Language statistics") - context["tab"] = "language" - context["data_all"] = mark_safe(json.dumps(self.language_data())) - context["lang_ac"] = mark_safe(json.dumps(self.ac_language_data())) - context["status_counts"] = mark_safe(json.dumps(self.status_data())) - context["ac_rate"] = mark_safe(json.dumps(self.ac_rate())) - return context +def repeat_chain(iterable): + return chain.from_iterable(repeat(iterable)) -def group_data_by_period(dates, data, period="1W"): - df = pd.DataFrame() - df["dates"] = pd.to_datetime(dates) - df["data"] = data - df = df.groupby([pd.Grouper(key="dates", freq=period)])["data"].mean().reset_index() - return list(df["dates"]), list(df["data"]) +def language_data(request, language_count=Language.objects.annotate(count=Count('submission'))): + languages = language_count.filter(count__gt=0).values('key', 'name', 'count').order_by('-count') + num_languages = min(len(languages), settings.DMOJ_STATS_LANGUAGE_THRESHOLD) + other_count = sum(map(itemgetter('count'), languages[num_languages:])) + + return JsonResponse({ + 'labels': list(map(itemgetter('name'), languages[:num_languages])) + ['Other'], + 'datasets': [ + { + 'backgroundColor': chart_colors[:num_languages] + ['#FDB45C'], + 'highlightBackgroundColor': highlight_colors[:num_languages] + ['#FFC870'], + 'data': list(map(itemgetter('count'), languages[:num_languages])) + [other_count], + }, + ], + }, safe=False) -class StatSite(StatViewBase): - template_name = "stats/site.html" +def ac_language_data(request): + return language_data(request, Language.objects.annotate(count=ac_count)) - def create_dataset(self, query, label): - labels = [] - data = [] - with connection.cursor() as cursor: - cursor.execute(query) - for row in cursor.fetchall(): - data.append(row[0]) - labels.append(row[1]) - labels, data = group_data_by_period(labels, data, self.period) +def status_data(request, statuses=None): + if not statuses: + statuses = (Submission.objects.values('result').annotate(count=Count('result')) + .values('result', 'count').order_by('-count')) + data = [] + for status in statuses: + res = status['result'] + if not res: + continue + count = status['count'] + data.append((str(Submission.USER_DISPLAY_CODES[res]), count)) - res = { - "labels": labels, - "datasets": [ - { - "label": label, - "data": data, - "borderColor": "rgb(75, 192, 192)", - "pointRadius": 1, - "borderWidth": 2, - } - ], - } - return mark_safe(json.dumps(res, default=str)) + return JsonResponse(get_pie_chart(data), safe=False) - def get_submissions(self): - query = """ - SELECT COUNT(*), CAST(date AS Date) as day from judge_submission GROUP BY day; - """ - return self.create_dataset(query, _("Submissions")) - def get_comments(self): - query = """ - SELECT COUNT(*), CAST(time AS Date) as day from judge_comment GROUP BY day; - """ - return self.create_dataset(query, _("Comments")) +def ac_rate(request): + rate = CombinedExpression(ac_count / Count('submission'), '*', Value(100.0), output_field=FloatField()) + data = Language.objects.annotate(total=Count('submission'), ac_rate=rate).filter(total__gt=0) \ + .order_by('total').values_list('name', 'ac_rate') + return JsonResponse(get_bar_chart(list(data))) - def get_new_users(self): - query = """ - SELECT COUNT(*), CAST(date_joined AS Date) as day from auth_user GROUP BY day; - """ - return self.create_dataset(query, _("New users")) - def get_chat_messages(self): - query = """ - SELECT COUNT(*), CAST(time AS Date) as day from chat_box_message GROUP BY day; - """ - return self.create_dataset(query, _("Chat messages")) - - def get_contests(self): - query = """ - SELECT COUNT(*), CAST(start_time AS Date) as day from judge_contest GROUP BY day; - """ - return self.create_dataset(query, _("Contests")) - - def get_groups(self): - query = """ - SELECT COUNT(*), CAST(creation_date AS Date) as day from judge_organization GROUP BY day; - """ - return self.create_dataset(query, _("Groups")) - - def get(self, request, *args, **kwargs): - self.period = request.GET.get("period", "1W") - return super().get(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["tab"] = "site" - context["title"] = _("Site statistics") - context["submissions"] = self.get_submissions() - context["comments"] = self.get_comments() - context["new_users"] = self.get_new_users() - context["chat_messages"] = self.get_chat_messages() - context["contests"] = self.get_contests() - context["groups"] = self.get_groups() - return context +def language(request): + return render(request, 'stats/language.html', { + 'title': _('Language statistics'), 'tab': 'language', + }) diff --git a/judge/views/status.py b/judge/views/status.py index c829206..a7f0b26 100644 --- a/judge/views/status.py +++ b/judge/views/status.py @@ -2,48 +2,41 @@ from collections import defaultdict from functools import partial from django.shortcuts import render +from django.utils import six from django.utils.translation import gettext as _ from packaging import version from judge.models import Judge, Language, RuntimeVersion -__all__ = ["status_all", "status_table"] +__all__ = ['status_all', 'status_table'] def get_judges(request): if request.user.is_superuser or request.user.is_staff: - return True, Judge.objects.order_by("-online", "name") + return True, Judge.objects.order_by('-online', 'name') else: return False, Judge.objects.filter(online=True) def status_all(request): see_all, judges = get_judges(request) - return render( - request, - "status/judge-status.html", - { - "title": _("Status"), - "judges": judges, - "see_all_judges": see_all, - }, - ) + return render(request, 'status/judge-status.html', { + 'title': _('Status'), + 'judges': judges, + 'see_all_judges': see_all, + }) def status_table(request): see_all, judges = get_judges(request) - return render( - request, - "status/judge-status-table.html", - { - "judges": judges, - "see_all_judges": see_all, - }, - ) + return render(request, 'status/judge-status-table.html', { + 'judges': judges, + 'see_all_judges': see_all, + }) class LatestList(list): - __slots__ = ("versions", "is_latest") + __slots__ = ('versions', 'is_latest') def compare_version_list(x, y): @@ -68,17 +61,15 @@ def version_matrix(request): judges = {judge.id: judge.name for judge in Judge.objects.filter(online=True)} languages = Language.objects.filter(judges__online=True).distinct() - for runtime in RuntimeVersion.objects.filter(judge__online=True).order_by( - "priority" - ): + for runtime in RuntimeVersion.objects.filter(judge__online=True).order_by('priority'): matrix[runtime.judge_id][runtime.language_id].append(runtime) - for judge, data in matrix.items(): - name_tuple = judges[judge].rpartition(".") + for judge, data in six.iteritems(matrix): + name_tuple = judges[judge].rpartition('.') groups[name_tuple[0] or name_tuple[-1]].append((judges[judge], data)) matrix = {} - for group, data in groups.items(): + for group, data in six.iteritems(groups): if len(data) == 1: judge, data = data[0] matrix[judge] = data @@ -101,24 +92,20 @@ def version_matrix(request): if ds[i] != rep: matrix[j] = x - for data in matrix.values(): - for language, versions in data.items(): + for data in six.itervalues(matrix): + for language, versions in six.iteritems(data): versions.versions = [version.parse(runtime.version) for runtime in versions] if versions.versions > latest[language]: latest[language] = versions.versions - for data in matrix.values(): - for language, versions in data.items(): + for data in six.itervalues(matrix): + for language, versions in six.iteritems(data): versions.is_latest = versions.versions == latest[language] languages = sorted(languages, key=lambda lang: version.parse(lang.name)) - return render( - request, - "status/versions.html", - { - "title": _("Version matrix"), - "judges": sorted(matrix.keys()), - "languages": languages, - "matrix": matrix, - }, - ) + return render(request, 'status/versions.html', { + 'title': _('Version matrix'), + 'judges': sorted(matrix.keys()), + 'languages': languages, + 'matrix': matrix, + }) diff --git a/judge/views/submission.py b/judge/views/submission.py index 09dcabe..e5f8b69 100644 --- a/judge/views/submission.py +++ b/judge/views/submission.py @@ -1,4 +1,6 @@ +import json import os.path +import zipfile from operator import attrgetter from django.conf import settings @@ -16,7 +18,6 @@ from django.http import HttpResponseRedirect from django.http import JsonResponse from django.shortcuts import get_object_or_404 from django.shortcuts import render -from django.template.defaultfilters import floatformat from django.urls import reverse from django.utils import timezone from django.utils.functional import cached_property @@ -28,246 +29,199 @@ from django.utils.translation import gettext_lazy from django.views.decorators.http import require_POST from django.views.generic import DetailView from django.views.generic import ListView -from django.views import View from judge import event_poster as event from judge.highlight_code import highlight_code -from judge.models import ( - Contest, - ContestParticipation, - Language, - Problem, - ProblemTestCase, - ProblemTranslation, - Profile, - Submission, -) +from judge.models import Contest +from judge.models import Language +from judge.models import Problem +from judge.models import ProblemTestCase +from judge.models import ProblemTranslation +from judge.models import Profile +from judge.models import Submission from judge.utils.problems import get_result_data +from judge.utils.problems import user_authored_ids +from judge.utils.problems import user_completed_ids +from judge.utils.problems import user_editable_ids from judge.utils.problem_data import get_problem_case from judge.utils.raw_sql import join_sql_subquery, use_straight_join from judge.utils.views import DiggPaginatorMixin -from judge.utils.infinite_paginator import InfinitePaginationMixin from judge.utils.views import TitleMixin -from judge.utils.timedelta import nice_repr -from judge.views.contests import ContestMixin -from judge.caching import cache_wrapper def submission_related(queryset): - return queryset.select_related("user", "problem", "language").only( - "id", - "user__id", - "problem__name", - "problem__code", - "problem__is_public", - "language__short_name", - "language__key", - "date", - "time", - "memory", - "points", - "result", - "status", - "case_points", - "case_total", - "current_testcase", - "contest_object__key", - "contest_object__name", - ) + return queryset.select_related('user__user', 'problem', 'language') \ + .only('id', 'user__user__username', 'user__display_rank', 'user__rating', 'problem__name', + 'problem__code', 'problem__is_public', 'language__short_name', 'language__key', 'date', 'time', 'memory', + 'points', 'result', 'status', 'case_points', 'case_total', 'current_testcase', 'contest_object') class SubmissionMixin(object): model = Submission - context_object_name = "submission" - pk_url_kwarg = "submission" + context_object_name = 'submission' + pk_url_kwarg = 'submission' class SubmissionDetailBase(LoginRequiredMixin, TitleMixin, SubmissionMixin, DetailView): - queryset = Submission.objects.select_related( - "language", "problem", "user", "contest_object" - ).defer("problem__description", "user__about", "contest_object__description") - def get_object(self, queryset=None): submission = super(SubmissionDetailBase, self).get_object(queryset) - if submission.is_accessible_by(self.request.profile): + profile = self.request.profile + problem = submission.problem + if self.request.user.has_perm('judge.view_all_submission'): return submission - + if submission.user_id == profile.id: + return submission + if problem.is_editor(profile): + return submission + if problem.is_public or problem.testers.filter(id=profile.id).exists(): + if Submission.objects.filter(user_id=profile.id, result='AC', problem_id=problem.id, + points=problem.points).exists(): + return submission raise PermissionDenied() def get_title(self): submission = self.object - return _("Submission of %(problem)s by %(user)s") % { - "problem": submission.problem.translated_name(self.request.LANGUAGE_CODE), - "user": submission.user.username, + return _('Submission of %(problem)s by %(user)s') % { + 'problem': submission.problem.translated_name(self.request.LANGUAGE_CODE), + 'user': submission.user.user.username, } def get_content_title(self): submission = self.object - return mark_safe( - escape(_("Submission of %(problem)s by %(user)s")) - % { - "problem": format_html( - '{1}', - reverse("problem_detail", args=[submission.problem.code]), - submission.problem.translated_name(self.request.LANGUAGE_CODE), - ), - "user": format_html( - '{1}', - reverse("user_page", args=[submission.user.username]), - submission.user.username, - ), - } - ) + return mark_safe(escape(_('Submission of %(problem)s by %(user)s')) % { + 'problem': format_html('{1}', + reverse('problem_detail', args=[ + submission.problem.code]), + submission.problem.translated_name(self.request.LANGUAGE_CODE)), + 'user': format_html('{1}', + reverse('user_page', args=[ + submission.user.user.username]), + submission.user.user.username), + }) -def get_hidden_subtasks(request, submission): - contest = submission.contest_object - if contest and contest.is_editable_by(request.user): - return set() - if contest and contest.format.has_hidden_subtasks: - try: - return contest.format.get_hidden_subtasks().get( - str(submission.contest.problem.id), set() - ) - except Exception: - pass - return set() +class SubmissionSource(SubmissionDetailBase): + template_name = 'submission/source.html' + + def get_queryset(self): + return super().get_queryset().select_related('source') + + def get_context_data(self, **kwargs): + context = super(SubmissionSource, self).get_context_data(**kwargs) + submission = self.object + context['raw_source'] = submission.source.source.rstrip('\n') + context['highlighted_source'] = highlight_code( + submission.source.source, submission.language.pygments, linenos=False) + return context -def make_batch(batch, cases, include_cases=True): - result = {"id": batch} - if include_cases: - result["cases"] = cases +def make_batch(batch, cases): + result = {'id': batch, 'cases': cases} if batch: - result["points"] = sum(map(attrgetter("points"), cases)) - result["total"] = sum(map(attrgetter("total"), cases)) - result["AC"] = abs(result["points"] - result["total"]) < 1e-5 - + result['points'] = min(map(attrgetter('points'), cases)) + result['total'] = max(map(attrgetter('total'), cases)) return result -def group_test_cases(submission, hidden_subtasks, include_cases=True): - cases = submission.test_cases.exclude(batch__in=hidden_subtasks) +def group_test_cases(cases): result = [] buf = [] last = None for case in cases: if case.batch != last and buf: - result.append(make_batch(last, buf, include_cases)) + result.append(make_batch(last, buf)) buf = [] buf.append(case) last = case.batch if buf: - result.append(make_batch(last, buf, include_cases)) + result.append(make_batch(last, buf)) return result def get_cases_data(submission): - testcases = ProblemTestCase.objects.filter(dataset=submission.problem).order_by( - "order" - ) - - if submission.is_pretested: + testcases = ProblemTestCase.objects.filter(dataset=submission.problem)\ + .order_by('order') + + if (submission.is_pretested): testcases = testcases.filter(is_pretest=True) files = [] for case in testcases: - if case.input_file: - files.append(case.input_file) - if case.output_file: - files.append(case.output_file) + if case.input_file: files.append(case.input_file) + if case.output_file: files.append(case.output_file) case_data = get_problem_case(submission.problem, files) problem_data = {} count = 0 for case in testcases: - if case.type != "C": - continue + if case.type != 'C': continue count += 1 problem_data[count] = { - "input": case_data.get(case.input_file, "") if case.input_file else "", - "answer": case_data.get(case.output_file, "") if case.output_file else "", + 'input': case_data[case.input_file] if case.input_file else '', + 'answer': case_data[case.output_file] if case.output_file else '', } return problem_data class SubmissionStatus(SubmissionDetailBase): - template_name = "submission/status.html" + template_name = 'submission/status.html' - def can_see_testcases(self): - contest_submission = self.object.contest_or_none - if contest_submission is None: - return True - - contest_problem = contest_submission.problem - problem = self.object.problem - contest = self.object.contest_object - - if contest_problem.show_testcases: - return True - if problem.is_editable_by(self.request.user): - return True - if contest.is_editable_by(self.request.user): - return True - if not problem.is_public: + def access_testcases_in_contest(self): + contest = self.object.contest_or_none + if contest is None: return False - if contest.is_in_contest(self.request.user): + if contest.problem.problem.is_editable_by(self.request.user): + return True + if contest.problem.contest.is_in_contest(self.request.user): return False - if not contest.ended: - return False - if contest_submission.participation.ended: + if contest.participation.ended: return True return False def get_context_data(self, **kwargs): context = super(SubmissionStatus, self).get_context_data(**kwargs) submission = self.object + context['last_msg'] = event.last() + context['batches'] = group_test_cases(submission.test_cases.all()) + context['time_limit'] = submission.problem.time_limit + context['can_see_testcases'] = False + + contest = submission.contest_or_none + prefix_length = 0 + can_see_testcases = self.access_testcases_in_contest() - context["hidden_subtasks"] = get_hidden_subtasks(self.request, self.object) - context["last_msg"] = event.last() - context["batches"] = group_test_cases( - submission, context["hidden_subtasks"], True - ) - context["time_limit"] = submission.problem.time_limit - context["can_see_testcases"] = False - context["highlighted_source"] = highlight_code( - submission.source.source, - submission.language.pygments, - linenos=True, - title=submission.language, - ) - - if self.can_see_testcases(): - context["cases_data"] = get_cases_data(submission) - context["can_see_testcases"] = True + if (contest is not None): + prefix_length = contest.problem.output_prefix_override + + if contest is None or prefix_length > 0 or can_see_testcases: + context['cases_data'] = get_cases_data(submission) + context['can_see_testcases'] = True try: lang_limit = submission.problem.language_limits.get( - language=submission.language - ) + language=submission.language) except ObjectDoesNotExist: pass else: - context["time_limit"] = lang_limit.time_limit + context['time_limit'] = lang_limit.time_limit return context class SubmissionTestCaseQuery(SubmissionStatus): - template_name = "submission/status-testcases.html" + template_name = 'submission/status-testcases.html' def get(self, request, *args, **kwargs): - if "id" not in request.GET or not request.GET["id"].isdigit(): + if 'id' not in request.GET or not request.GET['id'].isdigit(): return HttpResponseBadRequest() self.kwargs[self.pk_url_kwarg] = kwargs[self.pk_url_kwarg] = int( - request.GET["id"] - ) + request.GET['id']) return super(SubmissionTestCaseQuery, self).get(request, *args, **kwargs) -class SubmissionSourceRaw(SubmissionDetailBase): +class SubmissionSourceRaw(SubmissionSource): def get(self, request, *args, **kwargs): submission = self.get_object() - return HttpResponse(submission.source.source, content_type="text/plain") + return HttpResponse(submission.source.source, content_type='text/plain') @require_POST @@ -276,31 +230,28 @@ def abort_submission(request, submission): # if (not request.user.is_authenticated or (submission.was_rejudged or (request.profile != submission.user)) and # not request.user.has_perm('abort_any_submission')): # raise PermissionDenied() - if not request.user.is_authenticated or not request.user.has_perm( - "abort_any_submission" - ): + if (not request.user.is_authenticated + or not request.user.has_perm('abort_any_submission')): raise PermissionDenied() submission.abort() - return HttpResponseRedirect(reverse("submission_status", args=(submission.id,))) + return HttpResponseRedirect(reverse('submission_status', args=(submission.id,))) class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView): model = Submission paginate_by = 50 show_problem = True - title = gettext_lazy("All submissions") - content_title = gettext_lazy("All submissions") - page_type = "all_submissions_list" - template_name = "submission/list.html" - context_object_name = "submissions" + title = gettext_lazy('All submissions') + content_title = gettext_lazy('All submissions') + tab = 'all_submissions_list' + template_name = 'submission/list.html' + context_object_name = 'submissions' first_page_href = None - include_frozen = False - organization = None def get_result_data(self): result = self._get_result_data() - for category in result["categories"]: - category["name"] = _(category["name"]) + for category in result['categories']: + category['name'] = _(category['name']) return result def _get_result_data(self): @@ -309,170 +260,104 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView): def access_check(self, request): pass - def hide_contest_in_row(self): - return self.request.in_contest_mode - @cached_property def in_contest(self): - return ( - self.request.user.is_authenticated - and self.request.profile.current_contest is not None + return self.request.user.is_authenticated and self.request.profile.current_contest is not None \ and self.request.in_contest_mode - ) @cached_property def contest(self): return self.request.profile.current_contest.contest - def _get_entire_queryset(self): - organization = self.organization or self.request.organization - if organization: - queryset = Submission.objects.filter( - contest_object__organizations=organization - ) - else: - queryset = Submission.objects.all() + def _get_queryset(self): + queryset = Submission.objects.all() use_straight_join(queryset) - queryset = submission_related(queryset.order_by("-id")) + queryset = submission_related(queryset.order_by('-id')) if self.show_problem: - queryset = queryset.prefetch_related( - Prefetch( - "problem__translations", - queryset=ProblemTranslation.objects.filter( - language=self.request.LANGUAGE_CODE - ), - to_attr="_trans", - ) - ) + queryset = queryset.prefetch_related(Prefetch('problem__translations', + queryset=ProblemTranslation.objects.filter( + language=self.request.LANGUAGE_CODE), to_attr='_trans')) if self.in_contest: queryset = queryset.filter(contest_object=self.contest) if not self.contest.can_see_full_scoreboard(self.request.user): queryset = queryset.filter(user=self.request.profile) - if ( - self.contest.format.has_hidden_subtasks - and not self.contest.is_editable_by(self.request.user) - ): - queryset = queryset.filter(user=self.request.profile) - if self.contest.freeze_after and not self.include_frozen: - queryset = queryset.exclude( - ~Q(user=self.request.profile), - date__gte=self.contest.freeze_after + self.contest.start_time, - ) else: - queryset = queryset.select_related("contest_object").defer( - "contest_object__description" - ) + queryset = queryset.select_related( + 'contest_object').defer('contest_object__description') # This is not technically correct since contest organizers *should* see these, but # the join would be far too messy - if not self.request.user.has_perm("judge.see_private_contest"): + if not self.request.user.has_perm('judge.see_private_contest'): # Show submissions for any contest you can edit or visible scoreboard - contest_queryset = Contest.objects.filter( - Q(authors=self.request.profile) - | Q(curators=self.request.profile) - | Q(scoreboard_visibility=Contest.SCOREBOARD_VISIBLE) - | Q(end_time__lt=timezone.now()) - ).distinct() - queryset = queryset.filter( - Q(user=self.request.profile) - | Q(contest_object__in=contest_queryset) - | Q(contest_object__isnull=True) - ) + contest_queryset = Contest.objects.filter(Q(authors=self.request.profile) | + Q(curators=self.request.profile) | + Q(scoreboard_visibility=Contest.SCOREBOARD_VISIBLE) | + Q(end_time__lt=timezone.now())).distinct() + queryset = queryset.filter(Q(user=self.request.profile) | + Q(contest_object__in=contest_queryset) | + Q(contest_object__isnull=True)) if self.selected_languages: - queryset = queryset.filter(language__in=self.selected_languages) + queryset = queryset.filter( + language__in=Language.objects.filter(key__in=self.selected_languages)) if self.selected_statuses: - submission_results = [i for i, _ in Submission.RESULT] - if self.selected_statuses[0] in submission_results: - queryset = queryset.filter(result__in=self.selected_statuses) - else: - queryset = queryset.filter(status__in=self.selected_statuses) + queryset = queryset.filter(result__in=self.selected_statuses) return queryset def get_queryset(self): - queryset = self._get_entire_queryset() + queryset = self._get_queryset() if not self.in_contest: join_sql_subquery( queryset, - subquery=str( - Problem.get_visible_problems(self.request.user) - .distinct() - .only("id") - .query - ), + subquery=str(Problem.get_visible_problems(self.request.user).distinct().only('id').query), params=[], - join_fields=[("problem_id", "id")], - alias="visible_problems", - related_model=Problem, + join_fields=[('problem_id', 'id')], + alias='visible_problems', ) return queryset def get_my_submissions_page(self): return None - def get_friend_submissions_page(self): - return None - def get_all_submissions_page(self): - return reverse("all_submissions") + return reverse('all_submissions') def get_searchable_status_codes(self): - all_statuses = list(Submission.RESULT) - all_statuses.extend([i for i in Submission.STATUS if i not in all_statuses]) - hidden_codes = ["SC", "D", "G"] + hidden_codes = ['SC'] if not self.request.user.is_superuser and not self.request.user.is_staff: - hidden_codes += ["IE"] - return [(key, value) for key, value in all_statuses if key not in hidden_codes] - - def in_hidden_subtasks_contest(self): - return ( - self.in_contest - and self.contest.format.has_hidden_subtasks - and not self.contest.is_editable_by(self.request.user) - ) - - def modify_attrs(self, submission): - # Used to modify submission's info in contest with hidden subtasks - batches = group_test_cases( - submission, get_hidden_subtasks(self.request, submission), False - ) - setattr(submission, "case_points", sum([i.get("points", 0) for i in batches])) - setattr(submission, "batches", batches) - if submission.status in ("IE", "CE", "AB"): - setattr(submission, "_result_class", submission.result_class) - else: - setattr(submission, "_result_class", "TLE") + hidden_codes += ['IE'] + return [(key, value) for key, value in Submission.RESULT if key not in hidden_codes] def get_context_data(self, **kwargs): context = super(SubmissionsListBase, self).get_context_data(**kwargs) authenticated = self.request.user.is_authenticated - context["dynamic_update"] = False - context["show_problem"] = self.show_problem - context["profile"] = self.request.profile - context["all_languages"] = Language.objects.all().values_list("key", "name") - context["selected_languages"] = self.selected_languages_key - context["all_statuses"] = self.get_searchable_status_codes() - context["selected_statuses"] = self.selected_statuses - context["can_show_result_data"] = not self.in_hidden_subtasks_contest() - context["page_suffix"] = suffix = ( - ("?" + self.request.GET.urlencode()) if self.request.GET else "" - ) - context["first_page_href"] = (self.first_page_href or ".") + suffix - context["my_submissions_link"] = self.get_my_submissions_page() - context["friend_submissions_link"] = self.get_friend_submissions_page() - context["all_submissions_link"] = self.get_all_submissions_page() - context["page_type"] = self.page_type - context["hide_contest_in_row"] = self.hide_contest_in_row() + context['dynamic_update'] = False + context['show_problem'] = self.show_problem + context['completed_problem_ids'] = user_completed_ids( + self.request.profile) if authenticated else [] + context['authored_problem_ids'] = user_authored_ids( + self.request.profile) if authenticated else [] + context['editable_problem_ids'] = user_editable_ids( + self.request.profile) if authenticated else [] - context["in_hidden_subtasks_contest"] = self.in_hidden_subtasks_contest() - if context["in_hidden_subtasks_contest"]: - for submission in context["submissions"]: - self.modify_attrs(submission) - context[ - "is_in_editable_contest" - ] = self.in_contest and self.contest.is_editable_by(self.request.user) + context['all_languages'] = Language.objects.all( + ).values_list('key', 'name') + context['selected_languages'] = self.selected_languages + context['all_statuses'] = self.get_searchable_status_codes() + context['selected_statuses'] = self.selected_statuses + + context['results_json'] = mark_safe(json.dumps(self.get_result_data())) + context['results_colors_json'] = mark_safe( + json.dumps(settings.DMOJ_STATS_SUBMISSION_RESULT_COLORS)) + + context['page_suffix'] = suffix = ( + '?' + self.request.GET.urlencode()) if self.request.GET else '' + context['first_page_href'] = (self.first_page_href or '.') + suffix + context['my_submissions_link'] = self.get_my_submissions_page() + context['all_submissions_link'] = self.get_all_submissions_page() + context['tab'] = self.tab return context def get(self, request, *args, **kwargs): @@ -480,211 +365,120 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView): if check is not None: return check - self.selected_languages = request.GET.getlist("language") - self.selected_statuses = request.GET.getlist("status") - self.selected_languages_key = [] + self.selected_languages = set(request.GET.getlist('language')) + self.selected_statuses = set(request.GET.getlist('status')) - if self.selected_languages: - languages = Language.objects.filter(key__in=self.selected_languages).values( - "id", "key" - ) - self.selected_languages = [i["id"] for i in languages] - self.selected_languages_key = [i["key"] for i in languages] - if self.selected_statuses: - allowed_statuses = [i for i, _ in Submission.RESULT + Submission.STATUS] - self.selected_statuses = [ - i for i in self.selected_statuses if i in allowed_statuses - ] - - if self.in_contest and self.contest.is_editable_by(self.request.user): - self.include_frozen = True - - if "results" in request.GET: - response = {} - if not self.in_hidden_subtasks_contest(): - response["results_json"] = self.get_result_data() - response[ - "results_colors_json" - ] = settings.DMOJ_STATS_SUBMISSION_RESULT_COLORS - else: - response["results_json"] = None - return JsonResponse(response) + if 'results' in request.GET: + return JsonResponse(self.get_result_data()) return super(SubmissionsListBase, self).get(request, *args, **kwargs) class UserMixin(object): def get(self, request, *args, **kwargs): - if "user" not in kwargs and "participation" not in kwargs: - raise ImproperlyConfigured("Must pass a user or participation") - if "user" in kwargs: - self.profile = get_object_or_404(Profile, user__username=kwargs["user"]) - self.username = kwargs["user"] - else: - self.participation = get_object_or_404( - ContestParticipation, id=kwargs["participation"] - ) - self.profile = self.participation.user - self.username = self.profile.user.username - if self.profile == request.profile: - self.include_frozen = True + if 'user' not in kwargs: + raise ImproperlyConfigured('Must pass a user') + self.profile = get_object_or_404( + Profile, user__username=kwargs['user']) + self.username = kwargs['user'] return super(UserMixin, self).get(request, *args, **kwargs) class ConditionalUserTabMixin(object): def get_context_data(self, **kwargs): - context = super(ConditionalUserTabMixin, self).get_context_data(**kwargs) + context = super(ConditionalUserTabMixin, + self).get_context_data(**kwargs) if self.request.user.is_authenticated and self.request.profile == self.profile: - context["page_type"] = "my_submissions_tab" + context['tab'] = 'my_submissions_tab' else: - context["page_type"] = "user_submissions_tab" - context["tab_username"] = self.profile.user.username + context['tab'] = 'user_submissions_tab' + context['tab_username'] = self.profile.user.username return context -class GeneralSubmissions(SubmissionsListBase): - def get_my_submissions_page(self): - if self.request.user.is_authenticated: - return reverse( - "all_user_submissions", kwargs={"user": self.request.user.username} - ) - return None - - def get_friend_submissions_page(self): - if self.request.user.is_authenticated: - return reverse("all_friend_submissions") - return None - - -class AllUserSubmissions(ConditionalUserTabMixin, UserMixin, GeneralSubmissions): +class AllUserSubmissions(ConditionalUserTabMixin, UserMixin, SubmissionsListBase): def get_queryset(self): - return ( - super(AllUserSubmissions, self) - .get_queryset() - .filter(user_id=self.profile.id) - ) + return super(AllUserSubmissions, self).get_queryset().filter(user_id=self.profile.id) def get_title(self): if self.request.user.is_authenticated and self.request.profile == self.profile: - return _("All my submissions") - return _("All submissions by %s") % self.username + return _('All my submissions') + return _('All submissions by %s') % self.username def get_content_title(self): if self.request.user.is_authenticated and self.request.profile == self.profile: - return format_html(_("All my submissions")) - return format_html( - _('All submissions by {0}'), - self.username, - reverse("user_page", args=[self.username]), - ) + return format_html('All my submissions') + return format_html('All submissions by {0}', self.username, + reverse('user_page', args=[self.username])) + + def get_my_submissions_page(self): + if self.request.user.is_authenticated: + return reverse('all_user_submissions', kwargs={'user': self.request.user.username}) def get_context_data(self, **kwargs): context = super(AllUserSubmissions, self).get_context_data(**kwargs) - context["dynamic_update"] = context["page_obj"].number == 1 - context["dynamic_user_id"] = self.profile.id - context["last_msg"] = event.last() - return context - - -class AllFriendSubmissions( - LoginRequiredMixin, InfinitePaginationMixin, GeneralSubmissions -): - def get_queryset(self): - friends = self.request.profile.get_friends() - return ( - super(AllFriendSubmissions, self).get_queryset().filter(user_id__in=friends) - ) - - def get_title(self): - return _("All friend submissions") - - def get_context_data(self, **kwargs): - context = super(AllFriendSubmissions, self).get_context_data(**kwargs) - context["dynamic_update"] = False - context["page_type"] = "friend_tab" + context['dynamic_update'] = context['page_obj'].number == 1 + context['dynamic_user_id'] = self.profile.id + context['last_msg'] = event.last() return context class ProblemSubmissionsBase(SubmissionsListBase): show_problem = False dynamic_update = True - check_contest_in_access_check = False + check_contest_in_access_check = True def get_queryset(self): - if ( - self.in_contest - and not self.contest.contest_problems.filter( - problem_id=self.problem.id - ).exists() - ): + if self.in_contest and not self.contest.contest_problems.filter(problem_id=self.problem.id).exists(): raise Http404() - return ( - super(ProblemSubmissionsBase, self) - ._get_entire_queryset() - .filter(problem_id=self.problem.id) - ) + return super(ProblemSubmissionsBase, self)._get_queryset().filter(problem_id=self.problem.id) def get_title(self): - return _("All submissions for %s") % self.problem_name + return _('All submissions for %s') % self.problem_name def get_content_title(self): - return format_html( - 'All submissions for {0}', - self.problem_name, - reverse("problem_detail", args=[self.problem.code]), - ) + return format_html('All submissions for {0}', self.problem_name, + reverse('problem_detail', args=[self.problem.code])) def access_check_contest(self, request): - if self.in_contest: - if not self.contest.can_see_own_scoreboard(request.user): - raise Http404() - if not self.contest.is_accessible_by(request.user): - raise Http404() + if self.in_contest and not self.contest.can_see_own_scoreboard(request.user): + raise Http404() def access_check(self, request): + if not self.problem.is_accessible_by(request.user): + raise Http404() + if self.check_contest_in_access_check: self.access_check_contest(request) - else: - is_own = hasattr(self, "is_own") and self.is_own - if not is_own and not self.problem.is_accessible_by( - request.user, request.in_contest_mode - ): - raise Http404() def get(self, request, *args, **kwargs): - if "problem" not in kwargs: - raise ImproperlyConfigured(_("Must pass a problem")) - self.problem = get_object_or_404(Problem, code=kwargs["problem"]) - self.problem_name = self.problem.translated_name(self.request.LANGUAGE_CODE) + if 'problem' not in kwargs: + raise ImproperlyConfigured(_('Must pass a problem')) + self.problem = get_object_or_404(Problem, code=kwargs['problem']) + self.problem_name = self.problem.translated_name( + self.request.LANGUAGE_CODE) return super(ProblemSubmissionsBase, self).get(request, *args, **kwargs) def get_all_submissions_page(self): - return reverse( - "chronological_submissions", kwargs={"problem": self.problem.code} - ) + return reverse('chronological_submissions', kwargs={'problem': self.problem.code}) def get_context_data(self, **kwargs): - context = super(ProblemSubmissionsBase, self).get_context_data(**kwargs) + context = super(ProblemSubmissionsBase, + self).get_context_data(**kwargs) if self.dynamic_update: - context["dynamic_update"] = context["page_obj"].number == 1 - context["dynamic_problem_id"] = self.problem.id - context["last_msg"] = event.last() - context["best_submissions_link"] = reverse( - "ranked_submissions", kwargs={"problem": self.problem.code} - ) + context['dynamic_update'] = context['page_obj'].number == 1 + context['dynamic_problem_id'] = self.problem.id + context['last_msg'] = event.last() + context['best_submissions_link'] = reverse('ranked_submissions', kwargs={ + 'problem': self.problem.code}) return context class ProblemSubmissions(ProblemSubmissionsBase): def get_my_submissions_page(self): if self.request.user.is_authenticated: - return reverse( - "user_submissions", - kwargs={ - "problem": self.problem.code, - "user": self.request.user.username, - }, - ) + return reverse('user_submissions', kwargs={'problem': self.problem.code, + 'user': self.request.user.username}) class UserProblemSubmissions(ConditionalUserTabMixin, UserMixin, ProblemSubmissions): @@ -692,9 +486,7 @@ class UserProblemSubmissions(ConditionalUserTabMixin, UserMixin, ProblemSubmissi @cached_property def is_own(self): - return ( - self.request.user.is_authenticated and self.request.profile == self.profile - ) + return self.request.user.is_authenticated and self.request.profile == self.profile def access_check(self, request): super(UserProblemSubmissions, self).access_check(request) @@ -703,106 +495,87 @@ class UserProblemSubmissions(ConditionalUserTabMixin, UserMixin, ProblemSubmissi self.access_check_contest(request) def get_queryset(self): - return ( - super(UserProblemSubmissions, self) - .get_queryset() - .filter(user_id=self.profile.id) - ) + return super(UserProblemSubmissions, self).get_queryset().filter(user_id=self.profile.id) def get_title(self): if self.is_own: - return _("My submissions for %(problem)s") % {"problem": self.problem_name} - return _("%(user)s's submissions for %(problem)s") % { - "user": self.username, - "problem": self.problem_name, - } + return _("My submissions for %(problem)s") % {'problem': self.problem_name} + return _("%(user)s's submissions for %(problem)s") % {'user': self.username, 'problem': self.problem_name} def get_content_title(self): if self.request.user.is_authenticated and self.request.profile == self.profile: - return format_html( - """My submissions for {2}""", - self.username, - reverse("user_page", args=[self.username]), - self.problem_name, - reverse("problem_detail", args=[self.problem.code]), - ) - return format_html( - """{0}'s submissions for {2}""", - self.username, - reverse("user_page", args=[self.username]), - self.problem_name, - reverse("problem_detail", args=[self.problem.code]), - ) + return format_html('''My submissions for {2}''', + self.username, reverse( + 'user_page', args=[self.username]), + self.problem_name, reverse('problem_detail', args=[self.problem.code])) + return format_html('''{0}'s submissions for {2}''', + self.username, reverse( + 'user_page', args=[self.username]), + self.problem_name, reverse('problem_detail', args=[self.problem.code])) def get_context_data(self, **kwargs): - context = super(UserProblemSubmissions, self).get_context_data(**kwargs) - context["dynamic_user_id"] = self.profile.id + context = super(UserProblemSubmissions, + self).get_context_data(**kwargs) + context['dynamic_user_id'] = self.profile.id return context def single_submission(request, submission_id, show_problem=True): request.no_profile_update = True authenticated = request.user.is_authenticated - submission = get_object_or_404( - submission_related(Submission.objects.all()), id=int(submission_id) - ) - - is_in_editable_contest = False - if authenticated and request.in_contest_mode: - contest = request.profile.current_contest.contest - is_in_editable_contest = contest.is_editable_by(request.user) + submission = get_object_or_404(submission_related( + Submission.objects.all()), id=int(submission_id)) if not submission.problem.is_accessible_by(request.user): raise Http404() - return render( - request, - "submission/row.html", - { - "submission": submission, - "show_problem": show_problem, - "problem_name": show_problem - and submission.problem.translated_name(request.LANGUAGE_CODE), - "profile": request.profile if authenticated else None, - "is_in_editable_contest": is_in_editable_contest, - }, - ) + return render(request, 'submission/row.html', { + 'submission': submission, + 'authored_problem_ids': user_authored_ids(request.profile) if authenticated else [], + 'completed_problem_ids': user_completed_ids(request.profile) if authenticated else [], + 'editable_problem_ids': user_editable_ids(request.profile) if authenticated else [], + 'show_problem': show_problem, + 'problem_name': show_problem and submission.problem.translated_name(request.LANGUAGE_CODE), + 'profile_id': request.profile.id if authenticated else 0, + }) def single_submission_query(request): request.no_profile_update = True - if "id" not in request.GET or not request.GET["id"].isdigit(): + if 'id' not in request.GET or not request.GET['id'].isdigit(): return HttpResponseBadRequest() try: - show_problem = int(request.GET.get("show_problem", "1")) + show_problem = int(request.GET.get('show_problem', '1')) except ValueError: return HttpResponseBadRequest() - return single_submission(request, int(request.GET["id"]), bool(show_problem)) + return single_submission(request, int(request.GET['id']), bool(show_problem)) -class AllSubmissions(InfinitePaginationMixin, GeneralSubmissions): +class AllSubmissions(SubmissionsListBase): stats_update_interval = 3600 - @property - def use_infinite_pagination(self): - return not self.in_contest + def get_my_submissions_page(self): + if self.request.user.is_authenticated: + return reverse('all_user_submissions', kwargs={'user': self.request.user.username}) def get_context_data(self, **kwargs): context = super(AllSubmissions, self).get_context_data(**kwargs) - context["dynamic_update"] = ( - context["page_obj"].number == 1 - ) and not self.request.organization - context["last_msg"] = event.last() - context["stats_update_interval"] = self.stats_update_interval + context['dynamic_update'] = context['page_obj'].number == 1 + context['last_msg'] = event.last() + context['stats_update_interval'] = self.stats_update_interval return context def _get_result_data(self): - if self.request.organization or self.in_contest: + if self.in_contest or self.selected_languages or self.selected_statuses: return super(AllSubmissions, self)._get_result_data() - return _get_global_submission_result_data( - self.selected_statuses, self.selected_languages - ) + key = 'global_submission_result_data' + result = cache.get(key) + if result: + return result + result = super(AllSubmissions, self)._get_result_data() + cache.set(key, result, self.stats_update_interval) + return result class ForceContestMixin(object): @@ -817,76 +590,28 @@ class ForceContestMixin(object): def access_check(self, request): super(ForceContestMixin, self).access_check(request) - if not request.user.has_perm("judge.see_private_contest"): + if not request.user.has_perm('judge.see_private_contest'): if not self.contest.is_visible: raise Http404() - if ( - self.contest.start_time is not None - and self.contest.start_time > timezone.now() - ): + if self.contest.start_time is not None and self.contest.start_time > timezone.now(): raise Http404() def get_problem_number(self, problem): - return ( - self.contest.contest_problems.select_related("problem") - .get(problem=problem) - .order - ) + return self.contest.contest_problems.select_related('problem').get(problem=problem).order def get(self, request, *args, **kwargs): - if "contest" not in kwargs: - raise ImproperlyConfigured(_("Must pass a contest")) - self._contest = get_object_or_404(Contest, key=kwargs["contest"]) + if 'contest' not in kwargs: + raise ImproperlyConfigured(_('Must pass a contest')) + self._contest = get_object_or_404(Contest, key=kwargs['contest']) return super(ForceContestMixin, self).get(request, *args, **kwargs) -class ContestSubmissions( - LoginRequiredMixin, ContestMixin, ForceContestMixin, SubmissionsListBase -): - check_contest_in_access_check = True - template_name = "contest/submissions.html" - context_object_name = "submissions" - - def hide_contest_in_row(self): - return True - - def access_check(self, request): - super().contest_access_check(self.contest) - super().access_check(request) - - def get_title(self): - return _("Submissions in") + " " + self.contest.name - - def get_content_title(self): - return format_html( - _('Submissions in {1}'), - reverse("contest_view", args=[self.contest.key]), - self.contest.name, - ) - - def get_context_data(self, **kwargs): - self.object = self.contest - context = super(ContestSubmissions, self).get_context_data(**kwargs) - context["contest"] = self.contest - context["page_type"] = "submissions" - return context - - class UserContestSubmissions(ForceContestMixin, UserProblemSubmissions): - check_contest_in_access_check = True - def get_title(self): if self.problem.is_accessible_by(self.request.user): - return "%s's submissions for %s in %s" % ( - self.username, - self.problem_name, - self.contest.name, - ) + return "%s's submissions for %s in %s" % (self.username, self.problem_name, self.contest.name) return "%s's submissions for problem %s in %s" % ( - self.username, - self.get_problem_number(self.problem), - self.contest.name, - ) + self.username, self.get_problem_number(self.problem), self.contest.name) def access_check(self, request): super(UserContestSubmissions, self).access_check(request) @@ -895,181 +620,16 @@ class UserContestSubmissions(ForceContestMixin, UserProblemSubmissions): def get_content_title(self): if self.problem.is_accessible_by(self.request.user): - return format_html( - _( - '{0}\'s submissions for ' - '{2} in {4}' - ), - self.username, - reverse("user_page", args=[self.username]), - self.problem_name, - reverse("problem_detail", args=[self.problem.code]), - self.contest.name, - reverse("contest_view", args=[self.contest.key]), - ) - return format_html( - _( - '{0}\'s submissions for ' - 'problem {2} in {3}' - ), - self.username, - reverse("user_page", args=[self.username]), - self.get_problem_number(self.problem), - self.contest.name, - reverse("contest_view", args=[self.contest.key]), - ) - - -class UserContestSubmissionsAjax(UserContestSubmissions): - template_name = "submission/user-ajax.html" - - def contest_time(self, s): - if s.contest.participation.live: - if self.contest.time_limit: - return s.date - s.contest.participation.real_start - return s.date - self.contest.start_time - return None - - def get_best_subtask_points(self): - if self.contest.format.has_hidden_subtasks: - contest_problem = self.contest.contest_problems.get(problem=self.problem) - best_subtasks = {} - total_points = 0 - problem_points = 0 - achieved_points = 0 - hidden_subtasks = self.contest.format.get_hidden_subtasks() - - for ( - problem_id, - pp, - time, - subtask_points, - total_subtask_points, - subtask, - sub_id, - ) in self.contest.format.get_results_by_subtask( - self.participation, self.include_frozen - ): - if contest_problem.id != problem_id or total_subtask_points == 0: - continue - if not subtask: - subtask = 0 - problem_points = pp - submission = Submission.objects.get(id=sub_id) - if subtask in hidden_subtasks.get( - str(problem_id), set() - ) and not self.contest.is_editable_by(self.request.user): - best_subtasks[subtask] = { - "submission": None, - "contest_time": None, - "points": "???", - "total": total_subtask_points, - } - else: - best_subtasks[subtask] = { - "submission": submission, - "contest_time": nice_repr( - self.contest_time(submission), "noday" - ), - "points": subtask_points, - "total": total_subtask_points, - } - achieved_points += subtask_points - total_points += total_subtask_points - for subtask in best_subtasks.values(): - if subtask["points"] != "???": - subtask["points"] = floatformat( - subtask["points"] / total_points * problem_points, - -self.contest.points_precision, - ) - subtask["total"] = floatformat( - subtask["total"] / total_points * problem_points, - -self.contest.points_precision, - ) - if total_points > 0 and best_subtasks: - achieved_points = achieved_points / total_points * problem_points - return best_subtasks, achieved_points, problem_points - return None - - def get_context_data(self, **kwargs): - context = super(UserContestSubmissionsAjax, self).get_context_data(**kwargs) - context["contest"] = self.contest - context["problem"] = self.problem - context["profile"] = self.profile - - contest_problem = self.contest.contest_problems.get(problem=self.problem) - filtered_submissions = [] - - # Only show this for some users when using ioi16 - if not self.contest.format.has_hidden_subtasks or self.contest.is_editable_by( - self.request.user - ): - for s in context["submissions"]: - if not hasattr(s, "contest"): - continue - contest_time = self.contest_time(s) - if contest_time: - s.contest_time = nice_repr(contest_time, "noday") - else: - s.contest_time = None - total = floatformat( - contest_problem.points, -self.contest.points_precision - ) - points = floatformat(s.contest.points, -self.contest.points_precision) - s.display_point = f"{points} / {total}" - filtered_submissions.append(s) - context["submissions"] = filtered_submissions - else: - context["submissions"] = None - - best_subtasks = self.get_best_subtask_points() - if best_subtasks: - ( - context["best_subtasks"], - context["points"], - context["total"], - ) = best_subtasks - if context["points"] != "???": - context["points"] = floatformat( - context["points"], -self.contest.points_precision - ) - context["total"] = floatformat( - context["total"], -self.contest.points_precision - ) - context["subtasks"] = sorted(context["best_subtasks"].keys()) - return context - - def get(self, request, *args, **kwargs): - try: - return super(UserContestSubmissionsAjax, self).get(request, *args, **kwargs) - except Http404: - return HttpResponse(_("You don't have permission to access.")) - - -class SubmissionSourceFileView(View): - def get(self, request, filename): - filepath = os.path.join(settings.DMOJ_SUBMISSION_ROOT, filename) - if not os.path.exists(filepath): - raise Http404("File not found") - response = HttpResponse() - with open(filepath, "rb") as f: - response.content = f.read() - response["Content-Type"] = "application/octet-stream" - response["Content-Disposition"] = "attachment; filename=%s" % (filename,) - return response - - -@cache_wrapper(prefix="gsrd", timeout=3600, expected_type=dict) -def _get_global_submission_result_data(statuses, languages): - queryset = Submission.objects - if languages: - queryset = queryset.filter( - language__in=Language.objects.filter(id__in=languages) - ) - if statuses: - submission_results = [i for i, _ in Submission.RESULT] - if statuses[0] in submission_results: - queryset = queryset.filter(result__in=statuses) - else: - queryset = queryset.filter(status__in=statuses) - return get_result_data(queryset) + return format_html(_('{0}\'s submissions for ' + '{2} in {4}'), + self.username, reverse( + 'user_page', args=[self.username]), + self.problem_name, reverse( + 'problem_detail', args=[self.problem.code]), + self.contest.name, reverse('contest_view', args=[self.contest.key])) + return format_html(_('{0}\'s submissions for ' + 'problem {2} in {3}'), + self.username, reverse( + 'user_page', args=[self.username]), + self.get_problem_number(self.problem), + self.contest.name, reverse('contest_view', args=[self.contest.key])) diff --git a/judge/views/tasks.py b/judge/views/tasks.py index a1c0644..42e123f 100644 --- a/judge/views/tasks.py +++ b/judge/views/tasks.py @@ -4,12 +4,7 @@ from uuid import UUID from celery.result import AsyncResult from django.core.exceptions import PermissionDenied -from django.http import ( - Http404, - HttpResponseBadRequest, - HttpResponseRedirect, - JsonResponse, -) +from django.http import Http404, HttpResponseBadRequest, HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.urls import reverse from django.utils.http import is_safe_url @@ -22,19 +17,14 @@ from judge.utils.views import short_circuit_middleware def get_task_status(task_id): result = AsyncResult(task_id) info = result.result - if result.state == "PROGRESS": - return { - "code": "PROGRESS", - "done": info["done"], - "total": info["total"], - "stage": info["stage"], - } - elif result.state == "SUCCESS": - return {"code": "SUCCESS"} - elif result.state == "FAILURE": - return {"code": "FAILURE", "error": str(info)} + if result.state == 'PROGRESS': + return {'code': 'PROGRESS', 'done': info['done'], 'total': info['total'], 'stage': info['stage']} + elif result.state == 'SUCCESS': + return {'code': 'SUCCESS'} + elif result.state == 'FAILURE': + return {'code': 'FAILURE', 'error': str(info)} else: - return {"code": "WORKING"} + return {'code': 'WORKING'} def task_status(request, task_id): @@ -43,48 +33,34 @@ def task_status(request, task_id): except ValueError: raise Http404() - redirect = request.GET.get("redirect") + redirect = request.GET.get('redirect') if not is_safe_url(redirect, allowed_hosts={request.get_host()}): redirect = None status = get_task_status(task_id) - if status["code"] == "SUCCESS" and redirect: + if status['code'] == 'SUCCESS' and redirect: return HttpResponseRedirect(redirect) - return render( - request, - "task_status.html", - { - "task_id": task_id, - "task_status": json.dumps(status), - "message": request.GET.get("message", ""), - "redirect": redirect or "", - }, - ) + return render(request, 'task_status.html', { + 'task_id': task_id, 'task_status': json.dumps(status), + 'message': request.GET.get('message', ''), 'redirect': redirect or '', + }) @short_circuit_middleware def task_status_ajax(request): - if "id" not in request.GET: - return HttpResponseBadRequest( - 'Need to pass GET parameter "id"', content_type="text/plain" - ) - return JsonResponse(get_task_status(request.GET["id"])) + if 'id' not in request.GET: + return HttpResponseBadRequest('Need to pass GET parameter "id"', content_type='text/plain') + return JsonResponse(get_task_status(request.GET['id'])) def demo_task(request, task, message): if not request.user.is_superuser: raise PermissionDenied() result = task.delay() - return redirect_to_task_status(result, message=message, redirect=reverse("home")) + return redirect_to_task_status(result, message=message, redirect=reverse('home')) -demo_success = partial( - demo_task, task=success, message="Running example task that succeeds..." -) -demo_failure = partial( - demo_task, task=failure, message="Running example task that fails..." -) -demo_progress = partial( - demo_task, task=progress, message="Running example task that waits 10 seconds..." -) +demo_success = partial(demo_task, task=success, message='Running example task that succeeds...') +demo_failure = partial(demo_task, task=failure, message='Running example task that fails...') +demo_progress = partial(demo_task, task=progress, message='Running example task that waits 10 seconds...') diff --git a/judge/views/test_formatter/test_formatter.py b/judge/views/test_formatter/test_formatter.py deleted file mode 100644 index 4818bcc..0000000 --- a/judge/views/test_formatter/test_formatter.py +++ /dev/null @@ -1,207 +0,0 @@ -from django.views import View -from django.shortcuts import render, redirect, get_object_or_404 -from django.urls import reverse -from django.core.files import File -from django.core.files.base import ContentFile -from django.http import ( - FileResponse, - HttpResponseRedirect, - HttpResponseBadRequest, - HttpResponse, -) -from judge.models import TestFormatterModel -from judge.forms import TestFormatterForm -from judge.views.test_formatter import tf_logic, tf_utils -from django.utils.translation import gettext_lazy as _ -from zipfile import ZipFile, ZIP_DEFLATED - -import os -import uuid -from dmoj import settings - - -def id_to_path(id): - return os.path.join(settings.MEDIA_ROOT, "test_formatter/" + id + "/") - - -def get_names_in_archive(file_path): - suffixes = ("inp", "out", "INP", "OUT") - with ZipFile(os.path.join(settings.MEDIA_ROOT, file_path)) as f: - result = [ - x for x in f.namelist() if not x.endswith("/") and x.endswith(suffixes) - ] - return list(sorted(result, key=tf_utils.natural_sorting_key)) - - -def get_renamed_archive(file_str, file_name, file_path, bef, aft): - target_file_id = str(uuid.uuid4()) - source_path = os.path.join(settings.MEDIA_ROOT, file_str) - target_path = os.path.join(settings.MEDIA_ROOT, file_str + "_" + target_file_id) - new_path = os.path.join(settings.MEDIA_ROOT, "test_formatter/" + file_name) - - source = ZipFile(source_path, "r") - target = ZipFile(target_path, "w", ZIP_DEFLATED) - - for bef_name, aft_name in zip(bef, aft): - target.writestr(aft_name, source.read(bef_name)) - - os.remove(source_path) - os.rename(target_path, new_path) - - target.close() - source.close() - - return {"file_path": "test_formatter/" + file_name} - - -class TestFormatter(View): - form_class = TestFormatterForm() - - def get(self, request): - return render( - request, - "test_formatter/test_formatter.html", - {"title": _("Test Formatter"), "form": self.form_class}, - ) - - def post(self, request): - form = TestFormatterForm(request.POST, request.FILES) - if form.is_valid(): - form.save() - return HttpResponseRedirect("edit_page") - return render( - request, "test_formatter/test_formatter.html", {"form": self.form_class} - ) - - -class EditTestFormatter(View): - file_path = "" - - def get(self, request): - file = TestFormatterModel.objects.last() - filestr = str(file.file) - filename = filestr.split("/")[-1] - filepath = filestr.split("/")[0] - - bef_file = get_names_in_archive(filestr) - preview_data = { - "bef_inp_format": bef_file[0], - "bef_out_format": bef_file[1], - "aft_inp_format": "input.000", - "aft_out_format": "output.000", - "file_str": filestr, - } - - preview = tf_logic.preview(preview_data) - - response = "" - for i in range(len(bef_file)): - bef = preview["bef_preview"][i]["value"] - aft = preview["aft_preview"][i]["value"] - response = response + f"

{bef} => {aft}

\n" - - return render( - request, - "test_formatter/edit_test_formatter.html", - { - "title": _("Test Formatter"), - "check": 0, - "files_list": bef_file, - "file_name": filename, - "res": response, - }, - ) - - def post(self, request, *args, **kwargs): - action = request.POST.get("action") - if action == "convert": - try: - file = TestFormatterModel.objects.last() - filestr = str(file.file) - filename = filestr.split("/")[-1] - filepath = filestr.split("/")[0] - bef_inp_format = request.POST["bef_inp_format"] - bef_out_format = request.POST["bef_out_format"] - aft_inp_format = request.POST["aft_inp_format"] - aft_out_format = request.POST["aft_out_format"] - aft_file_name = request.POST["file_name"] - except KeyError: - return HttpResponseBadRequest("No data.") - - if filename != aft_file_name: - source_path = os.path.join(settings.MEDIA_ROOT, filestr) - new_path = os.path.join( - settings.MEDIA_ROOT, "test_formatter/" + aft_file_name - ) - os.rename(source_path, new_path) - filename = aft_file_name - - preview_data = { - "bef_inp_format": bef_inp_format, - "bef_out_format": bef_out_format, - "aft_inp_format": aft_inp_format, - "aft_out_format": aft_out_format, - "file_name": filename, - "file_path": filepath, - "file_str": filepath + "/" + filename, - } - - converted_zip = tf_logic.convert(preview_data) - - global file_path - file_path = converted_zip["file_path"] - - zip_instance = TestFormatterModel() - zip_instance.file = file_path - zip_instance.save() - - preview = tf_logic.preview(preview_data) - response = HttpResponse() - - for i in range(len(preview["bef_preview"])): - bef = preview["bef_preview"][i]["value"] - aft = preview["aft_preview"][i]["value"] - response.write(f"

{bef} => {aft}

") - - return response - - elif action == "download": - return HttpResponse(file_path) - - return HttpResponseBadRequest("Invalid action") - - -class DownloadTestFormatter(View): - def get(self, request): - file_path = request.GET.get("file_path") - file_name = file_path.split("/")[-1] - preview_file = tf_logic.preview_file(file_path) - - response = "" - for i in range(len(preview_file)): - response = response + (f"

{preview_file[i]}

\n") - - files_list = [preview_file[0], preview_file[1]] - - return render( - request, - "test_formatter/download_test_formatter.html", - { - "title": _("Test Formatter"), - "response": response, - "files_list": files_list, - "file_path": os.path.join(settings.MEDIA_ROOT, file_path), - "file_path_getnames": file_path, - "file_name": file_name, - }, - ) - - def post(self, request): - file_path = request.POST.get("file_path") - - with open(file_path, "rb") as zip_file: - response = HttpResponse(zip_file.read(), content_type="application/zip") - response[ - "Content-Disposition" - ] = f"attachment; filename={os.path.basename(file_path)}" - return response diff --git a/judge/views/test_formatter/tf_logic.py b/judge/views/test_formatter/tf_logic.py deleted file mode 100644 index f981745..0000000 --- a/judge/views/test_formatter/tf_logic.py +++ /dev/null @@ -1,116 +0,0 @@ -import os -from judge.views.test_formatter import test_formatter as tf -from judge.views.test_formatter import tf_pattern as pattern - - -class TestSuite: - def __init__( - self, - file_id: str, - pattern_pair: pattern.PatternPair, - test_id_list: list, - extra_files: list, - ): - self.file_id = file_id - self.pattern_pair = pattern_pair - self.test_id_list = test_id_list - self.extra_files = extra_files - - @classmethod - def get_test_suite(cls, file_name: str, inp_format: str, out_format: str): - pattern_pair = pattern.PatternPair.from_string_pair(inp_format, out_format) - names = tf.get_names_in_archive(file_name) - test_id_list, extra_files = pattern_pair.matches( - names, returns="test_id_with_extra_files" - ) - return cls(file_name, pattern_pair, test_id_list, extra_files) - - def get_name_list(self, add_extra_info=False): - important_files = [] - - for index, t in enumerate(self.test_id_list): - inp_name = self.pattern_pair.x.get_name(t, index=index, use_index=True) - out_name = self.pattern_pair.y.get_name(t, index=index, use_index=True) - important_files.extend([inp_name, out_name]) - - result = [] - - for name in important_files: - if add_extra_info: - result.append({"value": name, "is_extra_file": False}) - else: - result.append(name) - - for name in self.extra_files: - if add_extra_info: - result.append({"value": name, "is_extra_file": True}) - else: - result.append(name) - - return result - - -def is_valid_file_type(file_name): - _, ext = os.path.splitext(file_name) - return ext in [".zip", ".ZIP"] - - -def preview(params): - bif = params["bef_inp_format"] - bof = params["bef_out_format"] - aif = params["aft_inp_format"] - aof = params["aft_out_format"] - file_str = params["file_str"] - - try: - test_suite = TestSuite.get_test_suite(file_str, bif, bof) - bef_preview = test_suite.get_name_list(add_extra_info=True) - try: - test_suite.pattern_pair = pattern.PatternPair.from_string_pair(aif, aof) - aft_preview = test_suite.get_name_list(add_extra_info=True) - return {"bef_preview": bef_preview, "aft_preview": aft_preview} - except: - return {"bef_preview": bef_preview, "aft_preview": []} - except: - test_suite = TestSuite.get_test_suite(file_id, "*", "*") - preview = test_suite.get_name_list(add_extra_info=True) - return {"bef_preview": preview, "aft_preview": []} - - -def convert(params): - bif = params["bef_inp_format"] - bof = params["bef_out_format"] - aif = params["aft_inp_format"] - aof = params["aft_out_format"] - file_str = params["file_str"] - file_name = params["file_name"] - file_path = params["file_path"] - - test_suite = TestSuite.get_test_suite(file_str, bif, bof) - bef_preview = test_suite.get_name_list() - test_suite.pattern_pair = pattern.PatternPair.from_string_pair(aif, aof) - aft_preview = test_suite.get_name_list() - - result = tf.get_renamed_archive( - file_str, file_name, file_path, bef_preview, aft_preview - ) - return result - - -def prefill(params): - file_str = params["file_str"] - file_name = params["file_name"] - - names = tf.get_names_in_archive(file_str) - pattern_pair = pattern.find_best_pattern_pair(names) - - return { - "file_name": file_name, - "inp_format": pattern_pair.x.to_string(), - "out_format": pattern_pair.y.to_string(), - } - - -def preview_file(file_str): - names = tf.get_names_in_archive(file_str) - return names diff --git a/judge/views/test_formatter/tf_pattern.py b/judge/views/test_formatter/tf_pattern.py deleted file mode 100644 index 071976d..0000000 --- a/judge/views/test_formatter/tf_pattern.py +++ /dev/null @@ -1,268 +0,0 @@ -import os -import random -from judge.views.test_formatter import tf_utils as utils - -SAMPLE_SIZE = 16 -NUMBERED_MM = ["0", "1", "00", "01", "000", "001", "0000", "0001"] -VALID_MM = ["*"] + NUMBERED_MM - -MSG_TOO_MANY_OCCURRENCES = ( - "400: Invalid pattern: Pattern cannot have more than one '{}'" -) -MSG_MM_NOT_FOUND = "400: Invalid pattern: Wildcard not found. Wildcard list: {}" - - -class Pattern: - def __init__(self, ll, mm, rr): - assert mm in VALID_MM, "Invalid wildcard" - self.ll = ll - self.mm = mm - self.rr = rr - - def __repr__(self): - return "Pattern('{}', '{}', '{}')".format(self.ll, self.mm, self.rr) - - def __eq__(self, other): - return self.__repr__() == other.__repr__() - - def __hash__(self): - return self.__repr__().__hash__() - - @classmethod - def from_string(cls, text): - for mm in ["*"] + sorted(NUMBERED_MM, key=len, reverse=True): - if mm in text: - if text.count(mm) > 1: - raise Exception(MSG_TOO_MANY_OCCURRENCES.format(mm)) - i = text.index(mm) - return cls(text[:i], mm, text[i + len(mm) :]) - raise Exception(MSG_MM_NOT_FOUND.format(",".join(VALID_MM))) - - def to_string(self): - return self.ll + self.mm + self.rr - - def is_valid_test_id(self, test_id): - if self.mm == "*": - return True - if self.mm in NUMBERED_MM: - return test_id.isdigit() and len(test_id) >= len(self.mm) - raise NotImplementedError - - def matched(self, name): - return ( - name.startswith(self.ll) - and name.endswith(self.rr) - and len(name) >= len(self.ll) + len(self.rr) - and self.is_valid_test_id(self.get_test_id(name)) - ) - - def get_test_id(self, name): - return name[len(self.ll) : len(name) - len(self.rr)] - - def get_test_id_from_index(self, index): - assert self.mm in NUMBERED_MM, "Wildcard is not a number" - return str(int(self.mm) + index).zfill(len(self.mm)) - - def get_name(self, test_id, index=None, use_index=False): - if use_index and self.mm in NUMBERED_MM: - return self.ll + self.get_test_id_from_index(index) + self.rr - return self.ll + test_id + self.rr - - def matches(self, names, returns): - if returns == "test_id": - result = [n for n in names] - result = [n for n in result if self.matched(n)] - result = [self.get_test_id(n) for n in result] - return result - else: - raise NotImplementedError - - -class PatternPair: - def __init__(self, x: Pattern, y: Pattern): - assert x.mm == y.mm, "Input wildcard and output wildcard must be equal" - self.x = x - self.y = y - - def __repr__(self): - return "PatternPair({}, {})".format(self.x, self.y) - - def __eq__(self, other): - return self.__repr__() == other.__repr__() - - def __hash__(self): - return self.__repr__().__hash__() - - @classmethod - def from_string_pair(cls, inp_format, out_format): - return cls(Pattern.from_string(inp_format), Pattern.from_string(out_format)) - - def matches(self, names, returns): - x_test_ids = self.x.matches(names, returns="test_id") - y_test_ids = self.y.matches(names, returns="test_id") - - test_ids = set(x_test_ids) & set(y_test_ids) - test_ids = list(sorted(test_ids, key=utils.natural_sorting_key)) - - if returns == "fast_count": - if self.x.mm == "*": - return len(test_ids) - elif self.x.mm in NUMBERED_MM: - count_valid = 0 - for t in test_ids: - if t == self.x.get_test_id_from_index(count_valid): - count_valid += 1 - - return count_valid - - extra_files = list(names) - valid_test_ids = [] - for t in test_ids: - if self.x.mm in NUMBERED_MM: - if t != self.x.get_test_id_from_index(len(valid_test_ids)): - continue - - inp_name = self.x.get_name(t) - out_name = self.y.get_name(t) - - if inp_name == out_name: - continue - if inp_name not in extra_files: - continue - if out_name not in extra_files: - continue - - valid_test_ids.append(t) - extra_files.remove(inp_name) - extra_files.remove(out_name) - - if returns == "count": - return len(valid_test_ids) - elif returns == "test_id": - return valid_test_ids - elif returns == "test_id_with_extra_files": - return valid_test_ids, extra_files - else: - raise NotImplementedError - - def score(self, names): - def ls(s): - return len(s) - s.count("0") - - def zs(s): - return -s.count("0") - - def vs(s): - return sum( - s.lower().count(c) * w - for c, w in [("a", -1), ("e", -1), ("i", +1), ("o", -1), ("u", -1)] - ) - - count_score = self.matches(names, returns="fast_count") - - len_score = ls(self.x.ll + self.x.rr + self.y.ll + self.y.rr) - zero_score = zs(self.x.ll + self.x.rr + self.y.ll + self.y.rr) - - assert self.x.mm in ["*"] + NUMBERED_MM - specific_score = 0 if self.x.mm == "*" else len(self.x.mm) - - vowel_score = vs(self.x.ll + self.x.rr) - vs(self.y.ll + self.y.rr) - - return count_score, specific_score, len_score, zero_score, vowel_score - - def is_string_safe(self): - try: - x = Pattern.from_string(self.x.to_string()) - y = Pattern.from_string(self.y.to_string()) - return self == PatternPair(x, y) - except: - return False - - -def maximal(a, key): - max_score = max(map(key, a)) - result = [x for x in a if key(x) == max_score] - if len(result) == 1: - return result[0] - else: - print(result) - raise Exception("More than one maximum values") - - -def get_all_star_pattern_pairs(names): - sample = random.sample(names, min(len(names), SAMPLE_SIZE)) - - star_pattern_pairs = [] - - all_prefixes = [n[:i] for n in sample for i in range(len(n) + 1)] - all_prefixes = list(sorted(set(all_prefixes))) - all_suffixes = [n[i:] for n in sample for i in range(len(n) + 1)] - all_suffixes = list(sorted(set(all_suffixes))) - - for prefix in all_prefixes: - matched_names = [n for n in names if n.startswith(prefix)] - if len(matched_names) == 2: - mn0, mn1 = matched_names - for i in range(len(prefix) + 1): - x = Pattern(prefix[:i], "*", mn0[len(prefix) :]) - y = Pattern(prefix[:i], "*", mn1[len(prefix) :]) - star_pattern_pairs.append(PatternPair(x, y)) - - for suffix in all_suffixes: - matched_names = [n for n in names if n.endswith(suffix)] - if len(matched_names) == 2: - mn0, mn1 = matched_names - for i in range(len(suffix) + 1): - x = Pattern(mn0[: len(mn0) - len(suffix)], "*", suffix[i:]) - y = Pattern(mn1[: len(mn1) - len(suffix)], "*", suffix[i:]) - star_pattern_pairs.append(PatternPair(x, y)) - - star_pattern_pairs = list(set(star_pattern_pairs)) - return star_pattern_pairs - - -def get_variant_pattern_pairs(pp): - return [ - PatternPair(Pattern(pp.x.ll, mm, pp.x.rr), Pattern(pp.y.ll, mm, pp.y.rr)) - for mm in VALID_MM - ] + [ - PatternPair(Pattern(pp.y.ll, mm, pp.y.rr), Pattern(pp.x.ll, mm, pp.x.rr)) - for mm in VALID_MM - ] - - -def find_best_pattern_pair(names): - star_pattern_pairs = get_all_star_pattern_pairs(names) - star_pattern_pairs = [ - pp for pp in star_pattern_pairs if pp.matches(names, returns="fast_count") >= 2 - ] - # for pp in star_pattern_pairs: - # print(pp, pp.is_string_safe(), pp.score(names)) - - if len(star_pattern_pairs) == 0: - return PatternPair(Pattern("", "*", ""), Pattern("", "*", "")) - best_star_pattern_pair = maximal(star_pattern_pairs, key=lambda pp: pp.score(names)) - - pattern_pairs = get_variant_pattern_pairs(best_star_pattern_pair) - # for pp in pattern_pairs: - # print(pp, pp.is_string_safe(), pp.score(names)) - pattern_pairs = [pp for pp in pattern_pairs if pp.is_string_safe()] - best_pattern_pair = maximal(pattern_pairs, key=lambda pp: pp.score(names)) - - return best_pattern_pair - - -def list_dir_recursively(folder): - old_cwd = os.getcwd() - os.chdir(folder) - result = [] - for root, _, filenames in os.walk("."): - for filename in filenames: - result.append(os.path.join(root, filename)) - os.chdir(old_cwd) - return result - - -def test_with_dir(folder): - names = list_dir_recursively(folder) - print(folder, find_best_pattern_pair(names)) diff --git a/judge/views/test_formatter/tf_utils.py b/judge/views/test_formatter/tf_utils.py deleted file mode 100644 index 919b069..0000000 --- a/judge/views/test_formatter/tf_utils.py +++ /dev/null @@ -1,15 +0,0 @@ -def get_char_kind(char): - return 1 if char.isdigit() else 2 if char.isalpha() else 3 - - -def natural_sorting_key(name): - result = [] - last_kind = -1 - for char in name: - curr_kind = get_char_kind(char) - if curr_kind != last_kind: - result.append("") - result[-1] += char - last_kind = curr_kind - - return [x.zfill(16) if x.isdigit() else x for x in result] diff --git a/judge/views/ticket.py b/judge/views/ticket.py index 60a151e..afde514 100644 --- a/judge/views/ticket.py +++ b/judge/views/ticket.py @@ -4,18 +4,8 @@ from itertools import chain from django import forms from django.contrib.auth.mixins import LoginRequiredMixin -from django.core.exceptions import ( - ImproperlyConfigured, - PermissionDenied, - ValidationError, -) -from django.http import ( - Http404, - HttpResponse, - HttpResponseBadRequest, - HttpResponseRedirect, - JsonResponse, -) +from django.core.exceptions import ImproperlyConfigured, PermissionDenied, ValidationError +from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404 from django.template.defaultfilters import truncatechars from django.template.loader import get_template @@ -35,103 +25,91 @@ from judge.utils.tickets import filter_visible_tickets, own_ticket_filter from judge.utils.views import SingleObjectFormView, TitleMixin, paginate_query_context from judge.views.problem import ProblemMixin from judge.widgets import HeavyPreviewPageDownWidget -from judge.models.notification import make_notification -ticket_widget = ( - forms.Textarea() - if HeavyPreviewPageDownWidget is None - else HeavyPreviewPageDownWidget( - preview=reverse_lazy("ticket_preview"), - preview_timeout=1000, - hide_preview_button=True, - ) -) +ticket_widget = (forms.Textarea() if HeavyPreviewPageDownWidget is None else + HeavyPreviewPageDownWidget(preview=reverse_lazy('ticket_preview'), + preview_timeout=1000, hide_preview_button=True)) def add_ticket_notifications(users, author, link, ticket): - html = f'{ticket.linked_item}' + html = f"{ticket.linked_item}" + users = set(users) if author in users: users.remove(author) - make_notification(users, "Ticket", html, author) + + for user in users: + notification = Notification(owner=user, + html_link=html, + category='Ticket', + author=author) + notification.save() class TicketForm(forms.Form): - title = forms.CharField(max_length=100, label=gettext_lazy("Ticket title")) + title = forms.CharField(max_length=100, label=gettext_lazy('Ticket title')) body = forms.CharField(widget=ticket_widget) def __init__(self, request, *args, **kwargs): self.request = request super(TicketForm, self).__init__(*args, **kwargs) - self.fields["title"].widget.attrs.update({"placeholder": _("Ticket title")}) - self.fields["body"].widget.attrs.update({"placeholder": _("Issue description")}) + self.fields['title'].widget.attrs.update({'placeholder': _('Ticket title')}) + self.fields['body'].widget.attrs.update({'placeholder': _('Issue description')}) def clean(self): if self.request is not None and self.request.user.is_authenticated: profile = self.request.profile if profile.mute: - raise ValidationError(_("Your part is silent, little toad.")) + raise ValidationError(_('Your part is silent, little toad.')) return super(TicketForm, self).clean() class NewTicketView(LoginRequiredMixin, SingleObjectFormView): form_class = TicketForm - template_name = "ticket/new.html" + template_name = 'ticket/new.html' def get_assignees(self): return [] def get_form_kwargs(self): kwargs = super(NewTicketView, self).get_form_kwargs() - kwargs["request"] = self.request + kwargs['request'] = self.request return kwargs def form_valid(self, form): - ticket = Ticket(user=self.request.profile, title=form.cleaned_data["title"]) + ticket = Ticket(user=self.request.profile, title=form.cleaned_data['title']) ticket.linked_item = self.object ticket.save() - message = TicketMessage( - ticket=ticket, user=ticket.user, body=form.cleaned_data["body"] - ) + message = TicketMessage(ticket=ticket, user=ticket.user, body=form.cleaned_data['body']) message.save() ticket.assignees.set(self.get_assignees()) - link = reverse("ticket", args=[ticket.id]) + link = reverse('ticket', args=[ticket.id]) add_ticket_notifications(ticket.assignees.all(), ticket.user, link, ticket) if event.real: - event.post( - "tickets", - { - "type": "new-ticket", - "id": ticket.id, - "message": message.id, - "user": ticket.user_id, - "assignees": list(ticket.assignees.values_list("id", flat=True)), - }, - ) + event.post('tickets', { + 'type': 'new-ticket', 'id': ticket.id, + 'message': message.id, 'user': ticket.user_id, + 'assignees': list(ticket.assignees.values_list('id', flat=True)), + }) return HttpResponseRedirect(link) class NewProblemTicketView(ProblemMixin, TitleMixin, NewTicketView): - template_name = "ticket/new_problem.html" + template_name = 'ticket/new_problem.html' def get_assignees(self): return self.object.authors.all() def get_title(self): - return _("New ticket for %s") % self.object.name + return _('New ticket for %s') % self.object.name def get_content_title(self): - return mark_safe( - escape(_("New ticket for %s")) - % format_html( - '{1}', - reverse("problem_detail", args=[self.object.code]), - self.object.translated_name(self.request.LANGUAGE_CODE), - ) - ) + return mark_safe(escape(_('New ticket for %s')) % + format_html('{1}', reverse('problem_detail', args=[self.object.code]), + self.object.translated_name(self.request.LANGUAGE_CODE))) def form_valid(self, form): if not self.object.is_accessible_by(self.request.user): @@ -149,7 +127,7 @@ class TicketMixin(object): def get_object(self, queryset=None): ticket = super(TicketMixin, self).get_object(queryset) profile_id = self.request.profile.id - if self.request.user.has_perm("judge.change_ticket"): + if self.request.user.has_perm('judge.change_ticket'): return ticket if ticket.user_id == profile_id: return ticket @@ -163,55 +141,39 @@ class TicketMixin(object): class TicketView(TitleMixin, LoginRequiredMixin, TicketMixin, SingleObjectFormView): form_class = TicketCommentForm - template_name = "ticket/ticket.html" - context_object_name = "ticket" + template_name = 'ticket/ticket.html' + context_object_name = 'ticket' def form_valid(self, form): - message = TicketMessage( - user=self.request.profile, - body=form.cleaned_data["body"], - ticket=self.object, - ) + message = TicketMessage(user=self.request.profile, + body=form.cleaned_data['body'], + ticket=self.object) message.save() - link = "%s#message-%d" % (reverse("ticket", args=[self.object.id]), message.id) + link = '%s#message-%d' % (reverse('ticket', args=[self.object.id]), message.id) notify_list = list(chain(self.object.assignees.all(), [self.object.user])) add_ticket_notifications(notify_list, message.user, link, self.object) if event.real: - event.post( - "tickets", - { - "type": "ticket-message", - "id": self.object.id, - "message": message.id, - "user": self.object.user_id, - "assignees": list( - self.object.assignees.values_list("id", flat=True) - ), - }, - ) - event.post( - "ticket-%d" % self.object.id, - { - "type": "ticket-message", - "message": message.id, - }, - ) + event.post('tickets', { + 'type': 'ticket-message', 'id': self.object.id, + 'message': message.id, 'user': self.object.user_id, + 'assignees': list(self.object.assignees.values_list('id', flat=True)), + }) + event.post('ticket-%d' % self.object.id, { + 'type': 'ticket-message', 'message': message.id, + }) return HttpResponseRedirect(link) def get_title(self): - return _("%(title)s - Ticket %(id)d") % { - "title": self.object.title, - "id": self.object.id, - } + return _('%(title)s - Ticket %(id)d') % {'title': self.object.title, 'id': self.object.id} def get_context_data(self, **kwargs): context = super(TicketView, self).get_context_data(**kwargs) - context["ticket_messages"] = self.object.messages.select_related("user__user") - context["assignees"] = self.object.assignees.select_related("user") - context["last_msg"] = event.last() + context['ticket_messages'] = self.object.messages.select_related('user__user') + context['assignees'] = self.object.assignees.select_related('user') + context['last_msg'] = event.last() return context @@ -220,32 +182,21 @@ class TicketStatusChangeView(LoginRequiredMixin, TicketMixin, SingleObjectMixin, def post(self, request, *args, **kwargs): if self.open is None: - raise ImproperlyConfigured("Need to define open") + raise ImproperlyConfigured('Need to define open') ticket = self.get_object() if ticket.is_open != self.open: ticket.is_open = self.open ticket.save() if event.real: - event.post( - "tickets", - { - "type": "ticket-status", - "id": ticket.id, - "open": self.open, - "user": ticket.user_id, - "assignees": list( - ticket.assignees.values_list("id", flat=True) - ), - "title": ticket.title, - }, - ) - event.post( - "ticket-%d" % ticket.id, - { - "type": "ticket-status", - "open": self.open, - }, - ) + event.post('tickets', { + 'type': 'ticket-status', 'id': ticket.id, + 'open': self.open, 'user': ticket.user_id, + 'assignees': list(ticket.assignees.values_list('id', flat=True)), + 'title': ticket.title, + }) + event.post('ticket-%d' % ticket.id, { + 'type': 'ticket-status', 'open': self.open, + }) return HttpResponse(status=204) @@ -254,16 +205,16 @@ class TicketNotesForm(forms.Form): class TicketNotesEditView(LoginRequiredMixin, TicketMixin, SingleObjectFormView): - template_name = "ticket/edit-notes.html" + template_name = 'ticket/edit-notes.html' form_class = TicketNotesForm - context_object_name = "ticket" + context_object_name = 'ticket' def get_initial(self): - return {"notes": self.get_object().notes} + return {'notes': self.get_object().notes} def form_valid(self, form): ticket = self.get_object() - ticket.notes = notes = form.cleaned_data["notes"] + ticket.notes = notes = form.cleaned_data['notes'] ticket.save() if notes: return HttpResponse(linebreaks(notes, autoescape=True)) @@ -276,8 +227,8 @@ class TicketNotesEditView(LoginRequiredMixin, TicketMixin, SingleObjectFormView) class TicketList(LoginRequiredMixin, ListView): model = Ticket - template_name = "ticket/list.html" - context_object_name = "tickets" + template_name = 'ticket/list.html' + context_object_name = 'tickets' paginate_by = 50 paginator_class = DiggPaginator @@ -291,38 +242,32 @@ class TicketList(LoginRequiredMixin, ListView): @cached_property def can_edit_all(self): - return self.request.user.has_perm("judge.change_ticket") + return self.request.user.has_perm('judge.change_ticket') @cached_property def filter_users(self): - return self.request.GET.getlist("user") + return self.request.GET.getlist('user') @cached_property def filter_assignees(self): - return self.request.GET.getlist("assignee") + return self.request.GET.getlist('assignee') def GET_with_session(self, key): if not self.request.GET: return self.request.session.get(key, False) - return self.request.GET.get(key, None) == "1" + return self.request.GET.get(key, None) == '1' def _get_queryset(self): - return ( - Ticket.objects.select_related("user__user") - .prefetch_related("assignees__user") - .order_by("-id") - ) + return Ticket.objects.select_related('user__user').prefetch_related('assignees__user').order_by('-id') def get_queryset(self): queryset = self._get_queryset() - if self.GET_with_session("own"): + if self.GET_with_session('own'): queryset = queryset.filter(own_ticket_filter(self.profile.id)) elif not self.can_edit_all: queryset = filter_visible_tickets(queryset, self.user, self.profile) if self.filter_assignees: - queryset = queryset.filter( - assignees__user__username__in=self.filter_assignees - ) + queryset = queryset.filter(assignees__user__username__in=self.filter_assignees) if self.filter_users: queryset = queryset.filter(user__user__username__in=self.filter_users) return queryset.distinct() @@ -330,41 +275,29 @@ class TicketList(LoginRequiredMixin, ListView): def get_context_data(self, **kwargs): context = super(TicketList, self).get_context_data(**kwargs) - page = context["page_obj"] - context["title"] = _("Tickets - Page %(number)d of %(total)d") % { - "number": page.number, - "total": page.paginator.num_pages, + page = context['page_obj'] + context['title'] = _('Tickets - Page %(number)d of %(total)d') % { + 'number': page.number, + 'total': page.paginator.num_pages, } - context["can_edit_all"] = self.can_edit_all - context["filter_status"] = { - "own": self.GET_with_session("own"), - "user": self.filter_users, - "assignee": self.filter_assignees, - "user_id": json.dumps( - list( - Profile.objects.filter( - user__username__in=self.filter_users - ).values_list("id", flat=True) - ) - ), - "assignee_id": json.dumps( - list( - Profile.objects.filter( - user__username__in=self.filter_assignees - ).values_list("id", flat=True) - ) - ), - "own_id": self.profile.id if self.GET_with_session("own") else "null", + context['can_edit_all'] = self.can_edit_all + context['filter_status'] = { + 'own': self.GET_with_session('own'), 'user': self.filter_users, 'assignee': self.filter_assignees, + 'user_id': json.dumps(list(Profile.objects.filter(user__username__in=self.filter_users) + .values_list('id', flat=True))), + 'assignee_id': json.dumps(list(Profile.objects.filter(user__username__in=self.filter_assignees) + .values_list('id', flat=True))), + 'own_id': self.profile.id if self.GET_with_session('own') else 'null', } - context["last_msg"] = event.last() + context['last_msg'] = event.last() context.update(paginate_query_context(self.request)) return context def post(self, request, *args, **kwargs): - to_update = ("own",) + to_update = ('own',) for key in to_update: if key in request.GET: - val = request.GET.get(key) == "1" + val = request.GET.get(key) == '1' request.session[key] = val else: request.session.pop(key, None) @@ -373,56 +306,38 @@ class TicketList(LoginRequiredMixin, ListView): class ProblemTicketListView(TicketList): def _get_queryset(self): - problem = get_object_or_404(Problem, code=self.kwargs.get("problem")) + problem = get_object_or_404(Problem, code=self.kwargs.get('problem')) if problem.is_editable_by(self.request.user): - return problem.tickets.order_by("-id") + return problem.tickets.order_by('-id') elif problem.is_accessible_by(self.request.user): - return problem.tickets.filter(own_ticket_filter(self.profile.id)).order_by( - "-id" - ) + return problem.tickets.filter(own_ticket_filter(self.profile.id)).order_by('-id') raise Http404() class TicketListDataAjax(TicketMixin, SingleObjectMixin, View): def get(self, request, *args, **kwargs): try: - self.kwargs["pk"] = request.GET["id"] + self.kwargs['pk'] = request.GET['id'] except KeyError: return HttpResponseBadRequest() ticket = self.get_object() message = ticket.messages.first() - return JsonResponse( - { - "row": get_template("ticket/row.html").render( - {"ticket": ticket}, request - ), - "notification": { - "title": _("New Ticket: %s") % ticket.title, - "body": "%s\n%s" - % ( - _("#%(id)d, assigned to: %(users)s") - % { - "id": ticket.id, - "users": ( - _(", ").join( - ticket.assignees.values_list( - "user__username", flat=True - ) - ) - or _("no one") - ), - }, - truncatechars(message.body, 200), - ), - }, - } - ) + return JsonResponse({ + 'row': get_template('ticket/row.html').render({'ticket': ticket}, request), + 'notification': { + 'title': _('New Ticket: %s') % ticket.title, + 'body': '%s\n%s' % (_('#%(id)d, assigned to: %(users)s') % { + 'id': ticket.id, + 'users': (_(', ').join(ticket.assignees.values_list('user__username', flat=True)) or _('no one')), + }, truncatechars(message.body, 200)), + }, + }) class TicketMessageDataAjax(TicketMixin, SingleObjectMixin, View): def get(self, request, *args, **kwargs): try: - message_id = request.GET["message"] + message_id = request.GET['message'] except KeyError: return HttpResponseBadRequest() ticket = self.get_object() @@ -430,14 +345,10 @@ class TicketMessageDataAjax(TicketMixin, SingleObjectMixin, View): message = ticket.messages.get(id=message_id) except TicketMessage.DoesNotExist: return HttpResponseBadRequest() - return JsonResponse( - { - "message": get_template("ticket/message.html").render( - {"message": message}, request - ), - "notification": { - "title": _("New Ticket Message For: %s") % ticket.title, - "body": truncatechars(message.body, 200), - }, - } - ) + return JsonResponse({ + 'message': get_template('ticket/message.html').render({'message': message}, request), + 'notification': { + 'title': _('New Ticket Message For: %s') % ticket.title, + 'body': truncatechars(message.body, 200), + }, + }) diff --git a/judge/views/totp.py b/judge/views/totp.py index 88fd63a..097137f 100644 --- a/judge/views/totp.py +++ b/judge/views/totp.py @@ -21,7 +21,7 @@ class TOTPView(TitleMixin, LoginRequiredMixin, FormView): def get_form_kwargs(self): result = super(TOTPView, self).get_form_kwargs() - result["totp_key"] = self.profile.totp_key + result['totp_key'] = self.profile.totp_key return result def dispatch(self, request, *args, **kwargs): @@ -35,12 +35,12 @@ class TOTPView(TitleMixin, LoginRequiredMixin, FormView): raise NotImplementedError() def next_page(self): - return HttpResponseRedirect(reverse("user_edit_profile")) + return HttpResponseRedirect(reverse('user_edit_profile')) class TOTPEnableView(TOTPView): - title = _("Enable Two Factor Authentication") - template_name = "registration/totp_enable.html" + title = _('Enable Two Factor Authentication') + template_name = 'registration/totp_enable.html' def get(self, request, *args, **kwargs): profile = self.profile @@ -54,22 +54,20 @@ class TOTPEnableView(TOTPView): def post(self, request, *args, **kwargs): if not self.profile.totp_key: - return HttpResponseBadRequest("No TOTP key generated on server side?") + return HttpResponseBadRequest('No TOTP key generated on server side?') return super(TOTPEnableView, self).post(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super(TOTPEnableView, self).get_context_data(**kwargs) - context["totp_key"] = self.profile.totp_key - context["qr_code"] = self.render_qr_code( - self.request.user.username, self.profile.totp_key - ) + context['totp_key'] = self.profile.totp_key + context['qr_code'] = self.render_qr_code(self.request.user.username, self.profile.totp_key) return context def form_valid(self, form): self.profile.is_totp_enabled = True self.profile.save() # Make sure users don't get prompted to enter code right after enabling: - self.request.session["2fa_passed"] = True + self.request.session['2fa_passed'] = True return self.next_page() @classmethod @@ -81,17 +79,15 @@ class TOTPEnableView(TOTPView): qr.add_data(uri) qr.make(fit=True) - image = qr.make_image(fill_color="black", back_color="white") + image = qr.make_image(fill_color='black', back_color='white') buf = BytesIO() - image.save(buf, format="PNG") - return "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode( - "ascii" - ) + image.save(buf, format='PNG') + return 'data:image/png;base64,' + base64.b64encode(buf.getvalue()).decode('ascii') class TOTPDisableView(TOTPView): - title = _("Disable Two Factor Authentication") - template_name = "registration/totp_disable.html" + title = _('Disable Two Factor Authentication') + template_name = 'registration/totp_disable.html' def check_skip(self): if not self.profile.is_totp_enabled: @@ -106,25 +102,21 @@ class TOTPDisableView(TOTPView): class TOTPLoginView(SuccessURLAllowedHostsMixin, TOTPView): - title = _("Perform Two Factor Authentication") - template_name = "registration/totp_auth.html" + title = _('Perform Two Factor Authentication') + template_name = 'registration/totp_auth.html' def check_skip(self): - return not self.profile.is_totp_enabled or self.request.session.get( - "2fa_passed", False - ) + return not self.profile.is_totp_enabled or self.request.session.get('2fa_passed', False) def next_page(self): - redirect_to = self.request.GET.get("next", "") + redirect_to = self.request.GET.get('next', '') url_is_safe = is_safe_url( url=redirect_to, allowed_hosts=self.get_success_url_allowed_hosts(), require_https=self.request.is_secure(), ) - return HttpResponseRedirect( - (redirect_to if url_is_safe else "") or reverse("user_page") - ) + return HttpResponseRedirect((redirect_to if url_is_safe else '') or reverse('user_page')) def form_valid(self, form): - self.request.session["2fa_passed"] = True + self.request.session['2fa_passed'] = True return self.next_page() diff --git a/judge/views/user.py b/judge/views/user.py index 6feaffa..2265979 100644 --- a/judge/views/user.py +++ b/judge/views/user.py @@ -13,16 +13,8 @@ from django.db import transaction from django.db.models import Count, Max, Min from django.db.models.fields import DateField from django.db.models.functions import Cast, ExtractYear -from judge.models.bookmark import MakeBookMark from django.forms import Form -from django.http import ( - Http404, - HttpResponseRedirect, - JsonResponse, - HttpResponseForbidden, - HttpResponseBadRequest, - HttpResponse, -) +from django.http import Http404, HttpResponseRedirect, JsonResponse, HttpResponseForbidden, HttpResponseBadRequest, HttpResponse from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.utils import timezone @@ -35,50 +27,19 @@ from django.views.generic import DetailView, ListView, TemplateView from django.template.loader import render_to_string from reversion import revisions -from judge.forms import UserForm, ProfileForm, ProfileInfoForm -from judge.models import ( - Profile, - Rating, - Submission, - Friend, - ProfileInfo, - BlogPost, - Problem, - Contest, - Solution, -) +from judge.forms import ProfileForm, newsletter_id +from judge.models import Profile, Rating, Submission, Friend from judge.performance_points import get_pp_breakdown from judge.ratings import rating_class, rating_progress from judge.tasks import import_users from judge.utils.problems import contest_completed_ids, user_completed_ids from judge.utils.ranker import ranker +from judge.utils.subscription import Subscription from judge.utils.unicode import utf8text -from judge.utils.users import ( - get_rating_rank, - get_points_rank, - get_awards, - get_contest_ratings, -) -from judge.utils.views import ( - QueryStringSortMixin, - TitleMixin, - generic_message, - SingleObjectFormView, - DiggPaginatorMixin, -) -from judge.utils.infinite_paginator import InfinitePaginationMixin -from judge.views.problem import ProblemList +from judge.utils.views import DiggPaginatorMixin, QueryStringSortMixin, TitleMixin, generic_message, SingleObjectFormView from .contests import ContestRanking - -__all__ = [ - "UserPage", - "UserAboutPage", - "UserProblemsPage", - "UserBookMarkPage", - "users", - "edit_profile", -] +__all__ = ['UserPage', 'UserAboutPage', 'UserProblemsPage', 'users', 'edit_profile'] def remap_keys(iterable, mapping): @@ -87,16 +48,16 @@ def remap_keys(iterable, mapping): class UserMixin(object): model = Profile - slug_field = "user__username" - slug_url_kwarg = "user" - context_object_name = "user" + slug_field = 'user__username' + slug_url_kwarg = 'user' + context_object_name = 'user' def render_to_response(self, context, **response_kwargs): return super(UserMixin, self).render_to_response(context, **response_kwargs) class UserPage(TitleMixin, UserMixin, DetailView): - template_name = "user/user-base.html" + template_name = 'user/user-base.html' def get_object(self, queryset=None): if self.kwargs.get(self.slug_url_kwarg, None) is None: @@ -110,21 +71,15 @@ class UserPage(TitleMixin, UserMixin, DetailView): try: return super(UserPage, self).dispatch(request, *args, **kwargs) except Http404: - return generic_message( - request, - _("No such user"), - _('No user handle "%s".') % self.kwargs.get(self.slug_url_kwarg, None), - ) + return generic_message(request, _('No such user'), _('No user handle "%s".') % + self.kwargs.get(self.slug_url_kwarg, None)) def get_title(self): - return ( - _("My account") - if self.request.profile == self.object - else _("User %s") % self.object.username - ) + return (_('My account') if self.request.user == self.object.user else + _('User %s') % self.object.user.username) def get_content_title(self): - username = self.object.username + username = self.object.user.username css_class = self.object.css_class return mark_safe(f'{username}') @@ -137,11 +92,8 @@ class UserPage(TitleMixin, UserMixin, DetailView): @cached_property def in_contest(self): - return ( - self.profile is not None - and self.profile.current_contest is not None + return self.profile is not None and self.profile.current_contest is not None \ and self.request.in_contest_mode - ) def get_completed_problems(self): if self.in_contest: @@ -152,37 +104,29 @@ class UserPage(TitleMixin, UserMixin, DetailView): def get_context_data(self, **kwargs): context = super(UserPage, self).get_context_data(**kwargs) - context["followed"] = Friend.is_friend(self.request.profile, self.object) - context["hide_solved"] = int(self.hide_solved) - context["authored"] = self.object.authored_problems.filter( - is_public=True, is_organization_private=False - ).order_by("code") + context['followed'] = Friend.is_friend(self.request.profile, self.object) + context['hide_solved'] = int(self.hide_solved) + context['authored'] = self.object.authored_problems.filter(is_public=True, is_organization_private=False) \ + .order_by('code') - rating = self.object.ratings.order_by("-contest__end_time")[:1] - context["rating"] = rating[0] if rating else None + rating = self.object.ratings.order_by('-contest__end_time')[:1] + context['rating'] = rating[0] if rating else None - context["points_rank"] = get_points_rank(self.object) + context['rank'] = Profile.objects.filter( + is_unlisted=False, performance_points__gt=self.object.performance_points, + ).count() + 1 if rating: - context["rating_rank"] = get_rating_rank(self.object) - context["rated_users"] = Profile.objects.filter( - is_unlisted=False, rating__isnull=False - ).count() - context.update( - self.object.ratings.aggregate( - min_rating=Min("rating"), - max_rating=Max("rating"), - contests=Count("contest"), - ) - ) + context['rating_rank'] = Profile.objects.filter( + is_unlisted=False, rating__gt=self.object.rating, + ).count() + 1 + context['rated_users'] = Profile.objects.filter(is_unlisted=False, rating__isnull=False).count() + context.update(self.object.ratings.aggregate(min_rating=Min('rating'), max_rating=Max('rating'), + contests=Count('contest'))) return context def get(self, request, *args, **kwargs): - self.hide_solved = ( - request.GET.get("hide_solved") == "1" - if "hide_solved" in request.GET - else False - ) + self.hide_solved = request.GET.get('hide_solved') == '1' if 'hide_solved' in request.GET else False return super(UserPage, self).get(request, *args, **kwargs) @@ -190,296 +134,230 @@ EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) class UserAboutPage(UserPage): - template_name = "user/user-about.html" + template_name = 'user/user-about.html' + + def get_awards(self, ratings): + result = {} + + sorted_ratings = sorted(ratings, + key=lambda x: (x.rank, -x.contest.end_time.timestamp())) + + result['medals'] = [{ + 'label': rating.contest.name, + 'ranking': rating.rank, + 'link': reverse('contest_ranking', args=(rating.contest.key,)) + '#!' + self.object.username, + 'date': date_format(rating.contest.end_time, _('M j, Y')), + } for rating in sorted_ratings if rating.rank <= 3] + + num_awards = 0 + for i in result: + num_awards += len(result[i]) + + if num_awards == 0: + result = None + + return result def get_context_data(self, **kwargs): context = super(UserAboutPage, self).get_context_data(**kwargs) - ratings = context["ratings"] = get_contest_ratings(self.object) + ratings = context['ratings'] = self.object.ratings.order_by('-contest__end_time').select_related('contest') \ + .defer('contest__description') - context["rating_data"] = mark_safe( - json.dumps( - [ - { - "label": rating.contest.name, - "rating": rating.rating, - "ranking": rating.rank, - "link": reverse("contest_ranking", args=(rating.contest.key,)) - + "#!" - + self.object.username, - "timestamp": (rating.contest.end_time - EPOCH).total_seconds() - * 1000, - "date": date_format( - timezone.localtime(rating.contest.end_time), - _("M j, Y, G:i"), - ), - "class": rating_class(rating.rating), - "height": "%.3fem" % rating_progress(rating.rating), - } - for rating in ratings - ] - ) - ) + context['rating_data'] = mark_safe(json.dumps([{ + 'label': rating.contest.name, + 'rating': rating.rating, + 'ranking': rating.rank, + 'link': reverse('contest_ranking', args=(rating.contest.key,)), + 'timestamp': (rating.contest.end_time - EPOCH).total_seconds() * 1000, + 'date': date_format(rating.contest.end_time, _('M j, Y, G:i')), + 'class': rating_class(rating.rating), + 'height': '%.3fem' % rating_progress(rating.rating), + } for rating in ratings])) - context["awards"] = get_awards(self.object) + context['awards'] = self.get_awards(ratings) if ratings: - user_data = self.object.ratings.aggregate(Min("rating"), Max("rating")) - global_data = Rating.objects.aggregate(Min("rating"), Max("rating")) - min_ever, max_ever = global_data["rating__min"], global_data["rating__max"] - min_user, max_user = user_data["rating__min"], user_data["rating__max"] + user_data = self.object.ratings.aggregate(Min('rating'), Max('rating')) + global_data = Rating.objects.aggregate(Min('rating'), Max('rating')) + min_ever, max_ever = global_data['rating__min'], global_data['rating__max'] + min_user, max_user = user_data['rating__min'], user_data['rating__max'] delta = max_user - min_user - ratio = ( - (max_ever - max_user) / (max_ever - min_ever) - if max_ever != min_ever - else 1.0 - ) - context["max_graph"] = max_user + ratio * delta - context["min_graph"] = min_user + ratio * delta - delta + ratio = (max_ever - max_user) / (max_ever - min_ever) if max_ever != min_ever else 1.0 + context['max_graph'] = max_user + ratio * delta + context['min_graph'] = min_user + ratio * delta - delta + submissions = ( - self.object.submission_set.annotate(date_only=Cast("date", DateField())) - .values("date_only") - .annotate(cnt=Count("id")) - ) - - context["submission_data"] = mark_safe( - json.dumps( - { - date_counts["date_only"].isoformat(): date_counts["cnt"] - for date_counts in submissions - } - ) - ) - context["submission_metadata"] = mark_safe( - json.dumps( - { - "min_year": ( - self.object.submission_set.annotate( - year_only=ExtractYear("date") - ).aggregate(min_year=Min("year_only"))["min_year"] - ), - } - ) + self.object.submission_set + .annotate(date_only=Cast('date', DateField())) + .values('date_only').annotate(cnt=Count('id')) ) + context['submission_data'] = mark_safe(json.dumps({ + date_counts['date_only'].isoformat(): date_counts['cnt'] for date_counts in submissions + })) + context['submission_metadata'] = mark_safe(json.dumps({ + 'min_year': ( + self.object.submission_set + .annotate(year_only=ExtractYear('date')) + .aggregate(min_year=Min('year_only'))['min_year'] + ), + })) + return context + # follow/unfollow user + def post(self, request, user, *args, **kwargs): + try: + if not request.profile: + raise Exception('You have to login') + if (request.profile.username == user): + raise Exception('Cannot make friend with yourself') + + following_profile = Profile.objects.get(user__username=user) + Friend.toggle_friend(request.profile, following_profile) + finally: + return HttpResponseRedirect(request.path_info) + class UserProblemsPage(UserPage): - template_name = "user/user-problems.html" + template_name = 'user/user-problems.html' def get_context_data(self, **kwargs): context = super(UserProblemsPage, self).get_context_data(**kwargs) - result = ( - Submission.objects.filter( - user=self.object, - points__gt=0, - problem__is_public=True, - problem__is_organization_private=False, - ) - .exclude( - problem__in=self.get_completed_problems() if self.hide_solved else [] - ) - .values( - "problem__id", - "problem__code", - "problem__name", - "problem__points", - "problem__group__full_name", - ) - .distinct() - .annotate(points=Max("points")) - .order_by("problem__group__full_name", "problem__code") - ) + result = Submission.objects.filter(user=self.object, points__gt=0, problem__is_public=True, + problem__is_organization_private=False) \ + .exclude(problem__in=self.get_completed_problems() if self.hide_solved else []) \ + .values('problem__id', 'problem__code', 'problem__name', 'problem__points', 'problem__group__full_name') \ + .distinct().annotate(points=Max('points')).order_by('problem__group__full_name', 'problem__code') def process_group(group, problems_iter): problems = list(problems_iter) - points = sum(map(itemgetter("points"), problems)) - return {"name": group, "problems": problems, "points": points} + points = sum(map(itemgetter('points'), problems)) + return {'name': group, 'problems': problems, 'points': points} - context["best_submissions"] = [ - process_group(group, problems) - for group, problems in itertools.groupby( - remap_keys( - result, - { - "problem__code": "code", - "problem__name": "name", - "problem__points": "total", - "problem__group__full_name": "group", - }, - ), - itemgetter("group"), - ) + context['best_submissions'] = [ + process_group(group, problems) for group, problems in itertools.groupby( + remap_keys(result, { + 'problem__code': 'code', 'problem__name': 'name', 'problem__points': 'total', + 'problem__group__full_name': 'group', + }), itemgetter('group')) ] breakdown, has_more = get_pp_breakdown(self.object, start=0, end=10) - context["pp_breakdown"] = breakdown - context["pp_has_more"] = has_more - - return context - - -class UserBookMarkPage(DiggPaginatorMixin, ListView, UserPage): - template_name = "user/user-bookmarks.html" - context_object_name = "bookmarks" - paginate_by = 10 - - def get(self, request, *args, **kwargs): - self.current_tab = self.request.GET.get("tab", "problems") - self.user = self.object = self.get_object() - return super(UserBookMarkPage, self).get(request, *args, **kwargs) - - def get_queryset(self): - model = None - if self.current_tab == "posts": - model = BlogPost - elif self.current_tab == "contests": - model = Contest - elif self.current_tab == "editorials": - model = Solution - else: - model = Problem - - q = MakeBookMark.objects.filter(user=self.user).select_related("bookmark") - q = q.filter(bookmark__content_type=ContentType.objects.get_for_model(model)) - object_ids = q.values_list("bookmark__object_id", flat=True) - - res = model.objects.filter(id__in=object_ids) - if self.current_tab == "contests": - res = res.prefetch_related("organizations", "tags") - elif self.current_tab == "editorials": - res = res.select_related("problem") - - return res - - def get_context_data(self, **kwargs): - context = super(UserBookMarkPage, self).get_context_data(**kwargs) - - context["current_tab"] = self.current_tab - context["user"] = self.user - - context["page_prefix"] = ( - self.request.path + "?tab=" + self.current_tab + "&page=" - ) - context["first_page_href"] = self.request.path + context['pp_breakdown'] = breakdown + context['pp_has_more'] = has_more return context class UserPerformancePointsAjax(UserProblemsPage): - template_name = "user/pp-table-body.html" + template_name = 'user/pp-table-body.html' def get_context_data(self, **kwargs): context = super(UserPerformancePointsAjax, self).get_context_data(**kwargs) try: - start = int(self.request.GET.get("start", 0)) - end = int(self.request.GET.get("end", settings.DMOJ_PP_ENTRIES)) + start = int(self.request.GET.get('start', 0)) + end = int(self.request.GET.get('end', settings.DMOJ_PP_ENTRIES)) if start < 0 or end < 0 or start > end: raise ValueError except ValueError: start, end = 0, 100 breakdown, self.has_more = get_pp_breakdown(self.object, start=start, end=end) - context["pp_breakdown"] = breakdown + context['pp_breakdown'] = breakdown return context def get(self, request, *args, **kwargs): httpresp = super(UserPerformancePointsAjax, self).get(request, *args, **kwargs) httpresp.render() - return JsonResponse( - { - "results": utf8text(httpresp.content), - "has_more": self.has_more, - } - ) + return JsonResponse({ + 'results': utf8text(httpresp.content), + 'has_more': self.has_more, + }) @login_required def edit_profile(request): - profile = request.profile - profile_info, created = ProfileInfo.objects.get_or_create(profile=profile) - if request.method == "POST": - form_user = UserForm(request.POST, instance=request.user) - form = ProfileForm( - request.POST, request.FILES, instance=profile, user=request.user - ) - form_info = ProfileInfoForm(request.POST, instance=profile_info) - if form_user.is_valid() and form.is_valid(): - with revisions.create_revision(): - form_user.save() + profile = Profile.objects.get(user=request.user) + if profile.mute: + raise Http404() + if request.method == 'POST': + form = ProfileForm(request.POST, instance=profile, user=request.user) + if form.is_valid(): + with transaction.atomic(), revisions.create_revision(): form.save() - form_info.save() revisions.set_user(request.user) - revisions.set_comment(_("Updated on site")) + revisions.set_comment(_('Updated on site')) + + if newsletter_id is not None: + try: + subscription = Subscription.objects.get(user=request.user, newsletter_id=newsletter_id) + except Subscription.DoesNotExist: + if form.cleaned_data['newsletter']: + Subscription(user=request.user, newsletter_id=newsletter_id, subscribed=True).save() + else: + if subscription.subscribed != form.cleaned_data['newsletter']: + subscription.update(('unsubscribe', 'subscribe')[form.cleaned_data['newsletter']]) + + perm = Permission.objects.get(codename='test_site', content_type=ContentType.objects.get_for_model(Profile)) + if form.cleaned_data['test_site']: + request.user.user_permissions.add(perm) + else: + request.user.user_permissions.remove(perm) + return HttpResponseRedirect(request.path) else: - form_user = UserForm(instance=request.user) form = ProfileForm(instance=profile, user=request.user) - form_info = ProfileInfoForm(instance=profile_info) + if newsletter_id is not None: + try: + subscription = Subscription.objects.get(user=request.user, newsletter_id=newsletter_id) + except Subscription.DoesNotExist: + form.fields['newsletter'].initial = False + else: + form.fields['newsletter'].initial = subscription.subscribed + form.fields['test_site'].initial = request.user.has_perm('judge.test_site') tzmap = settings.TIMEZONE_MAP - - return render( - request, - "user/edit-profile.html", - { - "require_staff_2fa": settings.DMOJ_REQUIRE_STAFF_2FA, - "form_user": form_user, - "form": form, - "form_info": form_info, - "title": _("Edit profile"), - "profile": profile, - "TIMEZONE_MAP": tzmap or "http://momentjs.com/static/img/world.png", - "TIMEZONE_BG": settings.TIMEZONE_BG if tzmap else "#4E7CAD", - }, - ) + print(settings.REGISTER_NAME_URL) + return render(request, 'user/edit-profile.html', { + 'edit_name_url': settings.REGISTER_NAME_URL, + 'require_staff_2fa': settings.DMOJ_REQUIRE_STAFF_2FA, + 'form': form, 'title': _('Edit profile'), 'profile': profile, + 'has_math_config': bool(settings.MATHOID_URL), + 'TIMEZONE_MAP': tzmap or 'http://momentjs.com/static/img/world.png', + 'TIMEZONE_BG': settings.TIMEZONE_BG if tzmap else '#4E7CAD', + }) -class UserList(QueryStringSortMixin, InfinitePaginationMixin, TitleMixin, ListView): +class UserList(QueryStringSortMixin, DiggPaginatorMixin, TitleMixin, ListView): model = Profile - title = gettext_lazy("Leaderboard") - context_object_name = "users" - template_name = "user/list.html" + title = gettext_lazy('Leaderboard') + context_object_name = 'users' + template_name = 'user/list.html' paginate_by = 100 - all_sorts = frozenset(("points", "problem_count", "rating", "performance_points")) + all_sorts = frozenset(('points', 'problem_count', 'rating', 'performance_points')) default_desc = all_sorts - default_sort = "-performance_points" - filter_friend = False + default_sort = '-performance_points' def filter_friend_queryset(self, queryset): - friends = self.request.profile.get_friends() - ret = queryset.filter(id__in=friends) + friends = list(self.request.profile.get_friends()) + ret = queryset.filter(user__username__in=friends) return ret def get_queryset(self): - queryset = ( - Profile.objects.filter(is_unlisted=False) - .order_by(self.order, "id") - .only( - "display_rank", - "points", - "rating", - "performance_points", - "problem_count", - "about", - ) - ) - if self.request.organization: - queryset = queryset.filter(organizations=self.request.organization) - if (self.request.GET.get("friend") == "true") and self.request.profile: - queryset = self.filter_friend_queryset(queryset) - self.filter_friend = True - return queryset + ret = Profile.objects.filter(is_unlisted=False).order_by(self.order, 'id').select_related('user') \ + .only('display_rank', 'user__username', 'points', 'rating', 'performance_points', + 'problem_count') + + if (self.request.GET.get('friend') == 'true') and self.request.profile: + ret = self.filter_friend_queryset(ret) + return ret def get_context_data(self, **kwargs): context = super(UserList, self).get_context_data(**kwargs) - Profile.prefetch_profile_cache([u.id for u in context["users"]]) - context["users"] = ranker( - context["users"], rank=self.paginate_by * (context["page_obj"].number - 1) - ) - context["first_page_href"] = "." - context["page_type"] = "friends" if self.filter_friend else "list" + context['users'] = ranker(context['users'], rank=self.paginate_by * (context['page_obj'].number - 1)) + context['first_page_href'] = '.' context.update(self.get_sort_context()) context.update(self.get_sort_paginate_context()) return context @@ -500,36 +378,27 @@ def users(request): if request.in_contest_mode: participation = request.profile.current_contest contest = participation.contest - return FixedContestRanking.as_view(contest=contest)( - request, contest=contest.key - ) + return FixedContestRanking.as_view(contest=contest)(request, contest=contest.key) return user_list_view(request) def user_ranking_redirect(request): try: - username = request.GET["handle"] + username = request.GET['handle'] except KeyError: raise Http404() user = get_object_or_404(Profile, user__username=username) - rank = Profile.objects.filter( - is_unlisted=False, performance_points__gt=user.performance_points - ).count() + rank = Profile.objects.filter(is_unlisted=False, performance_points__gt=user.performance_points).count() rank += Profile.objects.filter( - is_unlisted=False, - performance_points__exact=user.performance_points, - id__lt=user.id, + is_unlisted=False, performance_points__exact=user.performance_points, id__lt=user.id, ).count() page = rank // UserList.paginate_by - return HttpResponseRedirect( - "%s%s#!%s" - % (reverse("user_list"), "?page=%d" % (page + 1) if page else "", username) - ) + return HttpResponseRedirect('%s%s#!%s' % (reverse('user_list'), '?page=%d' % (page + 1) if page else '', username)) class UserLogoutView(TitleMixin, TemplateView): - template_name = "registration/logout.html" - title = "You have been successfully logged out." + template_name = 'registration/logout.html' + title = 'You have been successfully logged out.' def post(self, request, *args, **kwargs): auth_logout(request) @@ -537,8 +406,8 @@ class UserLogoutView(TitleMixin, TemplateView): class ImportUsersView(TitleMixin, TemplateView): - template_name = "user/import/index.html" - title = _("Import Users") + template_name = 'user/import/index.html' + title = _('Import Users') def get(self, *args, **kwargs): if self.request.user.is_superuser: @@ -547,60 +416,43 @@ class ImportUsersView(TitleMixin, TemplateView): def import_users_post_file(request): - if not request.user.is_superuser or request.method != "POST": + if not request.user.is_superuser or request.method != 'POST': return HttpResponseForbidden() - users = import_users.csv_to_dict(request.FILES["csv_file"]) + users = import_users.csv_to_dict(request.FILES['csv_file']) if not users: - return JsonResponse( - { - "done": False, - "msg": "No valid row found. Make sure row containing username.", - } - ) - - table_html = render_to_string("user/import/table_csv.html", {"data": users}) - return JsonResponse({"done": True, "html": table_html, "data": users}) - + return JsonResponse({ + 'done': False, + 'msg': 'No valid row found. Make sure row containing username.' + }) + + table_html = render_to_string('user/import/table_csv.html', { + 'data': users + }) + return JsonResponse({ + 'done': True, + 'html': table_html, + 'data': users + }) + def import_users_submit(request): import json - - if not request.user.is_superuser or request.method != "POST": + if not request.user.is_superuser or request.method != 'POST': return HttpResponseForbidden() - users = json.loads(request.body)["users"] + users = json.loads(request.body)['users'] log = import_users.import_users(users) - return JsonResponse({"msg": log}) + return JsonResponse({ + 'msg': log + }) def sample_import_users(request): - if not request.user.is_superuser or request.method != "GET": + if not request.user.is_superuser or request.method != 'GET': return HttpResponseForbidden() - filename = "import_sample.csv" - content = ",".join(import_users.fields) + "\n" + ",".join(import_users.descriptions) - response = HttpResponse(content, content_type="text/plain") - response["Content-Disposition"] = "attachment; filename={0}".format(filename) - return response - - -def toggle_darkmode(request): - path = request.GET.get("next") - if not path: - return HttpResponseBadRequest() - request.session["darkmode"] = not request.session.get("darkmode", False) - return HttpResponseRedirect(path) - - -@login_required -def toggle_follow(request, user): - if request.method != "POST": - raise Http404() - - profile_to_follow = get_object_or_404(Profile, user__username=user) - - if request.profile.id == profile_to_follow.id: - raise Http404() - - Friend.toggle_friend(request.profile, profile_to_follow) - return HttpResponseRedirect(reverse("user_page", args=(user,))) + filename = 'import_sample.csv' + content = ','.join(import_users.fields) + '\n' + ','.join(import_users.descriptions) + response = HttpResponse(content, content_type='text/plain') + response['Content-Disposition'] = 'attachment; filename={0}'.format(filename) + return response \ No newline at end of file diff --git a/judge/views/volunteer.py b/judge/views/volunteer.py deleted file mode 100644 index acd3469..0000000 --- a/judge/views/volunteer.py +++ /dev/null @@ -1,32 +0,0 @@ -from django.http import HttpResponseBadRequest, JsonResponse -from django.db import transaction - -from judge.models import VolunteerProblemVote, Problem, ProblemType - - -def vote_problem(request): - if not request.user or not request.user.has_perm("judge.suggest_problem_changes"): - return HttpResponseBadRequest() - if not request.method == "POST": - return HttpResponseBadRequest() - try: - types_id = request.POST.getlist("types[]") - types = ProblemType.objects.filter(id__in=types_id) - problem = Problem.objects.get(code=request.POST["problem"]) - knowledge_points = request.POST["knowledge_points"] - thinking_points = request.POST["thinking_points"] - feedback = request.POST["feedback"] - except Exception as e: - return HttpResponseBadRequest() - - vote, _ = VolunteerProblemVote.objects.get_or_create( - voter=request.profile, - problem=problem, - defaults={"knowledge_points": 0, "thinking_points": 0}, - ) - vote.knowledge_points = knowledge_points - vote.thinking_points = thinking_points - vote.feedback = feedback - vote.types.set(types) - vote.save() - return JsonResponse({}) diff --git a/judge/views/widgets.py b/judge/views/widgets.py index f746704..57fe15b 100644 --- a/judge/views/widgets.py +++ b/judge/views/widgets.py @@ -2,90 +2,60 @@ import requests from django.conf import settings from django.contrib.auth.decorators import login_required from django.core.exceptions import ImproperlyConfigured -from django.http import ( - Http404, - HttpResponse, - HttpResponseBadRequest, - HttpResponseForbidden, - HttpResponseRedirect, -) +from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseRedirect from django.utils.translation import gettext as _ from django.views.generic import View from judge.models import Submission -__all__ = ["rejudge_submission", "DetectTimezone"] +__all__ = ['rejudge_submission', 'DetectTimezone'] @login_required def rejudge_submission(request): - if ( - request.method != "POST" - or not request.user.has_perm("judge.rejudge_submission") - or not request.user.has_perm("judge.edit_own_problem") - ): + if request.method != 'POST' or not request.user.has_perm('judge.rejudge_submission') or \ + not request.user.has_perm('judge.edit_own_problem'): return HttpResponseForbidden() - if "id" not in request.POST or not request.POST["id"].isdigit(): + if 'id' not in request.POST or not request.POST['id'].isdigit(): return HttpResponseBadRequest() try: - submission = Submission.objects.get(id=request.POST["id"]) + submission = Submission.objects.get(id=request.POST['id']) except Submission.DoesNotExist: return HttpResponseBadRequest() - if not request.user.has_perm( - "judge.edit_all_problem" - ) and not submission.problem.is_editor(request.profile): + if not request.user.has_perm('judge.edit_all_problem') and \ + not submission.problem.is_editor(request.profile): return HttpResponseForbidden() submission.judge(rejudge=True) - redirect = request.POST.get("path", None) + redirect = request.POST.get('path', None) - return ( - HttpResponseRedirect(redirect) - if redirect - else HttpResponse("success", content_type="text/plain") - ) + return HttpResponseRedirect(redirect) if redirect else HttpResponse('success', content_type='text/plain') class DetectTimezone(View): def askgeo(self, lat, long): - if not hasattr(settings, "ASKGEO_ACCOUNT_ID") or not hasattr( - settings, "ASKGEO_ACCOUNT_API_KEY" - ): + if not hasattr(settings, 'ASKGEO_ACCOUNT_ID') or not hasattr(settings, 'ASKGEO_ACCOUNT_API_KEY'): raise ImproperlyConfigured() - data = requests.get( - "http://api.askgeo.com/v1/%s/%s/query.json?databases=TimeZone&points=%f,%f" - % (settings.ASKGEO_ACCOUNT_ID, settings.ASKGEO_ACCOUNT_API_KEY, lat, long) - ).json() + data = requests.get('http://api.askgeo.com/v1/%s/%s/query.json?databases=TimeZone&points=%f,%f' % + (settings.ASKGEO_ACCOUNT_ID, settings.ASKGEO_ACCOUNT_API_KEY, lat, long)).json() try: - return HttpResponse( - data["data"][0]["TimeZone"]["TimeZoneId"], content_type="text/plain" - ) + return HttpResponse(data['data'][0]['TimeZone']['TimeZoneId'], content_type='text/plain') except (IndexError, KeyError): - return HttpResponse( - _("Invalid upstream data: %s") % data, - content_type="text/plain", - status=500, - ) + return HttpResponse(_('Invalid upstream data: %s') % data, content_type='text/plain', status=500) def geonames(self, lat, long): - if not hasattr(settings, "GEONAMES_USERNAME"): + if not hasattr(settings, 'GEONAMES_USERNAME'): raise ImproperlyConfigured() - data = requests.get( - "http://api.geonames.org/timezoneJSON?lat=%f&lng=%f&username=%s" - % (lat, long, settings.GEONAMES_USERNAME) - ).json() + data = requests.get('http://api.geonames.org/timezoneJSON?lat=%f&lng=%f&username=%s' % + (lat, long, settings.GEONAMES_USERNAME)).json() try: - return HttpResponse(data["timezoneId"], content_type="text/plain") + return HttpResponse(data['timezoneId'], content_type='text/plain') except KeyError: - return HttpResponse( - _("Invalid upstream data: %s") % data, - content_type="text/plain", - status=500, - ) + return HttpResponse(_('Invalid upstream data: %s') % data, content_type='text/plain', status=500) def default(self, lat, long): raise Http404() @@ -93,11 +63,10 @@ class DetectTimezone(View): def get(self, request, *args, **kwargs): backend = settings.TIMEZONE_DETECT_BACKEND try: - lat, long = float(request.GET["lat"]), float(request.GET["long"]) + lat, long = float(request.GET['lat']), float(request.GET['long']) except (ValueError, KeyError): - return HttpResponse( - _("Bad latitude or longitude"), content_type="text/plain", status=404 - ) - return {"askgeo": self.askgeo, "geonames": self.geonames,}.get( - backend, self.default - )(lat, long) + return HttpResponse(_('Bad latitude or longitude'), content_type='text/plain', status=404) + return { + 'askgeo': self.askgeo, + 'geonames': self.geonames, + }.get(backend, self.default)(lat, long) diff --git a/judge/widgets/__init__.py b/judge/widgets/__init__.py index a9455a9..51983a9 100644 --- a/judge/widgets/__init__.py +++ b/judge/widgets/__init__.py @@ -2,5 +2,3 @@ from judge.widgets.checkbox import CheckboxSelectMultipleWithSelectAll from judge.widgets.mixins import CompressorWidgetMixin from judge.widgets.pagedown import * from judge.widgets.select2 import * -from judge.widgets.datetime import * -from judge.widgets.image import * diff --git a/judge/widgets/checkbox.py b/judge/widgets/checkbox.py index b4b2249..0a7eebf 100644 --- a/judge/widgets/checkbox.py +++ b/judge/widgets/checkbox.py @@ -6,25 +6,17 @@ from django.utils.safestring import mark_safe class CheckboxSelectMultipleWithSelectAll(forms.CheckboxSelectMultiple): def render(self, name, value, attrs=None, renderer=None): - if "id" not in attrs: - raise FieldError("id required") + if 'id' not in attrs: + raise FieldError('id required') - select_all_id = attrs["id"] + "_all" - select_all_name = name + "_all" - original = super(CheckboxSelectMultipleWithSelectAll, self).render( - name, value, attrs, renderer - ) - template = get_template("widgets/select_all.html") - return mark_safe( - template.render( - { - "original_widget": original, - "select_all_id": select_all_id, - "select_all_name": select_all_name, - "all_selected": all(choice[0] in value for choice in self.choices) - if value - else False, - "empty": not self.choices, - } - ) - ) + select_all_id = attrs['id'] + '_all' + select_all_name = name + '_all' + original = super(CheckboxSelectMultipleWithSelectAll, self).render(name, value, attrs, renderer) + template = get_template('widgets/select_all.html') + return mark_safe(template.render({ + 'original_widget': original, + 'select_all_id': select_all_id, + 'select_all_name': select_all_name, + 'all_selected': all(choice[0] in value for choice in self.choices) if value else False, + 'empty': not self.choices, + })) diff --git a/judge/widgets/datetime.py b/judge/widgets/datetime.py deleted file mode 100644 index d205e1c..0000000 --- a/judge/widgets/datetime.py +++ /dev/null @@ -1,49 +0,0 @@ -from django import forms -from django.templatetags.static import static -from django.utils.html import format_html -from django.forms.utils import flatatt -from django.utils.dateparse import parse_datetime, parse_date - - -class DateTimePickerWidget(forms.DateTimeInput): - input_type = "datetime-local" - - def render(self, name, value, attrs=None, renderer=None): - if value is None: - value = "" - elif isinstance(value, str): - # Attempt to parse the string back to datetime - parsed_date = parse_datetime(value) - if parsed_date is not None: - value = parsed_date.strftime("%Y-%m-%dT%H:%M") - else: - value = "" - else: - value = value.strftime("%Y-%m-%dT%H:%M") - - final_attrs = self.build_attrs( - attrs, {"type": self.input_type, "name": name, "value": value} - ) - return format_html("", flatatt(final_attrs)) - - -class DatePickerWidget(forms.DateInput): - input_type = "date" - - def render(self, name, value, attrs=None, renderer=None): - if value is None: - value = "" - elif isinstance(value, str): - # Attempt to parse the string back to date - parsed_date = parse_date(value) - if parsed_date is not None: - value = parsed_date.strftime("%Y-%m-%d") - else: - value = "" - else: - value = value.strftime("%Y-%m-%d") - - final_attrs = self.build_attrs( - attrs, {"type": self.input_type, "name": name, "value": value} - ) - return format_html("", flatatt(final_attrs)) diff --git a/judge/widgets/image.py b/judge/widgets/image.py deleted file mode 100644 index 89b4fb4..0000000 --- a/judge/widgets/image.py +++ /dev/null @@ -1,16 +0,0 @@ -from django import forms - - -class ImageWidget(forms.ClearableFileInput): - template_name = "widgets/image.html" - - def __init__(self, attrs=None, width=80, height=80): - self.width = width - self.height = height - super().__init__(attrs) - - def get_context(self, name, value, attrs=None): - context = super().get_context(name, value, attrs) - context["widget"]["height"] = self.height - context["widget"]["width"] = self.height - return context diff --git a/judge/widgets/mixins.py b/judge/widgets/mixins.py index 6317bac..0270276 100644 --- a/judge/widgets/mixins.py +++ b/judge/widgets/mixins.py @@ -7,27 +7,23 @@ from lxml import html class CompressorWidgetMixin(object): - __template_css = dedent( - """\ + __template_css = dedent('''\ {% compress css %} {{ media.css }} {% endcompress %} - """ - ) + ''') - __template_js = dedent( - """\ + __template_js = dedent('''\ {% compress js %} {{ media.js }} {% endcompress %} - """ - ) + ''') __templates = { - (False, False): Template(""), - (True, False): Template("{% load compress %}" + __template_css), - (False, True): Template("{% load compress %}" + __template_js), - (True, True): Template("{% load compress %}" + __template_js + __template_css), + (False, False): Template(''), + (True, False): Template('{% load compress %}' + __template_css), + (False, True): Template('{% load compress %}' + __template_js), + (True, True): Template('{% load compress %}' + __template_js + __template_css), } compress_css = False @@ -38,19 +34,14 @@ class CompressorWidgetMixin(object): except ImportError: pass else: - if getattr(settings, "COMPRESS_ENABLED", not settings.DEBUG): - + if getattr(settings, 'COMPRESS_ENABLED', not settings.DEBUG): @property def media(self): media = super().media template = self.__templates[self.compress_css, self.compress_js] - result = html.fromstring(template.render(Context({"media": media}))) + result = html.fromstring(template.render(Context({'media': media}))) return forms.Media( - css={"all": [result.find(".//link").get("href")]} - if self.compress_css - else media._css, - js=[result.find(".//script").get("src")] - if self.compress_js - else media._js, + css={'all': [result.find('.//link').get('href')]} if self.compress_css else media._css, + js=[result.find('.//script').get('src')] if self.compress_js else media._js, ) diff --git a/judge/widgets/pagedown.py b/judge/widgets/pagedown.py index b0403d5..0968107 100644 --- a/judge/widgets/pagedown.py +++ b/judge/widgets/pagedown.py @@ -3,30 +3,23 @@ from django.forms.utils import flatatt from django.template.loader import get_template from django.utils.encoding import force_text from django.utils.html import conditional_escape -from django.conf import settings from judge.widgets.mixins import CompressorWidgetMixin -__all__ = [ - "PagedownWidget", - "AdminPagedownWidget", - "KatexPagedownWidget", - "KatexAdminPagedownWidget", - "HeavyPreviewPageDownWidget", - "HeavyPreviewAdminPageDownWidget", -] +__all__ = ['PagedownWidget', 'AdminPagedownWidget', + 'MathJaxPagedownWidget', 'MathJaxAdminPagedownWidget', + 'HeavyPreviewPageDownWidget', 'HeavyPreviewAdminPageDownWidget'] try: from pagedown.widgets import PagedownWidget as OldPagedownWidget except ImportError: PagedownWidget = None AdminPagedownWidget = None - KatexPagedownWidget = None - KatexAdminPagedownWidget = None + MathJaxPagedownWidget = None + MathJaxAdminPagedownWidget = None HeavyPreviewPageDownWidget = None HeavyPreviewAdminPageDownWidget = None else: - class PagedownWidget(CompressorWidgetMixin, OldPagedownWidget): # The goal here is to compress all the pagedown JS into one file. # We do not want any further compress down the chain, because @@ -35,94 +28,64 @@ else: compress_js = True def __init__(self, *args, **kwargs): + kwargs.setdefault('css', ('pagedown_widget.css',)) super(PagedownWidget, self).__init__(*args, **kwargs) - class Media: - extend = False - js = [ - "pagedown/Markdown.Converter.js", - "pagedown-extra/pagedown/Markdown.Converter.js", - "pagedown/Markdown.Sanitizer.js", - "pagedown/Markdown.Editor.js", - "pagedown-extra/Markdown.Extra.js", - "pagedown_init.js", - ] - class AdminPagedownWidget(PagedownWidget, admin_widgets.AdminTextareaWidget): class Media: - css = { - "all": [ - "pagedown_widget.css", - "content-description.css", - "admin/css/pagedown.css", - "pagedown.css", - "https://fonts.googleapis.com/css2?family=Fira+Code&family=Noto+Sans&display=swap", - ] - } - js = ["admin/js/pagedown.js"] + css = {'all': [ + 'content-description.css', + 'admin/css/pagedown.css', + ]} + js = ['admin/js/pagedown.js'] - class KatexPagedownWidget(PagedownWidget): + class MathJaxPagedownWidget(PagedownWidget): class Media: - css = { - "all": ["https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"] - } js = [ - "katex_config.js", - "https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js", - "https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js", - "pagedown_math.js", + 'mathjax_config.js', + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML', + 'pagedown_math.js', ] - class KatexAdminPagedownWidget(AdminPagedownWidget, KatexPagedownWidget): + class MathJaxAdminPagedownWidget(AdminPagedownWidget, MathJaxPagedownWidget): pass class HeavyPreviewPageDownWidget(PagedownWidget): def __init__(self, *args, **kwargs): - self.template = "pagedown.html" - self.id = kwargs.pop("id", None) - self.preview_url = kwargs.pop("preview") - self.preview_timeout = kwargs.pop("preview_timeout", None) - self.hide_preview_button = kwargs.pop("hide_preview_button", False) + kwargs.setdefault('template', 'pagedown.html') + self.preview_url = kwargs.pop('preview') + self.preview_timeout = kwargs.pop('preview_timeout', None) + self.hide_preview_button = kwargs.pop('hide_preview_button', False) super(HeavyPreviewPageDownWidget, self).__init__(*args, **kwargs) def render(self, name, value, attrs=None, renderer=None): if value is None: - value = "" - final_attrs = self.build_attrs(attrs, {"name": name}) - if "class" not in final_attrs: - final_attrs["class"] = "" - final_attrs["class"] += " wmd-input" - if self.id: - final_attrs["id"] = self.id - return get_template(self.template).render( - self.get_template_context(final_attrs, value) - ) + value = '' + final_attrs = self.build_attrs(attrs, {'name': name}) + if 'class' not in final_attrs: + final_attrs['class'] = '' + final_attrs['class'] += ' wmd-input' + return get_template(self.template).render(self.get_template_context(final_attrs, value)) def get_template_context(self, attrs, value): return { - "image_upload_enabled": getattr( - settings, "PAGEDOWN_IMAGE_UPLOAD_ENABLED", False - ), - "attrs": flatatt(attrs), - "body": conditional_escape(force_text(value)), - "postfix": attrs["id"], - "show_preview": True, - "preview_url": self.preview_url, - "preview_timeout": self.preview_timeout, - "extra_classes": "dmmd-no-button" if self.hide_preview_button else None, + 'attrs': flatatt(attrs), + 'body': conditional_escape(force_text(value)), + 'id': attrs['id'], + 'show_preview': self.show_preview, + 'preview_url': self.preview_url, + 'preview_timeout': self.preview_timeout, + 'extra_classes': 'dmmd-no-button' if self.hide_preview_button else None, } class Media: - js = ["dmmd-preview.js"] + css = {'all': ['dmmd-preview.css']} + js = ['dmmd-preview.js'] - class HeavyPreviewAdminPageDownWidget( - KatexPagedownWidget, AdminPagedownWidget, HeavyPreviewPageDownWidget - ): + class HeavyPreviewAdminPageDownWidget(AdminPagedownWidget, HeavyPreviewPageDownWidget): class Media: - css = { - "all": [ - "table.css", - "ranks.css", - "dmmd-preview.css", - ] - } + css = {'all': [ + 'pygment-github.css', + 'table.css', + 'ranks.css', + ]} diff --git a/judge/widgets/select2.py b/judge/widgets/select2.py index 62acc89..af4843b 100644 --- a/judge/widgets/select2.py +++ b/judge/widgets/select2.py @@ -45,23 +45,14 @@ from django.conf import settings from django.core import signing from django.forms.models import ModelChoiceIterator from django.urls import reverse_lazy -from django.utils.http import urlencode -DEFAULT_SELECT2_JS = settings.STATIC_URL + "libs/select2/select2.js" -DEFAULT_SELECT2_CSS = settings.STATIC_URL + ("libs/select2/select2.css") +DEFAULT_SELECT2_JS = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js' +DEFAULT_SELECT2_CSS = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css' -__all__ = [ - "Select2Widget", - "Select2MultipleWidget", - "Select2TagWidget", - "HeavySelect2Widget", - "HeavySelect2MultipleWidget", - "HeavySelect2TagWidget", - "AdminSelect2Widget", - "AdminSelect2MultipleWidget", - "AdminHeavySelect2Widget", - "AdminHeavySelect2MultipleWidget", -] +__all__ = ['Select2Widget', 'Select2MultipleWidget', 'Select2TagWidget', + 'HeavySelect2Widget', 'HeavySelect2MultipleWidget', 'HeavySelect2TagWidget', + 'AdminSelect2Widget', 'AdminSelect2MultipleWidget', 'AdminHeavySelect2Widget', + 'AdminHeavySelect2MultipleWidget'] class Select2Mixin(object): @@ -77,22 +68,22 @@ class Select2Mixin(object): """Add select2 data attributes.""" attrs = super(Select2Mixin, self).build_attrs(base_attrs, extra_attrs) if self.is_required: - attrs.setdefault("data-allow-clear", "false") + attrs.setdefault('data-allow-clear', 'false') else: - attrs.setdefault("data-allow-clear", "true") - attrs.setdefault("data-placeholder", "") + attrs.setdefault('data-allow-clear', 'true') + attrs.setdefault('data-placeholder', '') - attrs.setdefault("data-minimum-input-length", 0) - if "class" in attrs: - attrs["class"] += " django-select2" + attrs.setdefault('data-minimum-input-length', 0) + if 'class' in attrs: + attrs['class'] += ' django-select2' else: - attrs["class"] = "django-select2" + attrs['class'] = 'django-select2' return attrs def optgroups(self, name, value, attrs=None): """Add empty option for clearable selects.""" if not self.is_required and not self.allow_multiple_selected: - self.choices = list(chain([("", "")], self.choices)) + self.choices = list(chain([('', '')], self.choices)) return super(Select2Mixin, self).optgroups(name, value, attrs=attrs) @property @@ -104,7 +95,8 @@ class Select2Mixin(object): https://docs.djangoproject.com/en/1.8/topics/forms/media/#media-as-a-dynamic-property """ return forms.Media( - js=["django_select2.js"], + js=[settings.SELECT2_JS_URL, 'django_select2.js'], + css={'screen': [settings.SELECT2_CSS_URL]}, ) @@ -112,12 +104,8 @@ class AdminSelect2Mixin(Select2Mixin): @property def media(self): return forms.Media( - js=[ - "admin/js/jquery.init.js", - DEFAULT_SELECT2_JS, - "django_select2.js", - ], - css={"screen": [DEFAULT_SELECT2_CSS]}, + js=['admin/js/jquery.init.js', settings.SELECT2_JS_URL, 'django_select2.js'], + css={'screen': [settings.SELECT2_CSS_URL]}, ) @@ -127,9 +115,9 @@ class Select2TagMixin(object): def build_attrs(self, base_attrs, extra_attrs=None): """Add select2's tag attributes.""" extra_attrs = extra_attrs or {} - extra_attrs.setdefault("data-minimum-input-length", 1) - extra_attrs.setdefault("data-tags", "true") - extra_attrs.setdefault("data-token-separators", [",", " "]) + extra_attrs.setdefault('data-minimum-input-length', 1) + extra_attrs.setdefault('data-tags', 'true') + extra_attrs.setdefault('data-token-separators', [",", " "]) return super(Select2TagMixin, self).build_attrs(base_attrs, extra_attrs) @@ -194,8 +182,8 @@ class HeavySelect2Mixin(Select2Mixin): else: self.attrs = {} - self.data_view = kwargs.pop("data_view", None) - self.data_url = kwargs.pop("data_url", None) + self.data_view = kwargs.pop('data_view', None) + self.data_url = kwargs.pop('data_url', None) if not (self.data_view or self.data_url): raise ValueError('You must ether specify "data_view" or "data_url".') @@ -213,26 +201,23 @@ class HeavySelect2Mixin(Select2Mixin): # encrypt instance Id self.widget_id = signing.dumps(id(self)) - attrs["data-field_id"] = self.widget_id - attrs.setdefault("data-ajax--url", self.get_url()) - attrs.setdefault("data-ajax--cache", "true") - attrs.setdefault("data-ajax--type", "GET") - attrs.setdefault("data-minimum-input-length", 2) + attrs['data-field_id'] = self.widget_id + attrs.setdefault('data-ajax--url', self.get_url()) + attrs.setdefault('data-ajax--cache', "true") + attrs.setdefault('data-ajax--type', "GET") + attrs.setdefault('data-minimum-input-length', 2) - attrs["class"] += " django-select2-heavy" + attrs['class'] += ' django-select2-heavy' return attrs def format_value(self, value): result = super(HeavySelect2Mixin, self).format_value(value) if isinstance(self.choices, ModelChoiceIterator): chosen = copy(self.choices) - chosen.queryset = chosen.queryset.filter( - pk__in=[int(i) for i in result if isinstance(i, int) or i.isdigit()] - ) - self.choices = { - (value if isinstance(value, str) else value.value, label) - for value, label in chosen - } + chosen.queryset = chosen.queryset.filter(pk__in=[ + int(i) for i in result if isinstance(i, int) or i.isdigit() + ]) + self.choices = set(chosen) return result diff --git a/locale/vi/LC_MESSAGES/django.po b/locale/vi/LC_MESSAGES/django.po index 60a2668..b9d0339 100644 --- a/locale/vi/LC_MESSAGES/django.po +++ b/locale/vi/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: lqdoj2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-03 02:46+0700\n" +"POT-Creation-Date: 2022-01-10 18:14+0700\n" "PO-Revision-Date: 2021-07-20 03:44\n" "Last-Translator: Icyene\n" "Language-Team: Vietnamese\n" @@ -18,362 +18,324 @@ msgstr "" "X-Crowdin-Project-ID: 466004\n" "X-Crowdin-File-ID: 5\n" -#: chat_box/models.py:22 chat_box/models.py:84 -msgid "last seen" -msgstr "xem lần cuối" - -#: chat_box/models.py:55 chat_box/models.py:80 chat_box/models.py:96 -#: judge/admin/interface.py:151 judge/models/contest.py:722 -#: judge/models/contest.py:928 judge/models/course.py:129 -#: judge/models/profile.py:465 judge/models/profile.py:539 +#: chat_box/models.py:23 chat_box/models.py:41 chat_box/models.py:47 +#: judge/admin/interface.py:110 judge/models/contest.py:403 +#: judge/models/contest.py:528 judge/models/profile.py:215 msgid "user" msgstr "người dùng" -#: chat_box/models.py:57 judge/models/comment.py:45 -#: judge/models/notification.py:31 +#: chat_box/models.py:24 judge/models/comment.py:43 judge/models/comment.py:191 msgid "posted time" msgstr "thời gian đăng" -#: chat_box/models.py:59 judge/models/comment.py:50 +#: chat_box/models.py:25 judge/models/comment.py:47 msgid "body of comment" msgstr "nội dung bình luận" -#: chat_box/views.py:46 -msgid "LQDOJ Chat" +#: chat_box/models.py:43 +msgid "last seen" +msgstr "xem lần cuối" + +#: chat_box/views.py:29 templates/chat/chat.html:4 templates/chat/chat.html:541 +msgid "Chat Box" +msgstr "Chat Box" + +#: dmoj/settings.py:358 +msgid "German" msgstr "" -#: chat_box/views.py:173 -msgid "Mute chat" -msgstr "" - -#: chat_box/views.py:450 -msgid "Recent" -msgstr "Gần đây" - -#: chat_box/views.py:454 templates/base.html:198 -#: templates/comments/content-list.html:72 -#: templates/contest/contest-list-tabs.html:6 -#: templates/contest/ranking-table.html:52 templates/course/left_sidebar.html:9 -#: templates/internal/problem/problem.html:63 -#: templates/organization/org-left-sidebar.html:12 -#: templates/organization/users-table.html:19 -#: templates/problem/left-sidebar.html:13 -#: templates/problem/problem-list-tabs.html:6 -#: templates/submission/info-base.html:12 templates/submission/list.html:394 -#: templates/submission/submission-list-tabs.html:15 -msgid "Admin" -msgstr "Admin" - -#: dmoj/settings.py:366 -msgid "Vietnamese" -msgstr "Tiếng Việt" - -#: dmoj/settings.py:367 +#: dmoj/settings.py:359 msgid "English" msgstr "" -#: dmoj/urls.py:107 -msgid "Activation key invalid" -msgstr "Mã kích hoạt không hợp lệ" +#: dmoj/settings.py:360 +msgid "Spanish" +msgstr "" -#: dmoj/urls.py:112 -msgid "Register" -msgstr "Đăng ký" +#: dmoj/settings.py:361 +msgid "French" +msgstr "" -#: dmoj/urls.py:119 -msgid "Registration Completed" -msgstr "Đăng ký hoàn thành" +#: dmoj/settings.py:362 +msgid "Croatian" +msgstr "" -#: dmoj/urls.py:127 -msgid "Registration not allowed" -msgstr "Đăng ký không thành công" +#: dmoj/settings.py:363 +msgid "Hungarian" +msgstr "" -#: dmoj/urls.py:135 +#: dmoj/settings.py:364 +msgid "Japanese" +msgstr "" + +#: dmoj/settings.py:365 +msgid "Korean" +msgstr "" + +#: dmoj/settings.py:366 +msgid "Brazilian Portuguese" +msgstr "" + +#: dmoj/settings.py:367 +msgid "Romanian" +msgstr "" + +#: dmoj/settings.py:368 +msgid "Russian" +msgstr "" + +#: dmoj/settings.py:369 +msgid "Serbian (Latin)" +msgstr "" + +#: dmoj/settings.py:370 +msgid "Turkish" +msgstr "" + +#: dmoj/settings.py:371 +msgid "Vietnamese" +msgstr "Tiếng Việt" + +#: dmoj/settings.py:372 +msgid "Simplified Chinese" +msgstr "" + +#: dmoj/settings.py:373 +msgid "Traditional Chinese" +msgstr "" + +#: dmoj/urls.py:58 msgid "Login" msgstr "Đăng nhập" -#: dmoj/urls.py:221 templates/base.html:120 -#: templates/course/left_sidebar.html:2 -#: templates/organization/org-left-sidebar.html:2 +#: dmoj/urls.py:106 templates/base.html:249 msgid "Home" msgstr "Trang chủ" -#: judge/admin/comments.py:57 +#: judge/admin/comments.py:40 #, python-format msgid "%d comment successfully hidden." msgid_plural "%d comments successfully hidden." msgstr[0] "Đã ẩn %d bình luận." -#: judge/admin/comments.py:64 +#: judge/admin/comments.py:43 msgid "Hide comments" msgstr "Ẩn bình luận" -#: judge/admin/comments.py:71 +#: judge/admin/comments.py:47 #, python-format msgid "%d comment successfully unhidden." msgid_plural "%d comments successfully unhidden." msgstr[0] "Không ẩn được %d bình luận." -#: judge/admin/comments.py:78 +#: judge/admin/comments.py:50 msgid "Unhide comments" msgstr "Hiện bình luận" -#: judge/admin/contest.py:45 +#: judge/admin/comments.py:58 +msgid "Associated page" +msgstr "Trang liên kết" + +#: judge/admin/contest.py:30 msgid "Included contests" msgstr "" -#: judge/admin/contest.py:87 judge/admin/volunteer.py:54 -#: templates/contest/clarification.html:42 templates/contest/contest.html:121 -#: templates/contest/moss.html:41 templates/internal/left-sidebar.html:2 -#: templates/internal/problem/problem.html:41 templates/problem/list.html:17 -#: templates/problem/list.html:34 templates/problem/list.html:153 -#: templates/user/user-problems.html:56 templates/user/user-problems.html:98 +#: judge/admin/contest.py:66 templates/contest/clarification.html:42 +#: templates/contest/contest.html:83 templates/contest/moss.html:43 +#: templates/problem/list.html:214 templates/problem/list.html:232 +#: templates/problem/list.html:350 templates/user/user-problems.html:56 +#: templates/user/user-problems.html:98 msgid "Problem" msgstr "Bài tập" -#: judge/admin/contest.py:183 templates/base.html:213 -#: templates/user/user-tabs.html:12 +#: judge/admin/contest.py:119 msgid "Settings" msgstr "Cài đặt" -#: judge/admin/contest.py:198 +#: judge/admin/contest.py:121 msgid "Scheduling" msgstr "" -#: judge/admin/contest.py:202 +#: judge/admin/contest.py:122 templates/organization/home.html:100 msgid "Details" msgstr "Chi tiết" -#: judge/admin/contest.py:214 templates/contest/macros.html:83 -#: templates/contest/macros.html:93 +#: judge/admin/contest.py:123 msgid "Format" msgstr "Thể thức" -#: judge/admin/contest.py:218 templates/contest/ranking-table.html:5 -#: templates/profile-table.html:14 templates/profile-table.html:37 -#: templates/user/user-about.html:15 templates/user/user-about.html:44 +#: judge/admin/contest.py:124 templates/contest/ranking-table.html:7 +#: templates/user/user-about.html:15 templates/user/user-about.html:45 msgid "Rating" msgstr "" -#: judge/admin/contest.py:230 +#: judge/admin/contest.py:125 msgid "Access" msgstr "Truy cập" -#: judge/admin/contest.py:240 judge/admin/problem.py:233 +#: judge/admin/contest.py:127 judge/admin/problem.py:131 msgid "Justice" msgstr "Xử phạt" -#: judge/admin/contest.py:368 +#: judge/admin/contest.py:207 #, python-format msgid "%d contest successfully marked as visible." msgid_plural "%d contests successfully marked as visible." msgstr[0] "%d kỳ thi đã được đánh dấu hiển thị." -#: judge/admin/contest.py:375 +#: judge/admin/contest.py:210 msgid "Mark contests as visible" msgstr "Đánh dấu hiển thị các kỳ thi" -#: judge/admin/contest.py:386 +#: judge/admin/contest.py:216 #, python-format msgid "%d contest successfully marked as hidden." msgid_plural "%d contests successfully marked as hidden." msgstr[0] "%d kỳ thi đã được đánh dấu ẩn." -#: judge/admin/contest.py:393 +#: judge/admin/contest.py:219 msgid "Mark contests as hidden" msgstr "Ẩn các kỳ thi" -#: judge/admin/contest.py:414 judge/admin/submission.py:241 +#: judge/admin/contest.py:233 judge/admin/submission.py:164 #, python-format msgid "%d submission was successfully scheduled for rejudging." msgid_plural "%d submissions were successfully scheduled for rejudging." msgstr[0] "%d bài nộp đã được lên lịch thành công để chấm lại." -#: judge/admin/contest.py:522 +#: judge/admin/contest.py:308 #, python-format msgid "%d participation recalculated." msgid_plural "%d participations recalculated." msgstr[0] "%d thí sinh đã được tính điểm lại." -#: judge/admin/contest.py:529 +#: judge/admin/contest.py:311 msgid "Recalculate results" msgstr "Tính toán lại kết quả" -#: judge/admin/contest.py:534 judge/admin/organization.py:98 +#: judge/admin/contest.py:315 judge/admin/organization.py:65 msgid "username" msgstr "tên đăng nhập" -#: judge/admin/contest.py:540 templates/base.html:253 +#: judge/admin/contest.py:320 templates/base.html:337 msgid "virtual" msgstr "ảo" -#: judge/admin/interface.py:35 judge/models/interface.py:51 +#: judge/admin/interface.py:28 judge/models/interface.py:46 msgid "link path" msgstr "đường dẫn" -#: judge/admin/interface.py:95 templates/course/lesson.html:10 +#: judge/admin/interface.py:65 msgid "Content" msgstr "Nội dung" -#: judge/admin/interface.py:96 +#: judge/admin/interface.py:66 msgid "Summary" msgstr "Tổng kết" -#: judge/admin/interface.py:218 +#: judge/admin/interface.py:151 msgid "object" msgstr "" -#: judge/admin/interface.py:228 -msgid "Diff" -msgstr "" - -#: judge/admin/interface.py:233 -msgid "diff" -msgstr "" - -#: judge/admin/organization.py:60 judge/admin/problem.py:290 -#: judge/admin/profile.py:128 +#: judge/admin/organization.py:34 judge/admin/problem.py:171 +#: judge/admin/profile.py:80 msgid "View on site" msgstr "Xem trên trang" -#: judge/admin/problem.py:56 +#: judge/admin/problem.py:28 msgid "Describe the changes you made (optional)" msgstr "Mô tả các thay đổi (tùy chọn)" -#: judge/admin/problem.py:66 -#, fuzzy -#| msgid "Problem with code already exists." -msgid "A problem with this code already exists." -msgstr "Mã bài đã tồn tại." - -#: judge/admin/problem.py:122 -msgid "Memory unit" -msgstr "Đơn vị bộ nhớ" - -#: judge/admin/problem.py:226 +#: judge/admin/problem.py:126 msgid "Social Media" msgstr "Mạng Xã Hội" -#: judge/admin/problem.py:229 +#: judge/admin/problem.py:127 msgid "Taxonomy" msgstr "" -#: judge/admin/problem.py:230 judge/admin/problem.py:463 -#: judge/views/course.py:494 judge/views/course.py:579 -#: templates/contest/contest.html:122 -#: templates/contest/contests_summary.html:41 -#: templates/course/contest_list.html:21 templates/problem/data.html:535 -#: templates/problem/list.html:22 templates/problem/list.html:48 -#: templates/profile-table.html:31 templates/profile-table.html:41 -#: templates/user/base-users-table.html:10 templates/user/user-about.html:36 -#: templates/user/user-about.html:50 templates/user/user-problems.html:58 +#: judge/admin/problem.py:128 templates/contest/contest.html:84 +#: templates/problem/data.html:469 templates/problem/list.html:222 +#: templates/problem/list.html:248 templates/user/base-users-table.html:10 +#: templates/user/user-about.html:36 templates/user/user-about.html:52 +#: templates/user/user-problems.html:58 msgid "Points" msgstr "Điểm" -#: judge/admin/problem.py:231 +#: judge/admin/problem.py:129 msgid "Limits" msgstr "Giới hạn" -#: judge/admin/problem.py:232 judge/admin/submission.py:351 -#: templates/base.html:164 templates/stats/tab.html:4 -#: templates/submission/list.html:345 +#: judge/admin/problem.py:130 judge/admin/submission.py:232 +#: templates/stats/base.html:14 templates/submission/list.html:322 msgid "Language" msgstr "Ngôn ngữ" -#: judge/admin/problem.py:234 +#: judge/admin/problem.py:132 msgid "History" msgstr "Lịch sử" -#: judge/admin/problem.py:286 templates/problem/list-base.html:93 +#: judge/admin/problem.py:168 msgid "Authors" msgstr "Các tác giả" -#: judge/admin/problem.py:307 +#: judge/admin/problem.py:183 #, python-format msgid "%d problem successfully marked as public." msgid_plural "%d problems successfully marked as public." msgstr[0] "%d bài tập đã được đánh dấu công khai." -#: judge/admin/problem.py:314 +#: judge/admin/problem.py:187 msgid "Mark problems as public" msgstr "Công khai bài tập" -#: judge/admin/problem.py:323 +#: judge/admin/problem.py:193 #, python-format msgid "%d problem successfully marked as private." msgid_plural "%d problems successfully marked as private." msgstr[0] "%d bài tập đã được đánh dấu riêng tư." -#: judge/admin/problem.py:330 +#: judge/admin/problem.py:197 msgid "Mark problems as private" msgstr "Đánh dấu các bài tập là riêng tư" -#: judge/admin/problem.py:457 judge/admin/submission.py:314 -#: templates/problem/list.html:18 templates/problem/list.html:37 -msgid "Problem code" -msgstr "Mã bài" - -#: judge/admin/problem.py:469 judge/admin/submission.py:320 -msgid "Problem name" -msgstr "Tên bài" - -#: judge/admin/problem.py:475 -#, fuzzy -#| msgid "contest rating" -msgid "Voter rating" -msgstr "rating kỳ thi" - -#: judge/admin/problem.py:481 -#, fuzzy -#| msgid "Total points" -msgid "Voter point" -msgstr "Tổng điểm" - -#: judge/admin/problem.py:487 -msgid "Vote" -msgstr "" - -#: judge/admin/profile.py:47 +#: judge/admin/profile.py:34 msgid "timezone" msgstr "múi giờ" -#: judge/admin/profile.py:137 judge/admin/submission.py:327 -#: templates/notification/list.html:9 +#: judge/admin/profile.py:86 judge/admin/submission.py:211 +#: templates/notification/list.html:12 #: templates/organization/requests/log.html:9 -#: templates/organization/requests/pending.html:19 -#: templates/ticket/list.html:265 +#: templates/organization/requests/pending.html:12 +#: templates/ticket/list.html:263 msgid "User" msgstr "Thành viên" -#: judge/admin/profile.py:143 templates/registration/registration_form.html:40 -#: templates/user/edit-profile.html:123 templates/user/import/table_csv.html:8 +#: judge/admin/profile.py:91 templates/registration/registration_form.html:145 +#: templates/user/import/table_csv.html:8 msgid "Email" msgstr "Email" -#: judge/admin/profile.py:149 judge/views/register.py:36 -#: templates/registration/registration_form.html:68 -#: templates/user/edit-profile.html:147 +#: judge/admin/profile.py:96 judge/views/register.py:29 +#: templates/registration/registration_form.html:173 +#: templates/user/edit-profile.html:116 msgid "Timezone" msgstr "Múi giờ" -#: judge/admin/profile.py:155 +#: judge/admin/profile.py:101 msgid "date joined" msgstr "ngày tham gia" -#: judge/admin/profile.py:165 +#: judge/admin/profile.py:108 #, python-format msgid "%d user have scores recalculated." msgid_plural "%d users have scores recalculated." msgstr[0] "%d người dùng đã được tính điểm lại." -#: judge/admin/profile.py:172 +#: judge/admin/profile.py:111 msgid "Recalculate scores" msgstr "Tính điểm lại" -#: judge/admin/profile.py:179 judge/admin/profile.py:186 -msgid "Username can only contain letters, digits, and underscores." -msgstr "Tên đăng nhập phải chứa ký tự, chữ số, hoặc dấu gạch dưới" - #: judge/admin/runtime.py:19 msgid "Disallowed problems" msgstr "Các bài tập không được cho phép" @@ -382,90 +344,96 @@ msgstr "Các bài tập không được cho phép" msgid "These problems are NOT allowed to be submitted in this language" msgstr "Các bài này không cho phép sử dụng ngôn ngữ này" -#: judge/admin/runtime.py:117 templates/problem/list.html:155 +#: judge/admin/runtime.py:83 templates/problem/list.html:352 msgid "Description" msgstr "Mô tả" -#: judge/admin/runtime.py:119 +#: judge/admin/runtime.py:84 msgid "Information" msgstr "Thông tin" -#: judge/admin/runtime.py:122 +#: judge/admin/runtime.py:85 msgid "Capabilities" msgstr "Khả năng" -#: judge/admin/submission.py:31 judge/admin/submission.py:53 -#: judge/admin/submission.py:338 +#: judge/admin/submission.py:23 judge/admin/submission.py:42 +#: judge/admin/submission.py:221 msgid "None" msgstr "None" -#: judge/admin/submission.py:32 +#: judge/admin/submission.py:23 msgid "Not done" msgstr "Chưa xong" -#: judge/admin/submission.py:33 +#: judge/admin/submission.py:23 msgid "Exceptional" msgstr "Đặc biệt" -#: judge/admin/submission.py:53 +#: judge/admin/submission.py:42 msgid "Unaccepted" msgstr "" -#: judge/admin/submission.py:104 +#: judge/admin/submission.py:89 #, python-format msgctxt "contest problem" msgid "%(problem)s in %(contest)s" msgstr "%(problem)s trong %(contest)s" -#: judge/admin/submission.py:213 judge/admin/submission.py:254 +#: judge/admin/submission.py:149 judge/admin/submission.py:171 msgid "You do not have the permission to rejudge submissions." msgstr "Bạn không có quyền chấm lại bài." -#: judge/admin/submission.py:225 +#: judge/admin/submission.py:155 msgid "You do not have the permission to rejudge THAT many submissions." msgstr "Bạn không có quyền chấm lại nhiều bài nộp như vậy." -#: judge/admin/submission.py:248 +#: judge/admin/submission.py:167 msgid "Rejudge the selected submissions" msgstr "Chấm lại các bài nộp đã chọn" -#: judge/admin/submission.py:302 judge/views/problem_manage.py:218 +#: judge/admin/submission.py:193 judge/views/problem_manage.py:159 #, python-format msgid "%d submission were successfully rescored." msgid_plural "%d submissions were successfully rescored." msgstr[0] "%d bài nộp đã được tính điểm lại." -#: judge/admin/submission.py:309 +#: judge/admin/submission.py:196 msgid "Rescore the selected submissions" msgstr "Tính điểm lại cái bài nộp" -#: judge/admin/submission.py:332 templates/contest/list.html:175 -#: templates/contest/list.html:217 templates/contest/list.html:254 -#: templates/contest/list.html:288 templates/notification/list.html:12 +#: judge/admin/submission.py:200 templates/problem/list.html:216 +#: templates/problem/list.html:236 +msgid "Problem code" +msgstr "Mã bài" + +#: judge/admin/submission.py:205 +msgid "Problem name" +msgstr "Tên bài" + +#: judge/admin/submission.py:215 templates/notification/list.html:15 #: templates/organization/requests/log.html:10 -#: templates/organization/requests/pending.html:20 -#: templates/problem/list.html:154 -#: templates/submission/status-testcases.html:139 -#: templates/submission/status-testcases.html:141 -#: templates/user/user-bookmarks.html:84 +#: templates/organization/requests/pending.html:13 +#: templates/problem/list.html:351 +#: templates/submission/status-testcases.html:125 +#: templates/submission/status-testcases.html:127 msgid "Time" msgstr "Thời gian" -#: judge/admin/submission.py:340 +#: judge/admin/submission.py:223 #, python-format msgid "%d KB" msgstr "%d KB" -#: judge/admin/submission.py:342 +#: judge/admin/submission.py:225 #, python-format msgid "%.2f MB" msgstr "" -#: judge/admin/submission.py:345 templates/submission/status-testcases.html:146 +#: judge/admin/submission.py:227 templates/submission/status-testcases.html:132 msgid "Memory" msgstr "Bộ nhớ" -#: judge/admin/taxon.py:11 judge/admin/taxon.py:37 +#: judge/admin/taxon.py:11 judge/admin/taxon.py:34 msgid "Included problems" msgstr "" @@ -473,19 +441,31 @@ msgstr "" msgid "These problems are included in this group of problems" msgstr "Các bài tập trong nhóm này" -#: judge/admin/taxon.py:40 +#: judge/admin/taxon.py:37 msgid "These problems are included in this type of problems" msgstr "Các bài tập dạng này" -#: judge/admin/volunteer.py:60 templates/internal/problem/votes.html:17 -#: templates/problem/list.html:20 templates/problem/list.html:44 -msgid "Types" -msgstr "Dạng" - #: judge/apps.py:8 msgid "Online Judge" msgstr "" +#: judge/comments.py:60 +msgid "Comment body" +msgstr "Nội dung bình luận" + +#: judge/comments.py:66 judge/views/ticket.py:63 +msgid "Your part is silent, little toad." +msgstr "Bạn không được phép bình luận." + +#: judge/comments.py:69 templates/comments/list.html:132 +msgid "" +"You need to have solved at least one problem before your voice can be heard." +msgstr "Bạn phải giải ít nhất một bài trước khi được phép bình luận." + +#: judge/comments.py:115 +msgid "Posted comment" +msgstr "Bình luận đã đăng" + #: judge/contest_format/atcoder.py:19 msgid "AtCoder" msgstr "" @@ -502,425 +482,357 @@ msgstr "" msgid "ICPC" msgstr "" -#: judge/contest_format/ioi.py:20 +#: judge/contest_format/ioi.py:19 msgid "IOI" msgstr "" -#: judge/contest_format/new_ioi.py:11 -msgid "New IOI" -msgstr "IOI mới" +#: judge/forms.py:27 +msgid "Subscribe to contest updates" +msgstr "Đăng ký để nhận thông báo về các kỳ thi" -#: judge/contest_format/ultimate.py:12 -msgid "Ultimate" -msgstr "" +#: judge/forms.py:28 +msgid "Enable experimental features" +msgstr "Sử dụng các tính năng thử nghiệm" -#: judge/custom_translations.py:8 -#, python-format -msgid "" -"This password is too short. It must contain at least %(min_length)d " -"character." -msgid_plural "" -"This password is too short. It must contain at least %(min_length)d " -"characters." -msgstr[0] "Mật khẩu phải chứa ít nhất %(min_length)d ký tự." +#: judge/forms.py:57 judge/views/organization.py:168 judge/views/register.py:49 +#, python-brace-format +msgid "You may not be part of more than {count} public organizations." +msgstr "Bạn không thể tham gia nhiều hơn {count} tổ chức công khai." -#: judge/custom_translations.py:13 -#, python-format -msgid "Your password must contain at least %(min_length)d character." -msgid_plural "Your password must contain at least %(min_length)d characters." -msgstr[0] "Mật khẩu phải chứa ít nhất %(min_length)d ký tự." - -#: judge/custom_translations.py:17 -msgid "The two password fields didn’t match." -msgstr "Mật khẩu xác nhận không khớp." - -#: judge/custom_translations.py:18 -msgid "Your password can’t be entirely numeric." -msgstr "Mật khẩu không được toàn chữ số." - -#: judge/custom_translations.py:20 -msgid "Bug Report" -msgstr "Báo cáo lỗi" - -#: judge/custom_translations.py:21 judge/views/course.py:164 -#: templates/course/list.html:8 -msgid "Courses" -msgstr "Khóa học" - -#: judge/forms.py:120 judge/forms.py:220 -msgid "File size exceeds the maximum allowed limit of 5MB." -msgstr "File tải lên không được quá 5MB." - -#: judge/forms.py:151 +#: judge/forms.py:82 msgid "Any judge" msgstr "" -#: judge/forms.py:368 -msgid "Enter usernames separating by space" -msgstr "Nhập các tên đăng nhập, cách nhau bởi dấu cách" +#: judge/forms.py:112 judge/views/register.py:26 +#: templates/registration/registration_form.html:139 +#: templates/user/base-users-table.html:5 +#: templates/user/import/table_csv.html:4 +msgid "Username" +msgstr "Tên đăng nhập" -#: judge/forms.py:369 judge/views/stats.py:166 templates/stats/site.html:27 -msgid "New users" -msgstr "Thành viên mới" - -#: judge/forms.py:386 -#, python-brace-format -msgid "These usernames don't exist: {usernames}" -msgstr "Các tên đăng nhập này không tồn tại: {usernames}" - -#: judge/forms.py:445 -msgid "Username/Email" -msgstr "Tên đăng nhập / Email" - -#: judge/forms.py:447 judge/views/email.py:22 -#: templates/registration/registration_form.html:46 -#: templates/registration/registration_form.html:60 -#: templates/user/edit-profile.html:115 templates/user/import/table_csv.html:5 +#: judge/forms.py:113 templates/registration/registration_form.html:151 +#: templates/registration/registration_form.html:165 +#: templates/user/import/table_csv.html:5 msgid "Password" msgstr "Mật khẩu" -#: judge/forms.py:473 +#: judge/forms.py:135 msgid "Two Factor Authentication tokens must be 6 decimal digits." msgstr "Two Factor Authentication phải chứa 6 chữ số." -#: judge/forms.py:486 templates/registration/totp_auth.html:32 +#: judge/forms.py:144 templates/registration/totp_auth.html:32 msgid "Invalid Two Factor Authentication token." msgstr "Token Two Factor Authentication không hợp lệ." -#: judge/forms.py:493 judge/models/problem.py:133 +#: judge/forms.py:148 judge/models/problem.py:97 msgid "Problem code must be ^[a-z0-9]+$" msgstr "Mã bài phải có dạng ^[a-z0-9]+$" -#: judge/forms.py:500 +#: judge/forms.py:153 msgid "Problem with code already exists." msgstr "Mã bài đã tồn tại." -#: judge/forms.py:507 judge/models/contest.py:103 +#: judge/forms.py:158 judge/models/contest.py:61 msgid "Contest id must be ^[a-z0-9]+$" msgstr "Mã kỳ thi phải có dạng ^[a-z0-9]+$" -#: judge/forms.py:514 templates/contest/clone.html:47 -#: templates/contest/search-form.html:12 templates/problem/search-form.html:39 -msgid "Group" -msgstr "Nhóm" - -#: judge/forms.py:522 +#: judge/forms.py:163 msgid "Contest with key already exists." msgstr "Mã kỳ thi đã tồn tại." -#: judge/forms.py:530 -msgid "Group doesn't exist." -msgstr "Nhóm không tồn tại." - -#: judge/forms.py:532 -msgid "You don't have permission in this group." -msgstr "Bạn không có quyền trong nhóm này." - -#: judge/forms.py:582 -msgid "This problem is duplicated." -msgstr "Bài này bị lặp" - -#: judge/jinja2/datetime.py:26 templates/blog/blog.html:28 +#: judge/jinja2/datetime.py:26 templates/blog/blog.html:26 #: templates/blog/dashboard.html:21 msgid "N j, Y, g:i a" msgstr "g:i a j b, Y" #: judge/jinja2/datetime.py:26 templates/chat/message.html:13 -#: templates/comments/content-list.html:28 #, python-brace-format msgid "{time}" msgstr "{time}" -#: judge/middleware.py:135 -msgid "No permission" -msgstr "Không có quyền truy cập" +#: judge/jinja2/datetime.py:26 templates/blog/content.html:13 +#, python-brace-format +msgid "on {time}" +msgstr "vào {time}" -#: judge/middleware.py:136 -msgid "You need to join this group first" -msgstr "Bạn phải là thành viên của nhóm." +#: judge/models/choices.py:59 +msgid "Leave as LaTeX" +msgstr "Để định dạng LaTeX" -#: judge/middleware.py:146 judge/middleware.py:147 -msgid "No such group" -msgstr "Nhóm không tồn tại" +#: judge/models/choices.py:60 +msgid "SVG with PNG fallback" +msgstr "" -#: judge/models/bookmark.py:17 judge/models/comment.py:164 -#: judge/models/pagevote.py:16 -msgid "associated page" -msgstr "trang tương ứng" +#: judge/models/choices.py:61 +msgid "MathML only" +msgstr "chỉ dùng MathML" -#: judge/models/bookmark.py:20 judge/models/comment.py:49 -#: judge/models/pagevote.py:19 judge/models/problem.py:744 -msgid "votes" -msgstr "bình chọn" +#: judge/models/choices.py:62 +msgid "MathJax with SVG/PNG fallback" +msgstr "" -#: judge/models/bookmark.py:30 -msgid "bookmark" -msgstr "Lưu" +#: judge/models/choices.py:63 +msgid "Detect best quality" +msgstr "" -#: judge/models/bookmark.py:31 -msgid "bookmarks" -msgstr "Lưu" +#: judge/models/comment.py:26 +msgid "Page code must be ^[pcs]:[a-z0-9]+$|^b:\\d+$" +msgstr "Mã trang phải có dạng ^[pcs]:[a-z0-9]+$|^b:\\d+$" -#: judge/models/bookmark.py:52 -msgid "make bookmark" -msgstr "Lưu" - -#: judge/models/bookmark.py:53 -msgid "make bookmarks" -msgstr "Lưu" - -#: judge/models/comment.py:44 +#: judge/models/comment.py:42 msgid "commenter" msgstr "người bình luận" -#: judge/models/comment.py:51 +#: judge/models/comment.py:44 judge/models/comment.py:177 +msgid "associated page" +msgstr "trang tương ứng" + +#: judge/models/comment.py:46 +msgid "votes" +msgstr "" + +#: judge/models/comment.py:48 msgid "hide the comment" msgstr "ẩn bình luận" -#: judge/models/comment.py:54 +#: judge/models/comment.py:49 msgid "parent" msgstr "" -#: judge/models/comment.py:65 +#: judge/models/comment.py:54 judge/models/comment.py:192 msgid "comment" msgstr "bình luận" -#: judge/models/comment.py:66 +#: judge/models/comment.py:55 msgid "comments" msgstr "" -#: judge/models/comment.py:125 -msgid "Editorial for " -msgstr "Hướng dẫn cho {0}" +#: judge/models/comment.py:137 judge/models/problem.py:443 +#, python-format +msgid "Editorial for %s" +msgstr "" -#: judge/models/comment.py:157 +#: judge/models/comment.py:172 msgid "comment vote" msgstr "" -#: judge/models/comment.py:158 +#: judge/models/comment.py:173 msgid "comment votes" msgstr "" -#: judge/models/comment.py:169 +#: judge/models/comment.py:182 msgid "Override comment lock" msgstr "" -#: judge/models/contest.py:50 +#: judge/models/comment.py:190 +#: src/dmoj-wpadmin/test_project/apps/books/admin.py:24 +#: src/dmoj-wpadmin/test_project/apps/books/models.py:30 +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:30 +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:30 +msgid "owner" +msgstr "" + +#: judge/models/comment.py:193 judge/models/message.py:16 +msgid "read" +msgstr "" + +#: judge/models/comment.py:194 +#: src/dmoj-wpadmin/test_project/apps/books/models.py:28 +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:28 +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:28 +msgid "category" +msgstr "" + +#: judge/models/comment.py:195 +msgid "html link to comments, used for non-comments" +msgstr "" + +#: judge/models/comment.py:196 +msgid "who trigger, used for non-comment" +msgstr "" + +#: judge/models/contest.py:23 msgid "Invalid colour." msgstr "" -#: judge/models/contest.py:54 +#: judge/models/contest.py:25 msgid "tag name" msgstr "" -#: judge/models/contest.py:58 +#: judge/models/contest.py:26 msgid "Lowercase letters and hyphens only." msgstr "" -#: judge/models/contest.py:63 +#: judge/models/contest.py:27 msgid "tag colour" msgstr "" -#: judge/models/contest.py:65 +#: judge/models/contest.py:28 msgid "tag description" msgstr "" -#: judge/models/contest.py:86 +#: judge/models/contest.py:47 msgid "contest tag" msgstr "" -#: judge/models/contest.py:87 judge/models/contest.py:267 +#: judge/models/contest.py:48 judge/models/contest.py:118 msgid "contest tags" msgstr "nhãn kỳ thi" -#: judge/models/contest.py:95 +#: judge/models/contest.py:56 msgid "Visible" msgstr "Hiển thị" -#: judge/models/contest.py:96 +#: judge/models/contest.py:57 msgid "Hidden for duration of contest" msgstr "Ẩn trong thời gian kỳ thi" -#: judge/models/contest.py:97 +#: judge/models/contest.py:58 msgid "Hidden for duration of participation" msgstr "Ẩn trong thời gian tham gia" -#: judge/models/contest.py:101 +#: judge/models/contest.py:60 msgid "contest id" msgstr "ID kỳ thi" -#: judge/models/contest.py:106 +#: judge/models/contest.py:62 msgid "contest name" msgstr "tên kỳ thi" -#: judge/models/contest.py:110 judge/models/interface.py:80 -#: judge/models/problem.py:694 -msgid "authors" -msgstr "tác giả" - -#: judge/models/contest.py:111 +#: judge/models/contest.py:63 msgid "These users will be able to edit the contest." msgstr "Những người dùng này có quyền chỉnh sửa kỳ thi." -#: judge/models/contest.py:116 judge/models/problem.py:157 -msgid "curators" -msgstr "quản lý" - -#: judge/models/contest.py:118 +#: judge/models/contest.py:65 msgid "" "These users will be able to edit the contest, but will not be listed as " "authors." msgstr "Những người dùng này là tác giả và có quyền chỉnh sửa kỳ thi." -#: judge/models/contest.py:126 judge/models/problem.py:167 -msgid "testers" -msgstr "" - -#: judge/models/contest.py:128 +#: judge/models/contest.py:68 msgid "These users will be able to view the contest, but not edit it." msgstr "" "Những người dùng này có thể thấy kỳ thi nhưng không có quyền chỉnh sửa." -#: judge/models/contest.py:133 judge/models/runtime.py:217 +#: judge/models/contest.py:71 judge/models/runtime.py:136 +#: src/dmoj-wpadmin/test_project/apps/books/admin.py:20 +#: src/dmoj-wpadmin/test_project/apps/books/models.py:13 +#: src/dmoj-wpadmin/test_project/apps/books/models.py:27 +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:13 +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:27 +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:13 +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:27 msgid "description" msgstr "mô tả" -#: judge/models/contest.py:135 judge/models/problem.py:610 -#: judge/models/runtime.py:222 +#: judge/models/contest.py:72 judge/models/problem.py:390 +#: judge/models/runtime.py:138 msgid "problems" msgstr "bài tập" -#: judge/models/contest.py:137 judge/models/contest.py:727 +#: judge/models/contest.py:73 judge/models/contest.py:404 msgid "start time" msgstr "thời gian bắt đầu" -#: judge/models/contest.py:138 +#: judge/models/contest.py:74 msgid "end time" msgstr "thời gian kết thúc" -#: judge/models/contest.py:140 judge/models/problem.py:186 -#: judge/models/problem.py:645 +#: judge/models/contest.py:75 judge/models/problem.py:118 +#: judge/models/problem.py:414 msgid "time limit" msgstr "giới hạn thời gian" -#: judge/models/contest.py:144 -msgid "" -"Format hh:mm:ss. For example, if you want a 2-hour contest, enter 02:00:00" -msgstr "" -"Định dạng hh:mm:ss (giờ:phút:giây). Ví dụ, nếu muốn tạo kỳ thi dài 2h, hãy " -"nhập 02:00:00" - -#: judge/models/contest.py:148 -msgid "freeze after" -msgstr "đóng băng sau" - -#: judge/models/contest.py:152 -msgid "" -"Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, " -"enter 02:00:00" -msgstr "" -"Định dạng hh:mm:ss (giờ:phút:giây). Ví dụ, nếu muốn đóng băng kỳ thi sau 2h, " -"hãy nhập 02:00:00" - -#: judge/models/contest.py:156 judge/models/course.py:27 -#: judge/models/course.py:167 judge/models/problem.py:225 +#: judge/models/contest.py:76 judge/models/problem.py:136 msgid "publicly visible" -msgstr "công khai" +msgstr "hiển thị công khai" -#: judge/models/contest.py:159 +#: judge/models/contest.py:77 msgid "" "Should be set even for organization-private contests, where it determines " "whether the contest is visible to members of the specified organizations." msgstr "" -"Đánh dấu ngay cả với các kỳ thi riêng tư của nhóm, quyết định việc kỳ thi có " -"được hiển thị với các thành viên hay không." +"Đánh dấu ngay cả với các kỳ thi riêng tư của tổ chức, quyết định việc kỳ thi " +"có được hiển thị với các thành viên hay không." -#: judge/models/contest.py:165 +#: judge/models/contest.py:80 msgid "contest rated" msgstr "kỳ thi được xếp hạng" -#: judge/models/contest.py:166 +#: judge/models/contest.py:80 msgid "Whether this contest can be rated." msgstr "Quyết định kỳ thi có được xếp hạng không." -#: judge/models/contest.py:170 +#: judge/models/contest.py:82 msgid "scoreboard visibility" msgstr "khả năng hiển thị của bảng điểm" -#: judge/models/contest.py:173 +#: judge/models/contest.py:83 msgid "Scoreboard visibility through the duration of the contest" msgstr "Khả năng hiển thị của bảng điểm trong thời gian kỳ thi" -#: judge/models/contest.py:178 +#: judge/models/contest.py:85 msgid "view contest scoreboard" msgstr "xem bảng điểm kỳ thi" -#: judge/models/contest.py:181 +#: judge/models/contest.py:87 msgid "These users will be able to view the scoreboard." msgstr "Những người dùng này được phép xem bảng điểm." -#: judge/models/contest.py:184 -msgid "public scoreboard" -msgstr "công khai bảng điểm" - -#: judge/models/contest.py:185 -msgid "Ranking page is public even for private contests." -msgstr "Trang xếp hạng được công khai, kể cả cho kỳ thi riêng tư." - -#: judge/models/contest.py:189 +#: judge/models/contest.py:88 msgid "no comments" msgstr "không bình luận" -#: judge/models/contest.py:190 +#: judge/models/contest.py:89 msgid "Use clarification system instead of comments." msgstr "Dùng hệ thống thông báo thay vì bình luận." -#: judge/models/contest.py:195 +#: judge/models/contest.py:91 msgid "Rating floor for contest" msgstr "Cận dưới rating được xếp hạng trong kỳ thi" -#: judge/models/contest.py:201 +#: judge/models/contest.py:93 msgid "Rating ceiling for contest" msgstr "Cận trên rating được xếp hạng trong kỳ thi" -#: judge/models/contest.py:206 +#: judge/models/contest.py:95 msgid "rate all" msgstr "xếp hạng tất cả" -#: judge/models/contest.py:207 +#: judge/models/contest.py:95 msgid "Rate all users who joined." msgstr "Xếp hạng tất cả người dùng đã tham gia (kể cả không nộp)." -#: judge/models/contest.py:212 +#: judge/models/contest.py:96 msgid "exclude from ratings" msgstr "không xếp hạng" -#: judge/models/contest.py:217 +#: judge/models/contest.py:98 msgid "private to specific users" msgstr "riêng tư với các người dùng này" -#: judge/models/contest.py:222 +#: judge/models/contest.py:99 msgid "private contestants" msgstr "thí sinh riêng tư" -#: judge/models/contest.py:223 +#: judge/models/contest.py:100 msgid "If private, only these users may see the contest" msgstr "Nếu riêng tư, chỉ những người dùng này mới thấy kỳ thi" -#: judge/models/contest.py:227 +#: judge/models/contest.py:102 msgid "hide problem tags" msgstr "ẩn nhãn kỳ thi" -#: judge/models/contest.py:228 +#: judge/models/contest.py:103 msgid "Whether problem tags should be hidden by default." msgstr "" "Quyết định việc nhãn bài tập (DP, Tham lam, ...) được ẩn trong kỳ thi không." -#: judge/models/contest.py:232 +#: judge/models/contest.py:105 msgid "run pretests only" msgstr "chỉ chạy pretests" -#: judge/models/contest.py:234 +#: judge/models/contest.py:106 msgid "" "Whether judges should grade pretests only, versus all testcases. Commonly " "set during a contest, then unset prior to rejudging user submissions when " @@ -929,57 +841,50 @@ msgstr "" "Quyết định việc các máy chấm chỉ chấm pretests thay vì tất cả các test. Sau " "kỳ thi, hãy bỏ đánh dấu ô này và chấm lại tất cả các bài." -#: judge/models/contest.py:241 judge/models/interface.py:97 -#: judge/models/problem.py:285 +#: judge/models/contest.py:110 judge/models/interface.py:77 +#: judge/models/problem.py:157 msgid "private to organizations" msgstr "riêng tư với các tổ chức" -#: judge/models/contest.py:246 judge/models/course.py:33 -#: judge/models/interface.py:93 judge/models/problem.py:281 -#: judge/models/profile.py:167 +#: judge/models/contest.py:111 judge/models/interface.py:75 +#: judge/models/problem.py:155 judge/models/profile.py:77 msgid "organizations" msgstr "tổ chức" -#: judge/models/contest.py:247 +#: judge/models/contest.py:112 msgid "If private, only these organizations may see the contest" msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được kỳ thi" -#: judge/models/contest.py:250 -#, fuzzy -#| msgid "contest name" -msgid "contest in course" -msgstr "tên kỳ thi" - -#: judge/models/contest.py:254 judge/models/problem.py:256 +#: judge/models/contest.py:113 judge/models/problem.py:145 msgid "OpenGraph image" msgstr "Hình ảnh OpenGraph" -#: judge/models/contest.py:257 +#: judge/models/contest.py:114 judge/models/profile.py:48 msgid "Logo override image" msgstr "Hình ảnh ghi đè logo" -#: judge/models/contest.py:262 +#: judge/models/contest.py:116 msgid "" "This image will replace the default site logo for users inside the contest." msgstr "Ảnh này sẽ thay thế cho logo mặc định trong kỳ thi." -#: judge/models/contest.py:270 +#: judge/models/contest.py:119 msgid "the amount of live participants" msgstr "số lượng thí sinh thi trực tiếp" -#: judge/models/contest.py:274 +#: judge/models/contest.py:120 msgid "contest summary" msgstr "tổng kết kỳ thi" -#: judge/models/contest.py:276 judge/models/problem.py:262 +#: judge/models/contest.py:121 judge/models/problem.py:147 msgid "Plain-text, shown in meta description tag, e.g. for social media." msgstr "" -#: judge/models/contest.py:280 judge/models/profile.py:109 +#: judge/models/contest.py:122 judge/models/profile.py:47 msgid "access code" msgstr "mật khẩu truy cập" -#: judge/models/contest.py:285 +#: judge/models/contest.py:123 msgid "" "An optional code to prompt contestants before they are allowed to join the " "contest. Leave it blank to disable." @@ -987,488 +892,335 @@ msgstr "" "Mật khẩu truy cập cho các thí sinh muốn tham gia kỳ thi. Để trống nếu không " "dùng." -#: judge/models/contest.py:291 judge/models/problem.py:244 +#: judge/models/contest.py:125 judge/models/problem.py:141 msgid "personae non gratae" -msgstr "Chặn tham gia" +msgstr "" -#: judge/models/contest.py:293 +#: judge/models/contest.py:126 msgid "Bans the selected users from joining this contest." msgstr "Cấm những người dùng được chọn tham gia kỳ thi." -#: judge/models/contest.py:296 +#: judge/models/contest.py:127 msgid "contest format" msgstr "format kỳ thi" -#: judge/models/contest.py:300 +#: judge/models/contest.py:128 msgid "The contest format module to use." -msgstr "Format kỳ thi sử dụng." +msgstr "" -#: judge/models/contest.py:303 +#: judge/models/contest.py:129 msgid "contest format configuration" -msgstr "Tùy chỉnh format kỳ thi" +msgstr "" -#: judge/models/contest.py:307 +#: judge/models/contest.py:130 msgid "" "A JSON object to serve as the configuration for the chosen contest format " "module. Leave empty to use None. Exact format depends on the contest format " "selected." msgstr "" -#: judge/models/contest.py:320 +#: judge/models/contest.py:137 msgid "precision points" -msgstr "Hiển thị điểm" +msgstr "" -#: judge/models/contest.py:323 +#: judge/models/contest.py:139 msgid "Number of digits to round points to." -msgstr "Số chữ số thập phân trên bảng điểm." +msgstr "" -#: judge/models/contest.py:326 -msgid "rate limit" -msgstr "giới hạn bài nộp" - -#: judge/models/contest.py:331 -msgid "" -"Maximum number of submissions per minute. Leave empty if you don't want rate " -"limit." -msgstr "Số bài nộp tối đa mỗi phút. Để trống nếu không muốn giới hạn." - -#: judge/models/contest.py:362 -msgid "End time must be after start time" -msgstr "Thời gian kết thúc phải sau thời gian bắt đầu" - -#: judge/models/contest.py:681 +#: judge/models/contest.py:383 msgid "See private contests" msgstr "" -#: judge/models/contest.py:682 +#: judge/models/contest.py:384 msgid "Edit own contests" msgstr "" -#: judge/models/contest.py:683 +#: judge/models/contest.py:385 msgid "Edit all contests" msgstr "" -#: judge/models/contest.py:684 +#: judge/models/contest.py:386 msgid "Clone contest" msgstr "" -#: judge/models/contest.py:685 templates/contest/moss.html:72 +#: judge/models/contest.py:387 templates/contest/moss.html:74 msgid "MOSS contest" msgstr "" -#: judge/models/contest.py:686 +#: judge/models/contest.py:388 msgid "Rate contests" msgstr "" -#: judge/models/contest.py:687 +#: judge/models/contest.py:389 msgid "Contest access codes" msgstr "" -#: judge/models/contest.py:688 +#: judge/models/contest.py:390 msgid "Create private contests" msgstr "" -#: judge/models/contest.py:689 +#: judge/models/contest.py:391 msgid "Change contest visibility" msgstr "" -#: judge/models/contest.py:690 +#: judge/models/contest.py:392 msgid "Edit contest problem label script" -msgstr "Cách hiển thị thứ tự bài tập" +msgstr "" -#: judge/models/contest.py:692 judge/models/contest.py:853 -#: judge/models/contest.py:931 judge/models/contest.py:961 -#: judge/models/contest.py:1040 judge/models/submission.py:116 +#: judge/models/contest.py:394 judge/models/contest.py:492 +#: judge/models/contest.py:529 judge/models/contest.py:553 +#: judge/models/submission.py:84 msgid "contest" msgstr "kỳ thi" -#: judge/models/contest.py:693 +#: judge/models/contest.py:395 msgid "contests" msgstr "kỳ thi" -#: judge/models/contest.py:716 +#: judge/models/contest.py:402 msgid "associated contest" msgstr "" -#: judge/models/contest.py:729 judge/models/course.py:185 +#: judge/models/contest.py:405 msgid "score" msgstr "điểm" -#: judge/models/contest.py:730 +#: judge/models/contest.py:406 msgid "cumulative time" msgstr "tổng thời gian" -#: judge/models/contest.py:732 +#: judge/models/contest.py:407 msgid "is disqualified" msgstr "đã bị loại" -#: judge/models/contest.py:734 +#: judge/models/contest.py:408 msgid "Whether this participation is disqualified." msgstr "Quyết định thí sinh có bị loại không." -#: judge/models/contest.py:736 +#: judge/models/contest.py:409 msgid "tie-breaking field" msgstr "" -#: judge/models/contest.py:738 +#: judge/models/contest.py:410 msgid "virtual participation id" msgstr "id lần tham gia ảo" -#: judge/models/contest.py:740 +#: judge/models/contest.py:411 msgid "0 means non-virtual, otherwise the n-th virtual participation." msgstr "0 nghĩa là tham gia chính thức, ngược lại là lần tham gia ảo thứ n." -#: judge/models/contest.py:743 +#: judge/models/contest.py:412 msgid "contest format specific data" msgstr "" -#: judge/models/contest.py:746 -msgid "same as format_data, but including frozen results" -msgstr "" - -#: judge/models/contest.py:750 -msgid "final score" -msgstr "điểm" - -#: judge/models/contest.py:752 -msgid "final cumulative time" -msgstr "tổng thời gian" - -#: judge/models/contest.py:828 +#: judge/models/contest.py:478 #, python-format msgid "%s spectating in %s" msgstr "%s đang theo dõi trong %s" -#: judge/models/contest.py:833 +#: judge/models/contest.py:480 #, python-format msgid "%s in %s, v%d" msgstr "%s trong %s, v%d" -#: judge/models/contest.py:838 +#: judge/models/contest.py:481 #, python-format msgid "%s in %s" msgstr "%s trong %s" -#: judge/models/contest.py:841 +#: judge/models/contest.py:484 msgid "contest participation" msgstr "lần tham gia kỳ thi" -#: judge/models/contest.py:842 +#: judge/models/contest.py:485 msgid "contest participations" msgstr "lần tham gia kỳ thi" -#: judge/models/contest.py:849 judge/models/contest.py:902 -#: judge/models/contest.py:964 judge/models/problem.py:609 -#: judge/models/problem.py:616 judge/models/problem.py:637 -#: judge/models/problem.py:668 judge/models/problem_data.py:50 +#: judge/models/contest.py:491 judge/models/contest.py:513 +#: judge/models/contest.py:554 judge/models/problem.py:389 +#: judge/models/problem.py:394 judge/models/problem.py:412 +#: judge/models/problem_data.py:40 msgid "problem" msgstr "bài tập" -#: judge/models/contest.py:857 judge/models/contest.py:914 -#: judge/models/course.py:166 judge/models/course.py:199 -#: judge/models/problem.py:209 +#: judge/models/contest.py:493 judge/models/contest.py:517 +#: judge/models/problem.py:129 msgid "points" msgstr "điểm" -#: judge/models/contest.py:858 +#: judge/models/contest.py:494 msgid "partial" msgstr "thành phần" -#: judge/models/contest.py:859 judge/models/contest.py:916 +#: judge/models/contest.py:495 judge/models/contest.py:518 msgid "is pretested" msgstr "dùng pretest" -#: judge/models/contest.py:860 judge/models/course.py:165 -#: judge/models/course.py:184 judge/models/course.py:198 -#: judge/models/interface.py:48 +#: judge/models/contest.py:496 judge/models/interface.py:43 msgid "order" msgstr "thứ tự" -#: judge/models/contest.py:862 +#: judge/models/contest.py:497 +msgid "0 to not show testcases, 1 to show" +msgstr "0 để ẩn test, 1 để hiện" + +#: judge/models/contest.py:498 msgid "visible testcases" msgstr "hiển thị test" -#: judge/models/contest.py:867 +#: judge/models/contest.py:499 msgid "Maximum number of submissions for this problem, or 0 for no limit." msgstr "Số lần nộp tối đa, đặt là 0 nếu không có giới hạn." -#: judge/models/contest.py:869 -msgid "max submissions" -msgstr "số lần nộp tối đa" - -#: judge/models/contest.py:872 +#: judge/models/contest.py:501 msgid "Why include a problem you can't submit to?" msgstr "" -#: judge/models/contest.py:876 -#, fuzzy -#| msgid "Only for format new IOI. Separated by commas, e.g: 2, 3" -msgid "Separated by commas, e.g: 2, 3" -msgstr "" -"Chỉ dùng với format IOI mới. Các sub cách nhau bởi dấu phẩy. Ví dụ: 2, 3" - -#: judge/models/contest.py:877 -msgid "hidden subtasks" -msgstr "Đóng băng subtasks" - -#: judge/models/contest.py:889 +#: judge/models/contest.py:506 msgid "contest problem" msgstr "bài trong kỳ thi" -#: judge/models/contest.py:890 +#: judge/models/contest.py:507 msgid "contest problems" msgstr "bài trong kỳ thi" -#: judge/models/contest.py:896 judge/models/submission.py:274 +#: judge/models/contest.py:511 judge/models/submission.py:183 msgid "submission" msgstr "bài nộp" -#: judge/models/contest.py:909 judge/models/contest.py:935 +#: judge/models/contest.py:515 judge/models/contest.py:530 msgid "participation" msgstr "lần tham gia" -#: judge/models/contest.py:917 +#: judge/models/contest.py:519 msgid "Whether this submission was ran only on pretests." msgstr "Quyết định bài nộp chỉ được chạy trên pretest không." -#: judge/models/contest.py:922 +#: judge/models/contest.py:523 msgid "contest submission" msgstr "bài nộp kỳ thi" -#: judge/models/contest.py:923 +#: judge/models/contest.py:524 msgid "contest submissions" msgstr "bài nộp kỳ thi" -#: judge/models/contest.py:939 +#: judge/models/contest.py:532 msgid "rank" msgstr "rank" -#: judge/models/contest.py:940 +#: judge/models/contest.py:533 msgid "rating" msgstr "rating" -#: judge/models/contest.py:941 +#: judge/models/contest.py:534 msgid "raw rating" msgstr "rating thật" -#: judge/models/contest.py:942 +#: judge/models/contest.py:535 msgid "contest performance" msgstr "" -#: judge/models/contest.py:943 +#: judge/models/contest.py:536 msgid "last rated" msgstr "lần cuối được xếp hạng" -#: judge/models/contest.py:947 +#: judge/models/contest.py:540 msgid "contest rating" msgstr "rating kỳ thi" -#: judge/models/contest.py:948 +#: judge/models/contest.py:541 msgid "contest ratings" msgstr "rating kỳ thi" -#: judge/models/contest.py:972 +#: judge/models/contest.py:561 msgid "contest moss result" msgstr "kết quả MOSS kỳ thi" -#: judge/models/contest.py:973 +#: judge/models/contest.py:562 msgid "contest moss results" msgstr "kết quả MOSS kỳ thi" -#: judge/models/contest.py:978 -msgid "clarified problem" -msgstr "" - -#: judge/models/contest.py:980 -msgid "clarification body" -msgstr "" - -#: judge/models/contest.py:982 -msgid "clarification timestamp" -msgstr "" - -#: judge/models/contest.py:1001 -msgid "contests summary" -msgstr "tổng kết kỳ thi" - -#: judge/models/contest.py:1002 -msgid "contests summaries" -msgstr "tổng kết kỳ thi" - -#: judge/models/contest.py:1013 judge/models/contest.py:1020 -msgid "official contest category" -msgstr "loại kỳ thi chính thức" - -#: judge/models/contest.py:1021 -msgid "official contest categories" -msgstr "các loại kỳ thi chính thức" - -#: judge/models/contest.py:1026 judge/models/contest.py:1033 -msgid "official contest location" -msgstr "địa điểm kỳ thi chính thức" - -#: judge/models/contest.py:1034 -msgid "official contest locations" -msgstr "các địa điểm kỳ thi chính thức" - -#: judge/models/contest.py:1046 -msgid "contest category" -msgstr "loại kỳ thi" - -#: judge/models/contest.py:1049 -msgid "year" -msgstr "năm" - -#: judge/models/contest.py:1052 -msgid "contest location" -msgstr "địa điểm kỳ thi" - -#: judge/models/contest.py:1057 -msgid "official contest" -msgstr "kỳ thi chính thức" - -#: judge/models/contest.py:1058 -msgid "official contests" -msgstr "các kỳ thi chính thức" - -#: judge/models/course.py:12 templates/course/grades.html:88 -#: templates/course/grades_lesson.html:88 -msgid "Student" -msgstr "Học sinh" - -#: judge/models/course.py:13 -msgid "Assistant" -msgstr "Trợ giảng" - -#: judge/models/course.py:14 -msgid "Teacher" -msgstr "Giáo viên" - -#: judge/models/course.py:23 -msgid "course name" -msgstr "tên khóa học" - -#: judge/models/course.py:25 -msgid "course description" -msgstr "Mô tả khóa học" - -#: judge/models/course.py:34 -msgid "If private, only these organizations may see the course" -msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được khóa học" - -#: judge/models/course.py:38 -msgid "course slug" -msgstr "url khóa học" - -#: judge/models/course.py:39 -msgid "Course name shown in URL" -msgstr "Tên được hiển thị trong đường dẫn" - -#: judge/models/course.py:42 judge/models/profile.py:65 -msgid "Only alphanumeric and hyphens" -msgstr "Chỉ chứa chữ cái và dấu gạch ngang (-)" - -#: judge/models/course.py:46 -msgid "public registration" -msgstr "Cho phép đăng ký" - -#: judge/models/course.py:50 -msgid "course image" -msgstr "hình ảnh khóa học" - -#: judge/models/course.py:123 judge/models/course.py:159 -msgid "course" -msgstr "khóa học" - -#: judge/models/course.py:163 -#, fuzzy -#| msgid "message title" -msgid "lesson title" -msgstr "tiêu đề tin nhắn" - -#: judge/models/course.py:164 -#, fuzzy -#| msgid "post content" -msgid "lesson content" -msgstr "đăng nội dung" - -#: judge/models/interface.py:29 +#: judge/models/interface.py:24 msgid "configuration item" msgstr "" -#: judge/models/interface.py:30 +#: judge/models/interface.py:25 msgid "miscellaneous configuration" msgstr "" -#: judge/models/interface.py:42 +#: judge/models/interface.py:37 msgid "navigation item" msgstr "mục điều hướng" -#: judge/models/interface.py:43 +#: judge/models/interface.py:38 msgid "navigation bar" msgstr "thanh điều hướng" -#: judge/models/interface.py:49 +#: judge/models/interface.py:44 msgid "identifier" msgstr "" -#: judge/models/interface.py:50 +#: judge/models/interface.py:45 msgid "label" msgstr "nhãn" -#: judge/models/interface.py:53 +#: judge/models/interface.py:47 msgid "highlight regex" msgstr "" -#: judge/models/interface.py:57 +#: judge/models/interface.py:48 msgid "parent item" msgstr "mục cha" -#: judge/models/interface.py:79 +#: judge/models/interface.py:66 msgid "post title" msgstr "tiêu đề bài đăng" -#: judge/models/interface.py:81 +#: judge/models/interface.py:67 judge/models/problem.py:432 +msgid "authors" +msgstr "tác giả" + +#: judge/models/interface.py:68 msgid "slug" msgstr "slug" -#: judge/models/interface.py:82 judge/models/problem.py:692 +#: judge/models/interface.py:69 judge/models/problem.py:430 msgid "public visibility" msgstr "khả năng hiển thị công khai" -#: judge/models/interface.py:83 +#: judge/models/interface.py:70 msgid "sticky" msgstr "nổi lên đầu" -#: judge/models/interface.py:84 +#: judge/models/interface.py:71 msgid "publish after" msgstr "đăng sau khi" -#: judge/models/interface.py:85 +#: judge/models/interface.py:72 msgid "post content" msgstr "đăng nội dung" -#: judge/models/interface.py:86 +#: judge/models/interface.py:73 msgid "post summary" msgstr "đăng tổng kết" -#: judge/models/interface.py:88 +#: judge/models/interface.py:74 msgid "openGraph image" msgstr "hình ảnh openGraph" -#: judge/models/interface.py:94 +#: judge/models/interface.py:76 msgid "If private, only these organizations may see the blog post." msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được bài đăng." -#: judge/models/interface.py:141 +#: judge/models/interface.py:105 msgid "Edit all posts" msgstr "Chỉnh sửa tất cả bài đăng" -#: judge/models/interface.py:142 +#: judge/models/interface.py:107 msgid "blog post" msgstr "bài đăng" -#: judge/models/interface.py:143 +#: judge/models/interface.py:108 msgid "blog posts" msgstr "bài đăng" @@ -1476,974 +1228,784 @@ msgstr "bài đăng" msgid "message title" msgstr "tiêu đề tin nhắn" -#: judge/models/message.py:12 judge/models/ticket.py:48 +#: judge/models/message.py:12 judge/models/ticket.py:29 msgid "message body" msgstr "nội dung tin nhắn" -#: judge/models/message.py:15 +#: judge/models/message.py:13 msgid "sender" msgstr "người gửi" -#: judge/models/message.py:21 +#: judge/models/message.py:14 msgid "target" msgstr "người nhận" -#: judge/models/message.py:26 +#: judge/models/message.py:15 msgid "message timestamp" msgstr "thời gian gửi" -#: judge/models/message.py:28 -msgid "read" -msgstr "" - -#: judge/models/message.py:33 +#: judge/models/message.py:20 msgid "messages in the thread" msgstr "tin nhắn trong chuỗi" -#: judge/models/notification.py:11 -msgid "Added a post" -msgstr "Thêm bài đăng" - -#: judge/models/notification.py:12 -msgid "You are added to a group" -msgstr "Bạn đã được thêm vào nhóm." - -#: judge/models/notification.py:13 -msgid "You have a new comment" -msgstr "Bạn có bình luận mới" - -#: judge/models/notification.py:14 -msgid "Deleted a post" -msgstr "Đã xóa bài đăng" - -#: judge/models/notification.py:15 -msgid "Rejected a post" -msgstr "Đã từ chối bài đăng" - -#: judge/models/notification.py:16 -msgid "Approved a post" -msgstr "Đã chấp thuận bài đăng" - -#: judge/models/notification.py:17 -msgid "Edited a post" -msgstr "Đã chỉnh sửa bài đăng" - -#: judge/models/notification.py:18 -msgid "Mentioned you" -msgstr "Đã nhắc đến bạn" - -#: judge/models/notification.py:19 -msgid "Replied you" -msgstr "Đã phản hồi bạn" - -#: judge/models/notification.py:20 templates/blog/list.html:47 -msgid "Ticket" -msgstr "Báo cáo" - -#: judge/models/notification.py:27 -msgid "owner" -msgstr "" - -#: judge/models/notification.py:32 -msgid "category" -msgstr "" - -#: judge/models/notification.py:35 -msgid "html link to comments, used for non-comments" -msgstr "" - -#: judge/models/notification.py:41 -msgid "who trigger, used for non-comment" -msgstr "" - -#: judge/models/notification.py:54 -msgid "The problem is public to: " -msgstr "Bài tập được công khai trong: " - -#: judge/models/notification.py:56 -msgid "The problem is private to: " -msgstr "Bài tập riêng tư trong: " - -#: judge/models/notification.py:59 -msgid "The problem is public to everyone." -msgstr "Bài tập được công khai với mọi người." - -#: judge/models/notification.py:61 -msgid "The problem is private." -msgstr "Bài tập được đánh dấu riêng tư." - -#: judge/models/pagevote.py:25 -#, fuzzy -#| msgid "votes" -msgid "pagevote" -msgstr "bình chọn" - -#: judge/models/pagevote.py:26 -#, fuzzy -#| msgid "votes" -msgid "pagevotes" -msgstr "bình chọn" - -#: judge/models/pagevote.py:48 -#, fuzzy -#| msgid "volunteer vote" -msgid "pagevote vote" -msgstr "vote từ TNV" - -#: judge/models/pagevote.py:49 -#, fuzzy -#| msgid "volunteer votes" -msgid "pagevote votes" -msgstr "vote từ TNV" - -#: judge/models/problem.py:46 +#: judge/models/problem.py:26 msgid "problem category ID" msgstr "mã của nhóm bài" -#: judge/models/problem.py:49 +#: judge/models/problem.py:27 msgid "problem category name" msgstr "tên nhóm bài" -#: judge/models/problem.py:57 +#: judge/models/problem.py:34 msgid "problem type" msgstr "dạng bài" -#: judge/models/problem.py:58 judge/models/problem.py:176 -#: judge/models/volunteer.py:28 +#: judge/models/problem.py:35 judge/models/problem.py:113 msgid "problem types" msgstr "dạng bài" -#: judge/models/problem.py:63 +#: judge/models/problem.py:39 msgid "problem group ID" msgstr "mã của nhóm bài" -#: judge/models/problem.py:65 +#: judge/models/problem.py:40 msgid "problem group name" msgstr "tên nhóm bài" -#: judge/models/problem.py:72 judge/models/problem.py:181 +#: judge/models/problem.py:47 judge/models/problem.py:116 msgid "problem group" msgstr "nhóm bài" -#: judge/models/problem.py:73 +#: judge/models/problem.py:48 msgid "problem groups" msgstr "nhóm bài" -#: judge/models/problem.py:80 +#: judge/models/problem.py:52 msgid "key" msgstr "" -#: judge/models/problem.py:83 +#: judge/models/problem.py:54 msgid "link" msgstr "đường dẫn" -#: judge/models/problem.py:84 +#: judge/models/problem.py:55 msgid "full name" msgstr "tên đầy đủ" -#: judge/models/problem.py:88 judge/models/profile.py:70 -#: judge/models/runtime.py:35 +#: judge/models/problem.py:56 judge/models/profile.py:33 +#: judge/models/runtime.py:24 msgid "short name" msgstr "tên ngắn" -#: judge/models/problem.py:89 +#: judge/models/problem.py:57 msgid "Displayed on pages under this license" msgstr "Được hiển thị trên các trang theo giấy phép này" -#: judge/models/problem.py:94 +#: judge/models/problem.py:58 msgid "icon" msgstr "icon" -#: judge/models/problem.py:95 +#: judge/models/problem.py:58 msgid "URL to the icon" msgstr "Đường dẫn icon" -#: judge/models/problem.py:97 +#: judge/models/problem.py:59 msgid "license text" msgstr "văn bản giấy phép" -#: judge/models/problem.py:106 +#: judge/models/problem.py:68 msgid "license" msgstr "" -#: judge/models/problem.py:107 +#: judge/models/problem.py:69 msgid "licenses" msgstr "" -#: judge/models/problem.py:130 +#: judge/models/problem.py:96 msgid "problem code" -msgstr "mã bài" +msgstr "" -#: judge/models/problem.py:136 +#: judge/models/problem.py:98 msgid "A short, unique code for the problem, used in the url after /problem/" -msgstr "Mã bài ngắn, độc nhất cho bài tập, được dùng trong url sau /problem/" +msgstr "" -#: judge/models/problem.py:141 +#: judge/models/problem.py:100 msgid "problem name" -msgstr "Tên bài" +msgstr "" -#: judge/models/problem.py:143 +#: judge/models/problem.py:101 msgid "The full name of the problem, as shown in the problem list." -msgstr "Tên đầy đủ của bài, như được hiển thị trên danh sách bài tập" +msgstr "" -#: judge/models/problem.py:145 +#: judge/models/problem.py:103 msgid "problem body" -msgstr "Nội dung" +msgstr "" -#: judge/models/problem.py:148 +#: judge/models/problem.py:104 msgid "creators" msgstr "" -#: judge/models/problem.py:152 +#: judge/models/problem.py:105 msgid "These users will be able to edit the problem, and be listed as authors." msgstr "" -"Những người dùng này sẽ có thể chỉnh sửa bài tập, và nằm trong danh sách các " -"tác giả" -#: judge/models/problem.py:161 +#: judge/models/problem.py:107 +msgid "curators" +msgstr "" + +#: judge/models/problem.py:108 msgid "" "These users will be able to edit the problem, but not be listed as authors." msgstr "" -"Những người dùng này sẽ có thể chỉnh sửa bài tập, nhưng không nằm trong danh " -"sách các tác giả" -#: judge/models/problem.py:171 +#: judge/models/problem.py:110 +msgid "testers" +msgstr "" + +#: judge/models/problem.py:112 msgid "These users will be able to view the private problem, but not edit it." msgstr "" -"Những người dùng này sẽ thấy được bài tập này (dù riêng tư), nhưng không " -"chỉnh sửa được" -#: judge/models/problem.py:177 judge/models/volunteer.py:29 +#: judge/models/problem.py:114 msgid "The type of problem, as shown on the problem's page." -msgstr "Dạng bài, giống như trên trang bài tập" +msgstr "" -#: judge/models/problem.py:183 +#: judge/models/problem.py:117 msgid "The group of problem, shown under Category in the problem list." -msgstr "Nhóm bài, hiện ở mục Nhóm bài trong danh sách bài tập" +msgstr "" -#: judge/models/problem.py:188 +#: judge/models/problem.py:119 msgid "" "The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) " "are supported." msgstr "" -"Giới hạn thời gian cho bài tập này, theo đơn vị giây. Có thể nhập số thực " -"(ví dụ 1.5)" -#: judge/models/problem.py:197 judge/models/problem.py:652 +#: judge/models/problem.py:123 judge/models/problem.py:417 msgid "memory limit" -msgstr "Giới hạn bộ nhớ" +msgstr "" -#: judge/models/problem.py:199 +#: judge/models/problem.py:124 msgid "" -"The memory limit for this problem, in kilobytes (e.g. 256mb = 262144 " +"The memory limit for this problem, in kilobytes (e.g. 64mb = 65536 " "kilobytes)." msgstr "" -"Giới hạn bộ nhớ cho bài này, theo đơn vị kilobytes (ví dụ 256mb = 262144 " -"kilobytes)" -#: judge/models/problem.py:211 +#: judge/models/problem.py:130 msgid "" "Points awarded for problem completion. Points are displayed with a 'p' " "suffix if partial." msgstr "" -"Điểm thưởng khi hoàn thành bài tập. Điểm có thêm chữ 'p' ở sau cùng nếu như " -"chấp nhận cho điểm thành phần (có điểm ngay khi không đúng toàn bộ test)" -#: judge/models/problem.py:217 +#: judge/models/problem.py:133 msgid "allows partial points" -msgstr "cho phép điểm thành phần" +msgstr "" -#: judge/models/problem.py:221 +#: judge/models/problem.py:134 msgid "allowed languages" -msgstr "các ngôn ngữ được cho phép" +msgstr "" -#: judge/models/problem.py:222 +#: judge/models/problem.py:135 msgid "List of allowed submission languages." -msgstr "Danh sách các ngôn ngữ lập trình cho phép" +msgstr "" -#: judge/models/problem.py:228 +#: judge/models/problem.py:137 msgid "manually managed" msgstr "" -#: judge/models/problem.py:231 +#: judge/models/problem.py:138 msgid "Whether judges should be allowed to manage data or not." msgstr "" -#: judge/models/problem.py:234 +#: judge/models/problem.py:139 msgid "date of publishing" -msgstr "Ngày công bố" +msgstr "" -#: judge/models/problem.py:239 +#: judge/models/problem.py:140 msgid "" "Doesn't have magic ability to auto-publish due to backward compatibility" msgstr "" -#: judge/models/problem.py:246 +#: judge/models/problem.py:142 msgid "Bans the selected users from submitting to this problem." -msgstr "Cấm những người dùng được chọn nộp bài tập này." +msgstr "" -#: judge/models/problem.py:253 +#: judge/models/problem.py:144 msgid "The license under which this problem is published." -msgstr "Giấy phép xuất bản bài tập" +msgstr "" -#: judge/models/problem.py:260 +#: judge/models/problem.py:146 msgid "problem summary" -msgstr "Tóm tắt bài tập" +msgstr "" -#: judge/models/problem.py:266 +#: judge/models/problem.py:148 msgid "number of users" msgstr "" -#: judge/models/problem.py:268 +#: judge/models/problem.py:149 msgid "The number of users who solved the problem." -msgstr "Số lượng người dùng đã giải được bài" +msgstr "" -#: judge/models/problem.py:270 +#: judge/models/problem.py:150 msgid "solve rate" -msgstr "Tỉ lệ giải đúng" +msgstr "" -#: judge/models/problem.py:282 +#: judge/models/problem.py:156 msgid "If private, only these organizations may see the problem." -msgstr "Nếu bài riêng tư, chỉ những tổ chức này thấy được" +msgstr "" -#: judge/models/problem.py:288 -msgid "pdf statement" -msgstr "Đề bài bằng file pdf" - -#: judge/models/problem.py:621 judge/models/problem.py:642 -#: judge/models/problem.py:673 judge/models/runtime.py:159 +#: judge/models/problem.py:395 judge/models/problem.py:413 +#: judge/models/runtime.py:111 msgid "language" msgstr "" -#: judge/models/problem.py:624 +#: judge/models/problem.py:396 msgid "translated name" msgstr "" -#: judge/models/problem.py:626 +#: judge/models/problem.py:397 msgid "translated description" msgstr "" -#: judge/models/problem.py:630 +#: judge/models/problem.py:401 msgid "problem translation" msgstr "" -#: judge/models/problem.py:631 +#: judge/models/problem.py:402 msgid "problem translations" msgstr "" -#: judge/models/problem.py:661 +#: judge/models/problem.py:406 +msgid "clarified problem" +msgstr "" + +#: judge/models/problem.py:407 +msgid "clarification body" +msgstr "" + +#: judge/models/problem.py:408 +msgid "clarification timestamp" +msgstr "" + +#: judge/models/problem.py:423 msgid "language-specific resource limit" msgstr "" -#: judge/models/problem.py:662 +#: judge/models/problem.py:424 msgid "language-specific resource limits" msgstr "" -#: judge/models/problem.py:675 judge/models/submission.py:291 -msgid "source code" -msgstr "mã nguồn" - -#: judge/models/problem.py:679 -msgid "language-specific template" -msgstr "" - -#: judge/models/problem.py:680 -msgid "language-specific templates" -msgstr "" - -#: judge/models/problem.py:687 +#: judge/models/problem.py:428 msgid "associated problem" msgstr "" -#: judge/models/problem.py:693 +#: judge/models/problem.py:431 msgid "publish date" msgstr "" -#: judge/models/problem.py:695 +#: judge/models/problem.py:433 msgid "editorial content" msgstr "nội dung lời giải" -#: judge/models/problem.py:712 -#, python-format -msgid "Editorial for %s" -msgstr "" - -#: judge/models/problem.py:716 +#: judge/models/problem.py:449 msgid "solution" msgstr "lời giải" -#: judge/models/problem.py:717 +#: judge/models/problem.py:450 msgid "solutions" msgstr "lời giải" -#: judge/models/problem.py:722 -#, fuzzy -#| msgid "point value" -msgid "proposed point value" -msgstr "điểm" - -#: judge/models/problem.py:723 -msgid "The amount of points you think this problem deserves." -msgstr "Bạn nghĩ bài này đáng bao nhiêu điểm?" - -#: judge/models/problem.py:737 -msgid "The time this vote was cast" -msgstr "" - -#: judge/models/problem.py:743 -msgid "vote" -msgstr "" - -#: judge/models/problem_data.py:32 +#: judge/models/problem_data.py:26 msgid "Standard" msgstr "Tiêu chuẩn" -#: judge/models/problem_data.py:33 +#: judge/models/problem_data.py:27 msgid "Floats" msgstr "Số thực" -#: judge/models/problem_data.py:34 +#: judge/models/problem_data.py:28 msgid "Floats (absolute)" msgstr "Số thực (chênh lệch tuyệt đối)" -#: judge/models/problem_data.py:35 +#: judge/models/problem_data.py:29 msgid "Floats (relative)" msgstr "Số thực (chênh lệch tương đối)" -#: judge/models/problem_data.py:36 +#: judge/models/problem_data.py:30 msgid "Non-trailing spaces" -msgstr "Không xét dấu cách cuối dòng" +msgstr "Không cho phép dấu cách cuối dòng" -#: judge/models/problem_data.py:37 +#: judge/models/problem_data.py:31 msgid "Unordered" msgstr "Không thứ tự" -#: judge/models/problem_data.py:38 +#: judge/models/problem_data.py:32 msgid "Byte identical" msgstr "Giống từng byte" -#: judge/models/problem_data.py:39 +#: judge/models/problem_data.py:33 msgid "Line-by-line" msgstr "Chấm theo dòng (điểm = số dòng đúng)" -#: judge/models/problem_data.py:40 +#: judge/models/problem_data.py:34 msgid "Custom checker (PY)" msgstr "Trình chấm tự viết (Python)" -#: judge/models/problem_data.py:41 -msgid "Custom checker (CPP)" -msgstr "Trình chấm tự viết (CPP)" +#: judge/models/problem_data.py:35 +msgid "Custom validator (CPP)" +msgstr "Trình chấm tự viết (C++)" #: judge/models/problem_data.py:42 -msgid "Interactive" -msgstr "" - -#: judge/models/problem_data.py:43 -msgid "Testlib" -msgstr "" - -#: judge/models/problem_data.py:55 msgid "data zip file" msgstr "file zip chứa test" -#: judge/models/problem_data.py:62 +#: judge/models/problem_data.py:44 msgid "generator file" msgstr "file tạo test" -#: judge/models/problem_data.py:69 judge/models/problem_data.py:238 +#: judge/models/problem_data.py:46 judge/models/problem_data.py:121 msgid "output prefix length" msgstr "độ dài hiển thị output" -#: judge/models/problem_data.py:72 judge/models/problem_data.py:241 +#: judge/models/problem_data.py:47 judge/models/problem_data.py:122 msgid "output limit length" msgstr "giới hạn hiển thị output" -#: judge/models/problem_data.py:75 +#: judge/models/problem_data.py:48 msgid "init.yml generation feedback" msgstr "phản hồi của quá trình tạo file init.yml" -#: judge/models/problem_data.py:78 judge/models/problem_data.py:244 +#: judge/models/problem_data.py:49 judge/models/problem_data.py:123 msgid "checker" msgstr "trình chấm" -#: judge/models/problem_data.py:81 judge/models/problem_data.py:247 +#: judge/models/problem_data.py:50 judge/models/problem_data.py:124 msgid "checker arguments" msgstr "các biến trong trình chấm" -#: judge/models/problem_data.py:83 judge/models/problem_data.py:249 +#: judge/models/problem_data.py:51 judge/models/problem_data.py:125 msgid "checker arguments as a JSON object" msgstr "các biến trong trình chấm theo dạng JSON" -#: judge/models/problem_data.py:86 +#: judge/models/problem_data.py:52 msgid "custom checker file" -msgstr "trình chấm" +msgstr "file trình chấm" -#: judge/models/problem_data.py:94 -msgid "custom cpp checker file" -msgstr "trình chấm C++" +#: judge/models/problem_data.py:58 +msgid "custom validator file" +msgstr "file trình chấm" -#: judge/models/problem_data.py:102 -msgid "interactive judge" -msgstr "trình chấm interactive" - -#: judge/models/problem_data.py:110 judge/models/problem_data.py:229 -msgid "input file name" -msgstr "tên file input" - -#: judge/models/problem_data.py:113 -msgid "Leave empty for stdin" -msgstr "Để trống nếu nhập từ bàn phím" - -#: judge/models/problem_data.py:116 judge/models/problem_data.py:232 -msgid "output file name" -msgstr "tên file output" - -#: judge/models/problem_data.py:119 -msgid "Leave empty for stdout" -msgstr "Để trống nếu xuất ra màn hình" - -#: judge/models/problem_data.py:122 -msgid "is output only" -msgstr "Output-only?" - -#: judge/models/problem_data.py:123 -msgid "Support output-only problem" -msgstr "Dùng cho các bài output-only (nộp bằng file zip)" - -#: judge/models/problem_data.py:127 -msgid "is IOI signature" -msgstr "Nộp bài bằng hàm?" - -#: judge/models/problem_data.py:128 -msgid "Use IOI Signature" -msgstr "Nộp bài bằng hàm như IOI" - -#: judge/models/problem_data.py:132 -msgid "signature handler" -msgstr "File xử lý hàm" - -#: judge/models/problem_data.py:140 -msgid "signature header" -msgstr "File định nghĩa hàm" - -#: judge/models/problem_data.py:213 +#: judge/models/problem_data.py:108 msgid "problem data set" msgstr "tập hợp dữ liệu bài" -#: judge/models/problem_data.py:217 +#: judge/models/problem_data.py:110 msgid "case position" msgstr "vị trí test" -#: judge/models/problem_data.py:220 +#: judge/models/problem_data.py:111 msgid "case type" msgstr "loại test" -#: judge/models/problem_data.py:222 +#: judge/models/problem_data.py:112 msgid "Normal case" msgstr "Test bình thường" -#: judge/models/problem_data.py:223 +#: judge/models/problem_data.py:113 msgid "Batch start" msgstr "Bắt đầu nhóm" -#: judge/models/problem_data.py:224 +#: judge/models/problem_data.py:114 msgid "Batch end" msgstr "Kết thúc nhóm" -#: judge/models/problem_data.py:234 +#: judge/models/problem_data.py:116 +msgid "input file name" +msgstr "tên file input" + +#: judge/models/problem_data.py:117 +msgid "output file name" +msgstr "tên file output" + +#: judge/models/problem_data.py:118 msgid "generator arguments" msgstr "biến trong file sinh test" -#: judge/models/problem_data.py:235 +#: judge/models/problem_data.py:119 msgid "point value" msgstr "điểm" -#: judge/models/problem_data.py:236 +#: judge/models/problem_data.py:120 msgid "case is pretest?" msgstr "test là pretest?" -#: judge/models/profile.py:58 +#: judge/models/profile.py:30 msgid "organization title" msgstr "tiêu đề tổ chức" -#: judge/models/profile.py:61 +#: judge/models/profile.py:31 msgid "organization slug" -msgstr "tên ngắn đường dẫn" +msgstr "tên ngắn tổ chức" -#: judge/models/profile.py:62 +#: judge/models/profile.py:32 msgid "Organization name shown in URL" msgstr "Tên được hiển thị trong đường dẫn" -#: judge/models/profile.py:71 +#: judge/models/profile.py:34 msgid "Displayed beside user name during contests" msgstr "Hiển thị bên cạnh tên người dùng trong kỳ thi" -#: judge/models/profile.py:74 +#: judge/models/profile.py:35 msgid "organization description" msgstr "mô tả tổ chức" -#: judge/models/profile.py:78 +#: judge/models/profile.py:36 msgid "registrant" msgstr "người tạo" -#: judge/models/profile.py:81 +#: judge/models/profile.py:37 msgid "User who registered this organization" msgstr "Người tạo tổ chức" -#: judge/models/profile.py:85 +#: judge/models/profile.py:38 msgid "administrators" msgstr "người quản lý" -#: judge/models/profile.py:87 +#: judge/models/profile.py:39 msgid "Those who can edit this organization" msgstr "Những người có thể chỉnh sửa tổ chức" -#: judge/models/profile.py:90 +#: judge/models/profile.py:40 msgid "creation date" msgstr "ngày tạo" -#: judge/models/profile.py:93 +#: judge/models/profile.py:41 msgid "is open organization?" msgstr "tổ chức mở?" -#: judge/models/profile.py:94 +#: judge/models/profile.py:42 msgid "Allow joining organization" -msgstr "Cho phép mọi người tham gia tổ chức" +msgstr "Cho phép tham gia tổ chức" -#: judge/models/profile.py:98 +#: judge/models/profile.py:43 msgid "maximum size" msgstr "số lượng thành viên tối đa" -#: judge/models/profile.py:102 +#: judge/models/profile.py:44 msgid "" "Maximum amount of users in this organization, only applicable to private " "organizations" msgstr "Số người tối đa trong tổ chức, chỉ áp dụng với tổ chức riêng tư" -#: judge/models/profile.py:108 +#: judge/models/profile.py:46 msgid "Student access code" msgstr "Mã truy cập cho học sinh" -#: judge/models/profile.py:166 judge/models/profile.py:198 -#: judge/models/profile.py:471 judge/models/profile.py:546 +#: judge/models/profile.py:50 +msgid "" +"This image will replace the default site logo for users viewing the " +"organization." +msgstr "Ảnh này sẽ thay thế logo mặc định khi ở trong tổ chức." + +#: judge/models/profile.py:76 judge/models/profile.py:93 +#: judge/models/profile.py:216 msgid "organization" msgstr "" -#: judge/models/profile.py:173 +#: judge/models/profile.py:81 msgid "user associated" msgstr "" -#: judge/models/profile.py:176 +#: judge/models/profile.py:82 msgid "self-description" msgstr "" -#: judge/models/profile.py:180 +#: judge/models/profile.py:83 msgid "location" msgstr "" -#: judge/models/profile.py:186 +#: judge/models/profile.py:85 msgid "preferred language" msgstr "" -#: judge/models/profile.py:194 +#: judge/models/profile.py:91 msgid "last access time" msgstr "" -#: judge/models/profile.py:195 +#: judge/models/profile.py:92 msgid "last IP" msgstr "" -#: judge/models/profile.py:206 +#: judge/models/profile.py:95 msgid "display rank" msgstr "" -#: judge/models/profile.py:215 +#: judge/models/profile.py:97 msgid "comment mute" msgstr "" -#: judge/models/profile.py:216 +#: judge/models/profile.py:97 msgid "Some users are at their best when silent." msgstr "" -#: judge/models/profile.py:220 +#: judge/models/profile.py:99 msgid "unlisted user" msgstr "" -#: judge/models/profile.py:221 +#: judge/models/profile.py:99 msgid "User will not be ranked." msgstr "" -#: judge/models/profile.py:227 +#: judge/models/profile.py:102 +msgid "user script" +msgstr "" + +#: judge/models/profile.py:103 +msgid "User-defined JavaScript for site customization." +msgstr "" + +#: judge/models/profile.py:104 msgid "current contest" msgstr "kỳ thi hiện tại" -#: judge/models/profile.py:234 +#: judge/models/profile.py:106 +msgid "math engine" +msgstr "" + +#: judge/models/profile.py:108 +msgid "the rendering engine used to render math" +msgstr "" + +#: judge/models/profile.py:109 msgid "2FA enabled" msgstr "" -#: judge/models/profile.py:236 +#: judge/models/profile.py:110 msgid "check to enable TOTP-based two factor authentication" msgstr "đánh dấu để sử dụng TOTP-based two factor authentication" -#: judge/models/profile.py:242 +#: judge/models/profile.py:111 msgid "TOTP key" msgstr "mã TOTP" -#: judge/models/profile.py:243 +#: judge/models/profile.py:112 msgid "32 character base32-encoded key for TOTP" msgstr "" -#: judge/models/profile.py:245 +#: judge/models/profile.py:114 msgid "TOTP key must be empty or base32" msgstr "" -#: judge/models/profile.py:249 +#: judge/models/profile.py:115 msgid "internal notes" msgstr "ghi chú nội bộ" -#: judge/models/profile.py:252 +#: judge/models/profile.py:116 msgid "Notes for administrators regarding this user." msgstr "Ghi chú riêng cho quản trị viên." -#: judge/models/profile.py:257 -msgid "Custom background" -msgstr "Background tự chọn" - -#: judge/models/profile.py:260 -msgid "CSS custom background properties: url(\"image_url\"), color, etc" -msgstr "CSS background tự chọn. Ví dụ: url(\"image_url\"), white, ..." - -#: judge/models/profile.py:428 +#: judge/models/profile.py:210 msgid "user profile" msgstr "thông tin người dùng" -#: judge/models/profile.py:429 +#: judge/models/profile.py:211 msgid "user profiles" msgstr "thông tin người dùng" -#: judge/models/profile.py:435 -#, fuzzy -#| msgid "associated page" -msgid "profile associated" -msgstr "trang tương ứng" - -#: judge/models/profile.py:442 -msgid "t-shirt size" -msgstr "" - -#: judge/models/profile.py:447 -#, fuzzy -#| msgid "date of publishing" -msgid "date of birth" -msgstr "Ngày công bố" - -#: judge/models/profile.py:453 -msgid "address" -msgstr "" - -#: judge/models/profile.py:475 +#: judge/models/profile.py:218 msgid "request time" msgstr "thời gian đăng ký" -#: judge/models/profile.py:478 +#: judge/models/profile.py:219 msgid "state" msgstr "trạng thái" -#: judge/models/profile.py:485 +#: judge/models/profile.py:224 msgid "reason" msgstr "lý do" -#: judge/models/profile.py:488 +#: judge/models/profile.py:227 msgid "organization join request" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:489 +#: judge/models/profile.py:228 msgid "organization join requests" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:551 -#, fuzzy -#| msgid "last seen" -msgid "last visit" -msgstr "xem lần cuối" - -#: judge/models/runtime.py:22 +#: judge/models/runtime.py:19 msgid "short identifier" msgstr "tên ngắn" -#: judge/models/runtime.py:24 +#: judge/models/runtime.py:20 msgid "" "The identifier for this language; the same as its executor id for judges." msgstr "" -#: judge/models/runtime.py:30 +#: judge/models/runtime.py:22 msgid "long name" msgstr "tên dài" -#: judge/models/runtime.py:31 +#: judge/models/runtime.py:23 msgid "Longer name for the language, e.g. \"Python 2\" or \"C++11\"." msgstr "Tên dài, ví dụ \"Python 2\" or \"C++11\"." -#: judge/models/runtime.py:37 +#: judge/models/runtime.py:25 msgid "" "More readable, but short, name to display publicly; e.g. \"PY2\" or \"C+" "+11\". If left blank, it will default to the short identifier." msgstr "" -#: judge/models/runtime.py:46 +#: judge/models/runtime.py:29 msgid "common name" msgstr "" -#: judge/models/runtime.py:48 +#: judge/models/runtime.py:30 msgid "" "Common name for the language. For example, the common name for C++03, C++11, " "and C++14 would be \"C++\"" msgstr "" -#: judge/models/runtime.py:54 +#: judge/models/runtime.py:32 msgid "ace mode name" msgstr "" -#: judge/models/runtime.py:56 +#: judge/models/runtime.py:33 msgid "" "Language ID for Ace.js editor highlighting, appended to \"mode-\" to " "determine the Ace JavaScript file to use, e.g., \"python\"." msgstr "" -#: judge/models/runtime.py:62 +#: judge/models/runtime.py:35 msgid "pygments name" msgstr "" -#: judge/models/runtime.py:63 +#: judge/models/runtime.py:36 msgid "Language ID for Pygments highlighting in source windows." msgstr "" -#: judge/models/runtime.py:66 +#: judge/models/runtime.py:37 msgid "code template" msgstr "" -#: judge/models/runtime.py:67 +#: judge/models/runtime.py:38 msgid "Code template to display in submission editor." msgstr "" -#: judge/models/runtime.py:72 +#: judge/models/runtime.py:39 msgid "runtime info override" msgstr "" -#: judge/models/runtime.py:75 +#: judge/models/runtime.py:40 msgid "" "Do not set this unless you know what you're doing! It will override the " "usually more specific, judge-provided runtime info!" msgstr "" -#: judge/models/runtime.py:80 +#: judge/models/runtime.py:42 msgid "language description" msgstr "" -#: judge/models/runtime.py:82 +#: judge/models/runtime.py:43 msgid "" "Use this field to inform users of quirks with your environment, additional " "restrictions, etc." msgstr "" -#: judge/models/runtime.py:89 +#: judge/models/runtime.py:45 msgid "extension" msgstr "" -#: judge/models/runtime.py:90 +#: judge/models/runtime.py:46 msgid "The extension of source files, e.g., \"py\" or \"cpp\"." msgstr "" -#: judge/models/runtime.py:160 +#: judge/models/runtime.py:112 msgid "languages" msgstr "ngôn ngữ" -#: judge/models/runtime.py:174 +#: judge/models/runtime.py:116 msgid "language to which this runtime belongs" msgstr "" -#: judge/models/runtime.py:178 +#: judge/models/runtime.py:117 msgid "judge on which this runtime exists" msgstr "" -#: judge/models/runtime.py:180 +#: judge/models/runtime.py:118 msgid "runtime name" msgstr "" -#: judge/models/runtime.py:182 +#: judge/models/runtime.py:119 msgid "runtime version" msgstr "" -#: judge/models/runtime.py:185 +#: judge/models/runtime.py:120 msgid "order in which to display this runtime" msgstr "" -#: judge/models/runtime.py:191 +#: judge/models/runtime.py:124 msgid "Server name, hostname-style" msgstr "Tên web" -#: judge/models/runtime.py:194 +#: judge/models/runtime.py:125 msgid "time of creation" msgstr "ngày tạo" -#: judge/models/runtime.py:198 +#: judge/models/runtime.py:126 msgid "A key to authenticate this judge" msgstr "Chìa khóa xác thực" -#: judge/models/runtime.py:199 +#: judge/models/runtime.py:127 msgid "authentication key" msgstr "mã xác thực" -#: judge/models/runtime.py:202 +#: judge/models/runtime.py:128 msgid "block judge" msgstr "chặn máy chấm" -#: judge/models/runtime.py:205 +#: judge/models/runtime.py:129 msgid "" "Whether this judge should be blocked from connecting, even if its key is " "correct." msgstr "Quyết định có chặn máy chấm, ngay cả khi mã xác thực đúng." -#: judge/models/runtime.py:209 +#: judge/models/runtime.py:131 msgid "judge online status" msgstr "trạng thái online của máy chấm" -#: judge/models/runtime.py:210 +#: judge/models/runtime.py:132 msgid "judge start time" msgstr "thời gian khởi đầu máy chấm" -#: judge/models/runtime.py:211 +#: judge/models/runtime.py:133 msgid "response time" msgstr "thời gian trả lời" -#: judge/models/runtime.py:213 +#: judge/models/runtime.py:134 msgid "system load" msgstr "lưu lượng xử lý" -#: judge/models/runtime.py:215 +#: judge/models/runtime.py:135 msgid "Load for the last minute, divided by processors to be fair." msgstr "Lưu lượng được chia đều." -#: judge/models/runtime.py:225 judge/models/runtime.py:267 +#: judge/models/runtime.py:139 judge/models/runtime.py:179 msgid "judges" msgstr "máy chấm" -#: judge/models/runtime.py:266 +#: judge/models/runtime.py:178 msgid "judge" msgstr "máy chấm" #: judge/models/submission.py:20 judge/models/submission.py:47 -#: judge/utils/problems.py:116 +#: judge/utils/problems.py:79 msgid "Accepted" msgstr "Accepted" #: judge/models/submission.py:21 judge/models/submission.py:48 -#: judge/utils/problems.py:119 msgid "Wrong Answer" msgstr "Wrong Answer" #: judge/models/submission.py:22 judge/models/submission.py:50 -#: judge/utils/problems.py:129 msgid "Time Limit Exceeded" msgstr "Time Limit Exceeded" @@ -2464,7 +2026,7 @@ msgid "Runtime Error" msgstr "Runtime Error" #: judge/models/submission.py:27 judge/models/submission.py:41 -#: judge/models/submission.py:55 judge/utils/problems.py:124 +#: judge/models/submission.py:55 judge/utils/problems.py:81 msgid "Compile Error" msgstr "Compile Error" @@ -2501,295 +2063,242 @@ msgstr "Chấm xong" msgid "Internal Error (judging server error)" msgstr "Lỗi máy chấm" -#: judge/models/submission.py:67 +#: judge/models/submission.py:66 msgid "submission time" msgstr "thời gian bài nộp" -#: judge/models/submission.py:69 judge/models/submission.py:310 +#: judge/models/submission.py:67 judge/models/submission.py:203 msgid "execution time" msgstr "thời gian chạy" -#: judge/models/submission.py:70 judge/models/submission.py:311 +#: judge/models/submission.py:68 judge/models/submission.py:204 msgid "memory usage" msgstr "bộ nhớ sử dụng" -#: judge/models/submission.py:72 judge/models/submission.py:312 +#: judge/models/submission.py:69 judge/models/submission.py:205 msgid "points granted" msgstr "điểm" -#: judge/models/submission.py:75 +#: judge/models/submission.py:70 msgid "submission language" msgstr "ngôn ngữ bài nộp" -#: judge/models/submission.py:78 +#: judge/models/submission.py:71 msgid "status" msgstr "trạng thái" -#: judge/models/submission.py:85 +#: judge/models/submission.py:72 msgid "result" msgstr "kết quả" -#: judge/models/submission.py:93 +#: judge/models/submission.py:74 msgid "compile errors" msgstr "biên dịch lỗi" -#: judge/models/submission.py:95 +#: judge/models/submission.py:76 msgid "batched cases" msgstr "nhóm test" -#: judge/models/submission.py:96 +#: judge/models/submission.py:77 msgid "test case points" msgstr "điểm test" -#: judge/models/submission.py:97 +#: judge/models/submission.py:78 msgid "test case total points" msgstr "tổng điểm các test" -#: judge/models/submission.py:100 +#: judge/models/submission.py:79 msgid "judged on" msgstr "chấm trên" -#: judge/models/submission.py:106 +#: judge/models/submission.py:81 msgid "submission judge time" msgstr "thời điểm được chấm" -#: judge/models/submission.py:109 +#: judge/models/submission.py:82 msgid "was rejudged by admin" msgstr "được chấm lại bởi admin" -#: judge/models/submission.py:112 +#: judge/models/submission.py:83 msgid "was ran on pretests only" msgstr "chỉ chấm pretest" -#: judge/models/submission.py:275 templates/contest/moss.html:56 +#: judge/models/submission.py:184 templates/contest/moss.html:58 msgid "submissions" msgstr "bài nộp" -#: judge/models/submission.py:288 judge/models/submission.py:302 +#: judge/models/submission.py:188 judge/models/submission.py:199 msgid "associated submission" msgstr "bài nộp tương ứng" -#: judge/models/submission.py:306 +#: judge/models/submission.py:190 +msgid "source code" +msgstr "mã nguồn" + +#: judge/models/submission.py:201 msgid "test case ID" msgstr "test case ID" -#: judge/models/submission.py:308 +#: judge/models/submission.py:202 msgid "status flag" msgstr "" -#: judge/models/submission.py:313 +#: judge/models/submission.py:206 msgid "points possible" msgstr "" -#: judge/models/submission.py:314 +#: judge/models/submission.py:207 msgid "batch number" msgstr "số thứ tự của nhóm" -#: judge/models/submission.py:316 +#: judge/models/submission.py:208 msgid "judging feedback" msgstr "phản hồi từ máy chấm" -#: judge/models/submission.py:319 +#: judge/models/submission.py:209 msgid "extended judging feedback" msgstr "phản hồi thêm từ máy chấm" -#: judge/models/submission.py:321 +#: judge/models/submission.py:210 msgid "program output" msgstr "output chương trình" -#: judge/models/submission.py:329 +#: judge/models/submission.py:218 msgid "submission test case" msgstr "cái testcase trong bài nộp" -#: judge/models/submission.py:330 +#: judge/models/submission.py:219 msgid "submission test cases" msgstr "cái testcase trong bài nộp" -#: judge/models/test_formatter.py:22 -#, fuzzy -#| msgid "test case ID" -msgid "testcase file" -msgstr "test case ID" - #: judge/models/ticket.py:10 msgid "ticket title" msgstr "tiêu đề báo cáo" -#: judge/models/ticket.py:13 +#: judge/models/ticket.py:11 msgid "ticket creator" msgstr "người báo cáo" -#: judge/models/ticket.py:17 +#: judge/models/ticket.py:13 msgid "creation time" msgstr "thời gian tạo" -#: judge/models/ticket.py:19 +#: judge/models/ticket.py:14 msgid "assignees" msgstr "người được ủy thác" -#: judge/models/ticket.py:22 +#: judge/models/ticket.py:15 msgid "quick notes" msgstr "" -#: judge/models/ticket.py:24 +#: judge/models/ticket.py:16 msgid "Staff notes for this issue to aid in processing." msgstr "" -#: judge/models/ticket.py:27 +#: judge/models/ticket.py:17 msgid "linked item type" msgstr "" -#: judge/models/ticket.py:29 +#: judge/models/ticket.py:19 msgid "linked item ID" msgstr "" -#: judge/models/ticket.py:31 +#: judge/models/ticket.py:21 msgid "is ticket open?" msgstr "" -#: judge/models/ticket.py:37 +#: judge/models/ticket.py:25 msgid "ticket" msgstr "" -#: judge/models/ticket.py:44 +#: judge/models/ticket.py:27 msgid "poster" msgstr "" -#: judge/models/ticket.py:49 +#: judge/models/ticket.py:30 msgid "message time" msgstr "" -#: judge/models/volunteer.py:19 -msgid "knowledge points" -msgstr "Độ khó kiến thức" - -#: judge/models/volunteer.py:20 -msgid "Points awarded by knowledge difficulty" -msgstr "" - -#: judge/models/volunteer.py:23 -msgid "thinking points" -msgstr "Độ khó nghĩ" - -#: judge/models/volunteer.py:24 -msgid "Points awarded by thinking difficulty" -msgstr "" - -#: judge/models/volunteer.py:31 -msgid "feedback" -msgstr "phản hồi" - -#: judge/models/volunteer.py:34 -msgid "volunteer vote" -msgstr "vote từ TNV" - -#: judge/models/volunteer.py:35 -msgid "volunteer votes" -msgstr "vote từ TNV" - -#: judge/pdf_problems.py:161 judge/pdf_problems.py:221 -#: judge/pdf_problems.py:294 +#: judge/pdf_problems.py:147 judge/pdf_problems.py:199 +#: judge/pdf_problems.py:259 msgid "Page [page] of [topage]" -msgstr "Trang [page]/[topage]" +msgstr "" -#: judge/pdf_problems.py:324 +#: judge/pdf_problems.py:280 #, python-format msgid "Page %s of %s" -msgstr "Trang %s/%s" +msgstr "" -#: judge/social_auth.py:69 judge/views/register.py:32 -msgid "A username must contain letters, numbers, or underscores" -msgstr "Tên đăng nhập phải chứa ký tự, chữ số, hoặc dấu gạch dưới" - -#: judge/social_auth.py:75 -msgid "Sorry, the username is taken." -msgstr "Xin lỗi, tên đăng nhập đã bị trùng." - -#: judge/social_auth.py:93 -msgid "Choose a username" -msgstr "Chọn tên đăng nhập" - -#: judge/social_auth.py:122 -msgid "Create your profile" -msgstr "Khởi tạo thông tin" - -#: judge/tasks/contest.py:20 +#: judge/tasks/contest.py:19 msgid "Recalculating contest scores" msgstr "Tính lại điểm kỳ thi" -#: judge/tasks/contest.py:59 +#: judge/tasks/contest.py:40 msgid "Running MOSS" msgstr "Đang chạy MOSS" -#: judge/tasks/submission.py:47 +#: judge/tasks/submission.py:43 msgid "Modifying submissions" msgstr "Chỉnh sửa bài nộp" -#: judge/tasks/submission.py:67 +#: judge/tasks/submission.py:56 msgid "Recalculating user points" msgstr "Tính lại điểm người dùng" -#: judge/utils/problem_data.py:81 +#: judge/utils/problem_data.py:70 msgid "Empty batches not allowed." msgstr "Nhóm test trống là không hợp lệ." -#: judge/utils/problem_data.py:89 judge/utils/problem_data.py:97 -#: judge/utils/problem_data.py:112 +#: judge/utils/problem_data.py:78 judge/utils/problem_data.py:99 msgid "How did you corrupt the custom checker path?" msgstr "How did you corrupt the custom checker path?" -#: judge/utils/problem_data.py:140 +#: judge/utils/problem_data.py:120 #, python-format msgid "Points must be defined for non-batch case #%d." msgstr "Ô điểm số cho test #%d phải được điền." -#: judge/utils/problem_data.py:147 +#: judge/utils/problem_data.py:125 #, python-format msgid "Input file for case %d does not exist: %s" msgstr "File input cho test %d không tồn tại: %s" -#: judge/utils/problem_data.py:152 +#: judge/utils/problem_data.py:128 #, python-format msgid "Output file for case %d does not exist: %s" msgstr "File output cho test %d không tồn tại: %s" -#: judge/utils/problem_data.py:179 +#: judge/utils/problem_data.py:153 #, python-format msgid "Batch start case #%d requires points." msgstr "Nhóm test #%d cần được điền điểm số." -#: judge/utils/problem_data.py:202 +#: judge/utils/problem_data.py:174 #, python-format msgid "Attempt to end batch outside of one in case #%d" msgstr "Nhóm test #%d kết thúc không hợp lệ" -#: judge/utils/problem_data.py:221 +#: judge/utils/problem_data.py:192 msgid "How did you corrupt the zip path?" msgstr "" -#: judge/utils/problem_data.py:227 +#: judge/utils/problem_data.py:198 msgid "How did you corrupt the generator path?" msgstr "" -#: judge/utils/problem_data.py:245 -msgid "Invalid interactor judge" -msgstr "" +#: judge/utils/problems.py:80 +msgid "Wrong" +msgstr "Sai" -#: judge/utils/problem_data.py:269 -#, fuzzy -#| msgid "Invalid Return" -msgid "Invalid signature handler" -msgstr "Invalid Return" +#: judge/utils/problems.py:82 +msgid "Timeout" +msgstr "Quá thời gian" -#: judge/utils/problem_data.py:272 -msgid "Invalid signature header" -msgstr "" - -#: judge/utils/problems.py:134 +#: judge/utils/problems.py:83 msgid "Error" msgstr "Lỗi" -#: judge/utils/problems.py:151 +#: judge/utils/problems.py:94 msgid "Can't pass both queryset and keyword filters" msgstr "" @@ -2801,527 +2310,293 @@ msgstr "Mật khẩu không được quá phổ biến." msgid "This password is too common." msgstr "Mật khẩu này quá phổ biến." -#: judge/utils/timedelta.py:59 +#: judge/utils/timedelta.py:49 msgctxt "time format with day" msgid "%d day %h:%m:%s" msgid_plural "%d days %h:%m:%s" msgstr[0] "%d ngày %h:%m:%s" -#: judge/utils/timedelta.py:68 +#: judge/utils/timedelta.py:53 msgctxt "time format without day" msgid "%h:%m:%s" msgstr "%h:%m:%s" -#: judge/utils/timedelta.py:80 +#: judge/utils/timedelta.py:59 msgctxt "time format no seconds with day" msgid "%d day %h:%m" msgid_plural "%d days %h:%m" msgstr[0] "%d ngày %h:%m" -#: judge/utils/timedelta.py:88 +#: judge/utils/timedelta.py:61 #, python-format msgid "%d day" msgid_plural "%d days" msgstr[0] "%d ngày" -#: judge/utils/timedelta.py:91 +#: judge/utils/timedelta.py:63 msgctxt "hours and minutes" msgid "%h:%m" msgstr "%h:%m" -#: judge/utils/users.py:61 -msgid "M j, Y" -msgstr "j M, Y" - -#: judge/views/about.py:10 templates/course/course.html:27 -#: templates/organization/home.html:44 -#: templates/organization/org-right-sidebar.html:74 -#: templates/user/user-about.html:70 templates/user/user-tabs.html:4 -#: templates/user/users-table.html:22 +#: judge/views/about.py:7 templates/organization/home.html:105 +#: templates/user/user-about.html:83 templates/user/user-tabs.html:4 +#: templates/user/users-table.html:32 msgid "About" msgstr "Giới thiệu" -#: judge/views/about.py:251 +#: judge/views/about.py:13 msgid "Custom Checker Sample" msgstr "Hướng dẫn viết trình chấm" -#: judge/views/blog.py:131 +#: judge/views/blog.py:45 #, python-format msgid "Page %d of Posts" msgstr "Trang %d" -#: judge/views/blog.py:171 -msgid "Ticket feed" -msgstr "Báo cáo" - -#: judge/views/blog.py:188 -msgid "Comment feed" -msgstr "Bình luận" - -#: judge/views/comment.py:71 judge/views/pagevote.py:32 +#: judge/views/comment.py:28 msgid "Messing around, are we?" msgstr "Messing around, are we?" -#: judge/views/comment.py:87 judge/views/pagevote.py:48 +#: judge/views/comment.py:37 msgid "You must solve at least one problem before you can vote." msgstr "Bạn phải giải ít nhất 1 bài trước khi được vote." -#: judge/views/comment.py:113 +#: judge/views/comment.py:64 msgid "You already voted." msgstr "Bạn đã vote." -#: judge/views/comment.py:272 judge/views/organization.py:882 -#: judge/views/organization.py:1035 judge/views/organization.py:1208 +#: judge/views/comment.py:126 judge/views/organization.py:344 msgid "Edited from site" msgstr "Chỉnh sửa từ web" -#: judge/views/comment.py:293 +#: judge/views/comment.py:147 msgid "Editing comment" msgstr "Chỉnh sửa bình luận" -#: judge/views/comment.py:345 -msgid "Comment body" -msgstr "Nội dung bình luận" - -#: judge/views/comment.py:351 judge/views/ticket.py:73 -msgid "Your part is silent, little toad." -msgstr "Bạn không được phép bình luận." - -#: judge/views/comment.py:360 templates/comments/list.html:17 -msgid "" -"You need to have solved at least one problem before your voice can be heard." -msgstr "Bạn phải giải ít nhất một bài trước khi được phép bình luận." - -#: judge/views/comment.py:403 -msgid "Posted comment" -msgstr "Bình luận đã đăng" - -#: judge/views/contests.py:124 judge/views/contests.py:471 -#: judge/views/contests.py:476 judge/views/contests.py:784 +#: judge/views/contests.py:58 judge/views/contests.py:250 +#: judge/views/contests.py:253 judge/views/contests.py:427 msgid "No such contest" msgstr "Không có contest nào như vậy" -#: judge/views/contests.py:125 judge/views/contests.py:472 +#: judge/views/contests.py:59 judge/views/contests.py:251 #, python-format msgid "Could not find a contest with the key \"%s\"." msgstr "Không tìm thấy kỳ thi với mã \"%s\"." -#: judge/views/contests.py:153 judge/views/contests.py:1561 -#: judge/views/stats.py:178 templates/contest/list.html:171 -#: templates/contest/list.html:213 templates/contest/list.html:250 -#: templates/contest/list.html:284 templates/course/course.html:59 -#: templates/course/left_sidebar.html:5 -#: templates/organization/org-left-sidebar.html:5 templates/stats/site.html:21 -#: templates/user/user-bookmarks.html:19 templates/user/user-bookmarks.html:80 +#: judge/views/contests.py:72 msgid "Contests" msgstr "Kỳ thi" -#: judge/views/contests.py:331 -msgid "Start time (asc.)" -msgstr "Thời gian bắt đầu (tăng)" - -#: judge/views/contests.py:332 -msgid "Start time (desc.)" -msgstr "Thời gian bắt đầu (giảm)" - -#: judge/views/contests.py:333 judge/views/organization.py:314 -msgid "Name (asc.)" -msgstr "Tên (tăng)" - -#: judge/views/contests.py:334 judge/views/organization.py:315 -msgid "Name (desc.)" -msgstr "Tên (giảm)" - -#: judge/views/contests.py:335 -msgid "User count (asc.)" -msgstr "Số lượng tham gia (tăng)" - -#: judge/views/contests.py:336 -msgid "User count (desc.)" -msgstr "Số lượng tham gia (giảm)" - -#: judge/views/contests.py:476 +#: judge/views/contests.py:254 msgid "Could not find such contest." msgstr "Không tìm thấy kỳ thi nào như vậy." -#: judge/views/contests.py:484 +#: judge/views/contests.py:257 #, python-format msgid "Access to contest \"%s\" denied" msgstr "Truy cập tới kỳ thi \"%s\" bị từ chối" -#: judge/views/contests.py:570 +#: judge/views/contests.py:281 msgid "Clone Contest" msgstr "Nhân bản kỳ thi" -#: judge/views/contests.py:662 +#: judge/views/contests.py:346 msgid "Contest not ongoing" msgstr "Kỳ thi đang không diễn ra" -#: judge/views/contests.py:663 +#: judge/views/contests.py:347 #, python-format msgid "\"%s\" is not currently ongoing." msgstr "\"%s\" kỳ thi đang không diễn ra." -#: judge/views/contests.py:676 +#: judge/views/contests.py:351 +msgid "Already in contest" +msgstr "Đã ở trong kỳ thi" + +#: judge/views/contests.py:352 +#, python-format +msgid "You are already in a contest: \"%s\"." +msgstr "Bạn đã ở trong kỳ thi: \"%s\"." + +#: judge/views/contests.py:355 msgid "Banned from joining" msgstr "Bị cấm tham gia" -#: judge/views/contests.py:678 +#: judge/views/contests.py:356 msgid "" "You have been declared persona non grata for this contest. You are " "permanently barred from joining this contest." msgstr "Bạn không được phép tham gia kỳ thi này." -#: judge/views/contests.py:768 +#: judge/views/contests.py:417 #, python-format msgid "Enter access code for \"%s\"" msgstr "Nhập mật khẩu truy cập cho \"%s\"" -#: judge/views/contests.py:785 +#: judge/views/contests.py:428 #, python-format msgid "You are not in contest \"%s\"." msgstr "Bạn không ở trong kỳ thi \"%s\"." -#: judge/views/contests.py:808 +#: judge/views/contests.py:448 msgid "ContestCalendar requires integer year and month" msgstr "Lịch thi yêu cầu giá trị cho năm và tháng là số nguyên" -#: judge/views/contests.py:866 +#: judge/views/contests.py:488 #, python-format msgid "Contests in %(month)s" msgstr "Các kỳ thi trong %(month)s" -#: judge/views/contests.py:867 +#: judge/views/contests.py:488 msgid "F Y" msgstr "F Y" -#: judge/views/contests.py:927 +#: judge/views/contests.py:536 #, python-format msgid "%s Statistics" msgstr "%s Thống kê" -#: judge/views/contests.py:1206 +#: judge/views/contests.py:731 #, python-format msgid "%s Rankings" msgstr "%s Bảng điểm" -#: judge/views/contests.py:1217 +#: judge/views/contests.py:739 msgid "???" msgstr "???" -#: judge/views/contests.py:1281 +#: judge/views/contests.py:755 #, python-format msgid "Your participation in %s" msgstr "Lần tham gia trong %s" -#: judge/views/contests.py:1282 +#: judge/views/contests.py:756 #, python-format msgid "%s's participation in %s" msgstr "Lần tham gia của %s trong %s" -#: judge/views/contests.py:1296 +#: judge/views/contests.py:763 msgid "Live" msgstr "Trực tiếp" -#: judge/views/contests.py:1314 templates/contest/contest-tabs.html:21 +#: judge/views/contests.py:775 templates/contest/contest-tabs.html:13 msgid "Participation" msgstr "Lần tham gia" -#: judge/views/contests.py:1363 +#: judge/views/contests.py:822 #, python-format msgid "%s MOSS Results" msgstr "%s Kết quả MOSS" -#: judge/views/contests.py:1399 +#: judge/views/contests.py:849 #, python-format msgid "Running MOSS for %s..." msgstr "Đang chạy MOSS cho %s..." -#: judge/views/contests.py:1422 +#: judge/views/contests.py:872 #, python-format msgid "Contest tag: %s" msgstr "Nhãn kỳ thi: %s" -#: judge/views/contests.py:1437 judge/views/ticket.py:67 +#: judge/views/contests.py:882 judge/views/ticket.py:57 msgid "Issue description" msgstr "Mô tả vấn đề" -#: judge/views/contests.py:1480 +#: judge/views/contests.py:925 #, python-format msgid "New clarification for %s" msgstr "Thông báo mới cho %s" -#: judge/views/course.py:350 -#, python-format -msgid "Edit lessons for %(course_name)s" -msgstr "Chỉnh sửa bài học cho %(course_name)s" - -#: judge/views/course.py:354 -#, python-format -msgid "Edit lessons for %(course_name)s" -msgstr "Chỉnh sửa bài học cho %(course_name)s" - -#: judge/views/course.py:414 -#, fuzzy, python-format -#| msgid "Edit lessons for %(course_name)s" -msgid "Grades in %(course_name)s" -msgstr "Chỉnh sửa bài học cho %(course_name)s" - -#: judge/views/course.py:418 -#, python-format -msgid "Grades in %(course_name)s" -msgstr "Điểm trong %(course_name)s" - -#: judge/views/course.py:472 -#, fuzzy, python-format -msgid "Grades of %(lesson_name)s in %(course_name)s" -msgstr "Chỉnh sửa bài học cho %(course_name)s" - -#: judge/views/course.py:478 -#, fuzzy, python-format -msgid "" -"Grades of %(lesson_name)s in %(course_name)s" -msgstr "Điểm trong %(course_name)s" - -#: judge/views/course.py:493 judge/views/course.py:578 -#: templates/course/contest_list.html:20 -#, fuzzy -#| msgid "order" -msgid "Order" -msgstr "thứ tự" - -#: judge/views/course.py:540 judge/views/organization.py:930 -#: templates/organization/org-right-sidebar.html:47 -msgid "Add contest" -msgstr "Thêm kỳ thi" - -#: judge/views/course.py:549 -#, fuzzy -#| msgid "Added from site" -msgid "Added from course" -msgstr "Thêm từ web" - -#: judge/views/course.py:569 -#, fuzzy -#| msgid "Contests" -msgid "Contest list" -msgstr "Kỳ thi" - -#: judge/views/course.py:691 -#, fuzzy -#| msgid "Out contest" -msgid "Edit contest" -msgstr "Ngoài kỳ thi" - -#: judge/views/course.py:695 -#, fuzzy -#| msgid "Edited from site" -msgid "Edited from course" -msgstr "Chỉnh sửa từ web" - -#: judge/views/course.py:717 -#, fuzzy, python-format -#| msgid "Grades in %(course_name)s" -msgid "Edit %(contest_name)s" -msgstr "Điểm trong %(course_name)s" - -#: judge/views/custom_file_upload.py:42 -msgid "File Upload" -msgstr "Tải file lên" - -#: judge/views/email.py:21 -msgid "New Email" -msgstr "Email mới" - -#: judge/views/email.py:31 -msgid "An account with this email already exists." -msgstr "Email đã được dùng cho tài khoản khác." - -#: judge/views/email.py:55 -msgid "Email Change Request" -msgstr "Thay đổi Email" - -#: judge/views/email.py:58 -msgid "" -"We have received a request to change your email to this email. Click the " -"button below to change your email:" -msgstr "" -"Chúng tôi đã nhận được yêu cầu thay đổi địa chỉ email của bạn thành địa chỉ " -"email này. Vui lòng nhấp vào nút bên dưới để thay đổi địa chỉ email của bạn:" - -#: judge/views/email.py:60 -msgid "Email Change" -msgstr "Thay đổi Email" - -#: judge/views/email.py:61 -msgid "Change Email" -msgstr "Thay đổi Email" - -#: judge/views/email.py:83 templates/user/edit-profile.html:127 -msgid "Change email" -msgstr "Thay đổi email" - -#: judge/views/email.py:106 -msgid "Success" -msgstr "Thành công" - -#: judge/views/email.py:110 -msgid "Invalid" -msgstr "Không hợp lệ" - -#: judge/views/email.py:119 -msgid "Email change pending" -msgstr "Yêu cầu thay đổi email đang đợi xác thực." - -#: judge/views/error.py:17 +#: judge/views/error.py:14 msgid "404 error" msgstr "Lỗi 404" -#: judge/views/error.py:18 +#: judge/views/error.py:15 #, python-format msgid "Could not find page \"%s\"" msgstr "Không thể tìm thấy trang \"%s\"" -#: judge/views/error.py:29 +#: judge/views/error.py:22 #, python-format msgid "no permission for %s" msgstr "không có quyền cho %s" -#: judge/views/error.py:41 +#: judge/views/error.py:30 #, python-format msgid "corrupt page %s" msgstr "trang bị sập %s" -#: judge/views/internal.py:24 -#, fuzzy -#| msgid "contest problems" -msgid "Internal problems" -msgstr "bài trong kỳ thi" - -#: judge/views/internal.py:106 -#, fuzzy -#| msgid "request time" -msgid "Request times" -msgstr "thời gian đăng ký" - #: judge/views/language.py:12 templates/status/judge-status-table.html:9 #: templates/status/status-tabs.html:5 msgid "Runtimes" msgstr "Runtimes" -#: judge/views/markdown_editor.py:12 -msgid "Markdown Editor" -msgstr "" - -#: judge/views/notification.py:32 +#: judge/views/notification.py:40 #, python-format msgid "Notifications (%d unseen)" msgstr "Thông báo (%d chưa xem)" -#: judge/views/organization.py:157 judge/views/organization.py:164 +#: judge/views/organization.py:59 judge/views/organization.py:62 msgid "No such organization" msgstr "Không có tổ chức như vậy" -#: judge/views/organization.py:158 +#: judge/views/organization.py:60 #, python-format msgid "Could not find an organization with the key \"%s\"." msgstr "Không tìm thấy tổ chức với mã \"%s\"." -#: judge/views/organization.py:165 +#: judge/views/organization.py:63 msgid "Could not find such organization." msgstr "" -#: judge/views/organization.py:189 -msgid "Can't edit organization" -msgstr "Không thể chỉnh sửa tổ chức" +#: judge/views/organization.py:79 judge/views/register.py:34 +#: templates/organization/list.html:32 templates/user/import/table_csv.html:9 +#: templates/user/user-list-tabs.html:6 +msgid "Organizations" +msgstr "Tổ chức" -#: judge/views/organization.py:190 -msgid "You are not allowed to edit this organization." -msgstr "Bạn không được phép chỉnh sửa tổ chức này." - -#: judge/views/organization.py:202 judge/views/organization.py:400 -msgid "Can't access organization" -msgstr "Không thể truy cập nhóm" - -#: judge/views/organization.py:203 judge/views/organization.py:401 -msgid "You are not allowed to access this organization." -msgstr "Bạn không được phép chỉnh sửa tổ chức này." - -#: judge/views/organization.py:248 judge/views/stats.py:184 -#: templates/contest/list.html:78 templates/problem/list-base.html:90 -#: templates/stats/site.html:33 templates/user/user-list-tabs.html:6 -msgid "Groups" -msgstr "Nhóm" - -#: judge/views/organization.py:316 -msgid "Member count (asc.)" -msgstr "Số lượng thành viên (tăng)" - -#: judge/views/organization.py:317 -msgid "Member count (desc.)" -msgstr "Số lượng thành viên (giảm)" - -#: judge/views/organization.py:407 +#: judge/views/organization.py:130 #, python-format msgid "%s Members" msgstr "%s Thành viên" -#: judge/views/organization.py:529 -#, python-brace-format -msgid "All submissions in {0}" -msgstr "Bài nộp trong {0}" +#: judge/views/organization.py:159 judge/views/organization.py:162 +#: judge/views/organization.py:167 +msgid "Joining organization" +msgstr "Tham gia tổ chức" -#: judge/views/organization.py:537 judge/views/submission.py:858 -msgid "Submissions in" -msgstr "Bài nộp trong" +#: judge/views/organization.py:159 +msgid "You are already in the organization." +msgstr "Bạn đã ở trong tổ chức." -#: judge/views/organization.py:562 judge/views/organization.py:568 -#: judge/views/organization.py:575 -msgid "Joining group" -msgstr "Tham gia nhóm" +#: judge/views/organization.py:162 +msgid "This organization is not open." +msgstr "Tổ chức này không phải tổ chức mở." -#: judge/views/organization.py:563 -msgid "You are already in the group." -msgstr "Bạn đã ở trong nhóm." +#: judge/views/organization.py:179 +msgid "Leaving organization" +msgstr "Rời tổ chức" -#: judge/views/organization.py:568 -msgid "This group is not open." -msgstr "Nhóm này là nhóm kín." - -#: judge/views/organization.py:576 -#, python-brace-format -msgid "You may not be part of more than {count} public groups." -msgstr "Bạn không thể tham gia nhiều hơn {count} nhóm công khai." - -#: judge/views/organization.py:591 -msgid "Leaving group" -msgstr "Rời nhóm" - -#: judge/views/organization.py:592 +#: judge/views/organization.py:179 #, python-format msgid "You are not in \"%s\"." msgstr "Bạn không ở trong \"%s\"." -#: judge/views/organization.py:617 +#: judge/views/organization.py:203 #, python-format msgid "Request to join %s" msgstr "Đăng ký tham gia %s" -#: judge/views/organization.py:647 +#: judge/views/organization.py:221 msgid "Join request detail" msgstr "Chi tiết đơn đăng ký" -#: judge/views/organization.py:681 -msgid "Manage join requests" -msgstr "Quản lý đơn đăng ký" - -#: judge/views/organization.py:685 +#: judge/views/organization.py:254 #, python-format msgid "Managing join requests for %s" msgstr "Quản lý đơn đăng ký cho %s" -#: judge/views/organization.py:725 +#: judge/views/organization.py:285 #, python-format msgid "" "Your organization can only receive %d more members. You cannot approve %d " @@ -3330,269 +2605,213 @@ msgstr "" "Tổ chức chỉ có thể chứa %d thành viên. Bạn không thể chấp thuận nhiều hơn %d " "người." -#: judge/views/organization.py:743 +#: judge/views/organization.py:297 #, python-format msgid "Approved %d user." msgid_plural "Approved %d users." msgstr[0] "Đã chấp thuận %d người." -#: judge/views/organization.py:746 +#: judge/views/organization.py:298 #, python-format msgid "Rejected %d user." msgid_plural "Rejected %d users." msgstr[0] "Đã từ chối %d người." -#: judge/views/organization.py:786 +#: judge/views/organization.py:328 #, python-format -msgid "Add member for %s" -msgstr "Thêm thành viên cho %s" +msgid "Editing %s" +msgstr "Đang chỉnh sửa %s" -#: judge/views/organization.py:802 -#, fuzzy -#| msgid "Edited from site" -msgid "Added members from site" -msgstr "Chỉnh sửa từ web" +#: judge/views/organization.py:352 judge/views/organization.py:360 +msgid "Can't edit organization" +msgstr "Không thể chỉnh sửa tổ chức" -#: judge/views/organization.py:822 judge/views/organization.py:830 -#: judge/views/organization.py:839 +#: judge/views/organization.py:353 +msgid "You are not allowed to edit this organization." +msgstr "Bạn không được phép chỉnh sửa tổ chức này." + +#: judge/views/organization.py:361 +msgid "You are not allowed to kick people from this organization." +msgstr "Bạn không được phép đuổi người." + +#: judge/views/organization.py:366 judge/views/organization.py:370 msgid "Can't kick user" msgstr "Không thể đuổi" -#: judge/views/organization.py:823 +#: judge/views/organization.py:367 msgid "The user you are trying to kick does not exist!" msgstr "" -#: judge/views/organization.py:831 +#: judge/views/organization.py:371 #, python-format msgid "The user you are trying to kick is not in organization: %s." msgstr "" -#: judge/views/organization.py:840 -#, fuzzy -#| msgid "Are you sure you want to leave this organization?" -msgid "The user you are trying to kick is an organization admin." -msgstr "Bạn có chắc muốn rời tổ chức?" - -#: judge/views/organization.py:845 -#, fuzzy -#| msgid "Add members" -msgid "Kicked member" -msgstr "Thêm thành viên" - -#: judge/views/organization.py:865 judge/views/organization.py:1024 -#, python-format -msgid "Edit %s" -msgstr "Chỉnh sửa %s" - -#: judge/views/organization.py:893 templates/organization/search-form.html:19 -msgid "Create group" -msgstr "Tạo nhóm" - -#: judge/views/organization.py:908 -msgid "Exceeded limit" -msgstr "" - -#: judge/views/organization.py:909 -#, python-format -msgid "You created too many groups. You can only create at most %d groups" -msgstr "" - -#: judge/views/organization.py:914 judge/views/organization.py:939 -#: judge/views/organization.py:1093 -msgid "Added from site" -msgstr "Thêm từ web" - -#: judge/views/organization.py:973 judge/views/organization.py:1140 -msgid "Permission denied" -msgstr "Truy cập bị từ chối" - -#: judge/views/organization.py:974 -#, fuzzy -#| msgid "You are not allowed to edit this organization." -msgid "You are not allowed to edit this contest" -msgstr "Bạn không được phép chỉnh sửa tổ chức này." - -#: judge/views/organization.py:1028 templates/blog/blog.html:31 -#: templates/comments/content-list.html:53 -#: templates/comments/content-list.html:66 -#: templates/contest/contest-tabs.html:36 templates/contest/macros.html:14 -#: templates/contest/tag-title.html:9 templates/course/contest_list.html:25 -#: templates/flatpages/admin_link.html:3 templates/license.html:10 -#: templates/organization/blog/pending.html:56 -#: templates/problem/editorial.html:15 templates/problem/feed/items.html:50 -#: templates/test_formatter/download_test_formatter.html:83 -msgid "Edit" -msgstr "Chỉnh sửa" - -#: judge/views/organization.py:1082 -#, python-format -msgid "Add blog for %s" -msgstr "Thêm bài đăng cho %s" - -#: judge/views/organization.py:1134 -#, fuzzy -#| msgid "Those who can edit this organization" -msgid "This blog does not belong to this organization" -msgstr "Những người có thể chỉnh sửa tổ chức" - -#: judge/views/organization.py:1136 -msgid "Not allowed to edit this blog" -msgstr "Bạn không được phép chỉnh sửa bài đăng này." - -#: judge/views/organization.py:1193 -#, python-format -msgid "Edit blog %s" -msgstr "Chỉnh sửa %s" - -#: judge/views/organization.py:1239 -#, python-format -msgid "Pending blogs in %s" -msgstr "Bài đang đợi duyệt trong %s" - -#: judge/views/problem.py:135 +#: judge/views/problem.py:68 msgid "No such problem" msgstr "Không có bài nào như vậy" -#: judge/views/problem.py:136 +#: judge/views/problem.py:69 #, python-format msgid "Could not find a problem with the code \"%s\"." msgstr "Không tìm thấy bài tập với mã bài \"%s\"." -#: judge/views/problem.py:203 +#: judge/views/problem.py:113 #, python-brace-format msgid "Editorial for {0}" msgstr "Hướng dẫn cho {0}" -#: judge/views/problem.py:207 +#: judge/views/problem.py:116 #, python-brace-format msgid "Editorial for {0}" msgstr "Hướng dẫn cho {0}" -#: judge/views/problem.py:460 templates/contest/contest.html:117 -#: templates/course/lesson.html:14 -#: templates/organization/org-left-sidebar.html:4 -#: templates/profile-table.html:25 templates/user/user-about.html:28 -#: templates/user/user-bookmarks.html:16 templates/user/user-tabs.html:5 -#: templates/user/users-table.html:19 +#: judge/views/problem.py:227 +#, python-brace-format +msgid "Disscuss {0}" +msgstr "" + +#: judge/views/problem.py:230 +#, python-brace-format +msgid "Discuss {0}" +msgstr "Thảo luận {0}" + +#: judge/views/problem.py:298 templates/contest/contest.html:79 +#: templates/user/user-about.html:28 templates/user/user-tabs.html:5 +#: templates/user/users-table.html:29 msgid "Problems" msgstr "Bài tập" -#: judge/views/problem.py:842 -msgid "Problem feed" -msgstr "Bài tập" - -#: judge/views/problem.py:1046 judge/views/problem.py:1079 -msgid "

You have submitted too many submissions.

" -msgstr "

Bạn nộp quá nhiều bài.

" - -#: judge/views/problem.py:1058 +#: judge/views/problem.py:598 msgid "Banned from submitting" msgstr "Bị cấm nộp bài" -#: judge/views/problem.py:1060 +#: judge/views/problem.py:599 msgid "" "You have been declared persona non grata for this problem. You are " "permanently barred from submitting this problem." msgstr "Bạn đã bị cấm nộp bài này." -#: judge/views/problem.py:1098 +#: judge/views/problem.py:613 msgid "Too many submissions" msgstr "Quá nhiều lần nộp" -#: judge/views/problem.py:1100 +#: judge/views/problem.py:614 msgid "You have exceeded the submission limit for this problem." msgstr "Bạn đã vượt quá số lần nộp cho bài này." -#: judge/views/problem.py:1185 judge/views/problem.py:1190 +#: judge/views/problem.py:674 judge/views/problem.py:677 #, python-format msgid "Submit to %(problem)s" msgstr "Nộp bài cho %(problem)s" -#: judge/views/problem.py:1217 +#: judge/views/problem.py:692 msgid "Clone Problem" msgstr "Nhân bản bài tập" -#: judge/views/problem_data.py:71 +#: judge/views/problem_data.py:48 msgid "Checker arguments must be a JSON object" msgstr "" -#: judge/views/problem_data.py:73 +#: judge/views/problem_data.py:50 msgid "Checker arguments is invalid JSON" msgstr "" -#: judge/views/problem_data.py:80 +#: judge/views/problem_data.py:57 msgid "Your zip file is invalid!" msgstr "File Zip không hợp lệ!" -#: judge/views/problem_data.py:170 +#: judge/views/problem_data.py:120 #, python-brace-format msgid "Comparing submissions for {0}" msgstr "So sánh các bài nộp cho {0}" -#: judge/views/problem_data.py:174 +#: judge/views/problem_data.py:123 #, python-brace-format msgid "Comparing submissions for {0}" msgstr "So sánh các bài nộp cho {0}" -#: judge/views/problem_data.py:211 +#: judge/views/problem_data.py:158 #, python-brace-format msgid "Editing data for {0}" msgstr "Chỉnh sửa dữ liệu cho {0}" -#: judge/views/problem_data.py:215 +#: judge/views/problem_data.py:161 #, python-format msgid "Editing data for %s" msgstr "Chỉnh sửa dữ liệu cho %s" -#: judge/views/problem_data.py:348 judge/views/problem_data.py:350 +#: judge/views/problem_data.py:253 judge/views/problem_data.py:254 #, python-format msgid "Generated init.yml for %s" msgstr "File init.yml cho %s" -#: judge/views/problem_manage.py:57 judge/views/problem_manage.py:61 +#: judge/views/problem_manage.py:52 judge/views/problem_manage.py:55 #, python-format msgid "Managing submissions for %s" msgstr "Quản lý bài nộp cho %s" -#: judge/views/problem_manage.py:127 +#: judge/views/problem_manage.py:97 #, python-format msgid "Rejudging selected submissions for %s..." msgstr "Đang chấm lại các bài nộp cho %s..." -#: judge/views/problem_manage.py:187 +#: judge/views/problem_manage.py:141 #, python-format msgid "Rescoring all submissions for %s..." msgstr "Đang tính điểm lại các bài nộp cho %s..." -#: judge/views/problem_manage.py:202 +#: judge/views/problem_manage.py:150 #, python-format msgid "Successfully scheduled %d submission for rejudging." msgid_plural "Successfully scheduled %d submissions for rejudging." msgstr[0] "Đã lên lịch chấm lại cho %d bài nộp." -#: judge/views/ranked_submission.py:68 +#: judge/views/ranked_submission.py:58 #, python-format msgid "Best solutions for %s" msgstr "Các bài nộp tốt nhất cho %s" -#: judge/views/ranked_submission.py:72 +#: judge/views/ranked_submission.py:61 #, python-brace-format msgid "Best solutions for {0}" msgstr "Các bài nộp tốt nhất cho {0}" -#: judge/views/register.py:30 templates/course/grades.html:80 -#: templates/course/grades_lesson.html:81 -#: templates/registration/registration_form.html:34 -#: templates/user/base-users-table.html:5 -#: templates/user/import/table_csv.html:4 -msgid "Username" -msgstr "Tên đăng nhập" +#: judge/views/ranked_submission.py:71 +#, python-format +msgid "Best solutions for %(problem)s in %(contest)s" +msgstr "Các bài nộp tốt nhất cho %(problem)s trong %(contest)s" -#: judge/views/register.py:42 templates/user/edit-profile.html:151 +#: judge/views/ranked_submission.py:74 +#, python-format +msgid "Best solutions for problem %(number)s in %(contest)s" +msgstr "Các bài nộp tốt nhất cho bài %(number)s trong %(contest)s" + +#: judge/views/ranked_submission.py:80 +#, python-brace-format +msgid "Best solutions for {0} in {2}" +msgstr "" +"Các bài nộp tốt nhất cho {0} trong {2}" + +#: judge/views/ranked_submission.py:83 +#, python-brace-format +msgid "Best solutions for problem {0} in {1}" +msgstr "Các bài nộp tốt nhất cho bài {0} trong {1}" + +#: judge/views/register.py:27 +msgid "A username must contain letters, numbers, or underscores" +msgstr "Tên đăng nhập phải chứa ký tự, chữ số, hoặc dấu gạch dưới" + +#: judge/views/register.py:31 templates/user/edit-profile.html:120 msgid "Preferred language" msgstr "Ngôn ngữ ưa thích" -#: judge/views/register.py:54 +#: judge/views/register.py:38 +msgid "Subscribe to newsletter?" +msgstr "Đăng ký để nhận thông báo?" + +#: judge/views/register.py:55 #, python-format msgid "" "The email address \"%s\" is already taken. Only one registration is allowed " @@ -3600,7 +2819,7 @@ msgid "" msgstr "" "Email \"%s\" đã được sử dụng. Mỗi email chỉ có thể đăng ký một tài khoản." -#: judge/views/register.py:66 +#: judge/views/register.py:61 msgid "" "Your email provider is not allowed due to history of abuse. Please use a " "reputable email provider." @@ -3608,123 +2827,78 @@ msgstr "" "Your email provider is not allowed due to history of abuse. Please use a " "reputable email provider." -#: judge/views/register.py:74 judge/views/register.py:111 +#: judge/views/register.py:67 judge/views/register.py:105 msgid "Registration" msgstr "Đăng ký" -#: judge/views/register.py:125 +#: judge/views/register.py:116 msgid "Authentication failure" msgstr "Xác thực thất bại" -#: judge/views/resolver.py:11 templates/contest/contest-tabs.html:27 -#, fuzzy -#| msgid "solve rate" -msgid "Resolver" -msgstr "Tỉ lệ giải đúng" - -#: judge/views/stats.py:105 +#: judge/views/stats.py:67 msgid "Language statistics" msgstr "Thống kê ngôn ngữ" -#: judge/views/stats.py:154 templates/contest/contest-tabs.html:22 -#: templates/organization/org-left-sidebar.html:6 templates/stats/site.html:15 -#: templates/user/user-tabs.html:6 -msgid "Submissions" -msgstr "Bài nộp" - -#: judge/views/stats.py:160 templates/comments/list.html:4 -#: templates/stats/site.html:39 -msgid "Comments" -msgstr "Bình luận" - -#: judge/views/stats.py:172 templates/stats/site.html:45 -#, fuzzy -#| msgid "New message(s)" -msgid "Chat messages" -msgstr "Tin nhắn mới" - -#: judge/views/stats.py:193 -#, fuzzy -#| msgid "Statistics" -msgid "Site statistics" -msgstr "Thống kê" - -#: judge/views/status.py:26 templates/submission/list.html:336 +#: judge/views/status.py:24 templates/submission/list.html:313 msgid "Status" msgstr "Kết quả chấm" -#: judge/views/status.py:119 +#: judge/views/status.py:107 msgid "Version matrix" msgstr "Ma trận phiên bản" -#: judge/views/submission.py:99 judge/views/submission.py:107 +#: judge/views/submission.py:84 judge/views/submission.py:91 #, python-format msgid "Submission of %(problem)s by %(user)s" msgstr "Bài nộp của %(user)s cho bài %(problem)s" -#: judge/views/submission.py:291 judge/views/submission.py:292 -#: templates/problem/problem.html:192 +#: judge/views/submission.py:244 judge/views/submission.py:245 +#: templates/problem/problem.html:165 msgid "All submissions" msgstr "Tất cả bài nộp" -#: judge/views/submission.py:569 judge/views/submission.py:574 +#: judge/views/submission.py:405 msgid "All my submissions" msgstr "Tất cả bài nộp của tôi" -#: judge/views/submission.py:570 +#: judge/views/submission.py:406 #, python-format msgid "All submissions by %s" msgstr "Tất cả bài nộp của %s" -#: judge/views/submission.py:576 -#, python-brace-format -msgid "All submissions by {0}" -msgstr "Tất cả bài nộp của {0}" - -#: judge/views/submission.py:599 -#, fuzzy -#| msgid "All submissions" -msgid "All friend submissions" -msgstr "Tất cả bài nộp" - -#: judge/views/submission.py:628 +#: judge/views/submission.py:437 #, python-format msgid "All submissions for %s" msgstr "Tất cả bài nộp cho %s" -#: judge/views/submission.py:656 +#: judge/views/submission.py:456 msgid "Must pass a problem" msgstr "Phải làm được một bài" -#: judge/views/submission.py:714 +#: judge/views/submission.py:502 #, python-format msgid "My submissions for %(problem)s" msgstr "Bài nộp của tôi cho %(problem)s" -#: judge/views/submission.py:715 +#: judge/views/submission.py:503 #, python-format msgid "%(user)s's submissions for %(problem)s" msgstr "Các bài nộp của %(user)s cho %(problem)s" -#: judge/views/submission.py:838 +#: judge/views/submission.py:604 msgid "Must pass a contest" msgstr "Phải qua một kỳ thi" -#: judge/views/submission.py:862 -#, python-brace-format -msgid "Submissions in {1}" -msgstr "Bài nộp trong {1}" - -#: judge/views/submission.py:900 +#: judge/views/submission.py:623 #, python-brace-format msgid "" -"{0}'s submissions for {2} in {4}" +"{0}'s submissions for {2} in {4}" msgstr "" "Các bài nộp của {0} cho {2} trong {4}" -#: judge/views/submission.py:912 +#: judge/views/submission.py:630 #, python-brace-format msgid "" "{0}'s submissions for problem {2} in {3}" @@ -3733,56 +2907,44 @@ msgstr "" "Các bài nộp của {0} cho bài {2} trong {3}" "" -#: judge/views/submission.py:1046 -msgid "You don't have permission to access." -msgstr "Bạn không có quyền truy cập." - -#: judge/views/test_formatter/test_formatter.py:64 -#: judge/views/test_formatter/test_formatter.py:107 -#: judge/views/test_formatter/test_formatter.py:190 -#, fuzzy -#| msgid "contest format" -msgid "Test Formatter" -msgstr "format kỳ thi" - -#: judge/views/ticket.py:60 judge/views/ticket.py:66 +#: judge/views/ticket.py:50 judge/views/ticket.py:56 msgid "Ticket title" msgstr "Tiêu đề báo cáo" -#: judge/views/ticket.py:124 judge/views/ticket.py:128 +#: judge/views/ticket.py:107 judge/views/ticket.py:110 #, python-format msgid "New ticket for %s" msgstr "Báo cáo mới cho %s" -#: judge/views/ticket.py:205 +#: judge/views/ticket.py:170 #, python-format msgid "%(title)s - Ticket %(id)d" msgstr "%(title)s - Báo cáo %(id)d" -#: judge/views/ticket.py:334 +#: judge/views/ticket.py:279 #, python-format msgid "Tickets - Page %(number)d of %(total)d" msgstr "Báo cáo - Trang %(number)d trên %(total)d" -#: judge/views/ticket.py:400 +#: judge/views/ticket.py:328 #, python-format msgid "New Ticket: %s" msgstr "Báo cáo mới: %s" -#: judge/views/ticket.py:403 +#: judge/views/ticket.py:329 #, python-format msgid "#%(id)d, assigned to: %(users)s" msgstr "#%(id)d, được phân cho: %(users)s" -#: judge/views/ticket.py:407 +#: judge/views/ticket.py:331 msgid ", " msgstr ", " -#: judge/views/ticket.py:412 +#: judge/views/ticket.py:331 msgid "no one" msgstr "không người nào" -#: judge/views/ticket.py:439 +#: judge/views/ticket.py:351 #, python-format msgid "New Ticket Message For: %s" msgstr "Tin nhắn báo cáo mới cho: %s" @@ -3791,86 +2953,206 @@ msgstr "Tin nhắn báo cáo mới cho: %s" msgid "Enable Two Factor Authentication" msgstr "Kích hoạt Two Factor Authentication" -#: judge/views/totp.py:93 templates/registration/totp_disable.html:48 +#: judge/views/totp.py:89 templates/registration/totp_disable.html:48 msgid "Disable Two Factor Authentication" msgstr "Hủy kích hoạt Two Factor Authentication" -#: judge/views/totp.py:109 +#: judge/views/totp.py:105 msgid "Perform Two Factor Authentication" msgstr "Thực hiện Two Factor Authentication" -#: judge/views/user.py:115 +#: judge/views/user.py:74 msgid "No such user" msgstr "Không người dùng nào như vậy" -#: judge/views/user.py:116 +#: judge/views/user.py:74 #, python-format msgid "No user handle \"%s\"." msgstr "Không tồn tại tên người dùng \"%s\"." -#: judge/views/user.py:121 +#: judge/views/user.py:78 msgid "My account" msgstr "Tài khoản của tôi" -#: judge/views/user.py:123 +#: judge/views/user.py:79 #, python-format msgid "User %s" msgstr "Thành viên %s" -#: judge/views/user.py:213 +#: judge/views/user.py:149 +msgid "M j, Y" +msgstr "j M, Y" + +#: judge/views/user.py:172 msgid "M j, Y, G:i" msgstr "j M, Y, G:i" -#: judge/views/user.py:414 +#: judge/views/user.py:291 msgid "Updated on site" msgstr "Được cập nhật trên web" -#: judge/views/user.py:431 templates/admin/auth/user/change_form.html:14 -#: templates/admin/auth/user/change_form.html:17 +#: judge/views/user.py:326 templates/admin/auth/user/change_form.html:14 +#: templates/admin/auth/user/change_form.html:17 templates/base.html:297 +#: templates/user/user-tabs.html:10 msgid "Edit profile" msgstr "Chỉnh sửa thông tin" -#: judge/views/user.py:441 templates/user/user-left-sidebar.html:2 -#: templates/user/user-list-tabs.html:4 +#: judge/views/user.py:335 templates/user/user-list-tabs.html:4 msgid "Leaderboard" -msgstr "Xếp hạng" +msgstr "Bảng xếp hạng" -#: judge/views/user.py:541 +#: judge/views/user.py:410 msgid "Import Users" msgstr "" -#: judge/views/widgets.py:69 judge/views/widgets.py:85 +#: judge/views/widgets.py:48 judge/views/widgets.py:58 #, python-format msgid "Invalid upstream data: %s" msgstr "Dữ liệu không hợp lệ: %s" -#: judge/views/widgets.py:99 +#: judge/views/widgets.py:68 msgid "Bad latitude or longitude" msgstr "Kinh độ / Vĩ độ không hợp lệ" -#: templates/actionbar/list.html:15 -msgid "Like" -msgstr "Thích" +#: src/dmoj-wpadmin/test_project/apps/authors/models.py:9 +#, fuzzy +#| msgid "short name" +msgid "first name" +msgstr "tên ngắn" -#: templates/actionbar/list.html:28 templates/blog/list.html:46 -msgid "Comment" -msgstr "Bình luận" +#: src/dmoj-wpadmin/test_project/apps/authors/models.py:10 +#, fuzzy +#| msgid "short name" +msgid "last name" +msgstr "tên ngắn" -#: templates/actionbar/list.html:43 -msgid "Bookmark" -msgstr "Lưu" +#: src/dmoj-wpadmin/test_project/apps/authors/models.py:11 +msgid "biography" +msgstr "tiểu sử" -#: templates/actionbar/list.html:50 -msgid "Share" -msgstr "Chia sẻ" +#: src/dmoj-wpadmin/test_project/apps/books/models.py:12 +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:12 +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:12 +msgid "name" +msgstr "tên" -#: templates/actionbar/list.html:57 -msgid "Report" -msgstr "Báo cáo" +#: src/dmoj-wpadmin/test_project/apps/books/models.py:19 +msgid "Category of Books" +msgstr "Thể loại sách" -#: templates/actionbar/media-js.html:116 -msgid "Copied link" -msgstr "Đã sao chép link" +#: src/dmoj-wpadmin/test_project/apps/books/models.py:20 +msgid "Categories of Books" +msgstr "Thể loại sách" + +#: src/dmoj-wpadmin/test_project/apps/books/models.py:26 +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:26 +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:26 +msgid "title" +msgstr "tiêu đề" + +#: src/dmoj-wpadmin/test_project/apps/books/models.py:29 +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:29 +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:29 +msgid "author" +msgstr "tác giả" + +#: src/dmoj-wpadmin/test_project/apps/books/models.py:31 +msgid "publication date" +msgstr "ngày xuất bản" + +#: src/dmoj-wpadmin/test_project/apps/books/models.py:37 +msgid "Book" +msgstr "Sách" + +#: src/dmoj-wpadmin/test_project/apps/books/models.py:38 +#: src/dmoj-wpadmin/test_project/test_project/wp.py:136 +msgid "Books" +msgstr "Sách" + +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:19 +msgid "Category of CDs" +msgstr "Thể loại CDs" + +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:20 +msgid "Categories of CDs" +msgstr "Thể loại CDs" + +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:36 +msgid "CD" +msgstr "" + +#: src/dmoj-wpadmin/test_project/apps/cds/models.py:37 +#: src/dmoj-wpadmin/test_project/test_project/wp.py:141 +msgid "CDs" +msgstr "" + +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:19 +msgid "Category of DVDs" +msgstr "Thể loại DVDs" + +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:20 +msgid "Categories of DVDs" +msgstr "Thể loại DVDs" + +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:36 +msgid "DVD" +msgstr "" + +#: src/dmoj-wpadmin/test_project/apps/dvds/models.py:37 +#: src/dmoj-wpadmin/test_project/test_project/wp.py:146 +msgid "DVDs" +msgstr "" + +#: src/dmoj-wpadmin/test_project/templates/admin/base_site.html:7 +msgid "Django administration" +msgstr "Quản trị viên Django" + +#: src/dmoj-wpadmin/test_project/test_project/forms.py:12 +#, python-format +msgid "" +"Please enter the correct %(username)s and password for an admin account. " +"Note that both fields may be case-sensitive." +msgstr "" +"Hãy nhập %(username)s và mật khẩu hợp lệ cho tài khoản quản trị. Chú ý cả " +"hai trường có phân biệt chữ Hoa-thường." + +#: src/dmoj-wpadmin/test_project/test_project/forms.py:32 +#, python-format +msgid "" +"Please enter the correct %(username)s and password for an user account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Hãy nhập %(username)s và mật khẩu hợp lệ cho tài khoản thành viên. Chú ý cả " +"hai trường có phân biệt chữ Hoa-thường." + +#: src/dmoj-wpadmin/test_project/test_project/wp.py:27 +msgid "Site" +msgstr "Trang" + +#: src/dmoj-wpadmin/test_project/test_project/wp.py:38 +#: src/dmoj-wpadmin/test_project/test_project/wp.py:41 +#: src/dmoj-wpadmin/test_project/test_project/wp.py:130 +#: src/dmoj-wpadmin/test_project/test_project/wp.py:133 +msgid "Dashboard" +msgstr "Bảng điều khiển" + +#: src/dmoj-wpadmin/test_project/test_project/wp.py:48 +msgid "Applications" +msgstr "Ứng dụng" + +#: src/dmoj-wpadmin/test_project/test_project/wp.py:53 +#, fuzzy +#| msgid "administrators" +msgid "Administration" +msgstr "người quản lý" + +#: src/dmoj-wpadmin/test_project/test_project/wp.py:64 +msgid "Color theme" +msgstr "Chủ đề màu sắc" + +#: src/dmoj-wpadmin/test_project/test_project/wp.py:66 +msgid "Change color theme" +msgstr "Đổi chủ đề màu sắc" #: templates/admin/judge/contest/change_form.html:9 msgid "Are you sure you want to rejudge ALL the submissions?" @@ -3895,19 +3177,15 @@ msgstr "Ngắt kết nối" msgid "Terminate" msgstr "Dừng" -#: templates/admin/judge/problem/change_form.html:15 +#: templates/admin/judge/problem/change_form.html:14 msgid "View Submissions" msgstr "Xem Bài Nộp" -#: templates/admin/judge/problem/change_form.html:18 +#: templates/admin/judge/problem/change_form.html:17 +#: templates/user/user-base.html:112 msgid "View submissions" msgstr "Xem bài nộp" -#: templates/admin/judge/problem/change_form.html:19 -#: templates/admin/judge/problem/change_form.html:22 -msgid "View votes" -msgstr "Xem bình chọn" - #: templates/admin/judge/profile/change_form.html:14 #: templates/admin/judge/profile/change_form.html:17 msgid "Edit user" @@ -3915,277 +3193,295 @@ msgstr "Chỉnh sửa thông tin" #: templates/admin/judge/submission/change_form.html:14 #: templates/admin/judge/submission/change_form.html:17 -#: templates/submission/status.html:148 +#: templates/submission/source.html:38 templates/submission/status.html:67 msgid "Rejudge" msgstr "Chấm lại" -#: templates/base.html:146 +#: templates/base.html:266 templates/chat/chat.html:566 msgid "Chat" msgstr "Chat" -#: templates/base.html:156 +#: templates/base.html:272 msgid "Notification" msgstr "Thông báo" -#: templates/base.html:183 -msgid "Dark Mode" +#: templates/base.html:289 +#, python-format +msgid "Hello, %(username)s." +msgstr "Xin chào, %(username)s." + +#: templates/base.html:295 templates/chat/chat.html:20 +#: templates/comments/list.html:89 templates/contest/contest-list-tabs.html:24 +#: templates/contest/ranking-table.html:53 +#: templates/problem/problem-list-tabs.html:6 +#: templates/submission/info-base.html:12 +#: templates/submission/submission-list-tabs.html:15 +msgid "Admin" msgstr "" -#: templates/base.html:194 templates/profile-table.html:3 -msgid "Profile" -msgstr "Trang cá nhân" - -#: templates/base.html:203 -msgid "Internal" -msgstr "Nội bộ" - -#: templates/base.html:206 -msgid "Stats" -msgstr "Thống kê" - -#: templates/base.html:210 templates/user/user-tabs.html:11 -msgid "Bookmarks" -msgstr "Đã lưu" - -#: templates/base.html:217 -#, fuzzy -#| msgid "Stop spectating" -msgid "Stop impersonating" -msgstr "Ngừng theo dõi" - -#: templates/base.html:222 +#: templates/base.html:304 msgid "Log out" msgstr "Đăng xuất" -#: templates/base.html:232 +#: templates/base.html:313 #: templates/registration/password_reset_complete.html:4 msgid "Log in" msgstr "Đăng nhập" -#: templates/base.html:233 +#: templates/base.html:314 templates/registration/registration_form.html:177 +msgid "or" +msgstr "hoặc" + +#: templates/base.html:315 msgid "Sign up" msgstr "Đăng ký" -#: templates/base.html:247 +#: templates/base.html:331 msgid "spectating" msgstr "đang theo dõi" -#: templates/base.html:259 templates/contest/list.html:111 -msgid "In contest" -msgstr "Trong kỳ thi" +#: templates/base.html:343 +msgid "Compete" +msgstr "Thi" -#: templates/base.html:261 -msgid "Out contest" -msgstr "Ngoài kỳ thi" +#: templates/base.html:345 +msgid "General" +msgstr "Chung" -#: templates/base.html:271 +#: templates/base.html:352 msgid "This site works best with JavaScript enabled." msgstr "" -#: templates/blog/blog.html:28 +#: templates/blog/blog.html:13 templates/comments/list.html:68 +#: templates/comments/list.html:83 templates/contest/contest-tabs.html:23 +#: templates/contest/tag-title.html:9 templates/flatpages/admin_link.html:3 +#: templates/license.html:10 templates/problem/editorial.html:14 +msgid "Edit" +msgstr "Chỉnh sửa" + +#: templates/blog/blog.html:26 #, python-format msgid " posted on %(time)s" msgstr "đã đăng vào %(time)s" -#: templates/blog/blog.html:35 templates/contest/contest.html:93 -#: templates/contest/contest.html:97 -msgid "Edit in" -msgstr "Chỉnh sửa trong" - -#: templates/blog/content.html:43 templates/comments/feed.html:18 -#: templates/organization/blog/pending.html:42 -#: templates/problem/feed/items.html:109 templates/ticket/feed.html:26 -#: templates/user/user-bookmarks.html:61 templates/user/user-bookmarks.html:131 -#: templates/user/user-bookmarks.html:180 -msgid "...More" -msgstr "...Xem thêm" +#: templates/blog/content.html:13 +msgid "posted" +msgstr "đã đăng" #: templates/blog/dashboard.html:21 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| " on %(time)s\n" -#| " " +#, python-format msgid "" "\n" -" on %(time)s\n" -" " +" on %(time)s\n" +" " msgstr "" "\n" " vào %(time)s\n" " " -#: templates/blog/list.html:45 -msgid "News" -msgstr "Tin tức" +#: templates/blog/list.html:93 +msgid "Blog" +msgstr "" -#: templates/blog/list.html:48 +#: templates/blog/list.html:95 msgid "Events" msgstr "Sự kiện" -#: templates/blog/list.html:60 -msgid "You have no ticket" -msgstr "Bạn không có báo cáo" +#: templates/blog/list.html:100 +msgid "News" +msgstr "Tin tức" -#: templates/blog/list.html:73 templates/problem/list.html:150 -#: templates/problem/problem.html:448 +#: templates/blog/list.html:115 templates/problem/list.html:347 +#: templates/problem/problem.html:364 msgid "Clarifications" msgstr "Thông báo" -#: templates/blog/list.html:78 templates/course/contest_list.html:32 +#: templates/blog/list.html:121 msgid "Add" msgstr "Thêm mới" -#: templates/blog/list.html:98 templates/problem/list.html:172 -#: templates/problem/problem.html:459 +#: templates/blog/list.html:140 templates/problem/list.html:369 +#: templates/problem/problem.html:375 msgid "No clarifications have been made at this time." msgstr "Không có thông báo nào." -#: templates/chat/chat.html:5 templates/chat/chat_js.html:570 -msgid "Chat Box" -msgstr "Chat Box" +#: templates/blog/list.html:148 +msgid "Ongoing contests" +msgstr "Kỳ thi đang diễn ra" -#: templates/chat/chat.html:68 templates/chat/chat_js.html:530 -#: templates/user/base-users-js.html:10 -#: templates/user/base-users-two-col.html:19 -msgid "Search by handle..." -msgstr "Tìm kiếm theo tên..." +#: templates/blog/list.html:156 +msgid "Ends in" +msgstr "Còn" -#: templates/chat/chat.html:89 -msgid "Enter your message" -msgstr "Nhập tin nhắn" +#: templates/blog/list.html:166 +msgid "Upcoming contests" +msgstr "Kỳ thi sắp diễn ra" -#: templates/chat/chat.html:90 -msgid "Emoji" -msgstr "" +#: templates/blog/list.html:184 +msgid "My open tickets" +msgstr "Báo cáo dành cho tôi" -#: templates/chat/chat_js.html:133 +#: templates/blog/list.html:206 +msgid "New tickets" +msgstr "Báo cáo mới" + +#: templates/blog/list.html:227 +msgid "New problems" +msgstr "Bài tập mới" + +#: templates/blog/list.html:244 +msgid "Comment stream" +msgstr "Dòng bình luận" + +#: templates/chat/chat.html:18 +msgid "Recent" +msgstr "Gần đây" + +#: templates/chat/chat.html:19 +msgid "Following" +msgstr "Bạn bè" + +#: templates/chat/chat.html:21 +msgid "Other" +msgstr "Thành viên khác" + +#: templates/chat/chat.html:166 msgid "New message(s)" msgstr "Tin nhắn mới" -#: templates/chat/chat_js.html:440 -msgid "Mute this user and delete all messages?" -msgstr "Mute người dùng này và xóa tất cả tin nhắn chung?" +#: templates/chat/chat.html:507 templates/chat/chat.html:588 +#: templates/user/base-users.html:14 templates/user/base-users.html:80 +msgid "Search by handle..." +msgstr "Tìm kiếm theo tên..." + +#: templates/chat/chat.html:568 templates/chat/chat.html:575 +msgid "Online Users" +msgstr "Trực tuyến" + +#: templates/chat/chat.html:576 +msgid "Refresh" +msgstr "Làm mới" + +#: templates/chat/chat.html:609 +msgid "Emoji" +msgstr "" + +#: templates/chat/chat.html:610 +msgid "Enter your message" +msgstr "Nhập tin nhắn" #: templates/chat/message.html:20 templates/fine_uploader/script.html:25 -#: templates/organization/blog/edit.html:37 msgid "Delete" msgstr "Xóa" -#: templates/chat/message.html:25 -msgid "Mute" -msgstr "" - -#: templates/chat/message_list.html:8 +#: templates/chat/message_list.html:7 msgid "You are connect now. Say something to start the conversation." msgstr "Các bạn đã kết nối. Nhắn gì đó để bắt đầu cuộc trò chuyện." #: templates/chat/online_status.html:6 -#: templates/chat/user_online_status.html:21 +#: templates/chat/user_online_status.html:14 msgid "Lobby" msgstr "Sảnh chung" -#: templates/chat/online_status.html:52 -#: templates/chat/user_online_status.html:39 -msgid "Ignore" -msgstr "Tắt thông báo" - -#: templates/chat/user_online_status.html:26 +#: templates/chat/user_online_status.html:19 #, python-brace-format msgid "Last online on {time}" msgstr "Trực tuyến vào {time}" -#: templates/chat/user_online_status.html:37 +#: templates/chat/user_online_status.html:31 msgid "Unignore" msgstr "Mở lại thông báo" -#: templates/chat/user_online_status.html:45 +#: templates/chat/user_online_status.html:33 +msgid "Ignore" +msgstr "Tắt thông báo" + +#: templates/chat/user_online_status.html:40 msgid "users are online" msgstr "người đang trực tuyến" -#: templates/comments/content-list.html:11 -#: templates/comments/content-list.html:12 -#: templates/comments/content-list.html:20 -#: templates/comments/content-list.html:21 +#: templates/comments/list.html:2 +msgid "Comments" +msgstr "Bình luận" + +#: templates/comments/list.html:18 templates/comments/list.html:27 msgid "Please login to vote" msgstr "Đăng nhập để vote" -#: templates/comments/content-list.html:36 +#: templates/comments/list.html:40 +#, python-brace-format +msgid "commented on {time}" +msgstr "bình luận vào {time}" + +#: templates/comments/list.html:49 #, python-format msgid "edit %(edits)s" msgstr "chỉnh sửa %(edits)s" -#: templates/comments/content-list.html:38 templates/comments/media-js.html:67 +#: templates/comments/list.html:51 templates/comments/media-js.html:92 msgid "edited" msgstr "đã chỉnh sửa" -#: templates/comments/content-list.html:47 templates/notification/list.html:11 +#: templates/comments/list.html:60 templates/notification/list.html:14 msgid "Link" -msgstr "Đường dẫn" +msgstr "Link" -#: templates/comments/content-list.html:57 -#: templates/comments/content-list.html:63 +#: templates/comments/list.html:73 templates/comments/list.html:80 msgid "Reply" -msgstr "Phản hồi" +msgstr "Trả lời" -#: templates/comments/content-list.html:70 +#: templates/comments/list.html:86 templates/contest/list.html:91 +#: templates/contest/list.html:95 templates/contest/list.html:282 msgid "Hide" msgstr "Ẩn" -#: templates/comments/content-list.html:85 +#: templates/comments/list.html:101 #, python-format msgid "" -"This comment is hidden due to too much negative feedback. Click here " -"to view it." +"\n" +" This comment is hidden due " +"to too much negative feedback.\n" +" Click here to view it.\n" +" " msgstr "" -"Bình luận bị ẩn vì nhiều phản hồi tiêu cực. Nhấp vào đây để mở." +"\n" +" Bình luận bị ẩn vì có quá " +"nhiều phản hồi tiêu cực.\n" +" Nhấn vào đây để xem.\n" +" " -#: templates/comments/content-list.html:102 -msgid "reply" -msgid_plural "replies" -msgstr[0] "phản hồi" - -#: templates/comments/content-list.html:127 -msgid "more comment" -msgid_plural "more comments" -msgstr[0] "bình luận nữa" - -#: templates/comments/list.html:6 -msgid "Write comment" -msgstr "Thêm bình luận" - -#: templates/comments/list.html:12 -msgid "New comment" -msgstr "Bình luận mới" - -#: templates/comments/list.html:26 -msgid "Invalid comment body." -msgstr "Nội dung không hợp lệ." - -#: templates/comments/list.html:34 -msgid "Post!" -msgstr "Đăng!" - -#: templates/comments/list.html:44 +#: templates/comments/list.html:121 msgid "There are no comments at the moment." msgstr "Không có bình luận nào." -#: templates/comments/list.html:49 +#: templates/comments/list.html:127 +msgid "New comment" +msgstr "Bình luận mới" + +#: templates/comments/list.html:141 +msgid "Invalid comment body." +msgstr "Nội dung không hợp lệ." + +#: templates/comments/list.html:149 +msgid "Post!" +msgstr "Đăng!" + +#: templates/comments/list.html:157 msgid "Comments are disabled on this page." msgstr "Bình luận bị tắt trong trang này." -#: templates/comments/media-js.html:12 +#: templates/comments/media-js.html:38 msgid "Replying to comment" msgstr "Trả lời bình luận" -#: templates/comments/media-js.html:62 +#: templates/comments/media-js.html:87 #, python-brace-format msgid "edit {edits}" msgstr "chỉnh sửa {edits}" -#: templates/comments/media-js.html:65 +#: templates/comments/media-js.html:90 msgid "original" msgstr "original" @@ -4201,215 +3497,193 @@ msgstr "Nhập mật khẩu truy cập:" msgid "Join Contest" msgstr "Tham gia kỳ thi" -#: templates/contest/calendar.html:11 -msgid "Prev" -msgstr "Trước" - -#: templates/contest/calendar.html:14 -msgid "Today" -msgstr "Hôm nay" - -#: templates/contest/calendar.html:17 -msgid "Next" -msgstr "Tiếp" - -#: templates/contest/calendar.html:22 +#: templates/contest/calendar.html:12 msgid "Sunday" msgstr "Chủ nhật" -#: templates/contest/calendar.html:23 +#: templates/contest/calendar.html:13 msgid "Monday" msgstr "Thứ hai" -#: templates/contest/calendar.html:24 +#: templates/contest/calendar.html:14 msgid "Tuesday" msgstr "Thứ ba" -#: templates/contest/calendar.html:25 +#: templates/contest/calendar.html:15 msgid "Wednesday" msgstr "Thứ tư" -#: templates/contest/calendar.html:26 +#: templates/contest/calendar.html:16 msgid "Thursday" msgstr "Thứ năm" -#: templates/contest/calendar.html:27 +#: templates/contest/calendar.html:17 msgid "Friday" msgstr "Thứ sáu" -#: templates/contest/calendar.html:28 +#: templates/contest/calendar.html:18 msgid "Saturday" msgstr "Thứ bảy" -#: templates/contest/clarification.html:52 -#: templates/contest/search-form.html:35 templates/organization/new.html:10 +#: templates/contest/clarification.html:52 templates/organization/new.html:10 #: templates/ticket/new.html:38 msgid "Create" msgstr "Tạo mới" -#: templates/contest/clone.html:45 +#: templates/contest/clone.html:37 msgid "Enter a new key for the cloned contest:" msgstr "Nhập mã kỳ thi cho kỳ thi nhân bản:" -#: templates/contest/clone.html:55 templates/problem/clone.html:40 +#: templates/contest/clone.html:40 templates/problem/clone.html:40 msgid "Clone!" msgstr "Nhân bản!" -#: templates/contest/contest-datetime.html:6 +#: templates/contest/contest-list-tabs.html:7 +msgid "Prev" +msgstr "Trước" + +#: templates/contest/contest-list-tabs.html:10 +msgid "Today" +msgstr "Hôm nay" + +#: templates/contest/contest-list-tabs.html:13 +msgid "Next" +msgstr "Tiếp" + +#: templates/contest/contest-list-tabs.html:21 +#: templates/problem/problem-list-tabs.html:5 +msgid "List" +msgstr "Danh sách" + +#: templates/contest/contest-list-tabs.html:22 +msgid "Calendar" +msgstr "Lịch" + +#: templates/contest/contest-tabs.html:4 templates/organization/home.html:98 +msgid "Info" +msgstr "Thông tin" + +#: templates/contest/contest-tabs.html:6 templates/stats/base.html:9 +#: templates/submission/list.html:339 +msgid "Statistics" +msgstr "Thống kê" + +#: templates/contest/contest-tabs.html:11 +msgid "Rankings" +msgstr "Bảng xếp hạng" + +#: templates/contest/contest-tabs.html:16 +msgid "Hidden Rankings" +msgstr "Bảng xếp hạng ẩn" + +#: templates/contest/contest-tabs.html:21 +msgid "MOSS" +msgstr "MOSS" + +#: templates/contest/contest-tabs.html:26 +msgid "Clone" +msgstr "Nhân bản" + +#: templates/contest/contest-tabs.html:38 +#: templates/contest/contest-tabs.html:58 +msgid "Leave contest" +msgstr "Rời kỳ thi" + +#: templates/contest/contest-tabs.html:45 templates/contest/list.html:394 +msgid "Virtual join" +msgstr "Tham gia ảo" + +#: templates/contest/contest-tabs.html:56 +msgid "Stop spectating" +msgstr "Ngừng theo dõi" + +#: templates/contest/contest-tabs.html:65 +msgid "Spectate contest" +msgstr "Theo dõi kỳ thi" + +#: templates/contest/contest-tabs.html:72 +msgid "Join contest" +msgstr "Tham gia kỳ thi" + +#: templates/contest/contest-tabs.html:81 +msgid "Login to participate" +msgstr "Đăng nhập để tham gia" + +#: templates/contest/contest.html:33 #, python-format msgid "Spectating, contest ends in %(countdown)s." msgstr "Đang theo dõi, kỳ thi còn %(countdown)s." -#: templates/contest/contest-datetime.html:8 +#: templates/contest/contest.html:35 #, python-format msgid "Participating virtually, %(countdown)s remaining." msgstr "Đang tham gia ảo, còn %(countdown)s." -#: templates/contest/contest-datetime.html:10 +#: templates/contest/contest.html:37 msgid "Participating virtually." msgstr "Đang tham gia ảo." -#: templates/contest/contest-datetime.html:14 +#: templates/contest/contest.html:41 #, python-format msgid "Starting in %(countdown)s." msgstr "Kỳ thi bắt đầu trong %(countdown)s nữa." -#: templates/contest/contest-datetime.html:16 +#: templates/contest/contest.html:43 msgid "Contest is over." msgstr "Kỳ thi đã kết thúc." -#: templates/contest/contest-datetime.html:20 +#: templates/contest/contest.html:47 #, python-format msgid "Your time is up! Contest ends in %(countdown)s." msgstr "Hết giờ! Kỳ thi kết thúc trong %(countdown)s." -#: templates/contest/contest-datetime.html:22 +#: templates/contest/contest.html:49 #, python-format msgid "You have %(countdown)s remaining." msgstr "Bạn còn %(countdown)s." -#: templates/contest/contest-datetime.html:25 +#: templates/contest/contest.html:52 #, python-format msgid "Contest ends in %(countdown)s." msgstr "Kỳ thi kết thúc trong %(countdown)s" -#: templates/contest/contest-datetime.html:32 -#: templates/contest/contest-datetime.html:36 +#: templates/contest/contest.html:59 templates/contest/contest.html:63 msgid "F j, Y, G:i T" msgstr "G:i T, j F, Y" -#: templates/contest/contest-datetime.html:32 +#: templates/contest/contest.html:59 #, python-format msgid "" -"%(time_limit)s window between %(start_time)s and " -"%(end_time)s" +"%(time_limit)s window between %(start_time)s and " +"%(end_time)s" msgstr "" -"Dài %(time_limit)s từ %(start_time)s đến %(end_time)s" +"Một thời gian dài %(time_limit)s trong khoản %(start_time)s từ " +"%(end_time)s" -#: templates/contest/contest-datetime.html:36 +#: templates/contest/contest.html:63 #, python-format msgid "%(length)s long starting on %(start_time)s" -msgstr "Dài %(length)s bắt đầu từ %(start_time)s" +msgstr "Kéo dài %(length)s bắt đầu từ %(start_time)s" -#: templates/contest/contest-datetime.html:43 -msgid "Standing was frozen" -msgstr "Bảng điểm đã đóng băng" - -#: templates/contest/contest-datetime.html:43 -msgid "at" -msgstr "lúc" - -#: templates/contest/contest-list-tabs.html:2 -#: templates/problem/search-form.html:75 templates/problem/search-form.html:77 -#: templates/submission/list.html:380 -#: templates/submission/submission-list-tabs.html:4 -msgid "All" -msgstr "Tất cả" - -#: templates/contest/contest-list-tabs.html:3 -msgid "Official" -msgstr "Chính thức" - -#: templates/contest/contest-list-tabs.html:4 -msgid "Calendar" -msgstr "Lịch" - -#: templates/contest/contest-tabs.html:11 -msgid "Info" -msgstr "Thông tin" - -#: templates/contest/contest-tabs.html:13 templates/submission/list.html:362 -msgid "Statistics" -msgstr "Thống kê" - -#: templates/contest/contest-tabs.html:19 -msgid "Rankings" -msgstr "Bảng xếp hạng" - -#: templates/contest/contest-tabs.html:30 -msgid "Final rankings" -msgstr "BXH chung cuộc" - -#: templates/contest/contest-tabs.html:34 -msgid "MOSS" -msgstr "MOSS" - -#: templates/contest/contest.html:36 templates/contest/contest.html:56 -msgid "Leave contest" -msgstr "Rời kỳ thi" - -#: templates/contest/contest.html:43 templates/contest/list.html:116 -msgid "Virtual join" -msgstr "Tham gia ảo" - -#: templates/contest/contest.html:54 -msgid "Stop spectating" -msgstr "Ngừng theo dõi" - -#: templates/contest/contest.html:62 -msgid "Spectate contest" -msgstr "Theo dõi kỳ thi" - -#: templates/contest/contest.html:68 -msgid "Join contest" -msgstr "Tham gia kỳ thi" - -#: templates/contest/contest.html:77 -msgid "Login to participate" -msgstr "Đăng nhập để tham gia" - -#: templates/contest/contest.html:102 -msgid "Clone" -msgstr "Nhân bản" - -#: templates/contest/contest.html:123 +#: templates/contest/contest.html:85 msgid "AC Rate" msgstr "Tỷ lệ AC" -#: templates/contest/contest.html:124 templates/problem/list.html:24 +#: templates/contest/contest.html:86 templates/contest/list.html:237 +#: templates/contest/list.html:291 templates/contest/list.html:371 +#: templates/problem/list.html:223 templates/problem/list.html:254 msgid "Users" -msgstr "Người nộp" +msgstr "Số lượng" -#: templates/contest/contest.html:149 templates/problem/list.html:58 -#: templates/problem/list.html:133 +#: templates/contest/contest.html:111 templates/problem/list.html:330 msgid "Editorial" msgstr "Hướng dẫn" -#: templates/contest/contests_summary.html:36 -msgid "Rank" -msgstr "Rank" - -#: templates/contest/contests_summary.html:37 templates/course/course.html:64 -#: templates/status/language-list.html:34 -#: templates/user/import/table_csv.html:6 -msgid "Name" -msgstr "Tên" - -#: templates/contest/list.html:58 templates/contest/media-js.html:116 +#: templates/contest/list.html:83 templates/contest/media-js.html:9 msgid "Are you sure you want to join?" msgstr "Bạn có chắc tham gia?" -#: templates/contest/list.html:59 +#: templates/contest/list.html:84 msgid "" "Joining a contest for the first time starts your timer, after which it " "becomes unstoppable." @@ -4417,120 +3691,92 @@ msgstr "" "Tham gia kỳ thi lần đầu sẽ kích hoạt thời gian đếm ngược, không thể dừng lại " "sau đó." -#: templates/contest/list.html:61 templates/contest/media-js.html:119 -msgid "By joining in this contest, you will automatically leave contest" -msgstr "Khi tham gia kỳ thi này, bạn sẽ tự động rời khỏi kỳ thi" +#: templates/contest/list.html:92 templates/contest/list.html:280 +msgid "Show" +msgstr "Hiển thị" -#: templates/contest/list.html:122 +#: templates/contest/list.html:102 templates/problem/list.html:68 +msgid "Organizations..." +msgstr "Tổ chức..." + +#: templates/contest/list.html:135 +msgid "hidden" +msgstr "ẩn" + +#: templates/contest/list.html:140 +msgid "private" +msgstr "riêng tư" + +#: templates/contest/list.html:154 +msgid "rated" +msgstr "rated" + +#: templates/contest/list.html:180 +#, python-format +msgid "%(time_limit)s window" +msgstr "Cửa sổ thi dài %(time_limit)s" + +#: templates/contest/list.html:182 +#, python-format +msgid "%(duration)s long" +msgstr "Kéo dài %(duration)s" + +#: templates/contest/list.html:202 msgid "Spectate" msgstr "Theo dõi" -#: templates/contest/list.html:128 templates/organization/home.html:26 -#: templates/organization/list.html:70 +#: templates/contest/list.html:208 msgid "Join" msgstr "Tham gia" -#: templates/contest/list.html:138 -msgid "Active" -msgstr "Đang tham gia" +#: templates/contest/list.html:220 +msgid "Search contests..." +msgstr "Tìm kiếm kỳ thi..." -#: templates/contest/list.html:146 -msgid "Ongoing" -msgstr "Đang diễn ra" +#: templates/contest/list.html:229 +msgid "Search" +msgstr "Tìm kiếm" -#: templates/contest/list.html:153 -msgid "Upcoming" -msgstr "Sắp diễn ra" +#: templates/contest/list.html:232 +msgid "Active Contests" +msgstr "Kỳ thi bạn đang tham gia" -#: templates/contest/list.html:160 -msgid "Past" -msgstr "Đã diễn ra" +#: templates/contest/list.html:236 templates/contest/list.html:290 +#: templates/contest/list.html:329 templates/contest/list.html:368 +msgid "Contest" +msgstr "Kỳ thi" -#: templates/contest/list.html:181 +#: templates/contest/list.html:254 #, python-format msgid "Window ends in %(countdown)s" msgstr "Cửa số thi còn %(countdown)s" -#: templates/contest/list.html:184 templates/contest/list.html:222 -#: templates/course/course.html:91 +#: templates/contest/list.html:257 templates/contest/list.html:306 #, python-format msgid "Ends in %(countdown)s" msgstr "Kết thúc trong %(countdown)s" -#: templates/contest/list.html:204 -msgid "There is no active contest at this time." -msgstr "Không có kỳ thi nào đang tham gia." +#: templates/contest/list.html:277 +msgid "Ongoing Contests" +msgstr "Kỳ thi đang diễn ra" -#: templates/contest/list.html:241 -msgid "There is no ongoing contest at this time." -msgstr "Không có kỳ thi nào đang diễn ra hiện tại." +#: templates/contest/list.html:324 +msgid "Upcoming Contests" +msgstr "Kỳ thi sắp tới" -#: templates/contest/list.html:275 -msgid "There is no scheduled contest at this time." +#: templates/contest/list.html:352 +msgid "There are no scheduled contests at this time." msgstr "Không có kỳ thi nào được lên lịch hiện tại." -#: templates/contest/list.html:307 -msgid "There is no past contest." -msgstr "Không có kỳ thi nào trong quá khứ." +#: templates/contest/list.html:358 +msgid "Past Contests" +msgstr "Kỳ thi trong quá khứ" -#: templates/contest/macros.html:8 -msgid "hidden" -msgstr "ẩn" - -#: templates/contest/macros.html:20 -msgid "private" -msgstr "riêng tư" - -#: templates/contest/macros.html:32 -msgid "rated" -msgstr "rated" - -#: templates/contest/macros.html:51 templates/contest/macros.html:58 -#: templates/course/contest_list.html:22 templates/course/course.html:65 -msgid "Start" -msgstr "Bắt đầu" - -#: templates/contest/macros.html:54 templates/course/contest_list.html:23 -#: templates/course/course.html:66 -msgid "End" -msgstr "Kết thúc" - -#: templates/contest/macros.html:62 templates/course/course.html:67 -msgid "Length" -msgstr "Dài" - -#: templates/contest/macros.html:64 templates/course/course.html:97 -#, python-format -msgid "%(time_limit)s" -msgstr "%(time_limit)s" - -#: templates/contest/macros.html:66 templates/course/course.html:99 -#, python-format -msgid "%(duration)s" -msgstr "%(duration)s" - -#: templates/contest/macros.html:84 -#: templates/contest/official-search-form.html:19 -#: templates/problem/list.html:40 templates/problem/search-form.html:72 -#: templates/user/user-problems.html:57 -msgid "Category" -msgstr "Loại" - -#: templates/contest/macros.html:85 -#: templates/contest/official-search-form.html:30 -msgid "Location" -msgstr "Địa điểm" - -#: templates/contest/macros.html:86 -#: templates/contest/official-search-form.html:9 -msgid "Year" -msgstr "Năm" - -#: templates/contest/media-js.html:111 +#: templates/contest/media-js.html:4 msgid "Are you sure you want to leave?" msgstr "Bạn có chắc muốn rời?" -#: templates/contest/media-js.html:112 +#: templates/contest/media-js.html:5 msgid "" "You cannot come back to a virtual participation. You will have to start a " "new one." @@ -4538,63 +3784,31 @@ msgstr "" "Bạn không thể quay lại lần tham gia ảo này. Bạn sẽ phải tham gia ảo lại từ " "đầu." -#: templates/contest/media-js.html:117 +#: templates/contest/media-js.html:10 msgid "" "Joining a contest starts your timer, after which it becomes unstoppable." msgstr "Tham gia kỳ thi sẽ khởi động đồng hồ đếm ngược, và không thể dừng lại." -#: templates/contest/moss.html:25 +#: templates/contest/moss.html:28 msgid "Are you sure you want MOSS the contest?" msgstr "Bạn có chắc muốn MOSS kỳ thi này?" -#: templates/contest/moss.html:30 +#: templates/contest/moss.html:33 msgid "Are you sure you want to delete the MOSS results?" msgstr "Bạn có chắc muốn xóa kết quả MOSS?" -#: templates/contest/moss.html:58 +#: templates/contest/moss.html:60 msgid "No submissions" msgstr "Không có bài nộp" -#: templates/contest/moss.html:72 +#: templates/contest/moss.html:74 msgid "Re-MOSS contest" msgstr "MOSS lại kỳ thi" -#: templates/contest/moss.html:80 +#: templates/contest/moss.html:82 msgid "Delete MOSS results" msgstr "Xóa kết quả MOSS" -#: templates/contest/official-search-form.html:2 -#: templates/contest/search-form.html:2 -msgid "Contest search" -msgstr "Tìm kiếm kỳ thi" - -#: templates/contest/official-search-form.html:6 -#: templates/contest/search-form.html:6 -msgid "Search contests..." -msgstr "Tìm kiếm kỳ thi..." - -#: templates/contest/official-search-form.html:12 -msgid "From" -msgstr "Từ" - -#: templates/contest/official-search-form.html:14 -msgid "To" -msgstr "Đến" - -#: templates/contest/official-search-form.html:41 -#: templates/contest/search-form.html:22 -#: templates/organization/search-form.html:8 -msgid "Order by" -msgstr "Sắp xếp theo" - -#: templates/contest/official-search-form.html:53 -#: templates/contest/search-form.html:33 -#: templates/organization/search-form.html:20 -#: templates/problem/search-form.html:95 templates/submission/list.html:354 -#: templates/ticket/list.html:250 -msgid "Go" -msgstr "Lọc" - #: templates/contest/private.html:5 msgid "This contest is private to specific users." msgstr "Kỳ thi riêng tư với các thành viên này." @@ -4607,429 +3821,316 @@ msgstr "Thêm vào đó, chỉ những tổ chức này mới được tham gia msgid "Only the following organizations may access this contest:" msgstr "Chỉ những tổ chức sau được tham gia kỳ thi:" -#: templates/contest/ranking-table.html:43 +#: templates/contest/ranking-table.html:9 templates/problem/search-form.html:35 +msgid "Organization" +msgstr "Tổ chức" + +#: templates/contest/ranking-table.html:10 +msgid "Full Name" +msgstr "Họ tên" + +#: templates/contest/ranking-table.html:44 msgid "Un-Disqualify" msgstr "Khôi phục kết quả" -#: templates/contest/ranking-table.html:46 +#: templates/contest/ranking-table.html:47 msgid "Disqualify" msgstr "Hủy kết quả" -#: templates/contest/ranking-table.html:59 -msgid "Fullname" -msgstr "Tên đầy đủ" - -#: templates/contest/ranking-table.html:60 templates/user/edit-profile.html:99 -#: templates/user/import/table_csv.html:7 -msgid "School" -msgstr "Trường" - -#: templates/contest/ranking.html:18 +#: templates/contest/ranking.html:187 msgid "Are you sure you want to disqualify this participation?" msgstr "Bạn có chắc muốn hủy kết quả này?" -#: templates/contest/ranking.html:23 +#: templates/contest/ranking.html:192 msgid "Are you sure you want to un-disqualify this participation?" msgstr "Bạn có chắc muốn khôi phục kết quả này?" -#: templates/contest/ranking.html:134 +#: templates/contest/ranking.html:415 msgid "View user participation" msgstr "Xem các lần tham gia" -#: templates/contest/ranking.html:140 -msgid "Show schools" -msgstr "Hiển thị trường" +#: templates/contest/ranking.html:419 +msgid "Show organizations" +msgstr "Hiển thị tổ chức" -#: templates/contest/ranking.html:144 +#: templates/contest/ranking.html:423 msgid "Show full name" msgstr "Hiển thị họ tên" -#: templates/contest/ranking.html:147 +#: templates/contest/ranking.html:426 msgid "Show friends only" msgstr "Chỉ hiển thị bạn bè" -#: templates/contest/ranking.html:150 +#: templates/contest/ranking.html:429 msgid "Total score only" msgstr "Chỉ hiển thị tổng điểm" -#: templates/contest/ranking.html:152 +#: templates/contest/ranking.html:431 msgid "Show virtual participation" msgstr "Hiển thị tham gia ảo" -#: templates/contest/ranking.html:156 -msgid "Download as CSV" -msgstr "Tải file CSV" - -#: templates/contest/search-form.html:10 -msgid "Hide organization contests" -msgstr "Ẩn các kỳ thi riêng tư của nhóm" - -#: templates/contest/stats.html:48 +#: templates/contest/stats.html:51 msgid "Problem Status Distribution" msgstr "Phân bố theo kết quả" -#: templates/contest/stats.html:53 +#: templates/contest/stats.html:56 msgid "Problem AC Rate" msgstr "Tỷ lệ AC" -#: templates/contest/stats.html:59 +#: templates/contest/stats.html:62 msgid "Problem Point Distribution" msgstr "Phân bố điểm" -#: templates/contest/stats.html:73 templates/stats/language.html:16 +#: templates/contest/stats.html:76 templates/stats/language.html:16 msgid "Submissions by Language" msgstr "Số bài nộp theo ngôn ngữ" -#: templates/contest/stats.html:79 templates/stats/language.html:26 +#: templates/contest/stats.html:82 templates/stats/language.html:26 msgid "Language AC Rate" msgstr "Tỷ lệ AC theo ngôn ngữ" -#: templates/contests-countdown.html:3 -msgid "Ongoing contests" -msgstr "Kỳ thi đang diễn ra" +#: templates/fine_uploader/script.html:4 +msgid "Drop files here to upload" +msgstr "" -#: templates/contests-countdown.html:11 -msgid "Ends in" -msgstr "Còn" - -#: templates/contests-countdown.html:21 -msgid "Upcoming contests" -msgstr "Kỳ thi sắp diễn ra" - -#: templates/course/contest_list.html:29 -msgid "No contests available" -msgstr "Không có kì thi nào!" - -#: templates/course/course.html:33 -msgid "Lessons" -msgstr "Bài học" - -#: templates/course/course.html:68 templates/course/grades.html:81 -#: templates/course/grades_lesson.html:82 templates/user/user-problems.html:99 -msgid "Score" -msgstr "Điểm" - -#: templates/course/course.html:119 -msgid "Total achieved points" -msgstr "Tổng điểm" - -#: templates/course/edit_contest.html:44 templates/course/edit_lesson.html:65 -#: templates/organization/contest/edit.html:41 -#: templates/organization/form.html:6 -msgid "Please fix below errors" -msgstr "Vui lòng sửa các lỗi bên dưới" - -#: templates/course/edit_contest.html:64 -#: templates/organization/contest/edit.html:61 -msgid "If you run out of rows, click Save" -msgstr "Ấn nút lưu lại nếu cần thêm hàng" - -#: templates/course/edit_contest.html:90 templates/course/edit_lesson.html:114 -#: templates/markdown_editor/markdown_editor.html:122 -#: templates/organization/blog/edit.html:36 -#: templates/organization/contest/edit.html:87 -#: templates/organization/form.html:30 templates/pagedown.html:31 -msgid "Save" -msgstr "Lưu" - -#: templates/course/edit_lesson.html:57 -msgid "Add new" -msgstr "Thêm mới" - -#: templates/course/grades.html:78 templates/course/grades_lesson.html:79 -msgid "Sort by" -msgstr "Sắp xếp theo" - -#: templates/course/grades.html:83 templates/course/grades_lesson.html:84 -#: templates/internal/problem/problem.html:34 -msgid "Search" -msgstr "Tìm kiếm" - -#: templates/course/grades.html:89 templates/course/grades_lesson.html:99 -#: templates/submission/user-ajax.html:33 -msgid "Total" -msgstr "Tổng điểm" - -#: templates/course/left_sidebar.html:4 -msgid "Edit lessons" -msgstr "Chỉnh sửa bài học" - -#: templates/course/left_sidebar.html:6 -msgid "Grades" -msgstr "Điểm" - -#: templates/course/list.html:23 -msgid "Teachers" -msgstr "Giáo viên" - -#: templates/custom_file_upload.html:6 -msgid "Your file has been uploaded successfully" -msgstr "File đã được tải lên thành công" - -#: templates/custom_file_upload.html:7 -msgid "Upload another file" -msgstr "Tải file khác lên" - -#: templates/custom_file_upload.html:12 templates/fine_uploader/script.html:7 +#: templates/fine_uploader/script.html:7 msgid "Upload file" msgstr "Tải file lên" -#: templates/email_change/email_change.html:15 -msgid "Verify Email" -msgstr "Xác thực Email" - -#: templates/email_change/email_change_failure.html:3 -msgid "Invalid reset link." -msgstr "Đường dẫn không hợp lệ" - -#: templates/email_change/email_change_pending.html:4 -msgid "An email was sent to" -msgstr "Email đã được gửi đến" - -#: templates/email_change/email_change_pending.html:4 -msgid "If you don't see it, kindly check your spam folder as well." -msgstr "Nếu bạn không tìm thấy nó, vui lòng kiểm tra thư mục spam của bạn." - -#: templates/email_change/email_change_success.html:3 -msgid "Your email was sucessfully changed to" -msgstr "Bạn đã đổi email thành công." - -#: templates/feed/has_next.html:3 -msgid "View more" -msgstr "Xem thêm" - -#: templates/fine_uploader/script.html:4 -msgid "Drop files here to upload" -msgstr "Kéo file vào đây để tải lên" - #: templates/fine_uploader/script.html:23 -#: templates/markdown_editor/markdown_editor.html:123 -#: templates/pagedown.html:32 msgid "Cancel" -msgstr "Hủy" +msgstr "" #: templates/fine_uploader/script.html:24 msgid "Retry" -msgstr "Thử lại" +msgstr "" #: templates/fine_uploader/script.html:26 msgid "Pause" -msgstr "Dừng" +msgstr "" #: templates/fine_uploader/script.html:27 msgid "Continue" msgstr "Tiếp tục" -#: templates/general_email.html:15 -msgid "Dear" -msgstr "Xin chào" - -#: templates/internal/left-sidebar.html:3 -msgid "Average speed" -msgstr "Tốc độ trung bình" - -#: templates/internal/left-sidebar.html:4 -msgid "Slow requests" -msgstr "Requests chậm" - -#: templates/internal/problem/problem.html:42 -msgid "Code" -msgstr "" - -#: templates/internal/problem/problem.html:43 -#, fuzzy -#| msgid "Total points" -msgid "Vote count" -msgstr "Tổng điểm" - -#: templates/internal/problem/votes.html:1 -#, fuzzy -#| msgid "contest problem" -msgid "Votes for problem" -msgstr "bài trong kỳ thi" - -#: templates/internal/problem/votes.html:9 -msgid "Knowledge" -msgstr "" - -#: templates/internal/problem/votes.html:13 -#, fuzzy -#| msgid "Rankings" -msgid "Thinking" -msgstr "Bảng xếp hạng" - -#: templates/internal/problem/votes.html:21 -#: templates/problem/feed/items.html:97 -#, fuzzy -#| msgid "Feed" -msgid "Feedback" -msgstr "Gợi ý" - #: templates/license.html:12 msgid "Source:" msgstr "Nguồn:" -#: templates/markdown_editor/markdown_editor.html:103 templates/pagedown.html:9 -msgid "Update Preview" -msgstr "Cập nhật xem trước" +#: templates/newsletter/common.html:6 +#: templates/newsletter/newsletter_list.html:15 +#: templates/newsletter/subscription_unsubscribe_activated.html:3 +#: templates/newsletter/subscription_unsubscribe_activated.html:6 +#: templates/newsletter/subscription_update_activated.html:3 +#: templates/newsletter/subscription_update_activated.html:6 +msgid "Newsletter" +msgstr "" -#: templates/markdown_editor/markdown_editor.html:107 -#: templates/pagedown.html:15 -msgid "Insert Image" -msgstr "Chèn hình ảnh" +#: templates/newsletter/newsletter_list.html:2 +#: templates/newsletter/newsletter_list.html:3 +msgid "Newsletter list" +msgstr "" -#: templates/markdown_editor/markdown_editor.html:110 -#: templates/pagedown.html:18 -msgid "From the web" -msgstr "Từ web" +#: templates/newsletter/newsletter_list.html:6 +msgid "Subscribe to get the latest emails about upcoming contests and events." +msgstr "" -#: templates/markdown_editor/markdown_editor.html:116 -#: templates/pagedown.html:25 -msgid "From your computer" -msgstr "Từ máy tính của bạn" +#: templates/newsletter/newsletter_list.html:16 +msgid "Subscribe" +msgstr "" -#: templates/notification/list.html:5 +#: templates/newsletter/newsletter_list.html:30 +msgid "Update subscriptions" +msgstr "" + +#: templates/newsletter/subscription_unsubscribe_activated.html:3 +#: templates/newsletter/subscription_unsubscribe_activated.html:6 +#: templates/newsletter/subscription_update_activated.html:3 +#: templates/newsletter/subscription_update_activated.html:6 +msgid "activate" +msgstr "" + +#: templates/newsletter/subscription_unsubscribe_activated.html:8 +msgid "You have successfully been unsubscribed." +msgstr "" + +#: templates/newsletter/subscription_unsubscribe_email_sent.html:3 +#: templates/newsletter/subscription_unsubscribe_email_sent.html:6 +#: templates/newsletter/subscription_unsubscribe_user.html:3 +#: templates/newsletter/subscription_unsubscribe_user.html:6 +msgid "Newsletter unsubscribe" +msgstr "" + +#: templates/newsletter/subscription_unsubscribe_email_sent.html:8 +msgid "" +"Your unsubscription request has successfully been received. An email has " +"been sent to you with a link you need to follow in order to confirm your " +"unsubscription." +msgstr "" + +#: templates/newsletter/subscription_unsubscribe_user.html:17 +msgid "Do you want to unsubscribe from this newsletter?" +msgstr "" + +#: templates/newsletter/subscription_unsubscribe_user.html:21 +msgid "Unsubscribe" +msgstr "" + +#: templates/newsletter/subscription_update.html:3 +#: templates/newsletter/subscription_update.html:6 +#: templates/newsletter/subscription_update_email_sent.html:3 +#: templates/newsletter/subscription_update_email_sent.html:6 +msgid "Newsletter update" +msgstr "" + +#: templates/newsletter/subscription_update.html:9 +msgid "" +"Due to a technical error we were not able to submit your confirmation email. " +"This could be because your email address is invalid." +msgstr "" + +#: templates/newsletter/subscription_update.html:14 +msgid "Update subscription" +msgstr "" + +#: templates/newsletter/subscription_update_activated.html:8 +msgid "Your subscription has successfully been updated." +msgstr "" + +#: templates/newsletter/subscription_update_email_sent.html:8 +msgid "" +"Your update request was successfully received and an activation email has " +"been sent to you. In that email you will find a link which you need to " +"follow in order to update your subscription." +msgstr "" + +#: templates/notification/list.html:7 msgid "You have no notifications" msgstr "Bạn không có thông báo" -#: templates/notification/list.html:10 +#: templates/notification/list.html:13 msgid "Activity" msgstr "Hoạt động" -#: templates/organization/blog/pending.html:50 -msgid "Approve" -msgstr "Chấp thuận" +#: templates/organization/edit.html:46 +#: templates/organization/requests/pending.html:34 +#: templates/ticket/edit-notes.html:4 +msgid "Update" +msgstr "Cập nhật" -#: templates/organization/blog/pending.html:62 -msgid "Reject" -msgstr "Từ chối" - -#: templates/organization/home-js.html:4 +#: templates/organization/home.html:32 msgid "Are you sure you want to leave this organization?" msgstr "Bạn có chắc muốn rời tổ chức?" -#: templates/organization/home-js.html:6 +#: templates/organization/home.html:34 msgid "You will have to rejoin to show up on the organization leaderboard." msgstr "Bạn phải tham gia lại để được hiển thị trong bảng xếp hạng tổ chức." -#: templates/organization/home-js.html:8 +#: templates/organization/home.html:36 msgid "You will have to request membership in order to join again." msgstr "Bạn phải đăng ký thành viên để được tham gia lại." -#: templates/organization/home.html:31 templates/organization/list.html:73 +#: templates/organization/home.html:81 +msgid "Join organization" +msgstr "Tham gia tổ chức" + +#: templates/organization/home.html:85 msgid "Request membership" msgstr "Đăng ký thành viên" -#: templates/organization/list.html:38 templates/submission/list.html:382 -#: templates/submission/submission-list-tabs.html:6 -msgid "Mine" -msgstr "Tôi" +#: templates/organization/home.html:115 +msgid "Organization news" +msgstr "Tin tức tổ chức" -#: templates/organization/list.html:40 -msgid "Public" -msgstr "Nhóm mở" +#: templates/organization/home.html:121 +msgid "There is no news at this time." +msgstr "Không có tin tức." -#: templates/organization/list.html:41 -msgid "Private" -msgstr "Nhóm kín" - -#: templates/organization/list.html:63 -msgid "members" -msgstr "thành viên" - -#: templates/organization/list.html:66 -msgid "View" -msgstr "Xem" - -#: templates/organization/list.html:89 -msgid "You have not joined any organization yet." -msgstr "Bạn chưa tham gia nhóm nào." - -#: templates/organization/list.html:97 -msgid "There is no public organization." -msgstr "Không có nhóm mở nào" - -#: templates/organization/list.html:105 -msgid "There is no private organization." -msgstr "Không có nhóm kín nào" - -#: templates/organization/org-left-sidebar.html:9 -msgid "Members" -msgstr "Thành viên" - -#: templates/organization/org-right-sidebar.html:4 +#: templates/organization/home.html:130 msgid "Controls" msgstr "Quản lý" -#: templates/organization/org-right-sidebar.html:9 -msgid "Edit group" -msgstr "Chỉnh sửa nhóm" +#: templates/organization/home.html:135 +msgid "Edit organization" +msgstr "Chỉnh sửa tổ chức" -#: templates/organization/org-right-sidebar.html:15 +#: templates/organization/home.html:141 msgid "View requests" -msgstr "Đơn đăng ký" +msgstr "Xem các đơn đăng ký" -#: templates/organization/org-right-sidebar.html:26 -msgid "Add members" -msgstr "Thêm thành viên" +#: templates/organization/home.html:154 +msgid "Admin organization" +msgstr "Trang admin tổ chức" -#: templates/organization/org-right-sidebar.html:32 -msgid "Add blog" -msgstr "Thêm bài đăng" +#: templates/organization/home.html:160 +msgid "View members" +msgstr "Xem thành viên" -#: templates/organization/org-right-sidebar.html:37 -msgid "Pending blogs" -msgstr "Bài đăng đang chờ" +#: templates/organization/home.html:167 +msgid "Leave organization" +msgstr "Rời tổ chức" -#: templates/organization/org-right-sidebar.html:53 -msgid "Subdomain" -msgstr "Site riêng cho nhóm" +#: templates/organization/home.html:176 +msgid "New private contests" +msgstr "Kỳ thi riêng tư mới" -#: templates/organization/org-right-sidebar.html:62 -msgid "Leave group" -msgstr "Rời nhóm" +#: templates/organization/home.html:186 templates/organization/home.html:201 +msgid "View all" +msgstr "Tất cả" -#: templates/organization/requests/detail.html:6 +#: templates/organization/home.html:192 +msgid "New private problems" +msgstr "Bài tập riêng tư mới" + +#: templates/organization/list.html:40 +msgid "Show my organizations only" +msgstr "Chỉ hiển thị tổ chức của tôi" + +#: templates/organization/list.html:47 templates/status/language-list.html:34 +#: templates/user/import/table_csv.html:6 +msgid "Name" +msgstr "Tên" + +#: templates/organization/list.html:48 +msgid "Members" +msgstr "Thành viên" + +#: templates/organization/requests/detail.html:13 msgid "User:" msgstr "Thành viên:" -#: templates/organization/requests/detail.html:10 +#: templates/organization/requests/detail.html:17 msgid "Organization:" msgstr "Tổ chức:" -#: templates/organization/requests/detail.html:18 +#: templates/organization/requests/detail.html:25 msgid "Time:" msgstr "Thời gian:" -#: templates/organization/requests/detail.html:22 +#: templates/organization/requests/detail.html:29 msgid "Reason:" msgstr "Lý do:" #: templates/organization/requests/log.html:11 -#: templates/organization/requests/pending.html:21 +#: templates/organization/requests/pending.html:14 msgid "State" msgstr "Trạng thái" #: templates/organization/requests/log.html:12 -#: templates/organization/requests/pending.html:22 +#: templates/organization/requests/pending.html:15 msgid "Reason" msgstr "Lý do" #: templates/organization/requests/log.html:28 -#: templates/organization/requests/pending.html:44 +#: templates/organization/requests/pending.html:37 msgid "There are no requests to approve." msgstr "Không có đơn đăng ký." -#: templates/organization/requests/pending.html:24 -#: templates/problem/data.html:538 +#: templates/organization/requests/pending.html:17 +#: templates/problem/data.html:472 msgid "Delete?" msgstr "Xóa?" -#: templates/organization/requests/pending.html:41 -#: templates/ticket/edit-notes.html:4 -msgid "Update" -msgstr "Cập nhật" - #: templates/organization/requests/request.html:18 msgid "Your reason for joining:" msgstr "Lý do tham gia:" @@ -5054,15 +4155,7 @@ msgstr "Chấp thuận" msgid "Rejected" msgstr "Từ chối" -#: templates/organization/search-form.html:2 -msgid "Organization search" -msgstr "Tìm kiếm nhóm" - -#: templates/organization/search-form.html:6 -msgid "Search organizations..." -msgstr "Tìm kiếm nhóm" - -#: templates/organization/users-table.html:16 +#: templates/organization/users-table.html:15 msgid "Kick" msgstr "Đuổi" @@ -5070,64 +4163,39 @@ msgstr "Đuổi" msgid "Enter a new code for the cloned problem:" msgstr "Nhập mã bài mới cho bài tập được nhân bản:" -#: templates/problem/data.html:156 templates/problem/data.html:163 +#: templates/problem/data.html:144 msgid "Instruction" msgstr "Hướng dẫn" -#: templates/problem/data.html:487 +#: templates/problem/data.html:430 msgid "View YAML" msgstr "Xem YAML" -#: templates/problem/data.html:504 -msgid "Autofill testcases" -msgstr "Tự động điền test" - -#: templates/problem/data.html:508 templates/problem/problem.html:307 -msgid "Problem type" -msgid_plural "Problem types" -msgstr[0] "Dạng bài" - -#: templates/problem/data.html:515 -msgid "Fill testcases" -msgstr "Điền test" - -#: templates/problem/data.html:519 -msgid "Batch start positions" -msgstr "Vị trí bắt đầu nhóm" - -#: templates/problem/data.html:523 -msgid "" -"Leave empty if not use batch. If you want to divide to three batches [1, 4], " -"[5, 8], [9, 10], enter: 1, 5, 9" -msgstr "" -"Để trống nếu không dùng nhóm. Nếu muốn chia test thành các nhóm [1, 4], [5, " -"8], [9, 10], nhập: 1, 5, 9" - -#: templates/problem/data.html:527 templates/problem/data.html:578 +#: templates/problem/data.html:461 templates/problem/data.html:512 msgid "Apply!" msgstr "Lưu!" -#: templates/problem/data.html:532 +#: templates/problem/data.html:466 msgid "Type" msgstr "Kiểu" -#: templates/problem/data.html:533 +#: templates/problem/data.html:467 msgid "Input file" msgstr "File Input" -#: templates/problem/data.html:534 +#: templates/problem/data.html:468 msgid "Output file" msgstr "File Output" -#: templates/problem/data.html:536 +#: templates/problem/data.html:470 msgid "Pretest?" msgstr "Pretest?" -#: templates/problem/data.html:579 +#: templates/problem/data.html:513 msgid "Add new case" msgstr "Thêm test mới" -#: templates/problem/editorial.html:23 +#: templates/problem/editorial.html:22 msgid "" "Remember to use this editorial only when stuck, and not to copy-" "paste code from it. Please be respectful to the problem author and " @@ -5138,355 +4206,287 @@ msgstr "" "viết hướng dẫn này.

Chép code từ bài hướng dẫn để nộp bài là " "hành vi có thể dẫn đến khóa tài khoản." -#: templates/problem/feed.html:13 -msgid "FOR YOU" -msgstr "DÀNH CHO BẠN" - -#: templates/problem/feed.html:16 -msgid "NEW" -msgstr "MỚI NHẤT" - -#: templates/problem/feed.html:20 -msgid "VOLUNTEER" -msgstr "TÌNH NGUYỆN" - -#: templates/problem/feed.html:27 -msgid "View your votes" -msgstr "Xem các đơn đã điền của bạn" - -#: templates/problem/feed/items.html:43 -msgid "View source" -msgstr "Xem mã nguồn" - -#: templates/problem/feed/items.html:47 -msgid "Volunteer form" -msgstr "Phiếu tình nguyện" - -#: templates/problem/feed/items.html:53 templates/problem/problem.html:148 -#: templates/problem/problem.html:163 templates/problem/problem.html:173 -msgid "Submit" -msgstr "Nộp bài" - -#: templates/problem/feed/items.html:60 -msgid "Value" -msgstr "Giá trị" - -#: templates/problem/feed/items.html:67 -msgid "Knowledge point" -msgstr "Độ khó kiến thức" - -#: templates/problem/feed/items.html:75 -msgid "Thinking point" -msgstr "Độ khó nghĩ" - -#: templates/problem/feed/items.html:83 templates/problem/search-form.html:61 -msgid "Problem types" -msgstr "Dạng bài" - -#: templates/problem/feed/items.html:101 -msgid "Any additional note here" -msgstr "Lưu ý thêm cho admin" - -#: templates/problem/left-sidebar.html:10 -msgid "Feed" -msgstr "Gợi ý" - -#: templates/problem/left-sidebar.html:11 -#: templates/problem/problem-list-tabs.html:5 -msgid "List" -msgstr "Danh sách" - -#: templates/problem/list-base.html:89 +#: templates/problem/list.html:66 msgid "Filter by type..." msgstr "Lọc theo dạng..." -#: templates/problem/list-base.html:157 templates/problem/list-base.html:182 -msgid "Add types..." -msgstr "Thêm dạng" +#: templates/problem/list.html:193 +msgid "Hot problems" +msgstr "Bài tập mới" -#: templates/problem/list-base.html:198 -msgid "Fail to vote!" -msgstr "Hệ thống lỗi!" +#: templates/problem/list.html:218 templates/problem/list.html:240 +#: templates/problem/search-form.html:45 templates/user/user-problems.html:57 +msgid "Category" +msgstr "Nhóm" -#: templates/problem/list-base.html:201 -msgid "Successful vote! Thank you!" -msgstr "Đã gửi thành công! Cảm ơn bạn!" +#: templates/problem/list.html:220 templates/problem/list.html:244 +msgid "Types" +msgstr "Dạng" -#: templates/problem/list.html:51 +#: templates/problem/list.html:251 #, python-format msgid "AC %%" msgstr "AC %%" -#: templates/problem/list.html:54 -msgid "AC #" -msgstr "" - -#: templates/problem/list.html:145 +#: templates/problem/list.html:342 msgid "Add clarifications" msgstr "Thêm thông báo" -#: templates/problem/manage_submission.html:54 +#: templates/problem/manage_submission.html:55 msgid "Leave empty to not filter by language" msgstr "Để trống nếu không lọc theo ngôn ngữ" -#: templates/problem/manage_submission.html:59 +#: templates/problem/manage_submission.html:60 msgid "Leave empty to not filter by result" msgstr "Để trống nếu không lọc theo kết quả" -#: templates/problem/manage_submission.html:64 -msgid "Leave empty to not filter by contest" -msgstr "Để trống nếu không lọc theo kỳ thi" - -#: templates/problem/manage_submission.html:94 +#: templates/problem/manage_submission.html:80 msgid "Need valid values for both start and end IDs." msgstr "Cần số liệu hợp lệ cho ID bắt đầu và kết thúc." -#: templates/problem/manage_submission.html:97 +#: templates/problem/manage_submission.html:83 msgid "End ID must be after start ID." msgstr "ID kết thúc phải lớn hơn hoặc bằng ID khởi đầu." -#: templates/problem/manage_submission.html:110 +#: templates/problem/manage_submission.html:96 #, python-brace-format msgid "" "You are about to {action} {count} submissions. Are you sure you want to do " "this?" msgstr "Bạn chuẩn bị {action} {count} bài nộp. Tiếp tục?" -#: templates/problem/manage_submission.html:117 +#: templates/problem/manage_submission.html:103 #, python-brace-format msgid "" "You are about to {action} a few submissions. Are you sure you want to do " "this?" msgstr "Bạn chuẩn bị {action} vài bài nộp. Tiếp tục?" -#: templates/problem/manage_submission.html:141 -#: templates/submission/list.html:332 +#: templates/problem/manage_submission.html:127 +#: templates/submission/list.html:309 msgid "Filter submissions" msgstr "Lọc bài nộp" -#: templates/problem/manage_submission.html:146 +#: templates/problem/manage_submission.html:132 msgid "Filter by ID:" msgstr "Lọc theo ID:" -#: templates/problem/manage_submission.html:149 +#: templates/problem/manage_submission.html:135 msgid "Starting ID:" msgstr "ID bắt đầu:" -#: templates/problem/manage_submission.html:153 +#: templates/problem/manage_submission.html:139 msgid "Ending ID:" msgstr "ID kết thúc:" -#: templates/problem/manage_submission.html:157 +#: templates/problem/manage_submission.html:143 msgid "This range includes both endpoints." msgstr "Bao gồm hai đầu mút." -#: templates/problem/manage_submission.html:160 +#: templates/problem/manage_submission.html:146 msgid "Filter by language:" msgstr "Lọc theo ngôn ngữ:" -#: templates/problem/manage_submission.html:168 +#: templates/problem/manage_submission.html:154 msgid "Filter by result:" msgstr "Lọc theo kết quả:" -#: templates/problem/manage_submission.html:176 -msgid "Filter by contest:" -msgstr "Lọc theo kỳ thi:" - -#: templates/problem/manage_submission.html:186 +#: templates/problem/manage_submission.html:164 msgid "Action" msgstr "Hành động" -#: templates/problem/manage_submission.html:188 +#: templates/problem/manage_submission.html:166 msgid "Rejudge selected submissions" msgstr "Chấm lại những bài nộp này" -#: templates/problem/manage_submission.html:193 +#: templates/problem/manage_submission.html:171 msgid "Download selected submissions" msgstr "Tải các bài nộp này" -#: templates/problem/manage_submission.html:199 +#: templates/problem/manage_submission.html:177 #, python-format msgid "Are you sure you want to rescore %(count)d submissions?" msgstr "Bạn có chắc muốn tính điểm lại %(count)d bài nộp?" -#: templates/problem/manage_submission.html:200 +#: templates/problem/manage_submission.html:178 msgid "Rescore all submissions" msgstr "Tính điểm lại các bài nộp" -#: templates/problem/problem.html:137 +#: templates/problem/problem.html:130 msgid "View as PDF" msgstr "Xem PDF" -#: templates/problem/problem.html:155 +#: templates/problem/problem.html:139 templates/problem/problem.html:149 +#: templates/problem/problem.html:154 +msgid "Submit solution" +msgstr "Nộp bài" + +#: templates/problem/problem.html:142 #, python-format msgid "%(counter)s submission left" msgid_plural "%(counter)s submissions left" msgstr[0] "Còn %(counter)s lần nộp" -#: templates/problem/problem.html:168 +#: templates/problem/problem.html:150 msgid "0 submissions left" msgstr "Còn 0 lần nộp" -#: templates/problem/problem.html:186 +#: templates/problem/problem.html:162 msgid "My submissions" msgstr "Bài nộp của tôi" -#: templates/problem/problem.html:197 +#: templates/problem/problem.html:166 msgid "Best submissions" msgstr "Các bài nộp tốt nhất" -#: templates/problem/problem.html:204 +#: templates/problem/problem.html:170 +msgid "Discuss" +msgstr "Thảo luận" + +#: templates/problem/problem.html:174 msgid "Read editorial" msgstr "Xem hướng dẫn" -#: templates/problem/problem.html:213 +#: templates/problem/problem.html:179 msgid "Manage tickets" msgstr "Xử lý báo cáo" -#: templates/problem/problem.html:220 +#: templates/problem/problem.html:183 msgid "Edit problem" msgstr "Chỉnh sửa bài" -#: templates/problem/problem.html:226 +#: templates/problem/problem.html:185 msgid "Edit test data" msgstr "Chỉnh sửa test" -#: templates/problem/problem.html:234 +#: templates/problem/problem.html:190 msgid "My tickets" msgstr "Báo cáo của tôi" -#: templates/problem/problem.html:244 +#: templates/problem/problem.html:198 msgid "Manage submissions" msgstr "Quản lý bài nộp" -#: templates/problem/problem.html:252 +#: templates/problem/problem.html:204 msgid "Clone problem" msgstr "Nhân bản bài" -#: templates/problem/problem.html:262 templates/problem/problem.html:389 +#: templates/problem/problem.html:211 +msgid "Points:" +msgstr "Điểm:" + +#: templates/problem/problem.html:214 templates/problem/problem.html:216 +msgid "(partial)" +msgstr "(thành phần)" + +#: templates/problem/problem.html:221 msgid "Time limit:" msgstr "Thời gian:" -#: templates/problem/problem.html:275 templates/problem/problem.html:394 +#: templates/problem/problem.html:233 msgid "Memory limit:" msgstr "Bộ nhớ:" -#: templates/problem/problem.html:293 +#: templates/problem/problem.html:252 msgid "Author:" msgid_plural "Authors:" msgstr[0] "Tác giả:" -#: templates/problem/problem.html:320 +#: templates/problem/problem.html:267 +msgid "Problem type" +msgid_plural "Problem types" +msgstr[0] "Dạng bài" + +#: templates/problem/problem.html:280 msgid "Allowed languages" msgstr "Ngôn ngữ cho phép" -#: templates/problem/problem.html:328 +#: templates/problem/problem.html:288 #, python-format msgid "No %(lang)s judge online" msgstr "Không có máy chấm cho %(lang)s" -#: templates/problem/problem.html:340 -#: templates/status/judge-status-table.html:2 -msgid "Judge" -msgid_plural "Judges" -msgstr[0] "Máy chấm" +#: templates/problem/problem.html:299 +msgid "Judge:" +msgid_plural "Judges:" +msgstr[0] "Máy chấm:" -#: templates/problem/problem.html:358 +#: templates/problem/problem.html:316 msgid "none available" -msgstr "Bài này chưa có máy chấm" +msgstr "không có sẵn" -#: templates/problem/problem.html:370 +#: templates/problem/problem.html:328 #, python-format msgid "This problem has %(length)s clarification(s)" msgstr "Bài này có %(length)s thông báo" -#: templates/problem/problem.html:378 -msgid "Points:" -msgstr "Điểm:" - -#: templates/problem/problem.html:399 templates/problem/raw.html:67 -#: templates/submission/status-testcases.html:155 -msgid "Input:" -msgstr "Input:" - -#: templates/problem/problem.html:401 templates/problem/raw.html:67 -msgid "stdin" -msgstr "bàn phím" - -#: templates/problem/problem.html:406 templates/problem/raw.html:70 -#: templates/submission/status-testcases.html:159 -msgid "Output:" -msgstr "Output:" - -#: templates/problem/problem.html:407 templates/problem/raw.html:70 -msgid "stdout" -msgstr "màn hình" - -#: templates/problem/problem.html:434 +#: templates/problem/problem.html:353 msgid "Request clarification" msgstr "Yêu cầu làm rõ đề" -#: templates/problem/raw.html:73 +#: templates/problem/problem.html:355 +msgid "Report an issue" +msgstr "Báo cáo một vấn đề" + +#: templates/problem/raw.html:64 msgid "Time Limit:" msgstr "Giới hạn thời gian:" -#: templates/problem/raw.html:82 +#: templates/problem/raw.html:73 msgid "Memory Limit:" msgstr "Giới hạn bộ nhớ:" -#: templates/problem/recent-attempt.html:3 -msgid "Last unsolved" -msgstr "Nộp gần đây" - -#: templates/problem/related_problems.html:3 -msgid "Recommended problems" -msgstr "Bài tập gợi ý" - #: templates/problem/search-form.html:2 msgid "Problem search" msgstr "Tìm kiếm bài tập" -#: templates/problem/search-form.html:7 +#: templates/problem/search-form.html:8 msgid "Search problems..." msgstr "Tìm bài tập..." -#: templates/problem/search-form.html:13 +#: templates/problem/search-form.html:14 +msgid "Full text search" +msgstr "" + +#: templates/problem/search-form.html:21 msgid "Hide solved problems" msgstr "Ẩn các bài đã giải" -#: templates/problem/search-form.html:20 -msgid "Show solved problems" -msgstr "Hiện các bài đã giải" - #: templates/problem/search-form.html:27 msgid "Show problem types" msgstr "Hiển thị dạng bài" -#: templates/problem/search-form.html:34 +#: templates/problem/search-form.html:32 msgid "Show editorial" msgstr "Hiển thị hướng dẫn" -#: templates/problem/search-form.html:50 -msgid "Author" -msgstr "Tác giả" +#: templates/problem/search-form.html:48 templates/problem/search-form.html:50 +#: templates/submission/submission-list-tabs.html:4 +msgid "All" +msgstr "Tất cả" -#: templates/problem/search-form.html:88 +#: templates/problem/search-form.html:62 +msgid "Problem types" +msgstr "Dạng bài" + +#: templates/problem/search-form.html:73 msgid "Point range" msgstr "Mốc điểm" -#: templates/problem/search-form.html:96 +#: templates/problem/search-form.html:79 templates/submission/list.html:331 +#: templates/ticket/list.html:248 +msgid "Go" +msgstr "Lọc" + +#: templates/problem/search-form.html:80 msgid "Random" msgstr "Ngẫu nhiên" -#: templates/problem/submit.html:48 -msgid "Wait" -msgstr "Đợi" - -#: templates/problem/submit.html:148 +#: templates/problem/submit.html:117 msgid "Your source code must contain at most 65536 characters." msgstr "Code phải chứa không quá 65536 ký tự." -#: templates/problem/submit.html:195 +#: templates/problem/submit.html:204 #, python-format msgid "" "Warning! Your default language, %(default_language)s, is " @@ -5495,67 +4495,33 @@ msgstr "" "Cẩn thận! Ngôn ngữ ưa thích của bạn, %(default_language)s, " "không được sử dụng trong bài này." -#: templates/problem/submit.html:206 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| " You have %(left)s submission left\n" -#| " " -#| msgid_plural "" -#| "\n" -#| " You have %(left)s submissions left\n" -#| " " +#: templates/problem/submit.html:215 +#, python-format msgid "" "\n" -" You have %(left)s submission left\n" -" " +" You have %(left)s submission left\n" +" " msgid_plural "" "\n" -" You have %(left)s submissions left\n" -" " +" You have %(left)s submissions left\n" +" " msgstr[0] "" "\n" " Bạn còn %(left)s lần nộp\n" " " -#: templates/problem/submit.html:215 +#: templates/problem/submit.html:224 msgid "You have 0 submissions left" msgstr "Bạn đã hết lần nộp" -#: templates/problem/submit.html:249 +#: templates/problem/submit.html:258 msgid "No judge is available for this problem." msgstr "Không có máy chấm có thể chấm bài này." -#: templates/problem/submit.html:255 +#: templates/problem/submit.html:262 msgid "Submit!" msgstr "Nộp bài!" -#: templates/profile-table.html:20 templates/user/user-about.html:23 -#, python-format -msgid "%(counter)s problem solved" -msgid_plural "%(counter)s problems solved" -msgstr[0] "Đã giải %(counter)s bài" - -#: templates/profile-table.html:30 templates/user/user-about.html:35 -msgid "Total points" -msgstr "Tổng điểm" - -#: templates/profile-table.html:37 templates/user/user-about.html:44 -msgid "Rank by rating" -msgstr "Rank theo rating" - -#: templates/profile-table.html:41 templates/user/user-about.html:50 -msgid "Rank by points" -msgstr "Rank theo điểm" - -#: templates/profile-table.html:47 templates/user/user-about.html:86 -msgid "Awards" -msgstr "Thành tích" - -#: templates/recent-organization.html:3 -msgid "Recent groups" -msgstr "Nhóm gần đây" - #: templates/registration/activate.html:3 #, python-format msgid "%(key)s is an invalid activation key." @@ -5565,56 +4531,20 @@ msgstr "%(key)s không phải mã xác thực hợp lệ." msgid "Your account has been successfully activated." msgstr "Tài khoản được kích hoạt thành công." -#: templates/registration/activation_email.html:2 -msgid "Account activation" -msgstr "Kích hoạt tài khoản" +#: templates/registration/login.html:43 +msgid "Invalid username or password." +msgstr "Tên đăng nhập hoặc mật khẩu không hợp lệ." -#: templates/registration/activation_email.html:3 -#, python-format -msgid "" -"Thanks for registering! We're glad to have you. The last step is activating " -"your account. Please activate your account in the next %(expiration_days)d " -"days." -msgstr "" -"Cảm ơn bạn đã đăng ký! Chúng tôi rất vui được chào đón bạn. Bước cuối cùng " -"là kích hoạt tài khoản của bạn. Vui lòng kích hoạt tài khoản trong vòng " -"%(expiration_days)d ngày." - -#: templates/registration/activation_email.html:5 -msgid "Activate" -msgstr "Kích hoạt" - -#: templates/registration/activation_email.html:10 -msgid "" -"Alternatively, you can reply to this message to activate your account. Your " -"reply must keep the following text intact for this to work:" -msgstr "" -"Hoặc bạn có thể trả lời tin nhắn này để kích hoạt tài khoản của bạn. Email " -"trả lời của bạn phải giữ nguyên đoạn văn sau đây:" - -#: templates/registration/activation_email.html:16 -msgid "See you soon!" -msgstr "Hẹn sớm gặp lại bạn!" - -#: templates/registration/activation_email_subject.txt:1 -#, python-format -msgid "Activate your %(SITE_NAME)s account" -msgstr "Kích hoạt tài khoản %(SITE_NAME)s" - -#: templates/registration/login.html:9 -msgid "Invalid username/email or password." -msgstr "Tên đăng nhập/email hoặc mật khẩu không hợp lệ." - -#: templates/registration/login.html:27 +#: templates/registration/login.html:61 #: templates/registration/totp_auth.html:39 msgid "Login!" msgstr "Đăng nhập!" -#: templates/registration/login.html:30 +#: templates/registration/login.html:64 msgid "Forgot your password?" msgstr "Quên mật khẩu?" -#: templates/registration/login.html:33 +#: templates/registration/login.html:67 msgid "Or log in with..." msgstr "Đăng nhập với..." @@ -5639,7 +4569,6 @@ msgid "Your password has been set. You may go ahead and log in now" msgstr "Mật khẩu đã được cập nhật. Hãy thử đăng nhập lại" #: templates/registration/password_reset_confirm.html:9 -#: templates/registration/password_reset_email.html:5 msgid "Reset Password" msgstr "Reset mật khẩu" @@ -5647,7 +4576,7 @@ msgstr "Reset mật khẩu" msgid "" "We've emailed you instructions for setting your password. You should be " "receiving them shortly." -msgstr "Chúng tôi đã gửi email cho bạn để đặt lại mật khẩu." +msgstr "Kiểm tra email để xem hướng dẫn đặt mật khẩu." #: templates/registration/password_reset_done.html:5 msgid "" @@ -5655,25 +4584,13 @@ msgid "" "you registered with, and check your spam folder." msgstr "Nếu bạn không nhận được email, hãy kiểm tra hộp thư rác (spam)." -#: templates/registration/password_reset_email.html:2 -msgid "Password Reset" -msgstr "Đặt lại mật khẩu" - -#: templates/registration/password_reset_email.html:3 -msgid "" -"We have received a request to reset your password. Click the button below to " -"reset your password:" -msgstr "" -"Chúng tôi đã nhận được yêu cầu đặt lại mật khẩu của bạn. Nhấn vào nút bên " -"dưới để đặt lại mật khẩu của bạn:" - #: templates/registration/password_reset_email.txt:1 #, python-format msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." msgstr "" -"Bạn nhận được email này vì bạn đã yêu cầu đặt lại mật khẩu tại %(site_name)s." +"Bạn nhận được email này vì bạn đã yêu cầu reset mật khẩu tại %(site_name)s." #: templates/registration/password_reset_email.txt:3 msgid "Please go to the following page and choose a new password:" @@ -5695,9 +4612,9 @@ msgstr "%(site_name)s team" #: templates/registration/password_reset_subject.txt:1 #, python-format msgid "Password reset on %(site_name)s" -msgstr "Đặt lại mật khẩu trên %(site_name)s" +msgstr "Reset mật khẩu trên %(site_name)s" -#: templates/registration/profile_creation.html:37 +#: templates/registration/profile_creation.html:36 #: templates/registration/username_select.html:7 msgid "Continue >" msgstr "Tiếp tục >" @@ -5709,33 +4626,44 @@ msgstr "Đăng ký hiện tại đã bị dừng. Hãy liên hệ admin." #: templates/registration/registration_complete.html:3 msgid "" "You have successfully been registered. An email has been sent to the email " -"address you provided to confirm your registration. If you don't see it, " -"kindly check your spam folder as well." -msgstr "" -"Bạn đã đăng ký thành công. Kiểm tra email để hoàn thành việc xác thực. Nếu " -"bạn không tìm thấy nó, vui lòng kiểm tra thư mục spam của bạn." +"address you provided to confirm your registration." +msgstr "Bạn đã đăng ký thành công. Kiểm tra email để hoàn thành việc xác thực." -#: templates/registration/registration_form.html:61 +#: templates/registration/registration_form.html:166 msgid "(again, for confirmation)" msgstr "(một lần nữa)" -#: templates/registration/registration_form.html:68 +#: templates/registration/registration_form.html:173 msgid "(select your closest major city)" msgstr "(chọn thành phố gần nhất)" -#: templates/registration/registration_form.html:72 -msgid "or" -msgstr "hoặc" - -#: templates/registration/registration_form.html:73 +#: templates/registration/registration_form.html:178 msgid "pick from map" msgstr "chọn từ bản đồ" -#: templates/registration/registration_form.html:78 +#: templates/registration/registration_form.html:183 msgid "Default language" -msgstr "Ngôn ngữ mặc định" +msgstr "Ngôn ngữ ưa thích" -#: templates/registration/registration_form.html:89 +#: templates/registration/registration_form.html:186 +#: templates/user/edit-profile.html:183 +msgid "Affiliated organizations" +msgstr "Tổ chức bạn muốn tham gia" + +#: templates/registration/registration_form.html:195 +#: templates/user/edit-profile.html:138 +msgid "Notify me about upcoming contests" +msgstr "Nhận thông báo về các kỳ thi tương lai" + +#: templates/registration/registration_form.html:209 +msgid "By registering, you agree to our" +msgstr "Bạn đồng ý với" + +#: templates/registration/registration_form.html:210 +msgid "Terms & Conditions" +msgstr "Điều khoản của chúng tôi" + +#: templates/registration/registration_form.html:213 msgid "Register!" msgstr "Đăng ký!" @@ -5743,7 +4671,7 @@ msgstr "Đăng ký!" #: templates/registration/totp_disable.html:45 #: templates/registration/totp_enable.html:83 msgid "Enter the 6-digit code generated by your app:" -msgstr "Nhập mã xác thực gồm 6 chữ số từ app bạn chọn" +msgstr "" #: templates/registration/totp_auth.html:41 #, python-format @@ -5776,9 +4704,9 @@ msgstr "Thống kê" msgid "AC Submissions by Language" msgstr "Thống kê AC theo ngôn ngữ" -#: templates/stats/tab.html:5 -msgid "Site" -msgstr "Trang" +#: templates/status/judge-status-table.html:2 +msgid "Judge" +msgstr "Máy chấm" #: templates/status/judge-status-table.html:4 msgid "Online" @@ -5808,7 +4736,7 @@ msgstr "N/A" msgid "There are no judges available at this time." msgstr "Không có máy chấm nào hoạt động." -#: templates/status/language-list.html:33 templates/ticket/list.html:263 +#: templates/status/language-list.html:33 templates/ticket/list.html:261 #: templates/user/import/table_csv.html:3 msgid "ID" msgstr "ID" @@ -5843,272 +4771,187 @@ msgstr "Lỗi hệ thống xảy ra trong quá trình chấm." msgid "Error information" msgstr "Thông tin lỗi" -#: templates/submission/list.html:119 +#: templates/submission/list.html:76 msgid "Filter by status..." msgstr "Lọc theo kết quả..." -#: templates/submission/list.html:125 +#: templates/submission/list.html:82 msgid "Filter by language..." msgstr "Lọc theo ngôn ngữ..." -#: templates/submission/list.html:309 -msgid "You were disconnected. Refresh to show latest updates." -msgstr "Bạn bị ngắt kết nối. Hãy làm mới để xem cập nhật mới nhất." - -#: templates/submission/list.html:368 +#: templates/submission/list.html:345 msgid "Total:" msgstr "Tổng:" -#: templates/submission/list.html:385 -#: templates/submission/submission-list-tabs.html:9 -msgid "Best" -msgstr "Tốt nhất" +#: templates/submission/list.html:355 +msgid "You were disconnected. Refresh to show latest updates." +msgstr "Bạn bị ngắt kết nối. Hãy làm mới để xem cập nhật mới nhất." -#: templates/submission/list.html:388 -#, fuzzy, python-format -#| msgid "user" -msgid "%(user)s" -msgstr "người dùng" - -#: templates/submission/list.html:391 templates/user/user-left-sidebar.html:3 -#: templates/user/user-list-tabs.html:5 -msgid "Friends" -msgstr "Bạn bè" - -#: templates/submission/row.html:57 -msgid "d/m/Y" -msgstr "" - -#: templates/submission/row.html:84 +#: templates/submission/row.html:49 msgid "view" msgstr "xem" -#: templates/submission/row.html:88 +#: templates/submission/row.html:53 msgid "rejudge" msgstr "chấm lại" -#: templates/submission/row.html:93 +#: templates/submission/row.html:58 msgid "admin" msgstr "admin" -#: templates/submission/status-testcases.html:5 -msgid "We are waiting for a suitable judge to process your submission..." -msgstr "Các máy chấm đang bận. Hãy kiên nhẫn chờ đợi một chút..." +#: templates/submission/source.html:29 +msgid "View status" +msgstr "Xem kết quả chấm" -#: templates/submission/status-testcases.html:7 -msgid "Your submission is being processed..." -msgstr "Đang chấm..." +#: templates/submission/source.html:30 +msgid "View raw source" +msgstr "Xem mã nguồn" -#: templates/submission/status-testcases.html:9 -msgid "Compilation Error" -msgstr "Lỗi biên dịch" - -#: templates/submission/status-testcases.html:13 -msgid "Compilation Warnings" -msgstr "Cảnh báo khi biên dịch" - -#: templates/submission/status-testcases.html:18 -msgid "Pretest Execution Results" -msgstr "Kết quả chấm Pretest" - -#: templates/submission/status-testcases.html:20 -msgid "Execution Results" -msgstr "Kết quả chấm" - -#: templates/submission/status-testcases.html:27 -#: templates/submission/status-testcases.html:42 -#: templates/submission/status-testcases.html:93 -msgid "Batch " -msgstr "Nhóm " - -#: templates/submission/status-testcases.html:32 -#: templates/submission/status-testcases.html:34 -#: templates/submission/status-testcases.html:119 -msgid "Case" -msgstr "Test" - -#: templates/submission/status-testcases.html:53 -msgid "Overall: " -msgstr "Tổng cộng: " - -#: templates/submission/status-testcases.html:67 -msgid "Point: " -msgstr "Điểm: " - -#: templates/submission/status-testcases.html:72 -msgid "Time: " -msgstr "Thời gian: " - -#: templates/submission/status-testcases.html:81 -msgid "Memory: " -msgstr "Bộ nhớ: " - -#: templates/submission/status-testcases.html:104 -#: templates/submission/status-testcases.html:133 -msgid "Point" -msgstr "Điểm" - -#: templates/submission/status-testcases.html:121 -msgid "Pretest" -msgstr "Pretest" - -#: templates/submission/status-testcases.html:123 -msgid "Test case" -msgstr "Test" - -#: templates/submission/status-testcases.html:163 -msgid "Answer:" -msgstr "Kết quả:" - -#: templates/submission/status-testcases.html:168 -msgid "Judge feedback:" -msgstr "Phản hồi từ máy chấm:" - -#: templates/submission/status-testcases.html:190 -msgid "Passing pretests does not guarantee a full score on system tests." -msgstr "AC pretest không đồng nghĩa AC cả bài nhé :))" - -#: templates/submission/status-testcases.html:193 -msgid "Submission aborted!" -msgstr "Đã hủy chấm bài nộp!" - -#: templates/submission/status.html:141 +#: templates/submission/source.html:32 templates/submission/status.html:61 msgid "Resubmit" msgstr "Nộp lại" -#: templates/submission/status.html:157 -msgid "Source code" -msgstr "Mã nguồn" +#: templates/submission/status-testcases.html:10 +msgid "We are waiting for a suitable judge to process your submission..." +msgstr "Các máy chấm đang bận. Hãy kiên nhẫn chờ đợi một chút..." -#: templates/submission/status.html:171 +#: templates/submission/status-testcases.html:12 +msgid "Your submission is being processed..." +msgstr "Đang chấm..." + +#: templates/submission/status-testcases.html:14 +msgid "Compilation Error" +msgstr "Lỗi biên dịch" + +#: templates/submission/status-testcases.html:18 +msgid "Compilation Warnings" +msgstr "Cảnh báo khi biên dịch" + +#: templates/submission/status-testcases.html:23 +msgid "Pretest Execution Results" +msgstr "Kết quả chạy Pretest" + +#: templates/submission/status-testcases.html:25 +msgid "Execution Results" +msgstr "Kết quả chạy" + +#: templates/submission/status-testcases.html:34 +msgid "Overall: " +msgstr "Tổng cộng: " + +#: templates/submission/status-testcases.html:48 +msgid "Point: " +msgstr "Điểm: " + +#: templates/submission/status-testcases.html:53 +msgid "Time: " +msgstr "Thời gian: " + +#: templates/submission/status-testcases.html:62 +msgid "Memory: " +msgstr "Bộ nhớ: " + +#: templates/submission/status-testcases.html:73 +msgid "Batch " +msgstr "Nhóm " + +#: templates/submission/status-testcases.html:84 +#: templates/submission/status-testcases.html:113 +msgid "Point" +msgstr "Điểm" + +#: templates/submission/status-testcases.html:99 +msgid "Case" +msgstr "Test" + +#: templates/submission/status-testcases.html:101 +msgid "Pretest" +msgstr "Pretest" + +#: templates/submission/status-testcases.html:103 +msgid "Test case" +msgstr "Test" + +#: templates/submission/status-testcases.html:141 +msgid "Input:" +msgstr "Input:" + +#: templates/submission/status-testcases.html:145 +msgid "Output:" +msgstr "Output:" + +#: templates/submission/status-testcases.html:149 +msgid "Answer:" +msgstr "Kết quả:" + +#: templates/submission/status-testcases.html:154 +msgid "Judge feedback:" +msgstr "Phản hồi từ máy chấm:" + +#: templates/submission/status-testcases.html:175 +msgid "Passing pretests does not guarantee a full score on system tests." +msgstr "AC pretest không đồng nghĩa AC cả bài nhé :))" + +#: templates/submission/status-testcases.html:178 +msgid "Submission aborted!" +msgstr "Đã hủy chấm bài nộp!" + +#: templates/submission/status.html:59 +msgid "View source" +msgstr "Xem mã nguồn" + +#: templates/submission/status.html:88 msgid "Abort" msgstr "Hủy chấm" +#: templates/submission/submission-list-tabs.html:6 +msgid "Mine" +msgstr "Tôi" + +#: templates/submission/submission-list-tabs.html:9 +msgid "Best" +msgstr "Tốt nhất" + #: templates/submission/submission-list-tabs.html:12 #, python-format msgid "%(user)s's" msgstr "" -#: templates/submission/user-ajax.html:2 -msgid "Contest submissions of" -msgstr "Các bài nộp của" - -#: templates/submission/user-ajax.html:11 -msgid "Subtask" -msgstr "Subtask" - -#: templates/submission/user-ajax.html:51 -msgid "g:i a d/m/Y" -msgstr "" - -#: templates/submission/user-ajax.html:51 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| " on %(time)s\n" -#| " " -msgid "" -"\n" -" %(time)s\n" -" " -msgstr "" -"\n" -" vào %(time)s\n" -" " - -#: templates/submission/user-ajax.html:60 -msgid "pretests" -msgstr "pretests" - -#: templates/submission/user-ajax.html:62 -msgid "main tests" -msgstr "test chính thức" - -#: templates/test_formatter/download_test_formatter.html:75 -#: templates/test_formatter/download_test_formatter.html:82 -#: templates/test_formatter/edit_test_formatter.html:134 -msgid "Download" -msgstr "Tải xuống" - -#: templates/test_formatter/download_test_formatter.html:86 -#: templates/test_formatter/edit_test_formatter.html:137 -#: templates/test_formatter/test_formatter.html:20 -msgid "Copyright" -msgstr "" - -#: templates/test_formatter/edit_test_formatter.html:105 -msgid "Before" -msgstr "Trước" - -#: templates/test_formatter/edit_test_formatter.html:106 -#: templates/test_formatter/edit_test_formatter.html:114 -msgid "Input format" -msgstr "Định dạng đầu vào" - -#: templates/test_formatter/edit_test_formatter.html:108 -#: templates/test_formatter/edit_test_formatter.html:116 -msgid "Output format" -msgstr "Định dạng đầu ra" - -#: templates/test_formatter/edit_test_formatter.html:113 -msgid "After" -msgstr "Sau" - -#: templates/test_formatter/edit_test_formatter.html:122 -msgid "Preview" -msgstr "Xem trước" - -#: templates/test_formatter/edit_test_formatter.html:129 -msgid "File name" -msgstr "Tên file" - -#: templates/test_formatter/edit_test_formatter.html:133 -msgid "Convert" -msgstr "Chuyển đổi" - -#: templates/test_formatter/test_formatter.html:17 -msgid "Upload" -msgstr "Tải lên" - -#: templates/ticket/feed.html:22 -msgid "replied" -msgstr "phản hồi" - -#: templates/ticket/list.html:135 templates/ticket/ticket.html:96 +#: templates/ticket/list.html:135 templates/ticket/ticket.html:273 msgid "Reopened: " msgstr "Mở lại: " -#: templates/ticket/list.html:138 templates/ticket/ticket.html:97 +#: templates/ticket/list.html:138 templates/ticket/ticket.html:274 msgid "Closed: " msgstr "Đóng: " -#: templates/ticket/list.html:223 +#: templates/ticket/list.html:221 msgid "Use desktop notification" msgstr "Sử dụng thông báo từ desktop" -#: templates/ticket/list.html:229 +#: templates/ticket/list.html:227 msgid "Show my tickets only" msgstr "Chỉ hiển thị các báo cáo dành cho tôi" -#: templates/ticket/list.html:233 +#: templates/ticket/list.html:231 msgid "Filing user" msgstr "Người báo cáo" -#: templates/ticket/list.html:242 +#: templates/ticket/list.html:240 msgid "Assignee" msgstr "Người định uỷ thác" -#: templates/ticket/list.html:264 +#: templates/ticket/list.html:262 msgid "Title" msgstr "Tiêu đề" -#: templates/ticket/list.html:266 templates/ticket/ticket.html:192 +#: templates/ticket/list.html:264 templates/ticket/ticket.html:369 msgid "Assignees" msgstr "Người được ủy thác" #: templates/ticket/new_problem.html:7 msgid "Thanks for opening a ticket!" -msgstr "Cảm ơn bạn đã báo cáo!" +msgstr "Cảm ơn đã báo cáo!" #: templates/ticket/new_problem.html:9 msgid "" @@ -6119,99 +4962,87 @@ msgstr "" "Lưu ý rằng đây là đơn báo cáo các vấn đề trong đề bài, không phải nơi để hỏi " "bài hay nộp bài." -#: templates/ticket/ticket.html:178 +#: templates/ticket/ticket.html:355 msgid "Post" msgstr "Đăng" -#: templates/ticket/ticket.html:186 +#: templates/ticket/ticket.html:363 msgid "Associated object" msgstr "" -#: templates/ticket/ticket.html:197 +#: templates/ticket/ticket.html:374 msgid "No one is assigned." msgstr "Không ai được ủy thác." -#: templates/ticket/ticket.html:203 +#: templates/ticket/ticket.html:380 msgid "Close ticket" msgstr "Đóng báo cáo" -#: templates/ticket/ticket.html:205 +#: templates/ticket/ticket.html:382 msgid "Reopen ticket" msgstr "Mở lại báo cáo" -#: templates/ticket/ticket.html:209 +#: templates/ticket/ticket.html:386 msgid "Assignee notes" msgstr "Lưu ý cho người ủy thác" -#: templates/ticket/ticket.html:216 templates/widgets/select_all.html:4 +#: templates/ticket/ticket.html:393 templates/widgets/select_all.html:4 msgid "Nothing here." msgstr "Không có gì." -#: templates/top-users.html:3 -msgid "Top Rating" -msgstr "Top Rating" +#: templates/user/base-users-table.html:3 +msgid "Rank" +msgstr "Rank" -#: templates/top-users.html:22 -msgid "Top Score" -msgstr "Top Score" +#: templates/user/edit-profile.html:99 +msgid "Name and School" +msgstr "Họ tên và Trường" -#: templates/user/edit-profile.html:95 -msgid "Full name" -msgstr "Họ tên" +#: templates/user/edit-profile.html:101 +msgid "Enter this form" +msgstr "Điền vào link này" -#: templates/user/edit-profile.html:103 -msgid "Date of birth" -msgstr "Ngày sinh" +#: templates/user/edit-profile.html:102 +msgid "It takes some time for admin to approve" +msgstr "Ban quản trị sẽ phê duyệt" #: templates/user/edit-profile.html:107 -msgid "Address" -msgstr "Địa chỉ" - -#: templates/user/edit-profile.html:111 -msgid "T-Shirt size" -msgstr "Kích cỡ áo" - -#: templates/user/edit-profile.html:118 -msgid "Change your password" -msgstr "Đổi mật khẩu" - -#: templates/user/edit-profile.html:132 -msgid "Avatar" -msgstr "Ảnh đại diện" - -#: templates/user/edit-profile.html:138 msgid "Self-description" msgstr "Tự giới thiệu" -#: templates/user/edit-profile.html:146 +#: templates/user/edit-profile.html:115 msgid "Select your closest major city" msgstr "Chọn thành phố gần nhất" -#: templates/user/edit-profile.html:155 +#: templates/user/edit-profile.html:124 msgid "Editor theme" msgstr "Giao diện cho code editor" -#: templates/user/edit-profile.html:165 +#: templates/user/edit-profile.html:129 +msgid "Math engine" +msgstr "" + +#: templates/user/edit-profile.html:153 templates/user/edit-profile.html:154 +msgid "Change your avatar" +msgstr "Đổi ảnh đại diện" + +#: templates/user/edit-profile.html:160 +msgid "Change your password" +msgstr "Đổi mật khẩu" + +#: templates/user/edit-profile.html:167 msgid "Two Factor Authentication is enabled." msgstr "Two Factor Authentication đã được kích hoạt." -#: templates/user/edit-profile.html:169 -msgid "Disable" -msgstr "Tắt" - -#: templates/user/edit-profile.html:172 +#: templates/user/edit-profile.html:174 msgid "Two Factor Authentication is disabled." -msgstr "Two Factor Authentication chưa kích hoạt." +msgstr "Two Factor Authentication đã được hủy kích hoạt." -#: templates/user/edit-profile.html:173 -msgid "Enable" -msgstr "Bật" - -#: templates/user/edit-profile.html:177 -msgid "CSS background" +#: templates/user/edit-profile.html:191 +msgid "User-script" msgstr "" -#: templates/user/edit-profile.html:181 +#: templates/user/edit-profile.html:195 msgid "Update profile" msgstr "Cập nhật thông tin" @@ -6227,149 +5058,154 @@ msgstr "File người dùng" msgid "Sample" msgstr "" -#: templates/user/import/index.html:105 templates/user/user-left-sidebar.html:5 -#: templates/user/user-list-tabs.html:8 +#: templates/user/import/index.html:105 templates/user/user-list-tabs.html:8 msgid "Import" msgstr "" -#: templates/user/import/table_csv.html:9 -msgid "Organizations" -msgstr "Tổ chức" +#: templates/user/import/table_csv.html:7 +msgid "School" +msgstr "" -#: templates/user/pp-row.html:21 +#: templates/user/pp-row.html:22 #, python-format msgid "" "\n" -" weighted %(weight)s%%\n" -" " +" weighted %(weight)s%%\n" +" " msgstr "" -#: templates/user/pp-row.html:26 +#: templates/user/pp-row.html:27 #, python-format msgid "%(pp).1fpp" msgstr "%(pp).1fpp" -#: templates/user/pp-row.html:28 +#: templates/user/pp-row.html:29 #, python-format msgid "%(pp).0fpp" msgstr "%(pp).0fpp" -#: templates/user/user-about.html:63 +#: templates/user/user-about.html:23 +#, python-format +msgid "%(counter)s problem solved" +msgid_plural "%(counter)s problems solved" +msgstr[0] "Đã giải %(counter)s bài" + +#: templates/user/user-about.html:35 +msgid "Total points" +msgstr "Tổng điểm" + +#: templates/user/user-about.html:45 +msgid "Rank by rating" +msgstr "Rank theo rating" + +#: templates/user/user-about.html:52 +msgid "Rank by points" +msgstr "Rank theo điểm" + +#: templates/user/user-about.html:64 +msgid "From" +msgstr "Đến từ" + +#: templates/user/user-about.html:75 msgid "Admin Notes" msgstr "Lưu ý của admin" -#: templates/user/user-about.html:75 +#: templates/user/user-about.html:90 msgid "You have not shared any information." msgstr "Bạn chưa chia sẻ thông tin nào." -#: templates/user/user-about.html:77 +#: templates/user/user-about.html:92 msgid "This user has not shared any information." msgstr "Người dùng này chưa chia sẻ thông tin nào." -#: templates/user/user-about.html:97 +#: templates/user/user-about.html:101 +msgid "Awards" +msgstr "Thành tích" + +#: templates/user/user-about.html:112 #, python-format msgid "%(label)s (%(date)s)" msgstr "%(label)s (%(date)s)" -#: templates/user/user-about.html:115 +#: templates/user/user-about.html:130 msgid "Mon" msgstr "Thứ 2" -#: templates/user/user-about.html:120 +#: templates/user/user-about.html:135 msgid "Tues" msgstr "Thứ 3" -#: templates/user/user-about.html:125 +#: templates/user/user-about.html:140 msgid "Wed" msgstr "Thứ 4" -#: templates/user/user-about.html:130 +#: templates/user/user-about.html:145 msgid "Thurs" msgstr "Thứ 5" -#: templates/user/user-about.html:135 +#: templates/user/user-about.html:150 msgid "Fri" msgstr "Thứ 6" -#: templates/user/user-about.html:140 +#: templates/user/user-about.html:155 msgid "Sat" msgstr "Thứ 7" -#: templates/user/user-about.html:145 +#: templates/user/user-about.html:160 msgid "Sun" msgstr "CN" -#: templates/user/user-about.html:154 +#: templates/user/user-about.html:169 msgid "Less" msgstr "Ít" -#: templates/user/user-about.html:160 +#: templates/user/user-about.html:175 msgid "More" msgstr "Nhiều" -#: templates/user/user-about.html:169 +#: templates/user/user-about.html:184 msgid "Rating History" msgstr "Các lần thi" -#: templates/user/user-about.html:236 +#: templates/user/user-about.html:255 msgid "past year" msgstr "năm ngoái" -#: templates/user/user-about.html:253 +#: templates/user/user-about.html:272 msgid "total submission(s)" msgstr "bài nộp" -#: templates/user/user-about.html:257 +#: templates/user/user-about.html:276 msgid "submissions in the last year" msgstr "bài nộp trong năm qua" -#: templates/user/user-base.html:104 +#: templates/user/user-base.html:101 msgid "Unfollow" msgstr "Bỏ theo dõi" -#: templates/user/user-base.html:107 +#: templates/user/user-base.html:104 msgid "Follow" msgstr "Theo dõi" -#: templates/user/user-base.html:115 -msgid "Message" +#: templates/user/user-base.html:121 +msgid "Send message" msgstr "Nhắn tin" -#: templates/user/user-base.html:123 +#: templates/user/user-base.html:130 msgid "Contests written" msgstr "Số kỳ thi" -#: templates/user/user-base.html:127 +#: templates/user/user-base.html:134 msgid "Min. rating:" msgstr "Min. rating:" -#: templates/user/user-base.html:131 +#: templates/user/user-base.html:138 msgid "Max rating:" msgstr "Max rating:" -#: templates/user/user-bookmarks.html:22 -msgid "Editorials" -msgstr "Lời giải" - -#: templates/user/user-bookmarks.html:25 -msgid "Posts" -msgstr "Bài đăng" - -#: templates/user/user-bookmarks.html:71 -msgid "There is no saved problem." -msgstr "Không có bài tập nào đã lưu." - -#: templates/user/user-bookmarks.html:100 -msgid "There is no saved contest." -msgstr "Không có kỳ thi đã lưu." - -#: templates/user/user-bookmarks.html:141 -msgid "There is no saved editorial." -msgstr "Không có lời giải nào đã lưu." - -#: templates/user/user-bookmarks.html:190 -msgid "There is no saved post." -msgstr "Không có bài đăng nào đã lưu." +#: templates/user/user-list-tabs.html:5 +msgid "Friends" +msgstr "Bạn bè" #: templates/user/user-problems.html:35 msgid "Points Breakdown" @@ -6396,455 +5232,27 @@ msgstr "Ẩn các bài đã giải" msgid "%(points).1f points" msgstr "%(points).1f điểm" +#: templates/user/user-problems.html:99 +msgid "Score" +msgstr "Điểm" + #: templates/user/user-problems.html:110 #, python-format msgid "%(points)s / %(total)s" msgstr "%(points)s / %(total)s" -#: templates/user/user-tabs.html:8 +#: templates/user/user-tabs.html:7 msgid "Impersonate" msgstr "Giả danh" -#: templates/user/user-tabs.html:15 +#: templates/user/user-tabs.html:13 msgid "Admin User" msgstr "Người dùng" -#: templates/user/user-tabs.html:18 +#: templates/user/user-tabs.html:16 msgid "Admin Profile" msgstr "Thông tin" #: templates/widgets/select_all.html:8 msgid "Check all" msgstr "Chọn tất cả" - -#~ msgid "course title" -#~ msgstr "tiêu đề khóa học" - -#~ msgid "course content" -#~ msgstr "nội dung khóa học" - -#~ msgid "" -#~ "This image will replace the default site logo for users viewing the " -#~ "organization." -#~ msgstr "Ảnh này sẽ thay thế logo mặc định khi ở trong tổ chức." - -#~ msgid "A ticket is added or updated" -#~ msgstr "Báo cáo" - -#~ msgid "Post time" -#~ msgstr "Thời gian đăng" - -#~ msgid "My groups" -#~ msgstr "Nhóm của tôi" - -#~ msgid "Open groups" -#~ msgstr "Nhóm mở" - -#~ msgid "Private groups" -#~ msgstr "Nhóm kín" - -#~ msgid "Send message" -#~ msgstr "Nhắn tin" - -#~ msgid "Wrong" -#~ msgstr "Sai" - -#~ msgid "Timeout" -#~ msgstr "Quá thời gian" - -#~ msgid "Active Contests" -#~ msgstr "Kỳ thi bạn đang tham gia" - -#~ msgid "Past Contests" -#~ msgstr "Kỳ thi trong quá khứ" - -#~ msgid "Duration" -#~ msgstr "Thời hạn" - -#~ msgid "Submissions in {0}" -#~ msgstr "Bài nộp trong {0}" - -#~ msgid "Submissions in %s" -#~ msgstr "Bài nộp trong %s" - -#~ msgid "Hidden Rankings" -#~ msgstr "Bảng xếp hạng ẩn" - -#, fuzzy -#~| msgid "Banned from joining" -#~ msgid "banned from voting" -#~ msgstr "Bị cấm tham gia" - -#~ msgid "Already in contest" -#~ msgstr "Đã ở trong kỳ thi" - -#~ msgid "You are already in a contest: \"%s\"." -#~ msgstr "Bạn đã ở trong kỳ thi: \"%s\"." - -#~ msgid "Compete" -#~ msgstr "Thi" - -#~ msgid "General" -#~ msgstr "Chung" - -#~ msgid "custom validator file" -#~ msgstr "file trình chấm" - -#~ msgid "Leave as LaTeX" -#~ msgstr "Để định dạng LaTeX" - -#~ msgid "MathML only" -#~ msgstr "chỉ dùng MathML" - -#~ msgid "Custom validator (CPP)" -#~ msgstr "Trình chấm tự viết (C++)" - -#, fuzzy -#~| msgid "From the web" -#~ msgid "From the Web" -#~ msgstr "Từ web" - -#~ msgid "on {time}" -#~ msgstr "vào {time}" - -#, fuzzy -#~| msgid "end time" -#~ msgid "ending time" -#~ msgstr "thời gian kết thúc" - -#~ msgid "In current contest" -#~ msgstr "Trong kỳ thi hiện tại" - -#~ msgid "View status" -#~ msgstr "Xem kết quả chấm" - -#~ msgid "View raw source" -#~ msgstr "Xem mã nguồn" - -#, fuzzy -#~| msgid "contest summary" -#~ msgid "Contests Summary" -#~ msgstr "tổng kết kỳ thi" - -#~ msgid "Submit solution" -#~ msgstr "Nộp bài" - -#~ msgid "(partial)" -#~ msgstr "(thành phần)" - -#~ msgid "Following" -#~ msgstr "Bạn bè" - -#~ msgid "Other" -#~ msgstr "Thành viên khác" - -#~ msgid "Online Users" -#~ msgstr "Trực tuyến" - -#~ msgid "Group:" -#~ msgstr "Nhóm:" - -#~ msgid "Have editorial" -#~ msgstr "Có hướng dẫn" - -#~ msgid "Contest" -#~ msgstr "Kỳ thi" - -#~ msgid "0 to not show testcases, 1 to show" -#~ msgstr "0 để ẩn test, 1 để hiện" - -#~ msgid "{settings.SITE_NAME} - Email Change Request" -#~ msgstr "{settings.SITE_NAME} - Thay đổi email" - -#~ msgid "Affiliated organizations" -#~ msgstr "Tổ chức bạn muốn tham gia" - -#~ msgid "By registering, you agree to our" -#~ msgstr "Bạn đồng ý với" - -#~ msgid "Terms & Conditions" -#~ msgstr "Điều khoản của chúng tôi" - -#~ msgid "Change your avatar" -#~ msgstr "Đổi ảnh đại diện" - -#, fuzzy -#~| msgid "How did you corrupt the custom checker path?" -#~ msgid "How did you corrupt the interactor path?" -#~ msgstr "How did you corrupt the custom checker path?" - -#~ msgid "comment more" -#~ msgstr "bình luận nữa" - -#~ msgid "comments more" -#~ msgstr "bình luận nữa" - -#~ msgid "" -#~ "This comment is hidden due to too much negative feedback. Click here to view it." -#~ msgstr "" -#~ "Bình luận bị ẩn vì nhiều phản hồi tiêu cực. Nhấp vào đây để mở." - -#, fuzzy -#~| msgid "short name" -#~ msgid "first name" -#~ msgstr "tên ngắn" - -#, fuzzy -#~| msgid "short name" -#~ msgid "last name" -#~ msgstr "tên ngắn" - -#~ msgid "biography" -#~ msgstr "tiểu sử" - -#~ msgid "name" -#~ msgstr "tên" - -#~ msgid "Category of Books" -#~ msgstr "Thể loại sách" - -#~ msgid "Categories of Books" -#~ msgstr "Thể loại sách" - -#~ msgid "title" -#~ msgstr "tiêu đề" - -#~ msgid "author" -#~ msgstr "tác giả" - -#~ msgid "publication date" -#~ msgstr "ngày xuất bản" - -#~ msgid "Book" -#~ msgstr "Sách" - -#~ msgid "Books" -#~ msgstr "Sách" - -#~ msgid "Category of CDs" -#~ msgstr "Thể loại CDs" - -#~ msgid "Categories of CDs" -#~ msgstr "Thể loại CDs" - -#~ msgid "Category of DVDs" -#~ msgstr "Thể loại DVDs" - -#~ msgid "Categories of DVDs" -#~ msgstr "Thể loại DVDs" - -#~ msgid "Django administration" -#~ msgstr "Quản trị viên Django" - -#~ msgid "" -#~ "Please enter the correct %(username)s and password for an admin account. " -#~ "Note that both fields may be case-sensitive." -#~ msgstr "" -#~ "Hãy nhập %(username)s và mật khẩu hợp lệ cho tài khoản quản trị. Chú ý cả " -#~ "hai trường có phân biệt chữ Hoa-thường." - -#~ msgid "" -#~ "Please enter the correct %(username)s and password for an user account. " -#~ "Note that both fields may be case-sensitive." -#~ msgstr "" -#~ "Hãy nhập %(username)s và mật khẩu hợp lệ cho tài khoản thành viên. Chú ý " -#~ "cả hai trường có phân biệt chữ Hoa-thường." - -#~ msgid "Dashboard" -#~ msgstr "Bảng điều khiển" - -#~ msgid "Applications" -#~ msgstr "Ứng dụng" - -#, fuzzy -#~| msgid "administrators" -#~ msgid "Administration" -#~ msgstr "người quản lý" - -#~ msgid "Color theme" -#~ msgstr "Chủ đề màu sắc" - -#~ msgid "commented on {time}" -#~ msgstr "bình luận vào {time}" - -#~ msgid "Associated page" -#~ msgstr "Trang liên kết" - -#~ msgid "Page code must be ^[pcs]:[a-z0-9]+$|^b:\\d+$" -#~ msgstr "Mã trang phải có dạng ^[pcs]:[a-z0-9]+$|^b:\\d+$" - -#~ msgid "Best solutions for %(problem)s in %(contest)s" -#~ msgstr "Các bài nộp tốt nhất cho %(problem)s trong %(contest)s" - -#~ msgid "Best solutions for problem %(number)s in %(contest)s" -#~ msgstr "Các bài nộp tốt nhất cho bài %(number)s trong %(contest)s" - -#~ msgid "" -#~ "Best solutions for {0} in {2}" -#~ msgstr "" -#~ "Các bài nộp tốt nhất cho {0} trong {2}" -#~ "" - -#~ msgid "Best solutions for problem {0} in {1}" -#~ msgstr "Các bài nộp tốt nhất cho bài {0} trong {1}" - -#~ msgid "Refresh" -#~ msgstr "Làm mới" - -#~ msgid "View comments" -#~ msgstr "Xem bình luận" - -#~ msgid "Be the first to comment" -#~ msgstr "Bình luận đầu tiên" - -#, fuzzy -#~| msgid "Default" -#~ msgid "default" -#~ msgstr "Mặc định" - -#~ msgid "" -#~ "\n" -#~ " on %(time)s\n" -#~ " " -#~ msgstr "" -#~ "\n" -#~ " vào %(time)s\n" -#~ " " - -#~ msgid "Subscribe to contest updates" -#~ msgstr "Đăng ký để nhận thông báo về các kỳ thi" - -#~ msgid "Enable experimental features" -#~ msgstr "Sử dụng các tính năng thử nghiệm" - -#~ msgid "Subscribe to newsletter?" -#~ msgstr "Đăng ký để nhận thông báo?" - -#~ msgid "Notify me about upcoming contests" -#~ msgstr "Nhận thông báo về các kỳ thi tương lai" - -#~ msgid "Frozen" -#~ msgstr "Đã đóng băng" - -#~ msgid "Dislike" -#~ msgstr "Không thích" - -#~ msgid "Hello, %(username)s." -#~ msgstr "Xin chào, %(username)s." - -#~ msgid "Report an issue" -#~ msgstr "Báo cáo một vấn đề" - -#~ msgid "Edit in %(org_slug)s" -#~ msgstr "Chỉnh sửa trong %(org_slug)s" - -#~ msgid "Name and School" -#~ msgstr "Họ tên và Trường" - -#~ msgid "Enter this form" -#~ msgstr "Điền vào link này" - -#~ msgid "It takes some time for admin to approve" -#~ msgstr "Ban quản trị sẽ phê duyệt" - -#~ msgid "Show" -#~ msgstr "Hiển thị" - -#~ msgid "Judge:" -#~ msgid_plural "Judges:" -#~ msgstr[0] "Máy chấm:" - -#~ msgid "Organization" -#~ msgstr "Tổ chức" - -#~ msgid "Full Name" -#~ msgstr "Họ tên" - -#~ msgid "Show my groups only" -#~ msgstr "Chỉ hiển thị nhóm đang tham gia" - -#~ msgid "Edit difficulty" -#~ msgstr "Thay đổi độ khó" - -#~ msgid "Vote difficulty" -#~ msgstr "Bình chọn độ khó" - -#~ msgid "How difficult is this problem?" -#~ msgstr "Bạn thấy độ khó bài này thế nào?" - -#~ msgid "This helps us improve the site" -#~ msgstr "Bình chọn giúp admin cải thiện bài tập." - -#~ msgid "Voting Statistics" -#~ msgstr "Thống kê" - -#~ msgid "Median:" -#~ msgstr "Trung vị:" - -#~ msgid "Mean:" -#~ msgstr "Trung bình:" - -#~ msgid "Edit organization" -#~ msgstr "Chỉnh sửa" - -#~ msgid "You may not be part of more than {count} public organizations." -#~ msgstr "Bạn không thể tham gia nhiều hơn {count} tổ chức công khai." - -#~ msgid "Join organization" -#~ msgstr "Tham gia tổ chức" - -#~ msgid "Organizations..." -#~ msgstr "Tổ chức..." - -#~ msgid "Show my organizations only" -#~ msgstr "Chỉ hiển thị tổ chức của tôi" - -#~ msgid "Leave organization" -#~ msgstr "Rời tổ chức" - -#~ msgid "You are not allowed to kick people from this organization." -#~ msgstr "Bạn không được phép đuổi người." - -#~ msgid "Organization news" -#~ msgstr "Tin tức tổ chức" - -#~ msgid "Admin organization" -#~ msgstr "Trang admin tổ chức" - -#~ msgid "New private contests" -#~ msgstr "Kỳ thi riêng tư mới" - -#~ msgid "New private problems" -#~ msgstr "Bài tập riêng tư mới" - -#~ msgid "Hot problems" -#~ msgstr "Bài tập mới" - -#~ msgid "Discuss {0}" -#~ msgstr "Thảo luận {0}" - -#~ msgid "posted" -#~ msgstr "đã đăng" - -#~ msgid "My open tickets" -#~ msgstr "Báo cáo dành cho tôi" - -#~ msgid "New tickets" -#~ msgstr "Báo cáo mới" - -#~ msgid "New problems" -#~ msgstr "Bài tập mới" - -#~ msgid "Comment stream" -#~ msgstr "Dòng bình luận" - -#~ msgid "Discuss" -#~ msgstr "Thảo luận" - -#~ msgid "Easy" -#~ msgstr "Dễ" - -#~ msgid "Hard" -#~ msgstr "Khó" diff --git a/locale/vi/LC_MESSAGES/dmoj-user.po b/locale/vi/LC_MESSAGES/dmoj-user.po deleted file mode 100644 index 29ad707..0000000 --- a/locale/vi/LC_MESSAGES/dmoj-user.po +++ /dev/null @@ -1,607 +0,0 @@ -msgid "" -msgstr "" -"Content-Type: text/plain; charset=UTF-8\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Problems" -msgstr "Bài tập" - -msgid "Submissions" -msgstr "Bài nộp" - -msgid "Users" -msgstr "Thành viên" - -msgid "Contests" -msgstr "Kỳ thi" - -msgid "Groups" -msgstr "Nhóm" - -msgid "About" -msgstr "Giới thiệu" - -msgid "Status" -msgstr "Máy chấm" - -msgid "Courses" -msgstr "Khóa học" - -msgid "Suggestions" -msgstr "Đề xuất ý tưởng" - -msgid "Proposal" -msgstr "Đề xuất bài tập" - -msgid "TanKhoa" -msgstr "Tân Khoa" - -msgid "Name" -msgstr "Đăng ký tên" - -msgid "Report" -msgstr "Báo cáo tiêu cực" - -msgid "2sat" -msgstr "" - -msgid "adhoc" -msgstr "" - -msgid "aho-corasick" -msgstr "" - -msgid "articulation-point" -msgstr "" - -msgid "backtrack" -msgstr "" - -msgid "bellman-ford" -msgstr "" - -msgid "bfs-01" -msgstr "" - -msgid "biconnected-component" -msgstr "" - -msgid "bignum" -msgstr "" - -msgid "binary-lifting" -msgstr "" - -msgid "binary-search" -msgstr "" - -msgid "binary-search-parallel" -msgstr "" - -msgid "bit" -msgstr "" - -msgid "bit2d" -msgstr "" - -msgid "bitset" -msgstr "" - -msgid "bitwise" -msgstr "" - -msgid "block-cut-tree" -msgstr "" - -msgid "boruvka" -msgstr "" - -msgid "branch&bound" -msgstr "" - -msgid "bridge" -msgstr "" - -msgid "brute force" -msgstr "" - -msgid "cactus-graph" -msgstr "" - -msgid "casework" -msgstr "" - -msgid "centroid" -msgstr "" - -msgid "chinese-remainder" -msgstr "" - -msgid "combinatorics" -msgstr "" - -msgid "constructive" -msgstr "" - -msgid "convex-hull" -msgstr "" - -msgid "counting" -msgstr "" - -msgid "cycle" -msgstr "" - -msgid "data structures" -msgstr "" - -msgid "deep-optimization" -msgstr "" - -msgid "dfs/bfs/pfs" -msgstr "" - -msgid "dijkstra" -msgstr "" - -msgid "divide and conquer" -msgstr "" - -msgid "dp-alien" -msgstr "" - -msgid "dp-bitmask" -msgstr "" - -msgid "dp-cc" -msgstr "" - -msgid "dp-convexhull" -msgstr "" - -msgid "dp-count" -msgstr "" - -msgid "dp-dag" -msgstr "" - -msgid "dp-digit" -msgstr "" - -msgid "dp-dnc" -msgstr "" - -msgid "dp-general" -msgstr "" - -msgid "dp-knuth" -msgstr "" - -msgid "dp-matrix" -msgstr "" - -msgid "dp-openclose" -msgstr "" - -msgid "dp-permutation" -msgstr "" - -msgid "dp-slope" -msgstr "" - -msgid "dp-sos" -msgstr "" - -msgid "dp-swap-label" -msgstr "" - -msgid "dp-tree" -msgstr "" - -msgid "dsu" -msgstr "" - -msgid "dsu-roll-back" -msgstr "" - -msgid "dynamic programming" -msgstr "" - -msgid "euler-path" -msgstr "" - -msgid "euler-theorem" -msgstr "" - -msgid "euler-tour" -msgstr "" - -msgid "extgcd" -msgstr "" - -msgid "Fenwick Tree (Binary Indexed Tree)" -msgstr "" - -msgid "fft/ntt" -msgstr "" - -msgid "flow-demands" -msgstr "" - -msgid "flow-general" -msgstr "" - -msgid "flow-mincost" -msgstr "" - -msgid "flows" -msgstr "" - -msgid "floyd" -msgstr "" - -msgid "function-graph" -msgstr "" - -msgid "game theory" -msgstr "" - -msgid "game-ad-hoc" -msgstr "" - -msgid "game-minimax" -msgstr "" - -msgid "game-nim" -msgstr "" - -msgid "game-sprague-grundy" -msgstr "" - -msgid "gauss-elim" -msgstr "" - -msgid "genetic" -msgstr "" - -msgid "geometry" -msgstr "" - -msgid "geometry-general" -msgstr "" - -msgid "gomory-hu-tree" -msgstr "" - -msgid "graph" -msgstr "" - -msgid "graph theory" -msgstr "" - -msgid "greedy" -msgstr "" - -msgid "greedy-exchange" -msgstr "" - -msgid "greedy-general" -msgstr "" - -msgid "greedy-moore" -msgstr "" - -msgid "hashing" -msgstr "" - -msgid "heuristic" -msgstr "" - -msgid "hld" -msgstr "" - -msgid "HSG" -msgstr "" - -msgid "ICPC" -msgstr "" - -msgid "implementation" -msgstr "" - -msgid "inclusion-exclusion" -msgstr "" - -msgid "interactive" -msgstr "" - -msgid "inversive-geometry" -msgstr "" - -msgid "kd-tree" -msgstr "" - -msgid "kirchoff" -msgstr "" - -msgid "kmp" -msgstr "" - -msgid "kruskal" -msgstr "" - -msgid "lagrange-inter" -msgstr "" - -msgid "language" -msgstr "" - -msgid "lca" -msgstr "" - -msgid "lichao-tree" -msgstr "" - -msgid "local-search" -msgstr "" - -msgid "ltt" -msgstr "" - -msgid "manacher" -msgstr "" - -msgid "matching-bipartie" -msgstr "" - -msgid "matching-general" -msgstr "" - -msgid "matching-hall" -msgstr "" - -msgid "matching-hungarian" -msgstr "" - -msgid "matching-konig" -msgstr "" - -msgid "math" -msgstr "" - -msgid "math-general" -msgstr "" - -msgid "matrix multiplication" -msgstr "" - -msgid "max-clique" -msgstr "" - -msgid "meet-in-the-middle" -msgstr "" - -msgid "Miscellaneous" -msgstr "" - -msgid "mo-general" -msgstr "" - -msgid "mo-tree" -msgstr "" - -msgid "mo-update" -msgstr "" - -msgid "mobius" -msgstr "" - -msgid "modular" -msgstr "" - -msgid "modular-inverse" -msgstr "" - -msgid "monotonic-queue" -msgstr "" - -msgid "mst" -msgstr "" - -msgid "multiplicative" -msgstr "" - -msgid "NEW" -msgstr "" - -msgid "number theory" -msgstr "" - -msgid "offline" -msgstr "" - -msgid "order-statistic" -msgstr "" - -msgid "output-only" -msgstr "" - -msgid "palin-tree" -msgstr "" - -msgid "picks-theorem" -msgstr "" - -msgid "planar-graph" -msgstr "" - -msgid "polygon-triangulation" -msgstr "" - -msgid "precision-issue" -msgstr "" - -msgid "prefix-sum" -msgstr "" - -msgid "primality-test" -msgstr "" - -msgid "probability" -msgstr "" - -msgid "randomized" -msgstr "" - -msgid "Range Queries" -msgstr "" - -msgid "recursion" -msgstr "" - -msgid "rmq" -msgstr "" - -msgid "rmq-2d" -msgstr "" - -msgid "rmq2d" -msgstr "" - -msgid "scc" -msgstr "" - -msgid "scheduling" -msgstr "" - -msgid "search" -msgstr "" - -msgid "segtree-2d" -msgstr "" - -msgid "segtree-beats" -msgstr "" - -msgid "segtree-dynamic" -msgstr "" - -msgid "segtree-general" -msgstr "" - -msgid "segtree-persistent" -msgstr "" - -msgid "segtree-walk" -msgstr "" - -msgid "sequence" -msgstr "" - -msgid "sieve" -msgstr "" - -msgid "sieve-segment" -msgstr "" - -msgid "small-to-large" -msgstr "" - -msgid "sortings" -msgstr "" - -msgid "spanning-tree" -msgstr "" - -msgid "sparse-table" -msgstr "" - -msgid "spfa" -msgstr "" - -msgid "sqrt" -msgstr "" - -msgid "sqrt-babygiant" -msgstr "" - -msgid "sqrt-buckets" -msgstr "" - -msgid "sqrt-buffer" -msgstr "" - -msgid "sqrt-time" -msgstr "" - -msgid "stack-queue" -msgstr "" - -msgid "static-data" -msgstr "" - -msgid "stl" -msgstr "" - -msgid "string" -msgstr "" - -msgid "suffix-array" -msgstr "" - -msgid "sweep-line" -msgstr "" - -msgid "ternary-search" -msgstr "" - -msgid "topo-sort" -msgstr "" - -msgid "tortoise-hare" -msgstr "" - -msgid "Training" -msgstr "" - -msgid "treap/splay" -msgstr "" - -msgid "Tree - general" -msgstr "" - -msgid "tree-isomorphism" -msgstr "" - -msgid "trie" -msgstr "" - -msgid "TSP" -msgstr "" - -msgid "TST" -msgstr "" - -msgid "two-pointers" -msgstr "" - -msgid "unlabelled" -msgstr "" - -msgid "VOI" -msgstr "" - -msgid "voronoi" -msgstr "" - -msgid "z-function" -msgstr "" - -#~ msgid "Bug Report" -#~ msgstr "Báo cáo lỗi" - -#~ msgid "Insert Image" -#~ msgstr "Chèn hình ảnh" - -#~ msgid "Save" -#~ msgstr "Lưu" diff --git a/manage.py b/manage.py index f0470c1..498e387 100644 --- a/manage.py +++ b/manage.py @@ -11,7 +11,6 @@ if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dmoj.settings") from django.core.management import execute_from_command_line - # noinspection PyUnresolvedReferences import django_2_2_pymysql_patch # noqa: F401, imported for side effect diff --git a/requirements.txt b/requirements.txt index 99b2c0f..18eb9a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,16 @@ -Django>=3.2.17,<4 -django_compressor>=3 -django-mptt>=0.13 -django-pagedown -django-registration-redux>=2.10 -django-reversion>=3.0.5,<4 -django-reversion-compare +Django>=2.2,<3 +django_compressor +django-mptt +django-pagedown<2 +django-registration-redux +django-reversion django-social-share -django-sortedm2m>=3.1.0 +-e git://github.com/DMOJ/django-sortedm2m.git#egg=django-sortedm2m django-impersonate -dmoj-wpadmin @ git+https://github.com/LQDJudge/dmoj-wpadmin.git +-e git://github.com/DMOJ/dmoj-wpadmin.git#egg=dmoj-wpadmin lxml Pygments +mistune<2 social-auth-app-django pytz django-statici18n @@ -18,30 +18,21 @@ pika ua-parser pyyaml jinja2 -django_jinja>=2.5.0 +django_jinja llist requests django-fernet-fields pyotp qrcode[pil] -jsonfield @ git+https://github.com/DMOJ/jsonfield.git +jsonfield pymoss -packaging<22 +packaging celery -ansi2html @ git+https://github.com/DMOJ/ansi2html.git +-e git://github.com/DMOJ/ansi2html.git#egg=ansi2html sqlparse +django-newsletter netaddr redis lupa websocket-client -python-memcached<1.60 -numpy -pandas -markdown -bleach -pymdown-extensions -mdx-breakless-lists -beautifulsoup4 -pre-commit -django-ratelimit -lxml_html_clean \ No newline at end of file +python-memcached \ No newline at end of file diff --git a/resources/admin/css/pagedown.css b/resources/admin/css/pagedown.css index ee29196..27732de 100644 --- a/resources/admin/css/pagedown.css +++ b/resources/admin/css/pagedown.css @@ -2,12 +2,9 @@ padding-right: 15px !important; } + .wmd-preview { margin-top: 15px; padding: 15px; word-wrap: break-word; } - -.md-typeset, .wmd-input { - line-height: 1.4em !important; -} diff --git a/resources/base.scss b/resources/base.scss index 525a50d..369b91b 100644 --- a/resources/base.scss +++ b/resources/base.scss @@ -25,7 +25,7 @@ a { } &:active { - color: $theme_color; + color: #faa700; } } @@ -35,12 +35,8 @@ img { // height: auto } -* { - -webkit-tap-highlight-color: transparent; -} - .full { - width: 100% !important; + width: 100%; } table.sortable thead { @@ -112,10 +108,11 @@ body { position: relative; min-height: 100%; margin: 0 auto; + max-width: 107em; font-size: $base_font_size; line-height: 1.231; background: $background_light_gray; - font-family: "Noto Sans", Arial, "Lucida Grande", sans-serif; + font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif; color: #000; height: 100%; overflow-x: hidden; @@ -139,7 +136,7 @@ b { h2 { font-weight: 400; - font-size: 1.7em; + font-size: 2em; border-radius: $widget_border_radius; padding: 0; margin: 0; @@ -170,17 +167,54 @@ header { } #user-links { - display: flex; - text-align: center; - padding: 4px; - margin-right: 1em; - gap: 5px; - &:hover { - color: black; - border-radius: 2px; - border: 0.5px solid black; - cursor: pointer; - padding: 3.5px; + // display: inline; + float: right; + color: #5c5954; + + .anon { + margin-top: 1em; + padding-right: 10px; + display: inline-flex; + min-height: 100%; + align-items: center; + white-space: nowrap; + } + + a { + color: lightcoral; + } + + li { + text-transform: none; + } + + & > ul { + display: block; + margin: 0; + + & > li > a > span { + font-size: 13px; + height: 36px; + padding-top: 8px; + display: block; + white-space: nowrap; + + & > img { + vertical-align: middle; + border-radius: $widget_border_radius; + display: inline; + margin: 2px 6px 0 5px; + } + + & > span { + color: darkslateblue; + vertical-align: middle; + display: inline; + margin-top: 11px; + margin-right: 9px; + padding: 0; + } + } } } @@ -198,11 +232,10 @@ header { } #navigation { - position: fixed; + position: relative; top: 0; left: 0; - right: 10px; - height: $navbar_height; + right: 0; } nav { @@ -229,12 +262,6 @@ nav { text-transform: uppercase; position: relative; - &.home-nav-element { - display: flex; - align-items: center; - margin-right: 1em; - } - &.home-nav-element a { padding: 0; height: 44px; @@ -247,12 +274,14 @@ nav { } a, button { - display: flex; - height: 100%; - align-items: center; - gap: 2px; + display: inline-block; text-decoration: none; + vertical-align: middle; color: black; + padding: 13px 7px; + height: 18px; + border-top: 2px solid transparent; + border-bottom: 2px solid transparent; font-weight: bold; font-size: initial; @@ -261,16 +290,17 @@ nav { } &:hover { - border-top: 2px solid $theme_color; - color: black; + border-top: 2px solid #9c3706; + color: lightcoral; background: rgba(255, 255, 255, 0.25); margin: 0; } &.active { // color: #FFF; - border-top: 2px solid $theme_color; - color: $theme_color; + border-top: 2px solid #9c3706; + color: #9c3706; + background: white; } .nav-expand { @@ -284,22 +314,25 @@ nav { left: 5px; display: none; color: #fff; - background: white; + background: darkcyan; margin: 0 !important; box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4); + li { + &:first-child { + a.active { + border-top: 1px solid $widget_black; + } + } + } li { - &:hover { - background: lightgray; - } - display: block; a { - color: black !important; + color: white !important; } - + a, button { padding: 8px 20px 8px 8px !important; font-size: 0.8em; @@ -332,6 +365,16 @@ nav { } } } + + .nav-divider { + width: 1px; + vertical-align: middle; + padding-left: 3px; + display: inline-block; + height: 32px; + margin-right: 1px; + border-right: 3px solid rgba(255, 255, 255, 0.15); + } } hr { @@ -339,8 +382,7 @@ hr { } #content { - margin: $navbar_height auto 1em auto; - padding-top: 1em; + margin: 1em auto auto; // Header width: 90%; @@ -410,7 +452,6 @@ noscript #noscript { .toggle { font-weight: bold; - cursor: pointer; .fa { transition: transform 0.4s; @@ -435,6 +476,7 @@ noscript #noscript { #nav-placeholder { height: 47px; + max-width: 107em; background: white; border-right: 1px solid $border_gray; border-left: 1px solid $border_gray; @@ -442,6 +484,8 @@ noscript #noscript { #contest-info { font-size: 1.25em; + border: 5px solid $highlight_blue; + border-radius: 0 $widget_border_radius $widget_border_radius 0; z-index: 100000; cursor: move; position: fixed; @@ -495,12 +539,32 @@ noscript #noscript { flex: 1 1 1px; } +#user-links { + height: 100%; + + ul { + margin: 0; + + li { + display: block; + height: 100%; + + a { + display: block; + padding: 0; + height: 100%; + } + } + } +} + #page-container { min-height: 100%; position: relative; margin: 0 auto; border-right: 1px solid $border_gray; border-left: 1px solid $border_gray; + background: white; } // border-bottom: 1px solid rgb(204, 204, 204) @@ -518,6 +582,16 @@ noscript #noscript { margin-top: 1.2em; } +math { + font-size: 1.155em; +} + +.MathJax { + &:focus { + outline: none; + } +} + @media(max-width: 1498px) { #page-container { border-left: none; @@ -527,24 +601,21 @@ noscript #noscript { } } -@media (max-width: 799px) { +@media (max-width: 760px) { #navigation { - height: $navbar_height_mobile; - } - - #content { - margin-top: $navbar_height_mobile; + height: 36px; } #navicon { transition-duration: 0.25s; - display: flex; + display: block; + line-height: 26px; font-size: 2em; color: $widget_black; padding: 0 0.25em; margin: 4px 0.25em; white-space: nowrap; - flex-grow: 1; + float: left; &.hover { color: #4db7fe; @@ -557,7 +628,6 @@ noscript #noscript { display: none; padding: 0; margin-left: 0; - text-align: center; border-left: 4px solid $highlight_blue; position: fixed; top: 36px; @@ -574,8 +644,6 @@ noscript #noscript { a { display: block; font-weight: normal; - text-align: left; - padding: 7px 13px; .nav-expand { float: right; @@ -598,13 +666,30 @@ noscript #noscript { } } } + + #user-links { + & > ul > li { + & > a > span { + padding-top: 4px; + height: 32px; + } + + & > ul { + left: 0 !important; + margin-top: 0 !important; + } + } + } + + #content { + width: auto; + padding: 0 5px; + } } -@media(min-width: 800px) { +@media not all and (max-width: 760px) { #nav-list { - display: flex !important; - gap: 1.5em; - flex-grow: 1; + display: inline !important; li { &.home-menu-item { @@ -624,90 +709,18 @@ noscript #noscript { #notification { color: lightsteelblue; -} - -#notification:hover { - color: darkgray; + float: left; + margin-top: 0.6em; + margin-right: 0.8em; + font-size: 1.3em; } #chat-icon { - color: darkgreen; -} - -#chat-icon:hover { - color: $theme_color; -} - -#nav-lang-icon { - color: blue; - cursor: pointer; -} -#nav-lang-icon:hover { - color: darkblue; -} - -#nav-darkmode-icon { - cursor: pointer; - &:hover { - color: gray; - } -} - -.dropdown { - display: none; - background-color: white; - min-width: 160px; - box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); - padding: 4px 0; - z-index: 1; - border-radius: 5px; - - a { - display: block; - text-decoration: none; - transition: background-color 0.3s; - color: black; - } -} - -.dropdown-item { - font-size: 16px; - padding: 6px 40px 6px 15px; - cursor: pointer; - color: black; - font-weight: 600; - border-top: 1px solid #ccc; - - i { - width: 1.5em; - } -} - -.dropdown-item:hover { - color: $theme_color; - background-color: #f8f8f2; -} - -.popper-arrow, -.popper-arrow::before { - position: absolute; - width: 8px; - height: 8px; - background: inherit; -} - -.popper-arrow { - visibility: hidden; -} - -.popper-arrow::before { - visibility: visible; - content: ''; - transform: rotate(45deg); -} - -.popper-arrow { - top: -4px; + color: lightseagreen; + float: left; + margin-top: 0.6em; + margin-right: 0.5em; + font-size: 1.3em; } .unread_boxes { @@ -720,228 +733,12 @@ noscript #noscript { font-family: monospace; } -.sub-lang { - color: black; - font-size: x-small; - margin-left: -12px; - font-family: monospace; - text-transform: uppercase; +@media (max-width: 500px) { + #notification { + margin-top: 0.6em; + } } -.featherlight { - z-index: 1001 !important; -} - -// @media (max-width: 500px) { - // #notification { - // margin-top: 0.6em; - // } - // } - .notification-open #notification { color: green !important; -} - -.title-row { - color: #393630; - display: inline; -} - -.gray { - color: gray; -} - -.white { - color: white; -} - -.black { - color: black; -} - -.red { - color: red; -} - -.green { - color: green; -} - -.grayed { - color: #666; -} - -.darkcyan { - color: darkcyan; -} - -.peru { - color: peru; -} - -.blue { - color: blue; -} - -.background-d6e8f7 { - background-color: #d6e8f7; -} - -.background-bisque { - background-color: bisque; -} - -.background-royalblue { - background-color: royalblue !important; -} - -.background-green { - background-color: #28a745 !important; -} - -.background-red { - background-color: #dc3545 !important; -} - -.background-footer { - color: #808080; -} - -.view-next-page { - margin-left: auto; - margin-right: auto; - margin-top: 1em; - margin-bottom: 1em; -} - -#loading-bar { - position: fixed; - top: 0; - left: 0; - height: 2px; - background-color: $theme_color; - width: 0; - z-index: 9999; -} - -.nav-right-text { - font-weight: normal; - font-size: small; - text-align: center; -} - -.anon { - display: flex; - gap: 1em; - padding-right: 1em; - a { - color: black; - } -} - -@media (max-width: 799px) { - .nav-fa-icon { - display: none; - } - - .page-title { - margin-left: 0.5em; - } -} - -@media(min-width: 800px) { - .normal-text { - font-weight: normal; - font-size: small; - text-align: left; - } - #page-container { - background: #f1f2f2; - } - #event-tab { - display: none; - } - #content.wrapper { - background: white; - padding: 2em; - border-radius: 1em; - } - .view-next-page { - display: none; - } -} - -.colored-text { - color: black; -} - -.bold-text { - font-weight: bold; -} - -.non-italics { - font-style: normal; -} - -.margin-label{ - margin-bottom: 2.5px; - padding-bottom: 0.25em; - display: block; -} - -::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ - color: $theme_color; - opacity: 1; /* Firefox */ - text-align: center; -} - -:-ms-input-placeholder { /* Internet Explorer 10-11 */ - color: $theme_color; - text-align: center; -} - -::-ms-input-placeholder { /* Microsoft Edge */ - color: $theme_color; - text-align: center; -} - -input::placeholder{ - color: $theme_color; - text-align: center; -} - -::-webkit-input-placeholder { - color: $theme_color; - text-align: center; -} - -:-moz-placeholder { /* Firefox 18- */ - color: $theme_color; - text-align: center; -} - -::-moz-placeholder { /* Firefox 19+ */ - color: $theme_color; - text-align: center; -} - -.nav-fa-icon { - i { - margin-right: 0.1em; - color: #000; - font-size: 21px; - } -} - -.nav-fa-icon-active { - i { - color: $theme_color; - font-size: 22.5px; - margin-right: 0.1em; - } -} - -.featherlight-content { - max-height: 80% !important; - border-radius: 10px; } \ No newline at end of file diff --git a/resources/blog.scss b/resources/blog.scss index 1df8dc6..cd6aeb2 100644 --- a/resources/blog.scss +++ b/resources/blog.scss @@ -1,10 +1,10 @@ @import "vars"; -.middle-content { +.blog-content { padding-right: 0em; + flex: 73.5%; vertical-align: top; margin-right: 0; - width: 100%; .post { border: 1px dotted grey; @@ -32,14 +32,8 @@ } } -.left-sidebar-item.active { - color: white; - font-weight: bold; - background-color: $theme_color; - - .sidebar-icon { - color: white; - } +.blog-sidebar { + flex: 26.5%; } .blog-sidebox { @@ -49,6 +43,7 @@ } ul { + list-style: none; padding-left: 1em; padding-right: 0.5em; @@ -81,12 +76,6 @@ } } -.no-dot-blog-sidebox { - ul { - list-style: none; - } -} - .blog-comment-count { font-size: 12px; } @@ -99,6 +88,23 @@ color: #555; } +@media (min-width: 800px) { + .blog-content, .blog-sidebar { + display: block !important; + } + + .blog-content { + margin-right: 1em !important; + } + + #mobile.tabs { + display: none; + } + + #blog-container { + display: flex; + } +} #mobile.tabs { margin: 0; @@ -129,275 +135,3 @@ } } } - -.blog-box { - border-bottom: 1.4px solid lightgray; - border-top: 1.4px solid lightgray; - margin-bottom: 1.5em; - padding: 1em 1.25em 0.5em 1.25em; - background-color: white; - margin-left: auto; - margin-right: auto; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); - - .title { - margin-bottom: 0.2em; - font-weight: 500; - } -} - -.blog-box:hover, .blog-box:not(.pre-expand-blog) { - border-color: #8a8a8a; - box-shadow: 0 0 2px rgba(0, 0, 0, 0.1); -} - -.blog-description { - max-height: 30em; - overflow: hidden; - overflow-wrap: anywhere; - padding-bottom: 1em; - clear: both; - position: relative; -} - -.problem-feed-name { - display: inline; - font-weight: bold; -} - -.problem-feed-name a { - color: #0645ad; -} - -.problem-feed-info-entry { - display: inline; - float: right; -} - -.problem-feed-types { - color: gray; -} - -.left-sidebar-item { - display: flex; - align-items: center; - border-radius: .5em; - color: black; - - .sidebar-icon { - font-size: large; - display: inline-block; - - i { - width: 1.4em; - } - } -} - -.left-sidebar-item:hover { - background-color: #e3e3e3; - cursor: pointer; - color: black; -} - -.left-sidebar-item.active:hover { - background-color: $theme_color; - color: white; -} - -.sidebar-icon { - color: black; -} - -.left-sidebar-header { - text-align: center; - padding-bottom: 1em; - border-bottom: 1px solid black; - color: black; - border-radius: 0; -} - -.feed-table { - margin: 0; -} - -.pre-expand-blog { - position: relative; - padding-bottom: 0; -} - -.show-more { - display: flex; - color: black; - font-size: 16px; - font-weight: 700; - padding: 0px 12px; - margin-top: 5px; - position: absolute; - inset: 50% 0px 0px; - background: linear-gradient(transparent, white); - -webkit-box-pack: end; - justify-content: flex-end; - align-items: flex-end; - cursor: pointer; - padding: 16px 16px; -} - -.actionbar-box { - margin: 8px 16px; -} - -.post-full { - .post-title { - font-weight: bold; - margin-bottom: 10px; - font-family: serif; - } -} - -.middle-right-content.wrapper { - padding: 1em 0; - background: white; - border-radius: 1em; -} - -.post-content-header { - margin-left: 0; - display: inline-flex; - align-items: center; - gap: 0.2em; -} - - -@media (max-width: 799px) { - .blog-sidebar, - .right-sidebar { - width: 100%; - margin-left: auto; - margin-top: 2em; - } - - .actionbar-box { - margin: 8px 0; - } - - .left-sidebar-header { - display: none; - } - - .left-sidebar-item { - padding: 0.8em 0.2em 0.8em 0.2em; - display: inline-block; - flex: 1; - min-width: 5em; - overflow-wrap: anywhere; - - .sidebar-icon { - display: none; - } - } - - .left-sidebar { - text-align: center; - margin-bottom: 1em; - border-radius: 7px; - display: flex; - background: inherit; - gap: 0.3em; - overflow-x: auto; - } - - .blog-box { - padding-left: 5%; - padding-right: 5%; - margin-bottom: 0; - } - - .post-title { - font-size: 2em; - } -} - -@media (min-width: 800px) { - .left-sidebar-item { - margin-bottom: 10px; - margin-left: 10px; - border: 1px solid lightgray; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); - background-color: white; - padding: 0.8em 0.2em 0.8em 0.8em; - } - - .sidebar-text { - overflow: hidden; - text-overflow: ellipsis; - } - - .middle-content, - .blog-sidebar, - .right-sidebar { - display: block !important; - } - - .blog-sidebar, - .right-sidebar { - flex: 25%; - max-width: 25%; - } - - .middle-content { - margin-right: 2% !important; - } - - #mobile.tabs { - display: none; - } - - #three-col-container { - display: flex; - flex-direction: column; - } - - .middle-content { - flex: 75%; - max-width: 75%; - } - - .left-sidebar { - width: 11%; - max-width: 11%; - min-width: 11%; - position: fixed; - height: calc(100vh - $navbar_height - 20px); - overflow-y: auto; - - &::-webkit-scrollbar { - width: 0; - background-color: transparent; - } - } - - .feed-table { - font-size: small; - } - - .blog-box { - border-left: 1.4px solid lightgray; - border-right: 1.4px solid lightgray; - border-radius: 16px; - } - - .post-full { - width: 80%; - margin-left: auto; - margin-right: auto; - - .content-description { - font-size: 18px; - } - - .post-title { - font-size: 2.5em; - } - } -} diff --git a/resources/bootstrap/bootstrap.min.css b/resources/bootstrap/bootstrap.min.css deleted file mode 100644 index d65c66b..0000000 --- a/resources/bootstrap/bootstrap.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 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:"\2a"}.glyphicon-plus:before{content:"\2b"}.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:thin dotted;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:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.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[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:14.33px;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:thin dotted;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,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,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,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,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,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,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,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,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,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,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,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,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-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-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-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:3;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:2;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{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{min-height:16.43px;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);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:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{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,.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,.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/resources/chatbox.scss b/resources/chatbox.scss index 6c4f671..1ead74a 100644 --- a/resources/chatbox.scss +++ b/resources/chatbox.scss @@ -1,8 +1,3 @@ -@import "vars"; - -.chat { - background: white; -} #chat-log p { margin: 0; padding-top: 0.1em; @@ -13,6 +8,15 @@ float: right; margin-right: 1em; } +#emoji-button { + position: absolute; + right: 1em; + font-size: 2em; + color: lightgray; +} +#emoji-button:hover { + color: gray; +} #chat-log { padding: 0; padding-top: 2em; @@ -27,20 +31,21 @@ margin-top: 1em; } .big-emoji { - font-size: 1.2em; + font-size: 16px; } #chat-online { border-right: 1px solid #ccc; padding-bottom: 0 !important; + min-width: 25%; border-bottom: 0; - font-size: 1.2em; } #chat-online-content { + margin-top: 0.5em; margin-bottom: 0; overflow: hidden; overflow-wrap: break-word; overflow-y: auto; - max-height: 100%; + max-height: 77vh; } #chat-box { /*border: 1px solid #ccc;*/ @@ -51,101 +56,110 @@ overflow-y: scroll; border-bottom-left-radius: 0; border-bottom-right-radius: 0; - flex-grow: 1; - padding-left: 0.5em; + height: 75%; } #chat-input { + width: 100%; + padding: 0.4em 4em 0.6em 1.2em; + border: 0; color: black; - border: 2px solid black; -} -#chat-input::placeholder { - color: grey; + border-top-left-radius: 0; + border-top-right-radius: 0; + height: 100%; + font-size: 16px; } #chat-online-content { padding: 0; width: 100%; } -.selected-status-row { - background-color: lightgray; -} -.status_last_message { - color: darkgray; - font-size: 0.8em; -} - @media (min-width: 800px) { #chat-container { display: flex; width: 100%; - height: calc(100vh - $navbar_height); + height: 90vh; border: 1px solid #ccc; /*border-radius: 0 4px 0 0;*/ border-bottom: 0; } #chat-online { margin: 0; - min-width: 30%; - max-width: 30%; } - #chat-area { - flex-grow: 1; - min-width: 70%; - max-width: 70%; + .chat-left-panel, .chat-right-panel { + display: block !important; } } #chat-input, #chat-log .content-message { - font-family: "Noto Sans", Arial, "Lucida Grande", sans-serif; + font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif; } .info-pic { - height: 95%; - width: 100%; + height: 90%; + border-radius: 50%; + padding: 0.05em; + border: 0.1px solid #ccc; + margin-left: 3em; + margin-bottom: 1.5px; +} +.info-circle { + position: absolute; + cx: 86%; + cy: 80%; + r: 6px; + stroke: white; + stroke-width: 1; } - .info-name { margin-left: 10px; - font-size: 1.8em; + font-size: 2em; font-weight: bold !important; display: flex; - align-items: center; } .info-name a { display: table-caption; } #chat-info { + border-bottom: 2px solid darkgray; display: flex; - align-items: center; - box-shadow: 0px 2px 3px rgb(0 0 0 / 20%); - position: relative; - z-index: 100; +} +#refresh-button { + padding: 0; + margin-left: auto; + margin-right: 0.3em; + background: transparent; + border: none; + height: 1.5em; + width: 1.5em; +} +#refresh-button:hover { + background: lightgreen; + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + transition: 1.5s ease-in-out; } .status-pic { - height: 32px; - width: 32px; - border-radius: 50%; + height: 1.3em; + width: 1.3em; + border-radius: 0.3em; } .status-container { position: relative; display: inline-flex; - flex: 0 0 auto; - align-items: center; } .status-circle { - position: absolute; - bottom: 0; + position: absolute; + bottom: 0; right: 0; - cx: 27px; - cy: 27px; + cx: 18px; + cy: 18px; r: 4.5px; stroke: white; stroke-width: 1; } .status-row { display: flex; - padding: 15px; - padding-right: 0; - gap: 0.5em; - border-radius: 6px; + font-size: 15px; + padding: 0.2em 0.2em 0.2em 1em; + border-radius: 4px; } .status-row:hover { background: lightgray; @@ -153,7 +167,6 @@ } .status-list { padding: 0; - margin: 0; } .status-section-title { cursor: pointer; @@ -161,11 +174,9 @@ } .message-text { padding: 0.4em 0.6em 0.5em; - border-radius: 20px; + border-radius: 15px; max-width: 70%; width: fit-content; - font-size: 1rem; - line-height: 1.2; } .message-text-other { background: #eeeeee; @@ -175,56 +186,12 @@ background: rgb(0, 132, 255); color: white; } -.chat-input-icon { - color: white; - background-color: #3c8262; -} -.chat-input-icon:hover { - background: #57b28b; -} -.chat { - .active-span { - color: #636363; - margin-right: 1em; - } - - .unread-count { - color: white; - background-color: darkcyan; - border-radius: 50%; - align-self: center; - flex: 0 0 1.25rem; - height: 1.25rem; - font-size: smaller; - display: flex; - align-items: center; - justify-content: center; - } - .setting-content { - display: none; - position: absolute; - background-color: #f1f1f1; - min-width: 160px; - box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); - z-index: 1; - right: 0; - } - .setting-content a { - padding: 12px 16px; - text-decoration: none; - display: block; - font-weight: bold; - font-size: 1rem; - } - .setting-content a:hover { - background-color: #ddd; - cursor: pointer; - } -} @media (max-width: 799px) { #chat-area { - height: calc(100vh - $navbar_height_mobile); - max-height: -webkit-fill-available; + height: 500px; + } + #emoji-button { + display: none; } } diff --git a/resources/comments.scss b/resources/comments.scss index bceb225..8494cf1 100644 --- a/resources/comments.scss +++ b/resources/comments.scss @@ -21,135 +21,6 @@ a { h2 { margin-bottom: 20px; } - - .revision-text p:first-child { - margin-top: 0; - } - - .revision-text p:last-child { - margin-bottom: 0; - } - - .featherlight-edit .featherlight-content { - background: #FAFAFA; - padding: 10px 15px 10px 10px; - border-radius: 10px; - border: 1px solid #CCC; - } - - .comment-img { - display: flex; - margin-right: 4px; - height: 1.5em; - width: 1.5em; - } - - .new-comments .comment-display { - display: flex; - margin-top: -0.25em !important; - padding-left: 1em; - padding-top: 0.5em !important; - border: 1px solid #ccc; - background: #fafafa; - } - - .new-comments .comment .detail { - width: 100%; - padding-right: 1em; - } - - .new-comments .comment-edits { - padding-right: 0.75em; - } - - .new-comments .comment .detail .header { - display: flex; - flex-wrap: wrap; - padding: 2px 0px; - font-weight: normal; - border-bottom: 1px #888 solid; - color: #888; - text-align: right; - align-items: center; - } - - .previous-revision, .next-revision { - color: #444; - } - - .new-comments .header i { - color: #888 !important; - } - - .new-comments .info { - padding-top: 0.4em; - display: flex; - } - - .new-comments .gravatar { - width: 1.5em; - border-radius: 50%; - } - - .new-comments .vote { - margin-right: 1em; - height: 75px; - padding-top: 0.4em; - } - - .new-comments .comment-display { - border-radius: 4px; - } - - .new-comments .comment:target > .comment-display { - border: 1px solid #2980b9; - border-left: 10px solid #2980b9; - padding-left: 5px; - } - - .comments.top-level-comments { - margin-bottom: -3px; - } - - .bad-comment { - opacity: 0.3; - } - - .bad-comment:hover { - opacity: 1; - /* This is necessary to prevent random flickering */ - -webkit-transform: translatez(0); - -moz-transform: translatez(0); - -ms-transform: translatez(0); - -o-transform: translatez(0); - transform: translatez(0); - } - .reply-comment { - margin: -50px 23px 10px -40px; - padding-top: 50px; - } - - .show_more_reply { - font-weight: bold; - display: flex; - margin: 16px 40px; - align-items: center; - - svg { - margin-right: 4px; - } - } - - .show_more_comment { - font-weight: bold; - display: flex; - margin: 16px -40px; - align-items: center; - - svg { - margin-right: 4px; - } - } } .no-comments-message { @@ -187,7 +58,6 @@ a { } .comment-operation { - flex: auto; float: right; .fa { @@ -208,10 +78,17 @@ a { } .comment-post-wrapper { + div { + padding-bottom: 2px; + padding-right: 10px; + } + input, textarea { min-width: 100%; max-width: 100%; - font-size: 15px; + + // Hack for 4k on Chrome + font-size: $base_font_size; } } @@ -257,71 +134,6 @@ a { .comment-body { word-wrap: break-word; } - -.actionbar { - display: flex; - - .actionbar-button { - cursor: pointer; - padding: 0.8em; - border-radius: 5em; - font-weight: bold; - display: inherit; - background: lightgray; - } - .actionbar-block { - display: flex; - flex: 1; - justify-content: center; - } - .pagevote-score { - margin-right: 0.3em; - } - .like-button { - padding-right: 0.5em; - border-radius: 5em 0 0 5em; - } - .actionbar-button:hover { - background: darkgray; - } - .dislike-button { - padding-left: 0.5em; - border-radius: 0 5em 5em 0; - border-left: 0; - } - .like-button.voted { - color: blue; - } - .dislike-button.voted { - color: red; - } - .actionbar-text { - padding-left: 0.4em; - } - .bookmarked { - color: rgb(180, 180, 7); - } -} - -.show_more_comment { - margin-left: -20px; -} - -.pagedown-image-upload { - .submit-input { - display: flex; - min-width: inherit; - float: right; - } - .deletelink-box { - position: absolute; - top: 2px; - right: 1em; - } -} - -@media (max-width: 799px) { - .hide_texts_on_mobile .actionbar-text { - display: none; - } +.highlight { + background: #fff897; } \ No newline at end of file diff --git a/resources/common.js b/resources/common.js index 2cf67b9..1fed1ce 100644 --- a/resources/common.js +++ b/resources/common.js @@ -28,6 +28,46 @@ if (!String.prototype.endsWith) { }; } +// http://stackoverflow.com/a/1060034/1090657 +$(function () { + var hidden = 'hidden'; + + // Standards: + if (hidden in document) + document.addEventListener('visibilitychange', onchange); + else if ((hidden = 'mozHidden') in document) + document.addEventListener('mozvisibilitychange', onchange); + else if ((hidden = 'webkitHidden') in document) + document.addEventListener('webkitvisibilitychange', onchange); + else if ((hidden = 'msHidden') in document) + document.addEventListener('msvisibilitychange', onchange); + // IE 9 and lower: + else if ('onfocusin' in document) + document.onfocusin = document.onfocusout = onchange; + // All others: + else + window.onpageshow = window.onpagehide + = window.onfocus = window.onblur = onchange; + + function onchange(evt) { + var v = 'window-visible', h = 'window-hidden', evtMap = { + focus: v, focusin: v, pageshow: v, blur: h, focusout: h, pagehide: h + }; + + evt = evt || window.event; + if (evt.type in evtMap) + document.body.className = evtMap[evt.type]; + else + document.body.className = this[hidden] ? 'window-hidden' : 'window-visible'; + + if ('$' in window) + $(window).trigger('dmoj:' + document.body.className); + } + + // set the initial state (but only if browser supports the Page Visibility API) + if (document[hidden] !== undefined) + onchange({type: document[hidden] ? 'blur' : 'focus'}); +}); function register_toggle(link) { link.click(function () { @@ -44,11 +84,11 @@ function register_toggle(link) { }); } -function register_all_toggles() { +$(function register_all_toggles() { $('.toggle').each(function () { register_toggle($(this)); }); -}; +}); function featureTest(property, value, noPrefixes) { var prop = property + ':', @@ -81,6 +121,61 @@ window.fix_div = function (div, height) { }); }; +$(function () { + if (typeof window.orientation !== 'undefined') { + $(window).resize(function () { + var width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); + // $('#viewport').attr('content', width > 480 ? 'initial-scale=1' : 'width=480'); + }); + } + + var $nav_list = $('#nav-list'); + $('#navicon').click(function (event) { + event.stopPropagation(); + $nav_list.toggle(); + if ($nav_list.is(':hidden')) + $(this).blur().removeClass('hover'); + else { + $(this).addClass('hover'); + $nav_list.find('li ul').css('left', $('#nav-list').width()).hide(); + } + }).hover(function () { + $(this).addClass('hover'); + }, function () { + $(this).removeClass('hover'); + }); + + $nav_list.find('li a .nav-expand').click(function (event) { + event.preventDefault(); + $(this).parent().siblings('ul').css('display', 'block'); + }); + + $nav_list.find('li a').each(function () { + if (!$(this).siblings('ul').length) + return; + $(this).on('contextmenu', function (event) { + event.preventDefault(); + }).on('taphold', function () { + $(this).siblings('ul').css('display', 'block'); + }); + }); + + $nav_list.click(function (event) { + event.stopPropagation(); + }); + + $('html').click(function () { + $nav_list.hide(); + }); + + $.ajaxSetup({ + beforeSend: function (xhr, settings) { + if (!(/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type)) && !this.crossDomain) + xhr.setRequestHeader('X-CSRFToken', $.cookie('csrftoken')); + } + }); +}); + if (!Date.now) { Date.now = function () { return new Date().getTime(); @@ -147,8 +242,17 @@ function register_time(elems, limit) { // in hours }); } +$(function () { + register_time($('.time-with-rel')); + + $('form').submit(function (evt) { + // Prevent multiple submissions of forms, see #565 + $("input[type='submit']").prop('disabled', true); + }); +}); + window.notification_template = { - icon: '/logo.svg' + icon: '/logo.png' }; window.notification_timeout = 5000; @@ -230,6 +334,13 @@ window.register_contest_notification = function(url) { console.log("Fail to update clarification"); }) .done(function(data) { + try { + JSON.parse(data); + } + catch (e) { + return; + } + for (i of data) { window.notify_clarification(i); } @@ -242,6 +353,14 @@ window.register_contest_notification = function(url) { setInterval(get_clarifications, 60 * 1000); } +$(function () { + // Close dismissable boxes + $("a.close").click(function () { + var $closer = $(this); + $closer.parent().fadeOut(200); + }); +}); + $.fn.textWidth = function () { var html_org = $(this).html(); var html_calc = '' + html_org + ''; @@ -251,375 +370,7 @@ $.fn.textWidth = function () { return width; }; -function registerPopper($trigger, $dropdown) { - const popper = Popper.createPopper($trigger[0], $dropdown[0], { - placement: 'bottom-end', - modifiers: [ - { - name: 'offset', - options: { - offset: [0, 8], - }, - }, - ], - }); - $trigger.click(function(e) { - $dropdown.toggle(); - popper.update(); - }); - $dropdown.css("min-width", $trigger.width() + 'px'); - - $(document).on("click touchend", function(e) { - var target = $(e.target); - if (target.closest($trigger).length === 0 && target.closest($dropdown).length === 0) { - $dropdown.hide(); - } - }) -} - -function populateCopyButton() { - var copyButton; - $('pre code').each(function () { - var copyButton = $('', { - 'class': 'btn-clipboard', - 'data-clipboard-text': $(this).text(), - 'title': 'Click to copy' - }).append(''); - - if ($(this).parent().width() > 100) { - copyButton.append('Copy'); - } - - $(this).before($('
', {'class': 'copy-clipboard'}) - .append(copyButton)); - - $(copyButton.get(0)).mouseleave(function () { - $(this).attr('class', 'btn-clipboard'); - $(this).removeAttr('aria-label'); - }); - - var curClipboard = new Clipboard(copyButton.get(0)); - - curClipboard.on('success', function (e) { - e.clearSelection(); - showTooltip(e.trigger, 'Copied!'); - }); - - curClipboard.on('error', function (e) { - showTooltip(e.trigger, fallbackMessage(e.action)); - }); - }); -} - -function register_copy_clipboard($elements, callback) { - $elements.on('paste', function(event) { - const items = (event.clipboardData || event.originalEvent.clipboardData).items; - for (const index in items) { - const item = items[index]; - if (item.kind === 'file' && item.type.indexOf('image') !== -1) { - const blob = item.getAsFile(); - const formData = new FormData(); - formData.append('image', blob); - - $(this).prop('disabled', true); - - $.ajax({ - url: '/pagedown/image-upload/', - type: 'POST', - data: formData, - processData: false, - contentType: false, - success: function(data) { - // Assuming the server returns the URL of the image - const imageUrl = data.url; - const editor = $(event.target); // Get the textarea where the event was triggered - let currentMarkdown = editor.val(); - const markdownImageText = '![](' + imageUrl + ')'; // Markdown for an image - - if (currentMarkdown) currentMarkdown += "\n"; - currentMarkdown += markdownImageText; - - editor.val(currentMarkdown); - callback?.(); - }, - error: function() { - alert('There was an error uploading the image.'); - }, - complete: () => { - // Re-enable the editor - $(this).prop('disabled', false).focus(); - } - }); - - // We only handle the first image in the clipboard data - break; - } - } - }); -} - -function activateBlogBoxOnClick() { - $('.blog-box').on('click', function () { - var $description = $(this).children('.blog-description'); - var max_height = $description.css('max-height'); - if (max_height !== 'fit-content') { - $description.css('max-height', 'fit-content'); - $(this).css('cursor', 'auto'); - $(this).removeClass('pre-expand-blog'); - $(this).children().children('.show-more').hide(); - } - }); - - $('.blog-box').each(function () { - var $precontent = $(this).children('.blog-description').height(); - var $content = $(this).children().children('.content-description').height(); - if ($content == undefined) { - $content = $(this).children().children('.md-typeset').height() - } - if ($content > $precontent - 30) { - $(this).addClass('pre-expand-blog'); - $(this).css('cursor', 'pointer'); - } else { - $(this).children().children('.show-more').hide(); - } - }); -} - -function changeTabParameter(newTab) { - const url = new URL(window.location); - const searchParams = new URLSearchParams(url.search); - searchParams.set('tab', newTab); - searchParams.delete('page'); - url.search = searchParams.toString(); - return url.href; -} - -function submitFormWithParams($form, method) { - const currentUrl = new URL(window.location.href); - const searchParams = new URLSearchParams(currentUrl.search); - const formData = $form.serialize(); - - const params = new URLSearchParams(formData); - - if (searchParams.has('tab')) { - params.set('tab', searchParams.get('tab')); - } - - const fullUrl = currentUrl.pathname + '?' + params.toString(); - - if (method === "GET") { - window.location.href = fullUrl; - } - else { - var $formToSubmit = $('
') - .attr('action', fullUrl) - .attr('method', 'POST') - .appendTo('body'); - - $formToSubmit.append($('').attr({ - type: 'hidden', - name: 'csrfmiddlewaretoken', - value: $.cookie('csrftoken') - })); - - $formToSubmit.submit(); - } -} - -function initPagedown(maxRetries=5) { - // There's a race condition so we want to retry several times - let attempts = 0; - - function tryInit() { - try { - // make sure pagedown_init.js was loaded - if ('DjangoPagedown' in window) { - DjangoPagedown.init(); - } else if (attempts < maxRetries) { - attempts++; - setTimeout(tryInit, 1000); - } - } catch (error) { - // this may happen if Markdown.xyz.js was not loaded - if (attempts < maxRetries) { - attempts++; - setTimeout(tryInit, 1000); - } - } - } - - setTimeout(tryInit, 100); -} - -function navigateTo(url, reload_container, force_new_page=false) { - if (url === '#') return; - if (force_new_page) { - window.location.href = url; - return; - } - - if (!reload_container || !$(reload_container).length) { - reload_container = "#content"; - } - - if (window.currentRequest) { - window.currentRequest.abort(); - } - - const controller = new AbortController(); - const signal = controller.signal; - window.currentRequest = controller; - - $(window).off("scroll"); - - replaceLoadingPage(reload_container); - - const toUpdateElements = [ - "#nav-container", - "#js_media", - "#media", - ".left-sidebar", - "#bodyend", - ]; - - $.ajax({ - url: url, - method: 'GET', - dataType: 'html', - signal: signal, - success: function (data) { - let reload_content = $(data).find(reload_container).first(); - if (!reload_content.length) { - reload_container = "#content"; - reload_content = $(data).find(reload_container).first(); - } - - if (reload_content.length) { - window.history.pushState("", "", url); - $(window).scrollTop(0); - $("#loading-bar").stop(true, true); - $("#loading-bar").hide().css({ width: 0}); - - for (let elem of toUpdateElements) { - $(elem).replaceWith($(data).find(elem).first()); - } - - $(reload_container).replaceWith(reload_content); - - $(document).prop('title', $(data).filter('title').text()); - renderKatex($(reload_container)[0]); - initPagedown(); - onWindowReady(); - registerNavList(); - $('.xdsoft_datetimepicker').hide(); - } else { - window.location.href = url; - } - }, - error: function (xhr, status, error) { - if (status !== 'abort') { // Ignore aborted requests - window.location.href = url; - } - } - }); -} - -function isEligibleLinkToRegister($e) { - if ($e.attr('target') === '_blank') { - return false; - } - if ($e.data('initialized_navigation')) { - return false; - } - const href = $e.attr('href'); - if (!href) return false; - return ( - href.startsWith('http') - || href.startsWith('/') - || href.startsWith('#') - || href.startsWith('?') - ); -}; - -function replaceLoadingPage(reload_container) { - const loadingPage = ` -
- -
- `; - $(reload_container).fadeOut(100, function() { - $(this).html(loadingPage).fadeIn(100); - }); -} - -function registerNavigation() { - const containerMap = { - '.left-sidebar-item': '.middle-right-content', - '.pagination li a': '.middle-content', - '.tabs li a': '.middle-content', - '#control-panel a': '.middle-content', - }; - - $.each(containerMap, function(selector, target) { - $(selector).each(function() { - const href = $(this).attr('href'); - const force_new_page = $(this).data('force_new_page'); - - if (isEligibleLinkToRegister($(this))) { - $(this).data('initialized_navigation', true); - - $(this).on('click', function(e) { - e.preventDefault(); - let containerSelector = null; - for (let key in containerMap) { - if ($(this).is(key)) { - containerSelector = containerMap[key]; - break; - } - } - navigateTo(href, containerSelector, force_new_page); - }); - } - }); - }); -} - -function onWindowReady() { - // http://stackoverflow.com/a/1060034/1090657 - var hidden = 'hidden'; - - // Standards: - if (hidden in document) - document.addEventListener('visibilitychange', onchange); - else if ((hidden = 'mozHidden') in document) - document.addEventListener('mozvisibilitychange', onchange); - else if ((hidden = 'webkitHidden') in document) - document.addEventListener('webkitvisibilitychange', onchange); - else if ((hidden = 'msHidden') in document) - document.addEventListener('msvisibilitychange', onchange); - // IE 9 and lower: - else if ('onfocusin' in document) - document.onfocusin = document.onfocusout = onchange; - // All others: - else - window.onpageshow = window.onpagehide - = window.onfocus = window.onblur = onchange; - - function onchange(evt) { - var v = 'window-visible', h = 'window-hidden', evtMap = { - focus: v, focusin: v, pageshow: v, blur: h, focusout: h, pagehide: h - }; - - evt = evt || window.event; - if (evt.type in evtMap) - document.body.className = evtMap[evt.type]; - else - document.body.className = this[hidden] ? 'window-hidden' : 'window-visible'; - - if ('$' in window) - $(window).trigger('dmoj:' + document.body.className); - } - +$(function () { $('.tabs').each(function () { var $this = $(this), $h2 = $(this).find('h2'), $ul = $(this).find('ul'); var cutoff = ($h2.textWidth() || 400) + 20, handler; @@ -631,124 +382,4 @@ function onWindowReady() { }); handler(); }); - - // set the initial state (but only if browser supports the Page Visibility API) - if (document[hidden] !== undefined) - onchange({type: document[hidden] ? 'blur' : 'focus'}); - - $("a.close").click(function () { - var $closer = $(this); - $closer.parent().fadeOut(200); - }); - - register_time($('.time-with-rel')); - - if (typeof window.orientation !== 'undefined') { - $(window).resize(function () { - var width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); - // $('#viewport').attr('content', width > 480 ? 'initial-scale=1' : 'width=480'); - }); - } - - $.ajaxSetup({ - beforeSend: function (xhr, settings) { - if (!(/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type)) && !this.crossDomain) - xhr.setRequestHeader('X-CSRFToken', $.cookie('csrftoken')); - } - }); - - register_copy_clipboard($('textarea.wmd-input')); - - $('form').submit(function (evt) { - // Prevent multiple submissions of forms, see #565 - $("input[type='submit']").prop('disabled', true); - }); - - $('.lang-dropdown-item').click(function() { - $('select[name="language"]').val($(this).attr('value')); - $('#form-lang').submit(); - }) - $('#logout').on('click', () => $('#logout-form').submit()); - - populateCopyButton(); - - $('a').click(function() { - var href = $(this).attr('href'); - var target = $(this).attr('target'); - if (!href || href === '#' || href.startsWith("javascript") || - $(this).attr("data-featherlight") || - target === "_blank" - ) { - return; - } - - $("#loading-bar").show(); - $("#loading-bar").animate({ width: "100%" }, 2000, function() { - $(this).stop(true, true); - $(this).hide().css({ width: 0}); - }); - }); - - $('.errorlist').each(function() { - var errorList = $(this); - errorList.nextAll('input, select, textarea').first().after(errorList); - }); - register_all_toggles(); - activateBlogBoxOnClick(); - registerNavigation(); - registerPopper($('#nav-lang-icon'), $('#lang-dropdown')); - registerPopper($('#user-links'), $('#userlink_dropdown')); -} - -function registerNavList() { - var $nav_list = $('#nav-list'); - $('#navicon').click(function (event) { - event.stopPropagation(); - $nav_list.toggle(); - if ($nav_list.is(':hidden')) - $(this).blur().removeClass('hover'); - else { - $(this).addClass('hover'); - $nav_list.find('li ul').css('left', $('#nav-list').width()).hide(); - } - }).hover(function () { - $(this).addClass('hover'); - }, function () { - $(this).removeClass('hover'); - }); - - $nav_list.find('li a .nav-expand').click(function (event) { - event.preventDefault(); - $(this).parent().siblings('ul').css('display', 'block'); - }); - - $nav_list.find('li a').each(function () { - if (!$(this).siblings('ul').length) - return; - $(this).on('contextmenu', function (event) { - event.preventDefault(); - }).on('taphold', function () { - $(this).siblings('ul').css('display', 'block'); - }); - }); - - $nav_list.click(function (event) { - event.stopPropagation(); - }); - - $('html').click(function () { - $nav_list.hide(); - }); -} - -$(function() { - if (typeof window.currentRequest === 'undefined') { - window.currentRequest = null; - } - onWindowReady(); - registerNavList(); - - window.addEventListener('popstate', (e) => { - window.location.href = e.currentTarget.location.href; - }); -}); \ No newline at end of file +}); diff --git a/resources/content-description.scss b/resources/content-description.scss index 4b475f5..d2ff6f5 100644 --- a/resources/content-description.scss +++ b/resources/content-description.scss @@ -1,19 +1,80 @@ @import "vars"; .content-description { - line-height: 1.6em; - font-size: 16px; - font-family: "Segoe UI", "Noto Sans", Arial, "Lucida Grande", sans-serif; - overflow-wrap: anywhere; + line-height: 1.5em; + font-size: 1em; + font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif; - h1, h2, h3, h4, h5, .admonition-title, summary { - font-family: "Noto Sans", "Segoe UI", Arial, "Lucida Grande", sans-serif; + p { + margin: 1em 0 !important; + padding: 0 !important; } + img { max-width: 100%; height: auto; } + h1, h2, h3, h4, h5, h6 { + font-weight: normal; + color: #111; + margin-bottom: 0.75em; + padding: 0; + background: 0; + } + + h3, h4, h5, h6 { + font-weight: bold; + } + + h1 { + font-size: 2.5em; + } + + h2 { + font-size: 2em; + } + + h3 { + font-size: 1.6em; + margin: 0; + padding: 0; + } + + h4 { + font-size: 1.4em; + border-bottom: 1px solid #EEE; + line-height: 1.225; + padding-bottom: 0.3em; + padding-top: 0.5em; + } + + h5 { + font-size: 1.15em; + margin-top: 0; + } + + h6 { + font-size: 0.9em; + } + + blockquote { + color: #666; + margin: 0; + padding-left: 1.5em; + border-left: 0.5em #EEE solid; + } + + hr { + display: block; + height: 0; + border: 0; + font-style: italic; + border-bottom: 1px solid $border_gray; + margin: 25px 0 20px 0; + padding: 0; + } + pre, code, kbd, samp, span.code { color: #000; page-break-inside: avoid; @@ -25,9 +86,11 @@ font-family: $monospace-fonts !important; margin: 0 2px; padding: 0 5px; - background-color: var(--md-code-bg-color); + border: 1px solid $border_gray; + background-color: #f8f8f8; border-radius: $widget_border_radius; - color: var(--md-code-fg-color); + font-size: 0.95em; + color: #444; } pre { @@ -38,28 +101,17 @@ padding: 0; background: transparent; font-size: 1em; - color: var(--md-code-fg-color); - + color: black; } white-space: pre-wrap; word-wrap: break-word; - padding: 0.5em 1em; - background-color: var(--md-code-bg-color); - color: var(--md-code-fg-color); - border-radius: 3px; - } - - pre.no-border { - margin-top: 0.4em; - padding: 0.5em; - border: none; - background-color: inherit; - border-radius: none; - } - - .linenos pre { - padding-right: 0; + margin: 1.5em 0 1.5em 0; + padding: 1em; + border: 1px solid $border_gray; + background-color: #f8f8f8; + color: black; + border-radius: $widget_border_radius; } b, strong { @@ -141,7 +193,7 @@ .linenos { width: 4%; - + pre { color: rgba(0,0,0,.26); background-color: rgba(0,0,0,.07); @@ -167,18 +219,7 @@ width: 100%; } -textarea, -pre { - -moz-tab-size : 4; - -o-tab-size : 4; - tab-size : 4; -} - - -@media (min-width: 800px) { - .content-description pre:has(code) { - min-width: 3em; - } +@media (min-width: 700px) { #common-content { display: flex; flex-direction: row-reverse; @@ -214,7 +255,7 @@ pre { } } -@media not all and (min-width: 800px) { +@media not all and (min-width: 700px) { #content-right .info-float { float: none; width: 100% !important; @@ -223,13 +264,6 @@ pre { } } -@media (max-width: 799px) { - .content-description { - font-size: 16px; - line-height: 1.7em; - } -} - a.view-pdf { padding-top: 0.6em; display: inline-block; @@ -240,7 +274,6 @@ a.view-pdf { display: -webkit-flex; display: -ms-flexbox; display: flex; - align-items: center; .spacer { display: inline-block; @@ -261,3 +294,10 @@ a.view-pdf { details summary { cursor: pointer; } +details[open] p { + background: #def; + padding: 0.3em !important; + border: 1px solid grey; + border-radius: 4px; + margin-top: 0 !important; +} \ No newline at end of file diff --git a/resources/contest.json b/resources/contest.json deleted file mode 100644 index 7f7f91b..0000000 --- a/resources/contest.json +++ /dev/null @@ -1,470 +0,0 @@ -{ - "problem_sub":[6, 3, 3], - "sub_frozen":[6, 3, 3], - "problems":{ - "1":{ - "1":30, - "2":10, - "3":11, - "4":17, - "5":10, - "6":22 - }, - "2":{ - "1":20, - "2":45, - "3":35 - }, - "3":{ - "1":50, - "2":15, - "3":35 - } - }, - "users":{ - "1":{ - "username":"user1", - "name":"Nguyen Van AA", - "school":"School A", - "last_submission":13, - "problems":{ - "1":{ - "points":{ - "1":-1, - "2":-1, - "3":-1, - "4":-1, - "5":-1, - "6":0 - }, - "frozen_points":{ - "1":0, - "2":0, - "3":0, - "4":0, - "5":0, - "6":0 - } - }, - "2":{ - "points":{ - "1":20, - "2":1, - "3":0 - }, - "frozen_points":{ - "1":20, - "2":0, - "3":0 - } - }, - "3":{ - "points":{ - "1":30, - "2":15, - "3":0 - }, - "frozen_points":{ - "1":29, - "2":0, - "3":0 - } - } - } - }, - "2":{ - "username":"user2", - "name":"Nguyen Van AB", - "school":"School B", - "last_submission":37, - "problems":{ - "1":{ - "points":{ - "1":-1, - "2":-1, - "3":-1, - "4":-1, - "5":-1, - "6":0 - }, - "frozen_points":{ - "1":0, - "2":4, - "3":5, - "4":0, - "5":7, - "6":0 - } - }, - "2":{ - "points":{ - "1":0, - "2":21, - "3":35 - }, - "frozen_points":{ - "1":0, - "2":1, - "3":0 - } - }, - "3":{ - "points":{ - "1":50, - "2":15, - "3":19 - }, - "frozen_points":{ - "1":45, - "2":1, - "3":1 - } - } - } - }, - "3":{ - "username":"user3", - "name":"Nguyen Van AC", - "school":"School C", - "last_submission":21, - "problems":{ - "1":{ - "points":{ - "1":14, - "2":7, - "3":0, - "4":17, - "5":10, - "6":22 - }, - "frozen_points":{ - "1":5, - "2":5, - "3":0, - "4":1, - "5":10, - "6":4 - } - }, - "3":{ - "points":{ - "1":-1, - "2":15, - "3":35 - }, - "frozen_points":{ - "1":50, - "2":15, - "3":35 - } - } - } - }, - "4":{ - "username":"user4", - "name":"Nguyen Van AD", - "school":"School D", - "last_submission":79, - "problems":{ - "1":{ - "points":{ - "1":30, - "2":10, - "3":3, - "4":17, - "5":4, - "6":10 - }, - "frozen_points":{ - "1":19, - "2":8, - "3":0, - "4":17, - "5":2, - "6":7 - } - }, - "2":{ - "points":{ - "1":20, - "2":45, - "3":35 - }, - "frozen_points":{ - "1":20, - "2":45, - "3":35 - } - }, - "3":{ - "points":{ - "1":50, - "2":15, - "3":35 - }, - "frozen_points":{ - "1":50, - "2":9, - "3":11 - } - } - } - }, - "5":{ - "username":"user5", - "name":"Nguyen Van AE", - "school":"School E", - "last_submission":36, - "problems":{ - "1":{ - "points":{ - "1":30, - "2":7, - "3":11, - "4":17, - "5":10, - "6":1 - }, - "frozen_points":{ - "1":3, - "2":7, - "3":0, - "4":1, - "5":10, - "6":0 - } - }, - "2":{ - "points":{ - "1":20, - "2":45, - "3":0 - }, - "frozen_points":{ - "1":11, - "2":0, - "3":0 - } - }, - "3":{ - "points":{ - "1":15, - "2":15, - "3":35 - }, - "frozen_points":{ - "1":3, - "2":10, - "3":15 - } - } - } - }, - "6":{ - "username":"user6", - "name":"Nguyen Van AF", - "school":"School F", - "last_submission":17, - "problems":{ - } - }, - "7":{ - "username":"user7", - "name":"Nguyen Van AG", - "school":"School G", - "last_submission":91, - "problems":{ - "1":{ - "points":{ - "1":28, - "2":1, - "3":11, - "4":17, - "5":6, - "6":22 - }, - "frozen_points":{ - "1":7, - "2":0, - "3":1, - "4":9, - "5":1, - "6":7 - } - }, - "2":{ - "points":{ - "1":20, - "2":45, - "3":24 - }, - "frozen_points":{ - "1":20, - "2":13, - "3":0 - } - }, - "3":{ - "points":{ - "1":29, - "2":13, - "3":35 - }, - "frozen_points":{ - "1":10, - "2":0, - "3":35 - } - } - } - }, - "8":{ - "username":"user8", - "name":"Nguyen Van AH", - "school":"School H", - "last_submission":60, - "problems":{ - } - }, - "9":{ - "username":"user9", - "name":"Nguyen Van AI", - "school":"School I", - "last_submission":89, - "problems":{ - "1":{ - "points":{ - "1":17, - "2":10, - "3":1, - "4":17, - "5":5, - "6":13 - }, - "frozen_points":{ - "1":0, - "2":10, - "3":0, - "4":3, - "5":4, - "6":11 - } - } - } - }, - "10":{ - "username":"user10", - "name":"Nguyen Van AJ", - "school":"School J", - "last_submission":10, - "problems":{ - "1":{ - "points":{ - "1":30, - "2":7, - "3":5, - "4":0, - "5":7, - "6":17 - }, - "frozen_points":{ - "1":12, - "2":1, - "3":0, - "4":0, - "5":3, - "6":0 - } - }, - "2":{ - "points":{ - "1":20, - "2":45, - "3":35 - }, - "frozen_points":{ - "1":20, - "2":0, - "3":35 - } - }, - "3":{ - "points":{ - "1":50, - "2":0, - "3":35 - }, - "frozen_points":{ - "1":12, - "2":0, - "3":31 - } - } - } - }, - "11":{ - "username":"user11", - "name":"Nguyen Van AK", - "school":"School K", - "last_submission":61, - "problems":{ - "1":{ - "points":{ - "1":30, - "2":10, - "3":11, - "4":17, - "5":10, - "6":0 - }, - "frozen_points":{ - "1":26, - "2":7, - "3":0, - "4":14, - "5":2, - "6":0 - } - }, - "2":{ - "points":{ - "1":20, - "2":45, - "3":23 - }, - "frozen_points":{ - "1":12, - "2":45, - "3":1 - } - } - } - }, - "12":{ - "username":"user12", - "name":"Nguyen Van AL", - "school":"School L", - "last_submission":49, - "problems":{ - "1":{ - "points":{ - "1":30, - "2":10, - "3":0, - "4":17, - "5":0, - "6":1 - }, - "frozen_points":{ - "1":19, - "2":1, - "3":0, - "4":10, - "5":0, - "6":0 - } - } - } - } - } -} diff --git a/resources/contest.scss b/resources/contest.scss index 30cbca5..a48bfb8 100644 --- a/resources/contest.scss +++ b/resources/contest.scss @@ -1,30 +1,5 @@ @import "vars"; -.list-contest { - box-shadow: 0px 1px 2px lightgrey, 0px 1px 5px lightgrey; - border-radius: 15px; - padding: 20px; - margin-bottom: 20px; - width: 100%; - box-sizing: border-box; - display: flex; - background: white; - - .info-contest:first-child, .info-contest:nth-child(2) { - margin-right: 15px; - } - - .info-contest { - flex: 1; - } - - .contest-title { - font-size: 1.1em; - font-weight: 600; - margin-bottom: 5px; - } -} - #contest-calendar { border-collapse: collapse; width: 100%; @@ -116,6 +91,7 @@ } #banner { + border-bottom: 1px solid rgba(0, 0, 0, 0.2); padding-bottom: 1em; a.date { @@ -123,7 +99,7 @@ text-decoration: none; text-align: center; line-height: 1.3; - font-size: 2em; + font-size: 2.3em; padding-bottom: 0.15em; &:link, &:visited { @@ -135,7 +111,7 @@ } } - .time { + #time { text-align: center; display: block; color: rgb(85, 85, 85); @@ -143,85 +119,89 @@ } } -.time-left { - text-align: left; - padding-bottom: 0.5em; -} +.contest-list { + td { + vertical-align: middle !important; + + &:nth-child(2) { + min-width: 4em; + } + + &:nth-child(3) { + min-width: 6em; + } + } + + tbody tr { + height: 4em; + } + + .floating-time-left { + position: absolute; + left: 0; + } + + .floating-time-right { + position: absolute; + right: 0; + line-height: 1.2em; + } + + .floating-time { + position: absolute; + right: 0; + bottom: 0; + } -.list-contest { .contest-tags { + padding-left: 0.75em; vertical-align: top; - display: flex; - flex-wrap: wrap; - margin-top: 5px; } - .contest-tag-hidden { - background-color: #000000; - color: #ffffff; + .participate-button { + display: inline-block; + width: 90px; } -} -.first-solve { - background: #00f9a1; + .contest-block { + text-align: left; + padding: 0.5em 0.5em 0.5em 1em; + } } .contest-tag { + box-shadow: inset 0 -0.1em 0 rgba(0, 0, 0, 0.12); padding: 0.15em 0.3em; border-radius: 0.15em; font-weight: 600; margin-right: 0.45em; position: relative; - display: flex; - align-items: center; - gap: 0.2em; -} - -.contest-tag-edit { - background-color: green -} - -.contest-tag-private { - background-color: #666666; - color: #ffffff; -} - -.contest-tag-org { - background-color: #cccccc; - - a { - color: #000000; - } -} - -.contest-tag-rated { - background-color: #e54c14; - color: #ffffff; } .contest-list-title { - font-size: 1.1em; + font-size: 18px; font-weight: 600; } -.contest-list-sort { - color: #7dc7ff; -} - form.contest-join-pseudotab { display: inline; + padding: 6px 8px !important; line-height: 1.7em; - margin-left: auto; - float: right; + margin-left: 0.5em; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; input { - padding-left: 5px !important; - padding-right: 5px !important; + display: inline; + border: none; + padding: 0; + background: none; + font-weight: 600; } } .contest-participation-operation { - margin-left: auto; + float: right; .fa { color: #444; @@ -231,14 +211,5 @@ form.contest-join-pseudotab { padding-left: 1px; } - padding-left: 5px; -} - -#add-clarification { - float: left; - color: chartreuse; -} - -#add-clarification:hover { - color: cyan; + padding: 0 5px; } diff --git a/resources/course.scss b/resources/course.scss deleted file mode 100644 index f7c5b2d..0000000 --- a/resources/course.scss +++ /dev/null @@ -1,155 +0,0 @@ -@import "vars"; - -.course-content-title { - font-weight: bold; -} - -.course-list { - width: 100%; - margin: 0 auto; - list-style: none; - padding: 0; - - .course-item { - display: flex; - align-items: center; - border: 1px solid #ddd; - padding: 20px; - margin-bottom: 10px; - border-radius: 8px; - background-color: #fff; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); - transition: transform 0.2s ease-in-out; - } - .course-item:hover { - transform: translateY(-2px); - box-shadow: 0 6px 12px rgba(0,0,0,0.15); - } - .course-image { - flex: 0 0 auto; - width: 50px; - height: 50px; - margin-right: 20px; - border-radius: 5px; - overflow: hidden; - } - .course-image img { - width: 100%; - height: 100%; - object-fit: cover; - border-radius: 5px; - } - .course-content { - flex: 1; - } - .course-name { - font-size: 1.5em; - margin-bottom: 5px; - } -} - -.lesson-list { - list-style: none; - padding: 0; - - li:hover { - box-shadow: 0 6px 12px rgba(0,0,0,0.15); - background: #ffffe0; - } - - li { - background: #fff; - border: 1px solid #ddd; - margin-bottom: 20px; - padding-top: 10px; - border-radius: 5px; - box-shadow: 0 2px 4px #ccc; - } - .lesson-title { - font-size: 1.25em; - margin-left: 1em; - margin-right: 1em; - color: initial; - display: flex; - gap: 1em; - - .lesson-points { - margin-left: auto; - font-size: 0.9em; - align-self: flex-end; - color: #636363; - } - } - .progress-container { - background: #e0e0e0; - border-radius: 3px; - height: 10px; - width: 100%; - margin-top: 10px; - } - .progress-bar { - background: forestgreen; - height: 10px; - border-radius: 3px; - line-height: 10px; - color: white; - text-align: right; - font-size: smaller; - } -} - -.course-problem-list { - list-style-type: none; - padding: 0; - font-size: 15px; - - i { - font-size: large; - } - - li { - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid #eee; - padding: 10px; - border-radius: 5px; - } - .problem-name { - margin-left: 10px; - } - - li:hover { - background: #e0e0e0; - } - .score { - font-weight: bold; - margin-left: auto; - } - a { - text-decoration: none; - color: inherit; - } -} - -.course-contest-card { - border: 1px solid #ddd; - border-radius: 8px; - margin-bottom: 20px; - padding: 15px; - box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.1); - display: flex; - justify-content: space-between; - align-items: center; - - h5 { - margin: 0 0 10px; - font-size: 1.2em; - color: #333; - } - - p { - margin: 5px 0; - color: #555; - } -} \ No newline at end of file diff --git a/resources/darkmode-svg.css b/resources/darkmode-svg.css deleted file mode 100644 index 7a01444..0000000 --- a/resources/darkmode-svg.css +++ /dev/null @@ -1,25 +0,0 @@ -/* Add css `style="filter:invert(100%)"` for all , then use btoa to convert in js */ -.wmd-latex-button-display { - background-image: url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8bWV0YWRhdGE+IFN2ZyBWZWN0b3IgSWNvbnMgOiBodHRwOi8vd3d3Lm9ubGluZXdlYmZvbnRzLmNvbS9pY29uIDwvbWV0YWRhdGE+CjxnPjxwYXRoIHN0eWxlPSJmaWx0ZXI6aW52ZXJ0KDEwMCUpIiBkPSJNMTk1LjMsNDI1LjZsLTAuNi04Ny44aDEyOS44bDgtNTguMWM2LjYtNjUuNCwyOC43LTEyNS44LDY2LjQtMTgxLjJDNDM2LjUsNDMsNDk5LjEsMTMuNSw1ODYuNywxMGM0MC4yLDEuOSw2OS45LDguMyw4OS4yLDE5LjFsLTIxLDgxLjRjLTE5LjUtOC00Mi4yLTEyLjItNjguMi0xMi42Yy00NC4xLDEuNS03Ny4xLDE5LTk5LDUyLjdjLTIxLjgsMzMuNy0zNS4yLDc0LjYtNDAsMTIyLjhsLTgsNjQuNGgxNDkuNnY4Ny44SDQyMy40bC0zMS4xLDI3My45Yy05LDg4LjUtMzAuMiwxNTguNy02My43LDIxMC43Yy0zMy41LDUyLTg3LjQsNzguNi0xNjEuOCw3OS44Yy00NC4xLTAuOS03Ny44LTguMy0xMDEuMS0yMi4zbDIwLjctODEuNmMyMS42LDEwLjMsNDUuOCwxNS4yLDcyLjYsMTQuOGMzOS40LTAuMiw2OC41LTE4LjIsODcuMi01NGMxOC43LTM1LjgsMzEuNS04OCwzOC42LTE1Ni43bDI5LjgtMjY0LjVIMTk1LjN6IE02ODEuMSw3MTguNmwtMTIuNS02MS44bC0xMy4zLTU1LjNMNjQ0LDU2Ny45bC0xMi42LTExLjdsLTYuNywxLjhsLTE1LDE0LjFsLTIxLjMsMzEuNEw1NDQsNTg2LjJjMTYuOS0yNy40LDMzLjItNDcuOSw0OC44LTYxLjVjMTUuNi0xMy41LDM0LTIwLjMsNzMuMy0yMC4zbDM4LjgsMTEuNmwyMy43LDQwLjhsOS45LDQwbDUuNiwzMy43TDgyNSw1MjcuMWM5LjItOC4zLDE4LTE0LjIsMjYuNC0xNy42YzguNC0zLjQsMTkuNi01LjEsMzMuNy01LjFjMy4xLDAsNDEuMiwxLjUsNDkuMyw0LjZsLTE2LjEsNjkuNkg5MDBoLTEwaC0xOC4yYy0xLTYuNS0zLTExLjEtNi4yLTEzLjhsLTcuMyw0bC0xNy4yLDE2LjRsLTgxLjgsMTAyLjlsMTMuOCw2Ny41bDExLjUsNDcuM2w4LjgsMjkuOWw4LjMsMTYuNGwxMS4yLDguMmMzLjktMS41LDcuNy0zLjksMTEuNS03LjJjNi44LTYsMTYuNy0xOS41LDI5LjYtNDAuNmw0NC40LDE3LjNjLTE4LjcsMzAtMzUuNyw1MS4xLTUwLjksNjMuNGMtMTUuMiwxMi4zLTMzLjMsMTguNC01NC4yLDE4LjRjLTMsMC0zOS42LTIuMS00Ny43LTYuM2MtOC00LjItMTUtMTAuOS0yMS0yMC4yYy01LjktOS4yLTExLjItMjMuNi0xNS43LTQzLjFjLTYuMS0yNS41LTEwLjUtNDYuNi0xMy4xLTYzLjRMNjEwLDg4NmMtOSw4LjUtMTcuNywxNC40LTI2LjIsMTcuOGMtOC41LDMuNC0xOS43LDUuMS0zMy43LDUuMWMtMy4xLDAtNDEuMS0xLjUtNDkuMi00LjZsMTYuMS02OS42aDQ2LjVjMC45LDYuNSwzLDExLjEsNi4xLDEzLjhsMS43LTAuNGwxNy45LTE1LjFMNjgxLjEsNzE4LjZ6Ii8+PC9nPgo8L3N2Zz4="); -} -.wmd-link-button { - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaWQ9IkxheWVyXzIiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDMyIDMyIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48Zz48cGF0aCBzdHlsZT0iZmlsdGVyOmludmVydCgxMDAlKSIgZD0iTTIxLjAzOTg2LDAuNTAzM2MtMi43OTY0NSwwLTUuNDIyNjEsMS4wODUyNy03LjM5NDksMy4wNTgxMUwxMi4xMTkyLDUuMDg3NTkgICBjLTAuMzY2MTUsMC4zNjYxNS0wLjU2NzI2LDAuODUyNTQtMC41NjcyNiwxLjM3MDA2YzAsMC41MTg1NSwwLjIwMTU0LDEuMDA0ODgsMC41Njc3NSwxLjM3MDEyICAgYzAuNzMyNzksMC43MzIzNiwyLjAwODczLDAuNzMyMzYsMi43NDA2LDBsMS41MjUyNy0xLjUyNjE4YzIuNTY2MS0yLjU2NTEyLDYuNzQyMDctMi41NjQyMSw5LjMwODE3LDAgICBjMS4yNDUxOCwxLjI0NTE4LDEuOTMwNjYsMi44OTgxMywxLjkzMDY2LDQuNjU0M2MwLDEuNzU2MS0wLjY4NTk3LDMuNDA5MTItMS45MzExNSw0LjY1NDNsLTEuNTI1NzYsMS41MjYxOCAgIGMtMC4zNjYxNSwwLjM2NjIxLTAuNTY3MiwwLjg1MjU0LTAuNTY3MiwxLjM3MDEyYzAsMC41MTg0OSwwLjIwMTU0LDEuMDA0ODIsMC41Njc2OSwxLjM3MDA2ICAgYzAuMzY2MjEsMC4zNjYxNSwwLjg1MjU0LDAuNTY3NjksMS4zNzAxMiwwLjU2NzY5YzAuNTE3NTIsMCwxLjAwNDMzLTAuMjAxNTQsMS4zNzA1NC0wLjU2NzY5bDEuNTI1MjctMS41MjYxOCAgIEMzMC40MTA5NSwxNi4zNzI4LDMxLjUsMTMuNzQ3MTMsMzEuNSwxMC45NTU4N3MtMS4wODkwNS01LjQxNjkzLTMuMDY2MS03LjM5NDQ3QzI2LjQ2MjA0LDEuNTg4NTYsMjMuODM2MzYsMC41MDMzLDIxLjAzOTg2LDAuNTAzMyAgIHoiLz48cGF0aCBzdHlsZT0iZmlsdGVyOmludmVydCgxMDAlKSIgZD0iTTEwLjk1MTYsMzEuNDk2NjRjMi43OTYwMiwwLDUuNDIyMTItMS4wODYxOCw3LjM5NDk2LTMuMDU4MDRsMS41MjU3LTEuNTI2MTggICBjMC4zNjYyMS0wLjM2NjIxLDAuNTY3MjYtMC44NTI1NCwwLjU2NzI2LTEuMzcwMTJjMC0wLjUxODQ5LTAuMjAxNTQtMS4wMDQ4Mi0wLjU2NzY5LTEuMzcwMDYgICBjLTAuNzMyODUtMC43MzMzNC0yLjAwODc5LTAuNzMxNDUtMi43NDA2NiwwbC0xLjUyNTI3LDEuNTI2MThjLTIuNTY0MTUsMi41NjYwNC02Ljc0MDY2LDIuNTY3MDItOS4zMDg1OSwwICAgYy0yLjU2NjA0LTIuNTY2MDQtMi41NjYwNC02Ljc0MjU1LDAtOS4zMDg1OWwxLjUyNjE4LTEuNTI2MjVjMC43NTU1NS0wLjc1NSwwLjc1NTU1LTEuOTg1MTEsMC0yLjc0MDExICAgYy0wLjc1NTA3LTAuNzU1MDctMS45ODUxMS0wLjc1NTA3LTIuNzQwMTcsMGwtMS41MjYxOCwxLjUyNjE4Yy00LjA3NjE3LDQuMDc3MTUtNC4wNzYxNywxMC43MTE3OSwwLDE0Ljc4ODk0ICAgQzUuNTI4OTksMzAuNDEwNDYsOC4xNTUxNSwzMS40OTY2NCwxMC45NTE2LDMxLjQ5NjY0eiIvPjxwYXRoIHN0eWxlPSJmaWx0ZXI6aW52ZXJ0KDEwMCUpIiBkPSJNOS40MTY0NCwyMi41Nzk3N2MwLjM2NjE1LDAuMzY2MjEsMC44NTI0OCwwLjU2Nzc1LDEuMzcwMDYsMC41Njc3NWMwLjUxODA3LDAsMS4wMDQ4Mi0wLjIwMTU0LDEuMzcwMDYtMC41Njc3NSAgIGwxMC40MTg1Mi0xMC40MTk0M2MwLjM2NjE1LTAuMzY2MTUsMC41Njc2OS0wLjg1MjQ4LDAuNTY3NjktMS4zNzAwNnMtMC4yMDE1NC0xLjAwMzkxLTAuNTY3NjktMS4zNzAxMiAgIGMtMC43MzE4Ny0wLjczMjM2LTIuMDA5MjItMC43MzIzNi0yLjc0MDE3LDBMOS40MTY0NCwxOS44Mzk2Yy0wLjM2NjIxLDAuMzY2MjEtMC41Njc3NSwwLjg1MjU0LTAuNTY3NzUsMS4zNzAxMiAgIFM5LjA1MDIzLDIyLjIxMzYyLDkuNDE2NDQsMjIuNTc5Nzd6Ii8+PC9nPjwvc3ZnPg=="); -} -.wmd-code-button { - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pjxzdmcgdmlld0JveD0iMCAwIDY0MCA1MTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggc3R5bGU9ImZpbHRlcjppbnZlcnQoMTAwJSkiIGQ9Ik00MTYgMzEuOTRDNDE2IDIxLjc1IDQwOC4xIDAgMzg0LjEgMGMtMTMuOTggMC0yNi44NyA5LjA3Mi0zMC44OSAyMy4xOGwtMTI4IDQ0OGMtLjg0MDQgMi45MzUtMS4yNDEgNS44OTItMS4yNDEgOC44MDFDMjIzLjEgNDkwLjMgMjMyIDUxMiAyNTYgNTEyYzEzLjkyIDAgMjYuNzMtOS4xNTcgMzAuNzUtMjMuMjJsMTI4LTQ0OEM0MTUuNiAzNy44MSA0MTYgMzQuODUgNDE2IDMxLjk0ek0xNzYgMTQzLjFjMC0xOC4yOC0xNC45NS0zMi0zMi0zMmMtOC4xODggMC0xNi4zOCAzLjEyNS0yMi42MiA5LjM3NmwtMTEyIDExMkMzLjEyNSAyMzkuNiAwIDI0Ny44IDAgMjU1LjFTMy4xMjUgMjcyLjQgOS4zNzUgMjc4LjZsMTEyIDExMkMxMjcuNiAzOTYuOSAxMzUuOCAzOTkuMSAxNDQgMzk5LjFjMTcuMDUgMCAzMi0xMy43MyAzMi0zMmMwLTguMTg4LTMuMTI1LTE2LjM4LTkuMzc1LTIyLjYzTDc3LjI1IDI1NS4xbDg5LjM4LTg5LjM4QzE3Mi45IDE2MC4zIDE3NiAxNTIuMiAxNzYgMTQzLjF6TTY0MCAyNTUuMWMwLTguMTg4LTMuMTI1LTE2LjM4LTkuMzc1LTIyLjYzbC0xMTItMTEyQzUxMi40IDExNS4xIDUwNC4yIDExMS4xIDQ5NiAxMTEuMWMtMTcuMDUgMC0zMiAxMy43My0zMiAzMmMwIDguMTg4IDMuMTI1IDE2LjM4IDkuMzc1IDIyLjYzbDg5LjM4IDg5LjM4bC04OS4zOCA4OS4zOEM0NjcuMSAzNTEuNiA0NjQgMzU5LjggNDY0IDM2Ny4xYzAgMTguMjggMTQuOTUgMzIgMzIgMzJjOC4xODggMCAxNi4zOC0zLjEyNSAyMi42Mi05LjM3NmwxMTItMTEyQzYzNi45IDI3Mi40IDY0MCAyNjQuMiA2NDAgMjU1LjF6Ii8+PC9zdmc+"); -} -.wmd-image-button { - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSIxOHB4IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxOCAxOCIgd2lkdGg9IjE4cHgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48dGl0bGUvPjxkZXNjLz48ZGVmcy8+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSI+PGcgZmlsbD0iIzAwMDAwMCIgaWQ9IkNvcmUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNzEuMDAwMDAwLCAtMTcxLjAwMDAwMCkiPjxnIGlkPSJkcml2ZS1pbWFnZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTcxLjAwMDAwMCwgMTcxLjAwMDAwMCkiPjxwYXRoIHN0eWxlPSJmaWx0ZXI6aW52ZXJ0KDEwMCUpIiBkPSJNMTgsMTYgTDE4LDIgQzE4LDAuOSAxNy4xLDAgMTYsMCBMMiwwIEMwLjksMCAwLDAuOSAwLDIgTDAsMTYgQzAsMTcuMSAwLjksMTggMiwxOCBMMTYsMTggQzE3LjEsMTggMTgsMTcuMSAxOCwxNiBMMTgsMTYgWiBNNS41LDEwLjUgTDgsMTMuNSBMMTEuNSw5IEwxNiwxNSBMMiwxNSBMNS41LDEwLjUgTDUuNSwxMC41IFoiIGlkPSJTaGFwZSIvPjwvZz48L2c+PC9nPjwvc3ZnPg=="); -} -.wmd-heading-button { - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB3aWR0aD0iMTc5MiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBzdHlsZT0iZmlsdGVyOmludmVydCgxMDAlKSIgZD0iTTE2ODIgMTY2NHEtNDQgMC0xMzIuNS0zLjV0LTEzMy41LTMuNXEtNDQgMC0xMzIgMy41dC0xMzIgMy41cS0yNCAwLTM3LTIwLjV0LTEzLTQ1LjVxMC0zMSAxNy00NnQzOS0xNyA1MS03IDQ1LTE1cTMzLTIxIDMzLTE0MGwtMS0zOTFxMC0yMS0xLTMxLTEzLTQtNTAtNGgtNjc1cS0zOCAwLTUxIDQtMSAxMC0xIDMxbC0xIDM3MXEwIDE0MiAzNyAxNjQgMTYgMTAgNDggMTN0NTcgMy41IDQ1IDE1IDIwIDQ1LjVxMCAyNi0xMi41IDQ4dC0zNi41IDIycS00NyAwLTEzOS41LTMuNXQtMTM4LjUtMy41cS00MyAwLTEyOCAzLjV0LTEyNyAzLjVxLTIzIDAtMzUuNS0yMXQtMTIuNS00NXEwLTMwIDE1LjUtNDV0MzYtMTcuNSA0Ny41LTcuNSA0Mi0xNXEzMy0yMyAzMy0xNDNsLTEtNTd2LTgxM3EwLTMgLjUtMjZ0MC0zNi41LTEuNS0zOC41LTMuNS00Mi02LjUtMzYuNS0xMS0zMS41LTE2LTE4cS0xNS0xMC00NS0xMnQtNTMtMi00MS0xNC0xOC00NXEwLTI2IDEyLTQ4dDM2LTIycTQ2IDAgMTM4LjUgMy41dDEzOC41IDMuNXE0MiAwIDEyNi41LTMuNXQxMjYuNS0zLjVxMjUgMCAzNy41IDIydDEyLjUgNDhxMCAzMC0xNyA0My41dC0zOC41IDE0LjUtNDkuNSA0LTQzIDEzcS0zNSAyMS0zNSAxNjBsMSAzMjBxMCAyMSAxIDMyIDEzIDMgMzkgM2g2OTlxMjUgMCAzOC0zIDEtMTEgMS0zMmwxLTMyMHEwLTEzOS0zNS0xNjAtMTgtMTEtNTguNS0xMi41dC02Ni0xMy0yNS41LTQ5LjVxMC0yNiAxMi41LTQ4dDM3LjUtMjJxNDQgMCAxMzIgMy41dDEzMiAzLjVxNDMgMCAxMjktMy41dDEyOS0zLjVxMjUgMCAzNy41IDIydDEyLjUgNDhxMCAzMC0xNy41IDQ0dC00MCAxNC41LTUxLjUgMy00NCAxMi41cS0zNSAyMy0zNSAxNjFsMSA5NDNxMCAxMTkgMzQgMTQwIDE2IDEwIDQ2IDEzLjV0NTMuNSA0LjUgNDEuNSAxNS41IDE4IDQ0LjVxMCAyNi0xMiA0OHQtMzYgMjJ6Ii8+PC9zdmc+"); -} -.wmd-undo-button { - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSI0OHB4IiBpZD0ic3ZnMTEzMDAiIHdpZHRoPSI0OHB4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIiB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGRlZnMgaWQ9ImRlZnMzIj48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhckdyYWRpZW50MjMyNiI+PHN0b3AgaWQ9InN0b3AyMzI4IiBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjE7Ii8+PHN0b3AgaWQ9InN0b3AyMzMwIiBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjA7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhckdyYWRpZW50MjMxNiI+PHN0b3AgaWQ9InN0b3AyMzE4IiBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNjNGEwMDA7c3RvcC1vcGFjaXR5OjE7Ii8+PHN0b3AgaWQ9InN0b3AyMzIwIiBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNjNGEwMDA7c3RvcC1vcGFjaXR5OjA7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhckdyYWRpZW50MjMwOCI+PHN0b3AgaWQ9InN0b3AyMzEwIiBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNlZGQ0MDA7c3RvcC1vcGFjaXR5OjE7Ii8+PHN0b3AgaWQ9InN0b3AyMzEyIiBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNlZGQ0MDA7c3RvcC1vcGFjaXR5OjA7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhckdyYWRpZW50ODY2MiI+PHN0b3AgaWQ9InN0b3A4NjY0IiBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjE7Ii8+PHN0b3AgaWQ9InN0b3A4NjY2IiBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjA7Ii8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgY3g9IjI0LjgzNzEyNiIgY3k9IjM2LjQyMTEyNyIgZng9IjI0LjgzNzEyNiIgZnk9IjM2LjQyMTEyNyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjAwMDAwMCwwLjAwMDAwMCwwLjAwMDAwMCwwLjUzNjcyMywtNi4yMjcyNjVlLTE0LDE2Ljg3MzA2KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJyYWRpYWxHcmFkaWVudDg2NjgiIHI9IjE1LjY0NDczNyIgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50ODY2MiIvPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyR3JhZGllbnQyMTg3Ij48c3RvcCBpZD0ic3RvcDIxODkiIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MTsiLz48c3RvcCBpZD0ic3RvcDIxOTEiIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MDsiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0xLjgxMzQ3MWUtMTYsLTEuMTcxOTI2LC0xLjE3MTkyNiwxLjgxMzQ3MWUtMTYsNDYuMTc0NDAsNTQuMTAxMTEpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImxpbmVhckdyYWRpZW50MTc2NCIgeDE9IjE3LjA2MDgwNiIgeDI9IjEyLjYyNDMzNyIgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MjE4NyIgeTE9IjExLjM5NTAyIiB5Mj0iMTIuNTgzNzY5Ii8+PGxpbmVhckdyYWRpZW50IGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0ibGluZWFyR3JhZGllbnQyMzE0IiB4MT0iMjYuNSIgeDI9IjI2LjI1IiB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyMzA4IiB5MT0iMzQuMjUiIHkyPSI0My41NzE4MzEiLz48bGluZWFyR3JhZGllbnQgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJsaW5lYXJHcmFkaWVudDIzMjIiIHgxPSIyNi41IiB4Mj0iMjYuMjUiIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDIzMTYiIHkxPSIzNC4yNSIgeTI9IjQzLjU3MTgzMSIvPjxyYWRpYWxHcmFkaWVudCBjeD0iMTUuMDk0MDMiIGN5PSIxMy4yODI3MjEiIGZ4PSIxNS4wOTQwMyIgZnk9IjEzLjI4MjcyMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjQ5NjAzMSwtMS4xNTE5MDVlLTE2LDEuMDYxNzU2ZS0xNiwyLjMwMDY4OSwtMjUuMTI0MDIsLTE3LjgyNjM2KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJyYWRpYWxHcmFkaWVudDIzMzIiIHI9IjEwLjE2NDY2IiB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyMzI2Ii8+PC9kZWZzPjxnIGlkPSJsYXllcjEiPjxwYXRoIGQ9Ik0gNDAuNDgxODYzIDM2LjQyMTEyNyBBIDE1LjY0NDczNyA4LjM5Njg5MzUgMCAxIDEgIDkuMTkyMzg4NSwzNi40MjExMjcgQSAxNS42NDQ3MzcgOC4zOTY4OTM1IDAgMSAxICA0MC40ODE4NjMgMzYuNDIxMTI3IHoiIGlkPSJwYXRoODY2MCIgc3R5bGU9ImludmVydDoxMDAlO29wYWNpdHk6MC4xNDExNzY0Nztjb2xvcjojMDAwMDAwO2ZpbGw6dXJsKCNyYWRpYWxHcmFkaWVudDg2NjgpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO21hcmtlcjpub25lO21hcmtlci1zdGFydDpub25lO21hcmtlci1taWQ6bm9uZTttYXJrZXItZW5kOm5vbmU7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZSIgdHJhbnNmb3JtPSJtYXRyaXgoLTEuNDg5NzM2LDAuMDAwMDAwLDAuMDAwMDAwLC0xLjAwMTI1Miw2MC42MDQzNiw3NS4zMTI2MCkiLz48cGF0aCBkPSJNIDkuNTgyNDQxLDQ1LjAzNDM2OSBDIDQ5LjYwODI0OSw0Ni4zNTU1MDkgNDMuMjgyNDA1LDEyLjI5MzU1IDIyLjQ2MjQxMSwxMi40OTc2NSBMIDIyLjQ2MjQxMSwzLjEyMjIzOTYgTCA1LjgxMzkyOTgsMTcuNzA4ODE5IEwgMjIuNDYyNDExLDMzLjAwNjM0OSBDIDIyLjQ2MjQxMSwzMy4wMDYzNDkgMjIuNDYyNDExLDIzLjMzNzk2OSAyMi40NjI0MTEsMjMuMzM3OTY5IEMgMzYuNTI1NTIxLDIyLjc1MTk5OSA0MC42Mzk5MzksNDQuNzcwNTQ5IDkuNTgyNDQxLDQ1LjAzNDM2OSB6ICIgaWQ9InBhdGgxNDMyIiBzdHlsZT0iaW52ZXJ0OjEwMCU7b3BhY2l0eToxO2NvbG9yOiMwMDAwMDA7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MjMxNCk7ZmlsbC1vcGFjaXR5OjEuMDtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6dXJsKCNsaW5lYXJHcmFkaWVudDIzMjIpO3N0cm9rZS13aWR0aDoxLjAwMDAwMDEyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO21hcmtlcjpub25lO21hcmtlci1zdGFydDpub25lO21hcmtlci1taWQ6bm9uZTttYXJrZXItZW5kOm5vbmU7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmJsb2NrO292ZXJmbG93OnZpc2libGUiLz48cGF0aCBkPSJNIDMxLjAzMjI4MSwzOS4zMTU1MTkgQyA0Mi43NTUzOCwzMy4yMzU4OTIgMzkuMjIwMDczLDEzLjA4NzQ4OSAyMS40NDg3MDEsMTMuNTQ5OTU5IEwgMjEuNDQ4NzAxLDUuNDUwODY3OCBDIDIxLjQ0ODcwMSw1LjQ1MDg2NzggNy40MDA5NjI4LDE3LjcxNDU4OSA3LjQwMDk2MjgsMTcuNzE0NTg5IEwgMjEuNDQ4NzAxLDMwLjY1ODYxNyBDIDIxLjQ0ODcwMSwzMC42NTg2MTcgMjEuNDQ4NzAxLDIyLjM4MDk3OSAyMS40NDg3MDEsMjIuMzgwOTc5IEMgMzYuMjg4NTUxLDIyLjAzMjcwOSAzNS42MDg2MTEsMzUuMTM4NTc5IDMxLjAzMjI4MSwzOS4zMTU1MTkgeiAiIGlkPSJwYXRoMjE3NyIgc3R5bGU9ImludmVydDoxMDAlO29wYWNpdHk6MC42OTg4NjM2MTtjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6dXJsKCNsaW5lYXJHcmFkaWVudDE3NjQpO3N0cm9rZS13aWR0aDowLjk5OTk5OTc7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7bWFya2VyLXN0YXJ0Om5vbmU7bWFya2VyLW1pZDpub25lO21hcmtlci1lbmQ6bm9uZTtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmJsb2NrO292ZXJmbG93OnZpc2libGUiLz48cGF0aCBkPSJNIDYuNjI5MTI2MSwxNy42ODI3OTcgTCAxMi4yODU5OCwyMy4wNzQ0ODYgQyAxOC41NjE1NTMsMjIuODk3NzA5IDE1LjczMzEyNiwxNi43MTA1MjUgMjYuOTU4NDQ2LDEzLjYxNjkzMyBMIDIyLjAwODY5OSwxMi45OTgyMTQgTCAyMS45MjAzMSw0LjMzNjE1NjIgTCA2LjYyOTEyNjEsMTcuNjgyNzk3IHogIiBpZD0icGF0aDIzMjQiIHN0eWxlPSJpbnZlcnQ6MTAwJTtvcGFjaXR5OjAuNTExMzYzNjQ7Y29sb3I6IzAwMDAwMDtmaWxsOnVybCgjcmFkaWFsR3JhZGllbnQyMzMyKTtmaWxsLW9wYWNpdHk6MS4wO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO21hcmtlcjpub25lO21hcmtlci1zdGFydDpub25lO21hcmtlci1taWQ6bm9uZTttYXJrZXItZW5kOm5vbmU7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlIi8+PC9nPjwvc3ZnPg=="); -} -.wmd-redo-button { - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSI0OHB4IiBpZD0ic3ZnMTEzMDAiIHdpZHRoPSI0OHB4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIiB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGRlZnMgaWQ9ImRlZnMzIj48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhckdyYWRpZW50MjI0MCI+PHN0b3AgaWQ9InN0b3AyMjQyIiBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiM5OWIwMGI7c3RvcC1vcGFjaXR5OjE7Ii8+PHN0b3AgaWQ9InN0b3AyMjQ0IiBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiM5OWIwMGI7c3RvcC1vcGFjaXR5OjA7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhckdyYWRpZW50MjIzMiI+PHN0b3AgaWQ9InN0b3AyMjM0IiBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiM3ODg2MDA7c3RvcC1vcGFjaXR5OjE7Ii8+PHN0b3AgaWQ9InN0b3AyMjM2IiBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiM3ODg2MDA7c3RvcC1vcGFjaXR5OjA7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhckdyYWRpZW50NDk5MSI+PHN0b3AgaWQ9InN0b3A0OTkzIiBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjE7Ii8+PHN0b3AgaWQ9InN0b3A0OTk1IiBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjA7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhckdyYWRpZW50ODY2MiI+PHN0b3AgaWQ9InN0b3A4NjY0IiBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjE7Ii8+PHN0b3AgaWQ9InN0b3A4NjY2IiBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjA7Ii8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgY3g9IjI0LjgzNzEyNiIgY3k9IjM2LjQyMTEyNyIgZng9IjI0LjgzNzEyNiIgZnk9IjM2LjQyMTEyNyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjAwMDAwMCwwLjAwMDAwMCwwLjAwMDAwMCwwLjUzNjcyMywtNS44MjUzMjllLTE0LDE2Ljg3MzA2KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJyYWRpYWxHcmFkaWVudDg2NjgiIHI9IjE1LjY0NDczNyIgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50ODY2MiIvPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyR3JhZGllbnQyMTg3Ij48c3RvcCBpZD0ic3RvcDIxODkiIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MTsiLz48c3RvcCBpZD0ic3RvcDIxOTEiIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MDsiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuODEzNDcxZS0xNiwtMS4xNzE5MjYsMS4xNzE5MjYsMS44MTM0NzFlLTE2LDEuNzgyODAxLDU0LjEwMTExKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJsaW5lYXJHcmFkaWVudDE3NjQiIHgxPSIxNy4wNjA4MDYiIHgyPSIxMi42MjQzMzciIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDIxODciIHkxPSIxMS4zOTUwMiIgeTI9IjEyLjU4Mzc2OSIvPjxyYWRpYWxHcmFkaWVudCBjeD0iMTYuNTYzODM3IiBjeT0iMTEuMTMyMjM2IiBmeD0iMTYuNTYzODM3IiBmeT0iMTEuMTMyMjM2IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0xLjI5MDEyN2UtMiwxLjY4NTE5NywxLjcxMzA4MiwxLjMxMTQ3NWUtMiwtMS4wNDE0OTksLTEwLjExNTcxKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJyYWRpYWxHcmFkaWVudDQ5OTciIHI9IjE5LjA2MjUiIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDQ5OTEiLz48bGluZWFyR3JhZGllbnQgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJsaW5lYXJHcmFkaWVudDIyMzgiIHgxPSIzMyIgeDI9IjMxLjUiIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDIyMzIiIHkxPSIzNS43NSIgeTI9IjQyLjUiLz48bGluZWFyR3JhZGllbnQgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJsaW5lYXJHcmFkaWVudDIyNDYiIHgxPSIzMyIgeDI9IjMxLjUiIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDIyNDAiIHkxPSIzNS43NSIgeTI9IjQyLjUiLz48L2RlZnM+PGcgaWQ9ImxheWVyMSI+PHBhdGggZD0iTSA0MC40ODE4NjMgMzYuNDIxMTI3IEEgMTUuNjQ0NzM3IDguMzk2ODkzNSAwIDEgMSAgOS4xOTIzODg1LDM2LjQyMTEyNyBBIDE1LjY0NDczNyA4LjM5Njg5MzUgMCAxIDEgIDQwLjQ4MTg2MyAzNi40MjExMjcgeiIgaWQ9InBhdGg4NjYwIiBzdHlsZT0iZmlsdGVyOmludmVydCgxMDAlKTtvcGFjaXR5OjAuMTQxMTc2NDc7Y29sb3I6IzAwMDAwMDtmaWxsOnVybCgjcmFkaWFsR3JhZGllbnQ4NjY4KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTttYXJrZXItc3RhcnQ6bm9uZTttYXJrZXItbWlkOm5vbmU7bWFya2VyLWVuZDpub25lO3N0cm9rZS1taXRlcmxpbWl0OjEwO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjE7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGUiIHRyYW5zZm9ybT0ibWF0cml4KDEuNDg5NzM2LDAuMDAwMDAwLDAuMDAwMDAwLC0xLjAwMTI1MiwtMTIuNjQ3MTYsNzUuMzEyNjApIi8+PHBhdGggZD0iTSAzOC4zNzQ3Niw0NS4wMzQzNjkgQyAtMS42NTEwNDg2LDQ2LjM1NTUwOSA0LjY3NDc5NTQsMTIuMjkzNTUgMjUuNDk0NzksMTIuNDk3NjUgTCAyNS40OTQ3OSwzLjEyMjIzOTYgTCA0Mi4xNDMyNzEsMTcuNzA4ODE5IEwgMjUuNDk0NzksMzMuMDA2MzQ5IEMgMjUuNDk0NzksMzMuMDA2MzQ5IDI1LjQ5NDc5LDIzLjMzNzk2OSAyNS40OTQ3OSwyMy4zMzc5NjkgQyAxMS40MzE2OCwyMi43NTE5OTkgNy4zMTcyNjE0LDQ0Ljc3MDU0OSAzOC4zNzQ3Niw0NS4wMzQzNjkgeiAiIGlkPSJwYXRoMTQzMiIgc3R5bGU9ImZpbHRlcjppbnZlcnQoMTAwJSk7b3BhY2l0eToxO2NvbG9yOiMwMDAwMDA7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MjI0Nik7ZmlsbC1vcGFjaXR5OjEuMDtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6dXJsKCNsaW5lYXJHcmFkaWVudDIyMzgpO3N0cm9rZS13aWR0aDoxLjAwMDAwMDEyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO21hcmtlcjpub25lO21hcmtlci1zdGFydDpub25lO21hcmtlci1taWQ6bm9uZTttYXJrZXItZW5kOm5vbmU7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmJsb2NrO292ZXJmbG93OnZpc2libGUiLz48cGF0aCBkPSJNIDE2LjkyNDkyLDM5LjMxNTUxOSBDIDUuMjAxODIwNCwzMy4yMzU4OTIgOC43MzcxMjc0LDEzLjA4NzQ4OSAyNi41MDg1LDEzLjU0OTk1OSBMIDI2LjUwODUsNS40NTA4Njc4IEMgMjYuNTA4NSw1LjQ1MDg2NzggNDAuNTU2MjM4LDE3LjcxNDU4OSA0MC41NTYyMzgsMTcuNzE0NTg5IEwgMjYuNTA4NSwzMC42NTg2MTcgQyAyNi41MDg1LDMwLjY1ODYxNyAyNi41MDg1LDIyLjM4MDk3OSAyNi41MDg1LDIyLjM4MDk3OSBDIDExLjY2ODY1LDIyLjAzMjcwOSAxMi4zNDg1OSwzNS4xMzg1NzkgMTYuOTI0OTIsMzkuMzE1NTE5IHogIiBpZD0icGF0aDIxNzciIHN0eWxlPSJmaWx0ZXI6aW52ZXJ0KDEwMCUpO29wYWNpdHk6MC42OTg4NjM2MTtjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6dXJsKCNsaW5lYXJHcmFkaWVudDE3NjQpO3N0cm9rZS13aWR0aDowLjk5OTk5OTc7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7bWFya2VyLXN0YXJ0Om5vbmU7bWFya2VyLW1pZDpub25lO21hcmtlci1lbmQ6bm9uZTtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmJsb2NrO292ZXJmbG93OnZpc2libGUiLz48cGF0aCBkPSJNIDI2LjAzNjk4OSw0LjU2ODYwOTUgTCAzNi43MjM3MjcsMTQuNzk4MjQxIEMgMjkuNzg2MjI3LDE0Ljc5ODI0IDMyLjAzNjk4OSwyMy43MzU0MjQgMjUuOTExOTg5LDI2LjYxMDQyNCBMIDI1Ljk3NDQ4OSwyMi45NDM2MDkgQyAxMC43ODY5ODksMjIuODgxMTA5IDExLjY2MTk4OSwzOC40NDM2MDkgMjIuNzI0NDg5LDQyLjY5MzYwOSBDIDMuNjM2MzQxNCwzNy44MTE2ODEgNi4yODY5OTA0LDEzLjM4MTEwOSAyNS45MTE5ODksMTIuODgxMTEgTCAyNi4wMzY5ODksNC41Njg2MDk1IHogIiBpZD0icGF0aDQ5ODkiIHN0eWxlPSJmaWx0ZXI6aW52ZXJ0KDEwMCUpO29wYWNpdHk6MC40OTQzMTgxNztjb2xvcjojMDAwMDAwO2ZpbGw6dXJsKCNyYWRpYWxHcmFkaWVudDQ5OTcpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDowLjk5OTk5OTc7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7bWFya2VyLXN0YXJ0Om5vbmU7bWFya2VyLW1pZDpub25lO21hcmtlci1lbmQ6bm9uZTtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlIi8+PC9nPjwvc3ZnPg=="); -} -.wmd-italic-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgogICB3aWR0aD0iNTI1LjE0MnB4IiBoZWlnaHQ9IjUyNS4xNDJweCIgdmlld0JveD0iMCAwIDUyNS4xNDIgNTI1LjE0MiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTI1LjE0MiA1MjUuMTQyOyIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgogIDxnPgogICAgPHBhdGggc3R5bGU9ImZpbHRlcjppbnZlcnQoMTAwJSkiIGQ9Ik0zNzUuNTQsMC4yNDRjLTEzLjMzNiwxLjY4OS0yNi44MywyLjg3LTQwLjQ5NiwzLjU1NmMtMTUuNzQxLDAuNzk2LTI3Ljk4NywxLjE4OC0zNi43MjcsMS4xODgKICAgICAgYy0xMC4wNjcsMC0yMi4wODctMC41MTQtMzYuMDcxLTEuNTc5Yy0xMi4yMjgtMC45MTgtMjUuMjgyLTIuMDUtMzkuMTYyLTMuMzc4Yy0zLjM2Ni0wLjMyNC02Ljk1OCwyLjAxNC03LjgwMyw1LjI4OAogICAgICBjLTEuMDUzLDQuMDg4LTEuNTczLDcuNDE4LTEuNTczLDkuOTc2YzAsNi44NzMsNC44MDQsMTEuMDk1LDE0LjQyNSwxMi42OGMxMi42ODEsMS41NzksMjAuNjYxLDUuMDI1LDIzLjk0MSwxMC4zMDYKICAgICAgYzMuMjgsNS4yODIsNC4wMzksMTIuNjc1LDIuMjk1LDIyLjE5MWwtNjQuMjY2LDQwMy4zOTRjLTEuNzU2LDEwLjA0My01LjQ3OCwxNy41NzYtMTEuMTU3LDIyLjU4OQogICAgICBjLTUuNjg2LDUuMDI0LTE0LjIxLDguNTk5LTI1LjU3NSwxMC42OTdjLTQuODE2LDEuMDY1LTcuOTg3LDIuNjYyLTkuNTA0LDQuNzU1Yy0xLjU0MiwyLjExOC0yLjI5NSw1LjI4Mi0yLjI5NSw5LjUxOAogICAgICBjMCwyLjU2MywwLjI0NSw1LjMyNCwwLjcyOCw4LjI4YzAuNTQ1LDMuMzM1LDMuOTY2LDUuNjk3LDcuMzI2LDUuMzU0YzEyLjc0OC0xLjMxLDI1LjI1Ny0yLjQyNCwzNy41MjgtMy4zMjkKICAgICAgYzE0LjE5OC0xLjA2NSwyNS42NzMtMS41NzksMzQuNDI1LTEuNTc5YzEwLjA1NSwwLDIyLjk1NywwLjUxNCwzOC42OTcsMS41NzljMTMuNzY0LDAuOTI0LDI3LjY4MSwyLjA1LDQxLjc4MSwzLjM4NAogICAgICBjMy4zNjYsMC4zMTgsNi43ODctMi4wNjgsNy4zMzItNS40MDNjMC40ODMtMi45NTYsMC43MjktNS43MSwwLjcyOS04LjI4YzAtNy4zOTQtNC4xNjItMTIuMTQ4LTEyLjQ2MS0xNC4yNzIKICAgICAgYy0xMS44MDYtMi42MzgtMTkuODk2LTUuOTQyLTI0LjI2Ni05LjkwMWMtNC4zNzYtMy45Ni01LjY4Ni0xMS4yMjUtMy45MzUtMjEuNzk0TDMzMi40MTgsNjIuMDUKICAgICAgYzEuNzM4LTEwLjU2Myw1LjEyOS0xOC4yMjUsMTAuMTY1LTIyLjk4YzUuMDE5LTQuNzU1LDEzLjY2LTguMTgyLDI1LjkwNi0xMC4zMDZjNi4xMi0xLjA1MywxMC4xNzItMi42MzgsMTIuMTMtNC43NTUKICAgICAgYzEuOTcxLTIuMTA1LDIuOTUtNS4yNzUsMi45NS05LjUxMWMwLTIuNjkzLTAuMjY0LTUuNzEtMC44MDItOS4wNTFDMzgyLjI0MSwyLjExMSwzNzguODk0LTAuMTg0LDM3NS41NCwwLjI0NHoiLz4KICA8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+"); -} \ No newline at end of file diff --git a/resources/darkmode.css b/resources/darkmode.css deleted file mode 100644 index 81f4975..0000000 --- a/resources/darkmode.css +++ /dev/null @@ -1,3834 +0,0 @@ -/*! Dark reader generated CSS | Licensed under MIT https://github.com/darkreader/darkreader/blob/main/LICENSE */ - -/* User-Agent Style */ -@layer { -html { - background-color: #181a1b !important; -} -html { - color-scheme: dark !important; -} -iframe { - color-scheme: dark !important; -} -html, body, input, textarea, select, button, dialog { - background-color: #181a1b; -} -html, body, input, textarea, select, button { - border-color: #736b5e; - color: #e8e6e3; -} -a { - color: #3391ff; -} -table { - border-color: #545b5e; -} -mark { - color: #e8e6e3; -} -::placeholder { - color: #b2aba1; -} -input:-webkit-autofill, -textarea:-webkit-autofill, -select:-webkit-autofill { - background-color: #404400 !important; - color: #e8e6e3 !important; -} -* { - scrollbar-color: #454a4d #202324; -} -::selection { - background-color: #004daa !important; - color: #e8e6e3 !important; -} -::-moz-selection { - background-color: #004daa !important; - color: #e8e6e3 !important; -} -} - -/* Invert Style */ -.jfk-bubble.gtx-bubble, .captcheck_answer_label > input + img, span#closed_text > img[src^="https://www.gstatic.com/images/branding/googlelogo"], span[data-href^="https://www.hcaptcha.com/"] > #icon, img.Wirisformula { - filter: invert(100%) hue-rotate(180deg) contrast(90%) !important; -} - -/* Variables Style */ -:root { - --darkreader-neutral-background: #181a1b; - --darkreader-neutral-text: #e8e6e3; - --darkreader-selection-background: #004daa; - --darkreader-selection-text: #e8e6e3; -} - -/* Modified CSS */ -:root, -[data-md-color-scheme="default"] { - --darkreader-bg--md-accent-bg-color: #181a1b; - --darkreader-bg--md-accent-fg-color: #01189b; - --darkreader-bg--md-accent-fg-color--transparent: rgba(1, 24, 155, 0.1); - --darkreader-bg--md-admonition-bg-color: var(--darkreader-bg--md-default-bg-color); - --darkreader-bg--md-code-bg-color: #1e2021; - --darkreader-bg--md-code-hl-color: rgba(153, 153, 0, 0.5); - --darkreader-bg--md-default-bg-color: #181a1b; - --darkreader-bg--md-default-fg-color: rgba(0, 0, 0, 0.87); - --darkreader-bg--md-default-fg-color--light: rgba(0, 0, 0, 0.54); - --darkreader-bg--md-default-fg-color--lighter: rgba(0, 0, 0, 0.32); - --darkreader-bg--md-default-fg-color--lightest: rgba(0, 0, 0, 0.07); - --darkreader-bg--md-footer-bg-color: rgba(0, 0, 0, 0.87); - --darkreader-bg--md-footer-bg-color--dark: rgba(0, 0, 0, 0.32); - --darkreader-bg--md-primary-fg-color: #334191; - --darkreader-bg--md-primary-fg-color--dark: #263281; - --darkreader-bg--md-shadow-z1: 0 0.2rem 0.5rem rgba(0,0,0,.05),0 0 0.05rem rgba(0,0,0,.1); - --darkreader-bg--md-shadow-z2: 0 0.2rem 0.5rem rgba(0,0,0,.1),0 0 0.05rem rgba(0,0,0,.25); - --darkreader-bg--md-shadow-z3: 0 0.2rem 0.5rem rgba(0,0,0,.2),0 0 0.05rem rgba(0,0,0,.35); - --darkreader-bg--md-typeset-del-color: rgba(165, 25, 9, 0.15); - --darkreader-bg--md-typeset-ins-color: rgba(9, 170, 90, 0.15); - --darkreader-bg--md-typeset-kbd-accent-color: #181a1b; - --darkreader-bg--md-typeset-kbd-border-color: #404548; - --darkreader-bg--md-typeset-kbd-color: #1b1d1e; - --darkreader-bg--md-typeset-mark-color: rgba(153, 153, 0, 0.5); - --darkreader-border--md-accent-fg-color: #011899; - --darkreader-border--md-default-bg-color: #303436; - --darkreader-border--md-default-fg-color--light: rgba(140, 130, 115, 0.54); - --darkreader-border--md-default-fg-color--lighter: rgba(140, 130, 115, 0.32); - --darkreader-border--md-default-fg-color--lightest: rgba(140, 130, 115, 0.07); - --darkreader-border--md-primary-fg-color: #2f3c86; - --darkreader-border--md-typeset-table-color: rgba(140, 130, 115, 0.12); - --darkreader-text--md-accent-bg-color: #e8e6e3; - --darkreader-text--md-accent-fg-color: #539bfe; - --darkreader-text--md-admonition-fg-color: var(--darkreader-text--md-default-fg-color); - --darkreader-text--md-code-fg-color: #beb9b0; - --darkreader-text--md-code-hl-comment-color: var(--darkreader-text--md-default-fg-color--light); - --darkreader-text--md-code-hl-constant-color: #7561db; - --darkreader-text--md-code-hl-function-color: #b159c0; - --darkreader-text--md-code-hl-generic-color: var(--darkreader-text--md-default-fg-color--light); - --darkreader-text--md-code-hl-keyword-color: #518ecb; - --darkreader-text--md-code-hl-name-color: var(--darkreader-text--md-code-fg-color); - --darkreader-text--md-code-hl-number-color: #d93f3f; - --darkreader-text--md-code-hl-operator-color: var(--darkreader-text--md-default-fg-color--light); - --darkreader-text--md-code-hl-punctuation-color: var(--darkreader-text--md-default-fg-color--light); - --darkreader-text--md-code-hl-special-color: #ed3774; - --darkreader-text--md-code-hl-string-color: #7ee2b0; - --darkreader-text--md-code-hl-variable-color: var(--darkreader-text--md-default-fg-color--light); - --darkreader-text--md-default-bg-color: #e8e6e3; - --darkreader-text--md-default-fg-color: rgba(232, 230, 227, 0.87); - --darkreader-text--md-default-fg-color--light: rgba(232, 230, 227, 0.54); - --darkreader-text--md-default-fg-color--lighter: rgba(232, 230, 227, 0.32); - --darkreader-text--md-default-fg-color--lightest: rgba(232, 230, 227, 0.07); - --darkreader-text--md-footer-fg-color: #e8e6e3; - --darkreader-text--md-footer-fg-color--light: rgba(232, 230, 227, 0.7); - --darkreader-text--md-footer-fg-color--lighter: rgba(232, 230, 227, 0.3); - --darkreader-text--md-primary-bg-color: #e8e6e3; - --darkreader-text--md-primary-bg-color--light: rgba(232, 230, 227, 0.7); - --darkreader-text--md-primary-fg-color: #6d94cb; - --darkreader-text--md-typeset-a-color: var(--darkreader-text--md-primary-fg-color); - --md-accent-bg-color--light: hsla(0,0%,100%,.7); - --md-default-bg-color--light: hsla(0,0%,100%,.7); - --md-default-bg-color--lighter: hsla(0,0%,100%,.3); - --md-default-bg-color--lightest: hsla(0,0%,100%,.12); - --md-primary-fg-color--light: #5d6cc0; - --md-typeset-color: var(--md-default-fg-color); -} -.md-icon svg { - fill: currentcolor; -} -body { - --md-code-font-family: var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace; - --md-text-font-family: var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif; -} -:root { - --darkreader-bgimg--md-typeset-table-sort-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0ibTE4IDIxLTQtNGgzVjdoLTNsNC00IDQgNGgtM3YxMGgzTTIgMTl2LTJoMTB2Mk0yIDEzdi0yaDd2Mk0yIDdWNWg0djJIMloiLz48L3N2Zz4="); - --darkreader-bgimg--md-typeset-table-sort-icon--asc: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE5IDE3aDNsLTQgNC00LTRoM1YzaDJNMiAxN2gxMHYySDJNNiA1djJIMlY1bTAgNmg3djJIMnYtMloiLz48L3N2Zz4="); - --darkreader-bgimg--md-typeset-table-sort-icon--desc: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE5IDdoM2wtNC00LTQgNGgzdjE0aDJNMiAxN2gxMHYySDJNNiA1djJIMlY1bTAgNmg3djJIMnYtMloiLz48L3N2Zz4="); -} -.md-typeset h1 { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-typeset h5, -.md-typeset h6 { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-typeset hr { - border-bottom: .05rem solid var(--darkreader-border--md-default-fg-color--lightest); -} -.md-typeset code, -.md-typeset kbd, -.md-typeset pre { - color: var(--darkreader-text--md-code-fg-color, #e8e6e3); -} -.md-typeset kbd { - background-color: var(--darkreader-bg--md-typeset-kbd-color, #181a1b); - box-shadow: 0 .1rem 0 .05rem var(--darkreader-bg--md-typeset-kbd-border-color),0 .1rem 0 var(--darkreader-bg--md-typeset-kbd-border-color),0 -.1rem .2rem var(--darkreader-bg--md-typeset-kbd-accent-color) inset; - color: var(--darkreader-text--md-default-fg-color, #e8e6e3); -} -.md-typeset mark { - background-color: var(--darkreader-bg--md-typeset-mark-color, #181a1b); - color: inherit; -} -.md-typeset abbr { - border-bottom: .05rem dotted var(--darkreader-border--md-default-fg-color--light); - text-decoration-color: initial; -} -@media (hover: none) { - .md-typeset abbr[title]:-webkit-any(:focus, :hover)::after { - background-color: var(--darkreader-bg--md-default-fg-color, #181a1b); - box-shadow: var(--darkreader-bg--md-shadow-z3); - color: var(--darkreader-text--md-default-bg-color, #e8e6e3); - } - .md-typeset abbr[title]:is(:focus, :hover)::after { - background-color: var(--darkreader-bg--md-default-fg-color, #181a1b); - box-shadow: var(--darkreader-bg--md-shadow-z3); - color: var(--darkreader-text--md-default-bg-color, #e8e6e3); - } -} -.md-typeset blockquote { - border-left: .2rem solid var(--darkreader-border--md-default-fg-color--lighter); -} -[dir="rtl"] .md-typeset blockquote { - border-right: .2rem solid var(--darkreader-border--md-default-fg-color--lighter); -} -.md-typeset blockquote { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-typeset table:not([class]) { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - border: .05rem solid var(--darkreader-border--md-typeset-table-color); -} -.md-typeset table:not([class]) td { - border-top: .05rem solid var(--darkreader-border--md-typeset-table-color); -} -.md-typeset table:not([class]) tbody tr:hover { - background-color: rgba(0, 0, 0, 0.04); - box-shadow: 0 .05rem 0 var(--darkreader-bg--md-default-bg-color) inset; -} -.md-typeset table th[role="columnheader"]:hover::after { - background-color: var(--darkreader-bg--md-default-fg-color--lighter, #181a1b); -} -.md-typeset table th[role="columnheader"][aria-sort="ascending"]::after { - background-color: var(--darkreader-bg--md-default-fg-color--light, #181a1b); -} -.md-typeset table th[role="columnheader"][aria-sort="descending"]::after { - background-color: var(--darkreader-bg--md-default-fg-color--light, #181a1b); -} -.md-banner { - background-color: var(--darkreader-bg--md-footer-bg-color, #181a1b); - color: var(--darkreader-text--md-footer-fg-color, #e8e6e3); -} -.md-banner--warning { - background: var(--darkreader-bg--md-typeset-mark-color); - color: var(--darkreader-text--md-default-fg-color, #e8e6e3); -} -.md-banner__button { - color: inherit; -} -.md-option.focus-visible + label { - outline-color: var(--darkreader-border--md-accent-fg-color); -} -.md-skip { - background-color: var(--darkreader-bg--md-default-fg-color, #181a1b); - color: var(--darkreader-text--md-default-bg-color, #e8e6e3); - outline-color: var(--darkreader-border--md-accent-fg-color); -} -:root { - --darkreader-bgimg--md-clipboard-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCI+PGRlZnM+PGZpbHRlciBpZD0iZGFya3JlYWRlci1pbWFnZS1maWx0ZXIiPjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwLjI0OSAtMC42MTQgLTAuNjcyIDAuMDAwIDEuMDM1IC0wLjY0NiAwLjI4OCAtMC42NjQgMC4wMDAgMS4wMjAgLTAuNjM2IC0wLjYwOSAwLjI1MCAwLjAwMCAwLjk5NCAwLjAwMCAwLjAwMCAwLjAwMCAxLjAwMCAwLjAwMCIgLz48L2ZpbHRlcj48L2RlZnM+PGltYWdlIHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiBmaWx0ZXI9InVybCgjZGFya3JlYWRlci1pbWFnZS1maWx0ZXIpIiB4bGluazpocmVmPSJkYXRhOmltYWdlL3N2Zyt4bWw7Y2hhcnNldD11dGYtOCwmbHQ7c3ZnIHhtbG5zPSZxdW90O2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJnF1b3Q7IHZpZXdCb3g9JnF1b3Q7MCAwIDI0IDI0JnF1b3Q7Jmd0OyZsdDtwYXRoIGQ9JnF1b3Q7TTE5IDIxSDhWN2gxMW0wLTJIOGEyIDIgMCAwIDAtMiAydjE0YTIgMiAwIDAgMCAyIDJoMTFhMiAyIDAgMCAwIDItMlY3YTIgMiAwIDAgMC0yLTJtLTMtNEg0YTIgMiAwIDAgMC0yIDJ2MTRoMlYzaDEyVjFaJnF1b3Q7LyZndDsmbHQ7L3N2ZyZndDsiIC8+PC9zdmc+"); -} -.md-clipboard { - color: var(--darkreader-text--md-default-fg-color--lightest, #e8e6e3); - outline-color: var(--darkreader-border--md-accent-fg-color); -} -.md-clipboard:not(.focus-visible) { - -webkit-tap-highlight-color: transparent; - outline-color: initial; -} -:hover > .md-clipboard { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-clipboard:-webkit-any(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-clipboard:is(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-clipboard::after { - background-color: currentcolor; -} -.md-clipboard--inline:-webkit-any(:focus, :hover) code { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-clipboard--inline:is(:focus, :hover) code { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-consent__overlay { - background-color: rgba(0, 0, 0, 0.54); -} -.md-consent__inner { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - border-color: initial; - border-style: initial; - border-width: 0px; - box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0.2rem, rgba(0, 0, 0, 0.2) 0px 0.2rem 0.4rem; -} -.md-typeset .md-content__button { - color: var(--darkreader-text--md-default-fg-color--lighter, #e8e6e3); -} -.md-dialog { - background-color: var(--darkreader-bg--md-default-fg-color, #181a1b); - box-shadow: var(--darkreader-bg--md-shadow-z3); -} -.md-dialog__inner { - color: var(--darkreader-text--md-default-bg-color, #e8e6e3); -} -.md-feedback fieldset { - border-color: initial; - border-style: none; - border-width: initial; -} -.md-feedback__list:hover .md-icon:not(:disabled) { - color: var(--darkreader-text--md-default-fg-color--lighter, #e8e6e3); -} -.md-feedback__icon { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-feedback__icon:not(:disabled).md-icon:hover { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-feedback__icon:disabled { - color: var(--darkreader-text--md-default-fg-color--lightest, #e8e6e3); -} -.md-footer { - background-color: var(--darkreader-bg--md-footer-bg-color, #181a1b); - color: var(--darkreader-text--md-footer-fg-color, #e8e6e3); -} -.md-footer__link { - outline-color: var(--darkreader-border--md-accent-fg-color); -} -.md-footer-meta { - background-color: var(--darkreader-bg--md-footer-bg-color--dark, #181a1b); -} -html .md-footer-meta.md-typeset a { - color: var(--darkreader-text--md-footer-fg-color--light, #e8e6e3); -} -html .md-footer-meta.md-typeset a:-webkit-any(:focus, :hover) { - color: var(--darkreader-text--md-footer-fg-color, #e8e6e3); -} -html .md-footer-meta.md-typeset a:is(:focus, :hover) { - color: var(--darkreader-text--md-footer-fg-color, #e8e6e3); -} -.md-copyright { - color: var(--darkreader-text--md-footer-fg-color--lighter, #e8e6e3); -} -.md-copyright__highlight { - color: var(--darkreader-text--md-footer-fg-color--light, #e8e6e3); -} -.md-social__link svg { - fill: currentcolor; -} -.md-typeset .md-button { - border-color: initial; - color: var(--darkreader-text--md-primary-fg-color, #e8e6e3); -} -.md-typeset .md-button--primary { - background-color: var(--darkreader-bg--md-primary-fg-color, #181a1b); - border-color: var(--darkreader-border--md-primary-fg-color); - color: var(--darkreader-text--md-primary-bg-color, #e8e6e3); -} -.md-typeset .md-button:-webkit-any(:focus, :hover) { - background-color: var(--darkreader-bg--md-accent-fg-color, #181a1b); - border-color: var(--darkreader-border--md-accent-fg-color); - color: var(--darkreader-text--md-accent-bg-color, #e8e6e3); -} -.md-typeset .md-button:is(:focus, :hover) { - background-color: var(--darkreader-bg--md-accent-fg-color, #181a1b); - border-color: var(--darkreader-border--md-accent-fg-color); - color: var(--darkreader-text--md-accent-bg-color, #e8e6e3); -} -.md-typeset .md-input { - border-bottom: .1rem solid var(--darkreader-border--md-default-fg-color--lighter); - box-shadow: var(--darkreader-bg--md-shadow-z1); -} -.md-typeset .md-input:-webkit-any(:focus, :hover) { - border-bottom-color: var(--darkreader-border--md-accent-fg-color); - box-shadow: var(--darkreader-bg--md-shadow-z2); -} -.md-typeset .md-input:is(:focus, :hover) { - border-bottom-color: var(--darkreader-border--md-accent-fg-color); - box-shadow: var(--darkreader-bg--md-shadow-z2); -} -.md-header { - background-color: var(--darkreader-bg--md-primary-fg-color, #181a1b); - box-shadow: rgba(0, 0, 0, 0) 0px 0px 0.2rem, rgba(0, 0, 0, 0) 0px 0.2rem 0.4rem; - color: var(--darkreader-text--md-primary-bg-color, #e8e6e3); -} -.md-header--shadow { - box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0.2rem, rgba(0, 0, 0, 0.2) 0px 0.2rem 0.4rem; -} -.md-header__button { - color: currentcolor; - outline-color: var(--darkreader-border--md-accent-fg-color); -} -.md-header__button:not(.focus-visible) { - -webkit-tap-highlight-color: transparent; - outline-color: initial; -} -.md-header__button.md-logo :-webkit-any(img, svg) { - fill: currentcolor; -} -.md-header__button.md-logo :is(img, svg) { - fill: currentcolor; -} -:root { - --darkreader-bgimg--md-nav-icon--next: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTguNTkgMTYuNTggMTMuMTcgMTIgOC41OSA3LjQxIDEwIDZsNiA2LTYgNi0xLjQxLTEuNDJaIi8+PC9zdmc+"); - --darkreader-bgimg--md-nav-icon--prev: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTIwIDExdjJIOGw1LjUgNS41LTEuNDIgMS40Mkw0LjE2IDEybDcuOTItNy45MkwxMy41IDUuNSA4IDExaDEyWiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-toc-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTMgOWgxNFY3SDN2Mm0wIDRoMTR2LTJIM3YybTAgNGgxNHYtMkgzdjJtMTYgMGgydi0yaC0ydjJtMC0xMHYyaDJWN2gtMm0wIDZoMnYtMmgtMnYyWiIvPjwvc3ZnPg=="); -} -.md-nav__title .md-nav__button.md-logo :-webkit-any(img, svg) { - fill: currentcolor; -} -.md-nav__title .md-nav__button.md-logo :is(img, svg) { - fill: currentcolor; -} -.md-nav__list { - list-style-image: initial; -} -.md-nav__link--passed { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-nav__item .md-nav__link--active { - color: var(--darkreader-text--md-typeset-a-color, #e8e6e3); -} -.md-nav__link:-webkit-any(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-nav__link:is(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-nav__link.focus-visible { - outline-color: var(--darkreader-border--md-accent-fg-color); -} -.md-nav--primary .md-nav__link[for="__toc"] .md-icon::after { - background-color: currentcolor; -} -@media screen and (max-width: 76.1875em) { - .md-nav--primary, - .md-nav--primary .md-nav { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - } - .md-nav--primary .md-nav__title { - background-color: var(--darkreader-bg--md-default-fg-color--lightest, #181a1b); - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); - } - .md-nav--primary .md-nav__title .md-nav__icon::after { - background-color: currentcolor; - } - .md-nav--primary .md-nav__title ~ .md-nav__list { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - box-shadow: 0 .05rem 0 var(--darkreader-bg--md-default-fg-color--lightest) inset; - } - .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child { - border-top: 0px; - } - .md-nav--primary .md-nav__title[for="__drawer"] { - background-color: var(--darkreader-bg--md-primary-fg-color, #181a1b); - color: var(--darkreader-text--md-primary-bg-color, #e8e6e3); - } - .md-nav--primary .md-nav__item { - border-top: .05rem solid var(--darkreader-border--md-default-fg-color--lightest); - } - .md-nav--primary .md-nav__item--active > .md-nav__link { - color: var(--darkreader-text--md-typeset-a-color, #e8e6e3); - } - .md-nav--primary .md-nav__item--active > .md-nav__link:-webkit-any(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); - } - .md-nav--primary .md-nav__item--active > .md-nav__link:is(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); - } - .md-nav--primary .md-nav__link .md-nav__icon::after { - background-color: currentcolor; - } - .md-nav--primary .md-nav--secondary .md-nav { - background-color: initial; - } - .md-nav--secondary { - background-color: initial; - } -} -@media screen and (max-width: 59.9375em) { - .md-nav__source { - background-color: var(--darkreader-bg--md-primary-fg-color--dark, #181a1b); - color: var(--darkreader-text--md-primary-bg-color, #e8e6e3); - } -} -@media screen and (min-width: 60em) { - .md-nav--secondary .md-nav__title { - background: var(--darkreader-bg--md-default-bg-color); - box-shadow: 0 0 .4rem .4rem var(--darkreader-bg--md-default-bg-color); - } -} -@media screen and (min-width: 76.25em) { - .md-nav--primary .md-nav__title { - background: var(--darkreader-bg--md-default-bg-color); - box-shadow: 0 0 .4rem .4rem var(--darkreader-bg--md-default-bg-color); - } - .md-nav__icon:hover { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); - } - .md-nav__icon::after { - background-color: currentcolor; - } - .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link { - background: var(--darkreader-bg--md-default-bg-color); - box-shadow: 0 0 .4rem .4rem var(--darkreader-bg--md-default-bg-color); - } - .md-nav--integrated > .md-nav__list > .md-nav__item--active .md-nav--secondary { - border-left: .05rem solid var(--darkreader-border--md-primary-fg-color); - } - [dir="rtl"] .md-nav--integrated > .md-nav__list > .md-nav__item--active .md-nav--secondary { - border-right: .05rem solid var(--darkreader-border--md-primary-fg-color); - } -} -:root { - --darkreader-bgimg--md-search-result-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE0IDJINmEyIDIgMCAwIDAtMiAydjE2YTIgMiAwIDAgMCAyIDJoN2MtLjQxLS4yNS0uOC0uNTYtMS4xNC0uOS0uMzMtLjMzLS42MS0uNy0uODYtMS4xSDZWNGg3djVoNXYxLjE4Yy43MS4xNiAxLjM5LjQzIDIgLjgyVjhsLTYtNm02LjMxIDE2LjljMS4zMy0yLjExLjY5LTQuOS0xLjQtNi4yMi0yLjExLTEuMzMtNC45MS0uNjgtNi4yMiAxLjQtMS4zNCAyLjExLS42OSA0Ljg5IDEuNCA2LjIyIDEuNDYuOTMgMy4zMi45MyA0Ljc5LjAyTDIyIDIzLjM5IDIzLjM5IDIybC0zLjA4LTMuMW0tMy44MS4xYTIuNSAyLjUgMCAwIDEtMi41LTIuNSAyLjUgMi41IDAgMCAxIDIuNS0yLjUgMi41IDIuNSAwIDAgMSAyLjUgMi41IDIuNSAyLjUgMCAwIDEtMi41IDIuNVoiLz48L3N2Zz4="); -} -@media screen and (max-width: 59.9375em) { - .md-search__overlay { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - } -} -@media screen and (min-width: 60em) { - .md-search__overlay { - background-color: rgba(0, 0, 0, 0.54); - } -} -.md-search__form { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - box-shadow: rgba(0, 0, 0, 0) 0px 0px 0.6rem; -} -@media screen and (min-width: 60em) { - .md-search__form { - background-color: rgba(0, 0, 0, 0.26); - } - .md-search__form:hover { - background-color: rgba(24, 26, 27, 0.12); - } -} -[data-md-toggle="search"]:checked ~ .md-header .md-search__form { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - box-shadow: rgba(0, 0, 0, 0.07) 0px 0px 0.6rem; - color: var(--darkreader-text--md-default-fg-color, #e8e6e3); -} -.md-search__input { - background-color: transparent; - background-image: initial; -} -.md-search__input::placeholder, -.md-search__input ~ .md-search__icon { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -@media screen and (min-width: 60em) { - .md-search__input { - color: inherit; - } - .md-search__input::placeholder { - color: var(--darkreader-text--md-primary-bg-color--light, #e8e6e3); - } - .md-search__input + .md-search__icon { - color: var(--darkreader-text--md-primary-bg-color, #e8e6e3); - } - [data-md-toggle="search"]:checked ~ .md-header .md-search__input + .md-search__icon, - [data-md-toggle="search"]:checked ~ .md-header .md-search__input::placeholder { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); - } -} -.md-search__options > * { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-search__options > :not(.focus-visible) { - -webkit-tap-highlight-color: transparent; - outline-color: initial; -} -.md-search__suggest { - color: var(--darkreader-text--md-default-fg-color--lighter, #e8e6e3); -} -@media screen and (min-width: 60em) { - [data-md-toggle="search"]:checked ~ .md-header .md-search__output { - box-shadow: var(--darkreader-bg--md-shadow-z3); - } -} -.md-search__scrollwrap { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); -} -@media screen and (min-width: 60em) { - .md-search__scrollwrap::-webkit-scrollbar-thumb { - background-color: var(--darkreader-bg--md-default-fg-color--lighter, #181a1b); - } - .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: var(--darkreader-bg--md-accent-fg-color, #181a1b); - } -} -.md-search-result { - color: var(--darkreader-text--md-default-fg-color, #e8e6e3); -} -.md-search-result__meta { - background-color: var(--darkreader-bg--md-default-fg-color--lightest, #181a1b); - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-search-result__list { - list-style-image: initial; -} -.md-search-result__item { - box-shadow: 0 -.05rem var(--darkreader-bg--md-default-fg-color--lightest); -} -.md-search-result__item:first-child { - box-shadow: none; -} -.md-search-result__link { - outline-color: initial; -} -.md-search-result__link:-webkit-any(:focus, :hover) { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); -} -.md-search-result__link:is(:focus, :hover) { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); -} -.md-search-result__more summary { - color: var(--darkreader-text--md-typeset-a-color, #e8e6e3); - outline-color: initial; -} -.md-search-result__more summary:-webkit-any(:focus, :hover) { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-search-result__more summary:is(:focus, :hover) { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-search-result__icon { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-search-result__icon::after { - background-color: currentcolor; -} -.md-search-result__teaser { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-search-result__teaser mark { - background-color: initial; - text-decoration-color: initial; -} -.md-search-result mark { - background-color: initial; - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-select__inner { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - box-shadow: var(--darkreader-bg--md-shadow-z2); - color: var(--darkreader-text--md-default-fg-color, #e8e6e3); -} -.md-select__inner::after { - border-bottom-color: var(--darkreader-border--md-default-bg-color); - border-left-color: transparent; - border-right-color: transparent; - border-top: 0px; -} -.md-select__link { - outline-color: initial; -} -.md-select__link:-webkit-any(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-select__link:is(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-select__link:focus { - background-color: var(--darkreader-bg--md-default-fg-color--lightest, #181a1b); -} -@media screen and (max-width: 76.1875em) { - .md-sidebar--primary { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - } - [data-md-toggle="drawer"]:checked ~ .md-container .md-sidebar--primary { - box-shadow: var(--darkreader-bg--md-shadow-z3); - } -} -.md-sidebar__scrollwrap::-webkit-scrollbar-thumb { - background-color: var(--darkreader-bg--md-default-fg-color--lighter, #181a1b); -} -.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: var(--darkreader-bg--md-accent-fg-color, #181a1b); -} -@media screen and (max-width: 76.1875em) { - .md-overlay { - background-color: rgba(0, 0, 0, 0.54); - } -} -:root { - --darkreader-bgimg--md-source-forks-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCI+PGRlZnM+PGZpbHRlciBpZD0iZGFya3JlYWRlci1pbWFnZS1maWx0ZXIiPjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwLjI0OSAtMC42MTQgLTAuNjcyIDAuMDAwIDEuMDM1IC0wLjY0NiAwLjI4OCAtMC42NjQgMC4wMDAgMS4wMjAgLTAuNjM2IC0wLjYwOSAwLjI1MCAwLjAwMCAwLjk5NCAwLjAwMCAwLjAwMCAwLjAwMCAxLjAwMCAwLjAwMCIgLz48L2ZpbHRlcj48L2RlZnM+PGltYWdlIHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiBmaWx0ZXI9InVybCgjZGFya3JlYWRlci1pbWFnZS1maWx0ZXIpIiB4bGluazpocmVmPSJkYXRhOmltYWdlL3N2Zyt4bWw7Y2hhcnNldD11dGYtOCwmbHQ7c3ZnIHhtbG5zPSZxdW90O2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJnF1b3Q7IHZpZXdCb3g9JnF1b3Q7MCAwIDE2IDE2JnF1b3Q7Jmd0OyZsdDtwYXRoIGZpbGwtcnVsZT0mcXVvdDtldmVub2RkJnF1b3Q7IGQ9JnF1b3Q7TTUgMy4yNWEuNzUuNzUgMCAxIDEtMS41IDAgLjc1Ljc1IDAgMCAxIDEuNSAwem0wIDIuMTIyYTIuMjUgMi4yNSAwIDEgMC0xLjUgMHYuODc4QTIuMjUgMi4yNSAwIDAgMCA1Ljc1IDguNWgxLjV2Mi4xMjhhMi4yNTEgMi4yNTEgMCAxIDAgMS41IDBWOC41aDEuNWEyLjI1IDIuMjUgMCAwIDAgMi4yNS0yLjI1di0uODc4YTIuMjUgMi4yNSAwIDEgMC0xLjUgMHYuODc4YS43NS43NSAwIDAgMS0uNzUuNzVoLTQuNUEuNzUuNzUgMCAwIDEgNSA2LjI1di0uODc4em0zLjc1IDcuMzc4YS43NS43NSAwIDEgMS0xLjUgMCAuNzUuNzUgMCAwIDEgMS41IDB6bTMtOC43NWEuNzUuNzUgMCAxIDAgMC0xLjUuNzUuNzUgMCAwIDAgMCAxLjV6JnF1b3Q7LyZndDsmbHQ7L3N2ZyZndDsiIC8+PC9zdmc+"); - --darkreader-bgimg--md-source-repositories-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCI+PGRlZnM+PGZpbHRlciBpZD0iZGFya3JlYWRlci1pbWFnZS1maWx0ZXIiPjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwLjI0OSAtMC42MTQgLTAuNjcyIDAuMDAwIDEuMDM1IC0wLjY0NiAwLjI4OCAtMC42NjQgMC4wMDAgMS4wMjAgLTAuNjM2IC0wLjYwOSAwLjI1MCAwLjAwMCAwLjk5NCAwLjAwMCAwLjAwMCAwLjAwMCAxLjAwMCAwLjAwMCIgLz48L2ZpbHRlcj48L2RlZnM+PGltYWdlIHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiBmaWx0ZXI9InVybCgjZGFya3JlYWRlci1pbWFnZS1maWx0ZXIpIiB4bGluazpocmVmPSJkYXRhOmltYWdlL3N2Zyt4bWw7Y2hhcnNldD11dGYtOCwmbHQ7c3ZnIHhtbG5zPSZxdW90O2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJnF1b3Q7IHZpZXdCb3g9JnF1b3Q7MCAwIDE2IDE2JnF1b3Q7Jmd0OyZsdDtwYXRoIGZpbGwtcnVsZT0mcXVvdDtldmVub2RkJnF1b3Q7IGQ9JnF1b3Q7TTIgMi41QTIuNSAyLjUgMCAwIDEgNC41IDBoOC43NWEuNzUuNzUgMCAwIDEgLjc1Ljc1djEyLjVhLjc1Ljc1IDAgMCAxLS43NS43NWgtMi41YS43NS43NSAwIDEgMSAwLTEuNWgxLjc1di0yaC04YTEgMSAwIDAgMC0uNzE0IDEuNy43NS43NSAwIDAgMS0xLjA3MiAxLjA1QTIuNDk1IDIuNDk1IDAgMCAxIDIgMTEuNXYtOXptMTAuNS0xVjloLThjLS4zNTYgMC0uNjk0LjA3NC0xIC4yMDhWMi41YTEgMSAwIDAgMSAxLTFoOHpNNSAxMi4yNXYzLjI1YS4yNS4yNSAwIDAgMCAuNC4ybDEuNDUtMS4wODdhLjI1LjI1IDAgMCAxIC4zIDBMOC42IDE1LjdhLjI1LjI1IDAgMCAwIC40LS4ydi0zLjI1YS4yNS4yNSAwIDAgMC0uMjUtLjI1aC0zLjVhLjI1LjI1IDAgMCAwLS4yNS4yNXomcXVvdDsvJmd0OyZsdDsvc3ZnJmd0OyIgLz48L3N2Zz4="); - --darkreader-bgimg--md-source-stars-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNOCAuMjVhLjc1Ljc1IDAgMCAxIC42NzMuNDE4bDEuODgyIDMuODE1IDQuMjEuNjEyYS43NS43NSAwIDAgMSAuNDE2IDEuMjc5bC0zLjA0NiAyLjk3LjcxOSA0LjE5MmEuNzUuNzUgMCAwIDEtMS4wODguNzkxTDggMTIuMzQ3bC0zLjc2NiAxLjk4YS43NS43NSAwIDAgMS0xLjA4OC0uNzlsLjcyLTQuMTk0TC44MTggNi4zNzRhLjc1Ljc1IDAgMCAxIC40MTYtMS4yOGw0LjIxLS42MTFMNy4zMjcuNjY4QS43NS43NSAwIDAgMSA4IC4yNXptMCAyLjQ0NUw2LjYxNSA1LjVhLjc1Ljc1IDAgMCAxLS41NjQuNDFsLTMuMDk3LjQ1IDIuMjQgMi4xODRhLjc1Ljc1IDAgMCAxIC4yMTYuNjY0bC0uNTI4IDMuMDg0IDIuNzY5LTEuNDU2YS43NS43NSAwIDAgMSAuNjk4IDBsMi43NyAxLjQ1Ni0uNTMtMy4wODRhLjc1Ljc1IDAgMCAxIC4yMTYtLjY2NGwyLjI0LTIuMTgzLTMuMDk2LS40NWEuNzUuNzUgMCAwIDEtLjU2NC0uNDFMOCAyLjY5NHYuMDAxeiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-source-version-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCI+PGRlZnM+PGZpbHRlciBpZD0iZGFya3JlYWRlci1pbWFnZS1maWx0ZXIiPjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwLjI0OSAtMC42MTQgLTAuNjcyIDAuMDAwIDEuMDM1IC0wLjY0NiAwLjI4OCAtMC42NjQgMC4wMDAgMS4wMjAgLTAuNjM2IC0wLjYwOSAwLjI1MCAwLjAwMCAwLjk5NCAwLjAwMCAwLjAwMCAwLjAwMCAxLjAwMCAwLjAwMCIgLz48L2ZpbHRlcj48L2RlZnM+PGltYWdlIHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiBmaWx0ZXI9InVybCgjZGFya3JlYWRlci1pbWFnZS1maWx0ZXIpIiB4bGluazpocmVmPSJkYXRhOmltYWdlL3N2Zyt4bWw7Y2hhcnNldD11dGYtOCwmbHQ7c3ZnIHhtbG5zPSZxdW90O2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJnF1b3Q7IHZpZXdCb3g9JnF1b3Q7MCAwIDE2IDE2JnF1b3Q7Jmd0OyZsdDtwYXRoIGZpbGwtcnVsZT0mcXVvdDtldmVub2RkJnF1b3Q7IGQ9JnF1b3Q7TTIuNSA3Ljc3NVYyLjc1YS4yNS4yNSAwIDAgMSAuMjUtLjI1aDUuMDI1YS4yNS4yNSAwIDAgMSAuMTc3LjA3M2w2LjI1IDYuMjVhLjI1LjI1IDAgMCAxIDAgLjM1NGwtNS4wMjUgNS4wMjVhLjI1LjI1IDAgMCAxLS4zNTQgMGwtNi4yNS02LjI1YS4yNS4yNSAwIDAgMS0uMDczLS4xNzd6bS0xLjUgMFYyLjc1QzEgMS43ODQgMS43ODQgMSAyLjc1IDFoNS4wMjVjLjQ2NCAwIC45MS4xODQgMS4yMzguNTEzbDYuMjUgNi4yNWExLjc1IDEuNzUgMCAwIDEgMCAyLjQ3NGwtNS4wMjYgNS4wMjZhMS43NSAxLjc1IDAgMCAxLTIuNDc0IDBsLTYuMjUtNi4yNUExLjc1IDEuNzUgMCAwIDEgMSA3Ljc3NXpNNiA1YTEgMSAwIDEgMCAwIDIgMSAxIDAgMCAwIDAtMnomcXVvdDsvJmd0OyZsdDsvc3ZnJmd0OyIgLz48L3N2Zz4="); -} -.md-source { - outline-color: var(--darkreader-border--md-accent-fg-color); -} -.md-source__fact::before { - background-color: currentcolor; -} -.md-tabs { - background-color: var(--darkreader-bg--md-primary-fg-color, #181a1b); - color: var(--darkreader-text--md-primary-bg-color, #e8e6e3); -} -.md-tabs__list { - list-style-image: initial; -} -.md-tabs__link { - outline-color: var(--darkreader-border--md-accent-fg-color); -} -.md-tabs__link--active, -.md-tabs__link:-webkit-any(:focus, :hover) { - color: inherit; -} -.md-tabs__link--active, -.md-tabs__link:is(:focus, :hover) { - color: inherit; -} -:root { - --darkreader-bgimg--md-tag-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0ibTUuNDEgMjEgLjcxLTRoLTRsLjM1LTJoNGwxLjA2LTZoLTRsLjM1LTJoNGwuNzEtNGgybC0uNzEgNGg2bC43MS00aDJsLS43MSA0aDRsLS4zNSAyaC00bC0xLjA2IDZoNGwtLjM1IDJoLTRsLS43MSA0aC0ybC43MS00aC02bC0uNzEgNGgtMk05LjUzIDlsLTEuMDYgNmg2bDEuMDYtNmgtNloiLz48L3N2Zz4="); -} -.md-typeset .md-tag { - background: var(--darkreader-bg--md-default-fg-color--lightest); -} -.md-typeset .md-tag[href] { - -webkit-tap-highlight-color: transparent; - color: inherit; - outline-color: initial; -} -.md-typeset .md-tag[href]:focus, -.md-typeset .md-tag[href]:hover { - background-color: var(--darkreader-bg--md-accent-fg-color, #181a1b); - color: var(--darkreader-text--md-accent-bg-color, #e8e6e3); -} -.md-typeset .md-tag-icon::before { - background-color: var(--darkreader-bg--md-default-fg-color--lighter, #181a1b); -} -.md-typeset .md-tag-icon:-webkit-any(a:focus, a:hover)::before { - background-color: var(--darkreader-bg--md-accent-bg-color, #181a1b); -} -.md-typeset .md-tag-icon:is(a:focus, a:hover)::before { - background-color: var(--darkreader-bg--md-accent-bg-color, #181a1b); -} -:root { - --md-tooltip-width: 20rem; -} -.md-tooltip { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - box-shadow: var(--darkreader-bg--md-shadow-z2); - color: var(--darkreader-text--md-default-fg-color, #e8e6e3); -} -:is(.focus-visible > .md-tooltip, .md-tooltip:target) { - outline: var(--darkreader-border--md-accent-fg-color) auto; -} -.md-annotation { - outline-color: initial; -} -.md-annotation__index { - outline-color: initial; -} -.md-annotation .md-annotation__index { - color: rgb(232, 230, 227); -} -.md-annotation .md-annotation__index:-webkit-any(:focus, :hover) { - color: rgb(232, 230, 227); -} -.md-annotation .md-annotation__index:is(:focus, :hover) { - color: rgb(232, 230, 227); -} -.md-annotation__index::after { - background-color: var(--darkreader-bg--md-default-fg-color--lighter, #181a1b); -} -:is(.md-tooltip--active + .md-annotation__index, :hover > .md-annotation__index) { - color: var(--darkreader-text--md-accent-bg-color, #e8e6e3); -} -:is(.md-tooltip--active + .md-annotation__index, :hover > .md-annotation__index)::after { - background-color: var(--darkreader-bg--md-accent-fg-color, #181a1b); -} -.md-top { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - box-shadow: var(--darkreader-bg--md-shadow-z2); - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); - outline-color: initial; -} -.md-top:-webkit-any(:focus, :hover) { - background-color: var(--darkreader-bg--md-accent-fg-color, #181a1b); - color: var(--darkreader-text--md-accent-bg-color, #e8e6e3); -} -.md-top:is(:focus, :hover) { - background-color: var(--darkreader-bg--md-accent-fg-color, #181a1b); - color: var(--darkreader-text--md-accent-bg-color, #e8e6e3); -} -:root { - --darkreader-bgimg--md-version-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMjAgNTEyIj48IS0tISBGb250IEF3ZXNvbWUgRnJlZSA2LjIuMCBieSBAZm9udGF3ZXNvbWUgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbSBMaWNlbnNlIC0gaHR0cHM6Ly9mb250YXdlc29tZS5jb20vbGljZW5zZS9mcmVlIChJY29uczogQ0MgQlkgNC4wLCBGb250czogU0lMIE9GTCAxLjEsIENvZGU6IE1JVCBMaWNlbnNlKSBDb3B5cmlnaHQgMjAyMiBGb250aWNvbnMsIEluYy4tLT48cGF0aCBkPSJNMTM3LjQgMzc0LjZjMTIuNSAxMi41IDMyLjggMTIuNSA0NS4zIDBsMTI4LTEyOGM5LjItOS4yIDExLjktMjIuOSA2LjktMzQuOVMzMDEgMTkxLjkgMjg4IDE5MS45TDMyIDE5MmMtMTIuOSAwLTI0LjYgNy44LTI5LjYgMTkuOHMtMi4yIDI1LjcgNi45IDM0LjlsMTI4IDEyOHoiLz48L3N2Zz4="); -} -.md-version__current { - color: inherit; - outline-color: initial; -} -.md-version__current::after { - background-color: currentcolor; -} -.md-version__list { - background-color: var(--darkreader-bg--md-default-bg-color, #181a1b); - box-shadow: var(--darkreader-bg--md-shadow-z2); - color: var(--darkreader-text--md-default-fg-color, #e8e6e3); -} -.md-version__link { - outline-color: initial; -} -.md-version__link:-webkit-any(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-version__link:is(:focus, :hover) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-version__link:focus { - background-color: var(--darkreader-bg--md-default-fg-color--lightest, #181a1b); -} -:root { - --darkreader-bgimg--md-admonition-icon--abstract: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE3IDlIN1Y3aDEwbTAgNkg3di0yaDEwbS0zIDZIN3YtMmg3TTEyIDNhMSAxIDAgMCAxIDEgMSAxIDEgMCAwIDEtMSAxIDEgMSAwIDAgMS0xLTEgMSAxIDAgMCAxIDEtMW03IDBoLTQuMThDMTQuNCAxLjg0IDEzLjMgMSAxMiAxYy0xLjMgMC0yLjQuODQtMi44MiAySDVhMiAyIDAgMCAwLTIgMnYxNGEyIDIgMCAwIDAgMiAyaDE0YTIgMiAwIDAgMCAyLTJWNWEyIDIgMCAwIDAtMi0yWiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-admonition-icon--bug: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTExIDEzaDJ2MWgtMnYtMW0xMC04djZjMCA1LjUtMy44IDEwLjctOSAxMi01LjItMS4zLTktNi41LTktMTJWNWw5LTQgOSA0bS00IDVoLTIuMmMtLjItLjYtLjYtMS4xLTEuMS0xLjVsMS4yLTEuMi0uNy0uN0wxMi44IDhIMTJjLS4yIDAtLjUgMC0uNy4xTDkuOSA2LjZsLS44LjggMS4yIDEuMmMtLjUuMy0uOS44LTEuMSAxLjRIN3YxaDJ2MUg3djFoMnYxSDd2MWgyLjJjLjQgMS4yIDEuNSAyIDIuOCAyczIuNC0uOCAyLjgtMkgxN3YtMWgtMnYtMWgydi0xaC0ydi0xaDJ2LTFtLTYgMmgydi0xaC0ydjFaIi8+PC9zdmc+"); - --darkreader-bgimg--md-admonition-icon--danger: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0ibTExLjUgMjAgNC44Ni05LjczSDEzVjRsLTUgOS43M2gzLjVWMjBNMTIgMmMyLjc1IDAgNS4xIDEgNy4wNSAyLjk1QzIxIDYuOSAyMiA5LjI1IDIyIDEycy0xIDUuMS0yLjk1IDcuMDVDMTcuMSAyMSAxNC43NSAyMiAxMiAyMnMtNS4xLTEtNy4wNS0yLjk1QzMgMTcuMSAyIDE0Ljc1IDIgMTJzMS01LjEgMi45NS03LjA1QzYuOSAzIDkuMjUgMiAxMiAyWiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-admonition-icon--example: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTcgMnYyaDF2MTRhNCA0IDAgMCAwIDQgNCA0IDQgMCAwIDAgNC00VjRoMVYySDdtNCAxNGMtLjYgMC0xLS40LTEtMXMuNC0xIDEtMSAxIC40IDEgMS0uNCAxLTEgMW0yLTRjLS42IDAtMS0uNC0xLTFzLjQtMSAxLTEgMSAuNCAxIDEtLjQgMS0xIDFtMS01aC00VjRoNHYzWiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-admonition-icon--failure: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE5IDYuNDEgMTcuNTkgNSAxMiAxMC41OSA2LjQxIDUgNSA2LjQxIDEwLjU5IDEyIDUgMTcuNTkgNi40MSAxOSAxMiAxMy40MSAxNy41OSAxOSAxOSAxNy41OSAxMy40MSAxMiAxOSA2LjQxWiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-admonition-icon--info: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEzIDloLTJWN2gybTAgMTBoLTJ2LTZoMm0tMS05QTEwIDEwIDAgMCAwIDIgMTJhMTAgMTAgMCAwIDAgMTAgMTAgMTAgMTAgMCAwIDAgMTAtMTBBMTAgMTAgMCAwIDAgMTIgMloiLz48L3N2Zz4="); - --darkreader-bgimg--md-admonition-icon--note: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEyIDJDNi40NyAyIDIgNi40NyAyIDEyczQuNDcgMTAgMTAgMTAgMTAtNC40NyAxMC0xMFMxNy41MyAyIDEyIDJtMy4xIDUuMDdjLjE0IDAgLjI4LjA1LjQuMTZsMS4yNyAxLjI3Yy4yMy4yMi4yMy41NyAwIC43OGwtMSAxLTIuMDUtMi4wNSAxLTFjLjEtLjExLjI0LS4xNi4zOC0uMTZtLTEuOTcgMS43NCAyLjA2IDIuMDYtNi4wNiA2LjA2SDcuMDd2LTIuMDZsNi4wNi02LjA2WiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-admonition-icon--question: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0ibTE1LjA3IDExLjI1LS45LjkyQzEzLjQ1IDEyLjg5IDEzIDEzLjUgMTMgMTVoLTJ2LS41YzAtMS4xMS40NS0yLjExIDEuMTctMi44M2wxLjI0LTEuMjZjLjM3LS4zNi41OS0uODYuNTktMS40MWEyIDIgMCAwIDAtMi0yIDIgMiAwIDAgMC0yIDJIOGE0IDQgMCAwIDEgNC00IDQgNCAwIDAgMSA0IDQgMy4yIDMuMiAwIDAgMS0uOTMgMi4yNU0xMyAxOWgtMnYtMmgyTTEyIDJBMTAgMTAgMCAwIDAgMiAxMmExMCAxMCAwIDAgMCAxMCAxMCAxMCAxMCAwIDAgMCAxMC0xMGMwLTUuNTMtNC41LTEwLTEwLTEwWiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-admonition-icon--quote: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE0IDE3aDNsMi00VjdoLTZ2NmgzTTYgMTdoM2wyLTRWN0g1djZoM2wtMiA0WiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-admonition-icon--success: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTIxIDcgOSAxOWwtNS41LTUuNSAxLjQxLTEuNDFMOSAxNi4xNyAxOS41OSA1LjU5IDIxIDdaIi8+PC9zdmc+"); - --darkreader-bgimg--md-admonition-icon--tip: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE3LjY2IDExLjJjLS4yMy0uMy0uNTEtLjU2LS43Ny0uODItLjY3LS42LTEuNDMtMS4wMy0yLjA3LTEuNjZDMTMuMzMgNy4yNiAxMyA0Ljg1IDEzLjk1IDNjLS45NS4yMy0xLjc4Ljc1LTIuNDkgMS4zMi0yLjU5IDIuMDgtMy42MSA1Ljc1LTIuMzkgOC45LjA0LjEuMDguMi4wOC4zMyAwIC4yMi0uMTUuNDItLjM1LjUtLjIzLjEtLjQ3LjA0LS42Ni0uMTJhLjU4LjU4IDAgMCAxLS4xNC0uMTdjLTEuMTMtMS40My0xLjMxLTMuNDgtLjU1LTUuMTJDNS43OCAxMCA0Ljg3IDEyLjMgNSAxNC40N2MuMDYuNS4xMiAxIC4yOSAxLjUuMTQuNi40MSAxLjIuNzEgMS43MyAxLjA4IDEuNzMgMi45NSAyLjk3IDQuOTYgMy4yMiAyLjE0LjI3IDQuNDMtLjEyIDYuMDctMS42IDEuODMtMS42NiAyLjQ3LTQuMzIgMS41My02LjZsLS4xMy0uMjZjLS4yMS0uNDYtLjc3LTEuMjYtLjc3LTEuMjZtLTMuMTYgNi4zYy0uMjguMjQtLjc0LjUtMS4xLjYtMS4xMi40LTIuMjQtLjE2LTIuOS0uODIgMS4xOS0uMjggMS45LTEuMTYgMi4xMS0yLjA1LjE3LS44LS4xNS0xLjQ2LS4yOC0yLjIzLS4xMi0uNzQtLjEtMS4zNy4xNy0yLjA2LjE5LjM4LjM5Ljc2LjYzIDEuMDYuNzcgMSAxLjk4IDEuNDQgMi4yNCAyLjguMDQuMTQuMDYuMjguMDYuNDMuMDMuODItLjMzIDEuNzItLjkzIDIuMjdaIi8+PC9zdmc+"); - --darkreader-bgimg--md-admonition-icon--warning: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEzIDE0aC0yVjloMm0wIDloLTJ2LTJoMk0xIDIxaDIyTDEyIDIgMSAyMVoiLz48L3N2Zz4="); -} -.md-typeset .admonition, -.md-typeset details { - background-color: var(--darkreader-bg--md-admonition-bg-color, #181a1b); - border-color: rgb(0, 59, 158); - box-shadow: var(--darkreader-bg--md-shadow-z1); - color: var(--darkreader-text--md-admonition-fg-color, #e8e6e3); -} -.md-typeset .admonition-title, -.md-typeset summary { - background-color: rgba(0, 61, 163, 0.1); - border-color: initial; - border-style: none; - border-width: initial; -} -.md-typeset .admonition-title::before, -.md-typeset summary::before { - background-color: rgb(73, 165, 255); -} -.md-typeset .admonition-title code, -.md-typeset summary code { - box-shadow: 0 0 0 .05rem var(--darkreader-bg--md-default-fg-color--lightest); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.note) { - border-color: rgb(0, 59, 158); -} -.md-typeset :is(.admonition, details):is(.note) { - border-color: rgb(0, 59, 158); -} -.md-typeset :-webkit-any(.note) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(0, 61, 163, 0.1); -} -.md-typeset :is(.note) > :is(.admonition-title, summary) { - background-color: rgba(0, 61, 163, 0.1); -} -.md-typeset :-webkit-any(.note) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(73, 165, 255); -} -.md-typeset :is(.note) > :is(.admonition-title, summary)::before { - background-color: rgb(73, 165, 255); -} -.md-typeset :-webkit-any(.note) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(73, 165, 255); -} -.md-typeset :is(.note) > :is(.admonition-title, summary)::after { - color: rgb(73, 165, 255); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.abstract, .summary, .tldr) { - border-color: rgb(0, 123, 179); -} -.md-typeset :is(.admonition, details):is(.abstract, .summary, .tldr) { - border-color: rgb(0, 123, 179); -} -.md-typeset :-webkit-any(.abstract, .summary, .tldr) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(0, 141, 204, 0.1); -} -.md-typeset :is(.abstract, .summary, .tldr) > :is(.admonition-title, summary) { - background-color: rgba(0, 141, 204, 0.1); -} -.md-typeset :-webkit-any(.abstract, .summary, .tldr) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(26, 184, 255); -} -.md-typeset :is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::before { - background-color: rgb(26, 184, 255); -} -.md-typeset :-webkit-any(.abstract, .summary, .tldr) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(26, 184, 255); -} -.md-typeset :is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::after { - color: rgb(26, 184, 255); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.info, .todo) { - border-color: rgb(0, 166, 191); -} -.md-typeset :is(.admonition, details):is(.info, .todo) { - border-color: rgb(0, 166, 191); -} -.md-typeset :-webkit-any(.info, .todo) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(0, 147, 170, 0.1); -} -.md-typeset :is(.info, .todo) > :is(.admonition-title, summary) { - background-color: rgba(0, 147, 170, 0.1); -} -.md-typeset :-webkit-any(.info, .todo) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(56, 229, 255); -} -.md-typeset :is(.info, .todo) > :is(.admonition-title, summary)::before { - background-color: rgb(56, 229, 255); -} -.md-typeset :-webkit-any(.info, .todo) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(56, 229, 255); -} -.md-typeset :is(.info, .todo) > :is(.admonition-title, summary)::after { - color: rgb(56, 229, 255); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.tip, .hint, .important) { - border-color: rgb(0, 198, 171); -} -.md-typeset :is(.admonition, details):is(.tip, .hint, .important) { - border-color: rgb(0, 198, 171); -} -.md-typeset :-webkit-any(.tip, .hint, .important) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(0, 153, 132, 0.1); -} -.md-typeset :is(.tip, .hint, .important) > :is(.admonition-title, summary) { - background-color: rgba(0, 153, 132, 0.1); -} -.md-typeset :-webkit-any(.tip, .hint, .important) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(70, 255, 230); -} -.md-typeset :is(.tip, .hint, .important) > :is(.admonition-title, summary)::before { - background-color: rgb(70, 255, 230); -} -.md-typeset :-webkit-any(.tip, .hint, .important) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(70, 255, 230); -} -.md-typeset :is(.tip, .hint, .important) > :is(.admonition-title, summary)::after { - color: rgb(70, 255, 230); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.success, .check, .done) { - border-color: rgb(0, 195, 81); -} -.md-typeset :is(.admonition, details):is(.success, .check, .done) { - border-color: rgb(0, 195, 81); -} -.md-typeset :-webkit-any(.success, .check, .done) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(0, 160, 66, 0.1); -} -.md-typeset :is(.success, .check, .done) > :is(.admonition-title, summary) { - background-color: rgba(0, 160, 66, 0.1); -} -.md-typeset :-webkit-any(.success, .check, .done) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(64, 255, 143); -} -.md-typeset :is(.success, .check, .done) > :is(.admonition-title, summary)::before { - background-color: rgb(64, 255, 143); -} -.md-typeset :-webkit-any(.success, .check, .done) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(64, 255, 143); -} -.md-typeset :is(.success, .check, .done) > :is(.admonition-title, summary)::after { - color: rgb(64, 255, 143); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.question, .help, .faq) { - border-color: rgb(75, 165, 17); -} -.md-typeset :is(.admonition, details):is(.question, .help, .faq) { - border-color: rgb(75, 165, 17); -} -.md-typeset :-webkit-any(.question, .help, .faq) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(80, 177, 18, 0.1); -} -.md-typeset :is(.question, .help, .faq) > :is(.admonition-title, summary) { - background-color: rgba(80, 177, 18, 0.1); -} -.md-typeset :-webkit-any(.question, .help, .faq) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(124, 234, 54); -} -.md-typeset :is(.question, .help, .faq) > :is(.admonition-title, summary)::before { - background-color: rgb(124, 234, 54); -} -.md-typeset :-webkit-any(.question, .help, .faq) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(124, 234, 54); -} -.md-typeset :is(.question, .help, .faq) > :is(.admonition-title, summary)::after { - color: rgb(124, 234, 54); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.warning, .caution, .attention) { - border-color: rgb(179, 102, 0); -} -.md-typeset :is(.admonition, details):is(.warning, .caution, .attention) { - border-color: rgb(179, 102, 0); -} -.md-typeset :-webkit-any(.warning, .caution, .attention) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(204, 116, 0, 0.1); -} -.md-typeset :is(.warning, .caution, .attention) > :is(.admonition-title, summary) { - background-color: rgba(204, 116, 0, 0.1); -} -.md-typeset :-webkit-any(.warning, .caution, .attention) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(255, 156, 26); -} -.md-typeset :is(.warning, .caution, .attention) > :is(.admonition-title, summary)::before { - background-color: rgb(255, 156, 26); -} -.md-typeset :-webkit-any(.warning, .caution, .attention) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(255, 156, 26); -} -.md-typeset :is(.warning, .caution, .attention) > :is(.admonition-title, summary)::after { - color: rgb(255, 156, 26); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.failure, .fail, .missing) { - border-color: rgb(154, 0, 0); -} -.md-typeset :is(.admonition, details):is(.failure, .fail, .missing) { - border-color: rgb(154, 0, 0); -} -.md-typeset :-webkit-any(.failure, .fail, .missing) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(155, 0, 0, 0.1); -} -.md-typeset :is(.failure, .fail, .missing) > :is(.admonition-title, summary) { - background-color: rgba(155, 0, 0, 0.1); -} -.md-typeset :-webkit-any(.failure, .fail, .missing) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(255, 83, 83); -} -.md-typeset :is(.failure, .fail, .missing) > :is(.admonition-title, summary)::before { - background-color: rgb(255, 83, 83); -} -.md-typeset :-webkit-any(.failure, .fail, .missing) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(255, 83, 83); -} -.md-typeset :is(.failure, .fail, .missing) > :is(.admonition-title, summary)::after { - color: rgb(255, 83, 83); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.danger, .error) { - border-color: rgb(172, 0, 33); -} -.md-typeset :is(.admonition, details):is(.danger, .error) { - border-color: rgb(172, 0, 33); -} -.md-typeset :-webkit-any(.danger, .error) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(190, 0, 37, 0.1); -} -.md-typeset :is(.danger, .error) > :is(.admonition-title, summary) { - background-color: rgba(190, 0, 37, 0.1); -} -.md-typeset :-webkit-any(.danger, .error) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(255, 42, 83); -} -.md-typeset :is(.danger, .error) > :is(.admonition-title, summary)::before { - background-color: rgb(255, 42, 83); -} -.md-typeset :-webkit-any(.danger, .error) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(255, 42, 83); -} -.md-typeset :is(.danger, .error) > :is(.admonition-title, summary)::after { - color: rgb(255, 42, 83); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.bug) { - border-color: rgb(182, 0, 64); -} -.md-typeset :is(.admonition, details):is(.bug) { - border-color: rgb(182, 0, 64); -} -.md-typeset :-webkit-any(.bug) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(196, 0, 70, 0.1); -} -.md-typeset :is(.bug) > :is(.admonition-title, summary) { - background-color: rgba(196, 0, 70, 0.1); -} -.md-typeset :-webkit-any(.bug) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(255, 33, 112); -} -.md-typeset :is(.bug) > :is(.admonition-title, summary)::before { - background-color: rgb(255, 33, 112); -} -.md-typeset :-webkit-any(.bug) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(255, 33, 112); -} -.md-typeset :is(.bug) > :is(.admonition-title, summary)::after { - color: rgb(255, 33, 112); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.example) { - border-color: rgb(41, 0, 155); -} -.md-typeset :is(.admonition, details):is(.example) { - border-color: rgb(41, 0, 155); -} -.md-typeset :-webkit-any(.example) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(42, 0, 158, 0.1); -} -.md-typeset :is(.example) > :is(.admonition-title, summary) { - background-color: rgba(42, 0, 158, 0.1); -} -.md-typeset :-webkit-any(.example) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(126, 79, 255); -} -.md-typeset :is(.example) > :is(.admonition-title, summary)::before { - background-color: rgb(126, 79, 255); -} -.md-typeset :-webkit-any(.example) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(126, 79, 255); -} -.md-typeset :is(.example) > :is(.admonition-title, summary)::after { - color: rgb(126, 79, 255); -} -.md-typeset :-webkit-any(.admonition, details):-webkit-any(.quote, .cite) { - border-color: rgb(75, 82, 85); -} -.md-typeset :is(.admonition, details):is(.quote, .cite) { - border-color: rgb(75, 82, 85); -} -.md-typeset :-webkit-any(.quote, .cite) > :-webkit-any(.admonition-title, summary) { - background-color: rgba(79, 85, 89, 0.1); -} -.md-typeset :is(.quote, .cite) > :is(.admonition-title, summary) { - background-color: rgba(79, 85, 89, 0.1); -} -.md-typeset :-webkit-any(.quote, .cite) > :-webkit-any(.admonition-title, summary)::before { - background-color: rgb(171, 163, 152); -} -.md-typeset :is(.quote, .cite) > :is(.admonition-title, summary)::before { - background-color: rgb(171, 163, 152); -} -.md-typeset :-webkit-any(.quote, .cite) > :-webkit-any(.admonition-title, summary)::after { - color: rgb(171, 163, 152); -} -.md-typeset :is(.quote, .cite) > :is(.admonition-title, summary)::after { - color: rgb(171, 163, 152); -} -:root { - --darkreader-bgimg--md-footnotes-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE5IDd2NEg1LjgzbDMuNTgtMy41OUw4IDZsLTYgNiA2IDYgMS40MS0xLjQyTDUuODMgMTNIMjFWN2gtMloiLz48L3N2Zz4="); -} -.md-typeset .footnote { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-typeset .footnote > ol > li:target { - color: var(--darkreader-text--md-default-fg-color, #e8e6e3); -} -.md-typeset [id^="fnref:"]:target > .footnote-ref { - outline-color: initial; -} -.md-typeset .footnote-backref { - color: var(--darkreader-text--md-typeset-a-color, #e8e6e3); -} -.md-typeset .footnote-backref:hover { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-typeset .footnote-backref::before { - background-color: currentcolor; -} -.md-typeset .headerlink { - color: var(--darkreader-text--md-default-fg-color--lighter, #e8e6e3); -} -.md-typeset .headerlink:-webkit-any(:focus, :hover), -.md-typeset :target > .headerlink { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-typeset .headerlink:is(:focus, :hover), -.md-typeset :target > .headerlink { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-typeset :target { - --md-scroll-margin: 3.6rem; - --md-scroll-offset: 0rem; -} -@media screen and (min-width: 76.25em) { - .md-header--lifted ~ .md-container .md-typeset :target { - --md-scroll-margin: 6rem; - } -} -.md-typeset :-webkit-any(h1, h2, h3):target { - --md-scroll-offset: 0.2rem; -} -.md-typeset :is(h1, h2, h3):target { - --md-scroll-offset: 0.2rem; -} -.md-typeset h4:target { - --md-scroll-offset: 0.15rem; -} -.md-typeset del.critic { - background-color: var(--darkreader-bg--md-typeset-del-color, #181a1b); -} -.md-typeset ins.critic { - background-color: var(--darkreader-bg--md-typeset-ins-color, #181a1b); -} -.md-typeset .critic.comment { - color: var(--darkreader-text--md-code-hl-comment-color, #e8e6e3); -} -.md-typeset .critic.block { - box-shadow: none; -} -:root { - --darkreader-bgimg--md-details-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTguNTkgMTYuNTggMTMuMTcgMTIgOC41OSA3LjQxIDEwIDZsNiA2LTYgNi0xLjQxLTEuNDJaIi8+PC9zdmc+"); -} -.md-typeset details:not([open]) { - box-shadow: none; -} -.md-typeset summary.focus-visible { - outline-color: var(--darkreader-border--md-accent-fg-color); -} -.md-typeset summary:not(.focus-visible) { - -webkit-tap-highlight-color: transparent; - outline-color: initial; -} -.md-typeset summary::after { - background-color: currentcolor; -} -.md-typeset :-webkit-any(.emojione, .twemoji, .gemoji) svg { - fill: currentcolor; -} -.md-typeset :is(.emojione, .twemoji, .gemoji) svg { - fill: currentcolor; -} -.highlight :-webkit-any(.o, .ow) { - color: var(--darkreader-text--md-code-hl-operator-color, #e8e6e3); -} -.highlight :is(.o, .ow) { - color: var(--darkreader-text--md-code-hl-operator-color, #e8e6e3); -} -.highlight .p { - color: var(--darkreader-text--md-code-hl-punctuation-color, #e8e6e3); -} -.highlight :-webkit-any(.cpf, .l, .s, .sb, .sc, .s2, .si, .s1, .ss) { - color: var(--darkreader-text--md-code-hl-string-color, #e8e6e3); -} -.highlight :is(.cpf, .l, .s, .sb, .sc, .s2, .si, .s1, .ss) { - color: var(--darkreader-text--md-code-hl-string-color, #e8e6e3); -} -.highlight :-webkit-any(.cp, .se, .sh, .sr, .sx) { - color: var(--darkreader-text--md-code-hl-special-color, #e8e6e3); -} -.highlight :is(.cp, .se, .sh, .sr, .sx) { - color: var(--darkreader-text--md-code-hl-special-color, #e8e6e3); -} -.highlight :-webkit-any(.m, .mb, .mf, .mh, .mi, .il, .mo) { - color: var(--darkreader-text--md-code-hl-number-color, #e8e6e3); -} -.highlight :is(.m, .mb, .mf, .mh, .mi, .il, .mo) { - color: var(--darkreader-text--md-code-hl-number-color, #e8e6e3); -} -.highlight :-webkit-any(.k, .kd, .kn, .kp, .kr, .kt) { - color: var(--darkreader-text--md-code-hl-keyword-color, #e8e6e3); -} -.highlight :is(.k, .kd, .kn, .kp, .kr, .kt) { - color: var(--darkreader-text--md-code-hl-keyword-color, #e8e6e3); -} -.highlight :-webkit-any(.kc, .n) { - color: var(--darkreader-text--md-code-hl-name-color, #e8e6e3); -} -.highlight :is(.kc, .n) { - color: var(--darkreader-text--md-code-hl-name-color, #e8e6e3); -} -.highlight :-webkit-any(.no, .nb, .bp) { - color: var(--darkreader-text--md-code-hl-constant-color, #e8e6e3); -} -.highlight :is(.no, .nb, .bp) { - color: var(--darkreader-text--md-code-hl-constant-color, #e8e6e3); -} -.highlight :-webkit-any(.nc, .ne, .nf, .nn) { - color: var(--darkreader-text--md-code-hl-function-color, #e8e6e3); -} -.highlight :is(.nc, .ne, .nf, .nn) { - color: var(--darkreader-text--md-code-hl-function-color, #e8e6e3); -} -.highlight :-webkit-any(.nd, .ni, .nl, .nt) { - color: var(--darkreader-text--md-code-hl-keyword-color, #e8e6e3); -} -.highlight :is(.nd, .ni, .nl, .nt) { - color: var(--darkreader-text--md-code-hl-keyword-color, #e8e6e3); -} -.highlight :-webkit-any(.c, .cm, .c1, .ch, .cs, .sd) { - color: var(--darkreader-text--md-code-hl-comment-color, #e8e6e3); -} -.highlight :is(.c, .cm, .c1, .ch, .cs, .sd) { - color: var(--darkreader-text--md-code-hl-comment-color, #e8e6e3); -} -.highlight :-webkit-any(.na, .nv, .vc, .vg, .vi) { - color: var(--darkreader-text--md-code-hl-variable-color, #e8e6e3); -} -.highlight :is(.na, .nv, .vc, .vg, .vi) { - color: var(--darkreader-text--md-code-hl-variable-color, #e8e6e3); -} -.highlight :-webkit-any(.ge, .gr, .gh, .go, .gp, .gs, .gu, .gt) { - color: var(--darkreader-text--md-code-hl-generic-color, #e8e6e3); -} -.highlight :is(.ge, .gr, .gh, .go, .gp, .gs, .gu, .gt) { - color: var(--darkreader-text--md-code-hl-generic-color, #e8e6e3); -} -.highlight .gd { - background-color: var(--darkreader-bg--md-typeset-del-color, #181a1b); -} -.highlight .gi { - background-color: var(--darkreader-bg--md-typeset-ins-color, #181a1b); -} -.highlight .hll { - background-color: var(--darkreader-bg--md-code-hl-color, #181a1b); -} -.highlight span.filename { - background-color: var(--darkreader-bg--md-default-fg-color--lighter, #181a1b); - border-bottom: .05rem solid var(--darkreader-border--md-default-fg-color--lightest); -} -.highlight [data-linenos]::before { - background-color: var(--darkreader-bg--md-code-bg-color, #181a1b); - box-shadow: -.05rem 0 var(--darkreader-bg--md-default-fg-color--lightest) inset; - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.highlighttable .linenos { - background-color: var(--darkreader-bg--md-code-bg-color, #181a1b); -} -.highlighttable .linenodiv { - box-shadow: -.05rem 0 var(--darkreader-bg--md-default-fg-color--lightest) inset; -} -.highlighttable .linenodiv pre { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.linenodiv a { - color: inherit; -} -.md-typeset .keys span { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -:root { - --darkreader-bgimg--md-tabbed-icon--next: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTguNTkgMTYuNTggMTMuMTcgMTIgOC41OSA3LjQxIDEwIDZsNiA2LTYgNi0xLjQxLTEuNDJaIi8+PC9zdmc+"); - --darkreader-bgimg--md-tabbed-icon--prev: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTE1LjQxIDE2LjU4IDEwLjgzIDEybDQuNTgtNC41OUwxNCA2bC02IDYgNiA2IDEuNDEtMS40MloiLz48L3N2Zz4="); -} -.md-typeset .tabbed-set > input:target { - --md-scroll-offset: 0.625em; -} -.md-typeset .tabbed-labels { - box-shadow: 0 -.05rem var(--darkreader-bg--md-default-fg-color--lightest) inset; -} -@media screen { - .js .md-typeset .tabbed-labels::before { - background: var(--darkreader-bg--md-accent-fg-color); - } -} -.md-typeset .tabbed-labels > label { - border-bottom-color: transparent; - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-typeset .tabbed-labels > label:hover { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-typeset .tabbed-button { - color: var(--darkreader-text--md-default-fg-color--light, #e8e6e3); -} -.md-typeset .tabbed-button:hover { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); -} -.md-typeset .tabbed-button::after { - background-color: currentcolor; -} -.md-typeset .tabbed-control { - background: linear-gradient(to right,var(--darkreader-bg--md-default-bg-color) 60%,transparent); -} -.md-typeset .tabbed-control--next { - background: linear-gradient(to left,var(--darkreader-bg--md-default-bg-color) 60%,transparent); -} -@media screen { - .md-typeset .tabbed-set > input:first-child:checked ~ .tabbed-labels > :first-child, - .md-typeset .tabbed-set > input:nth-child(10):checked ~ .tabbed-labels > :nth-child(10), - .md-typeset .tabbed-set > input:nth-child(11):checked ~ .tabbed-labels > :nth-child(11), - .md-typeset .tabbed-set > input:nth-child(12):checked ~ .tabbed-labels > :nth-child(12), - .md-typeset .tabbed-set > input:nth-child(13):checked ~ .tabbed-labels > :nth-child(13), - .md-typeset .tabbed-set > input:nth-child(14):checked ~ .tabbed-labels > :nth-child(14), - .md-typeset .tabbed-set > input:nth-child(15):checked ~ .tabbed-labels > :nth-child(15), - .md-typeset .tabbed-set > input:nth-child(16):checked ~ .tabbed-labels > :nth-child(16), - .md-typeset .tabbed-set > input:nth-child(17):checked ~ .tabbed-labels > :nth-child(17), - .md-typeset .tabbed-set > input:nth-child(18):checked ~ .tabbed-labels > :nth-child(18), - .md-typeset .tabbed-set > input:nth-child(19):checked ~ .tabbed-labels > :nth-child(19), - .md-typeset .tabbed-set > input:nth-child(2):checked ~ .tabbed-labels > :nth-child(2), - .md-typeset .tabbed-set > input:nth-child(20):checked ~ .tabbed-labels > :nth-child(20), - .md-typeset .tabbed-set > input:nth-child(3):checked ~ .tabbed-labels > :nth-child(3), - .md-typeset .tabbed-set > input:nth-child(4):checked ~ .tabbed-labels > :nth-child(4), - .md-typeset .tabbed-set > input:nth-child(5):checked ~ .tabbed-labels > :nth-child(5), - .md-typeset .tabbed-set > input:nth-child(6):checked ~ .tabbed-labels > :nth-child(6), - .md-typeset .tabbed-set > input:nth-child(7):checked ~ .tabbed-labels > :nth-child(7), - .md-typeset .tabbed-set > input:nth-child(8):checked ~ .tabbed-labels > :nth-child(8), - .md-typeset .tabbed-set > input:nth-child(9):checked ~ .tabbed-labels > :nth-child(9) { - color: var(--darkreader-text--md-accent-fg-color, #e8e6e3); - } - .md-typeset .no-js .tabbed-set > input:first-child:checked ~ .tabbed-labels > :first-child, - .md-typeset .no-js .tabbed-set > input:nth-child(10):checked ~ .tabbed-labels > :nth-child(10), - .md-typeset .no-js .tabbed-set > input:nth-child(11):checked ~ .tabbed-labels > :nth-child(11), - .md-typeset .no-js .tabbed-set > input:nth-child(12):checked ~ .tabbed-labels > :nth-child(12), - .md-typeset .no-js .tabbed-set > input:nth-child(13):checked ~ .tabbed-labels > :nth-child(13), - .md-typeset .no-js .tabbed-set > input:nth-child(14):checked ~ .tabbed-labels > :nth-child(14), - .md-typeset .no-js .tabbed-set > input:nth-child(15):checked ~ .tabbed-labels > :nth-child(15), - .md-typeset .no-js .tabbed-set > input:nth-child(16):checked ~ .tabbed-labels > :nth-child(16), - .md-typeset .no-js .tabbed-set > input:nth-child(17):checked ~ .tabbed-labels > :nth-child(17), - .md-typeset .no-js .tabbed-set > input:nth-child(18):checked ~ .tabbed-labels > :nth-child(18), - .md-typeset .no-js .tabbed-set > input:nth-child(19):checked ~ .tabbed-labels > :nth-child(19), - .md-typeset .no-js .tabbed-set > input:nth-child(2):checked ~ .tabbed-labels > :nth-child(2), - .md-typeset .no-js .tabbed-set > input:nth-child(20):checked ~ .tabbed-labels > :nth-child(20), - .md-typeset .no-js .tabbed-set > input:nth-child(3):checked ~ .tabbed-labels > :nth-child(3), - .md-typeset .no-js .tabbed-set > input:nth-child(4):checked ~ .tabbed-labels > :nth-child(4), - .md-typeset .no-js .tabbed-set > input:nth-child(5):checked ~ .tabbed-labels > :nth-child(5), - .md-typeset .no-js .tabbed-set > input:nth-child(6):checked ~ .tabbed-labels > :nth-child(6), - .md-typeset .no-js .tabbed-set > input:nth-child(7):checked ~ .tabbed-labels > :nth-child(7), - .md-typeset .no-js .tabbed-set > input:nth-child(8):checked ~ .tabbed-labels > :nth-child(8), - .md-typeset .no-js .tabbed-set > input:nth-child(9):checked ~ .tabbed-labels > :nth-child(9), - .no-js .md-typeset .tabbed-set > input:first-child:checked ~ .tabbed-labels > :first-child, - .no-js .md-typeset .tabbed-set > input:nth-child(10):checked ~ .tabbed-labels > :nth-child(10), - .no-js .md-typeset .tabbed-set > input:nth-child(11):checked ~ .tabbed-labels > :nth-child(11), - .no-js .md-typeset .tabbed-set > input:nth-child(12):checked ~ .tabbed-labels > :nth-child(12), - .no-js .md-typeset .tabbed-set > input:nth-child(13):checked ~ .tabbed-labels > :nth-child(13), - .no-js .md-typeset .tabbed-set > input:nth-child(14):checked ~ .tabbed-labels > :nth-child(14), - .no-js .md-typeset .tabbed-set > input:nth-child(15):checked ~ .tabbed-labels > :nth-child(15), - .no-js .md-typeset .tabbed-set > input:nth-child(16):checked ~ .tabbed-labels > :nth-child(16), - .no-js .md-typeset .tabbed-set > input:nth-child(17):checked ~ .tabbed-labels > :nth-child(17), - .no-js .md-typeset .tabbed-set > input:nth-child(18):checked ~ .tabbed-labels > :nth-child(18), - .no-js .md-typeset .tabbed-set > input:nth-child(19):checked ~ .tabbed-labels > :nth-child(19), - .no-js .md-typeset .tabbed-set > input:nth-child(2):checked ~ .tabbed-labels > :nth-child(2), - .no-js .md-typeset .tabbed-set > input:nth-child(20):checked ~ .tabbed-labels > :nth-child(20), - .no-js .md-typeset .tabbed-set > input:nth-child(3):checked ~ .tabbed-labels > :nth-child(3), - .no-js .md-typeset .tabbed-set > input:nth-child(4):checked ~ .tabbed-labels > :nth-child(4), - .no-js .md-typeset .tabbed-set > input:nth-child(5):checked ~ .tabbed-labels > :nth-child(5), - .no-js .md-typeset .tabbed-set > input:nth-child(6):checked ~ .tabbed-labels > :nth-child(6), - .no-js .md-typeset .tabbed-set > input:nth-child(7):checked ~ .tabbed-labels > :nth-child(7), - .no-js .md-typeset .tabbed-set > input:nth-child(8):checked ~ .tabbed-labels > :nth-child(8), - .no-js .md-typeset .tabbed-set > input:nth-child(9):checked ~ .tabbed-labels > :nth-child(9) { - border-color: var(--darkreader-border--md-accent-fg-color); - } -} -.md-typeset .tabbed-set > input:first-child.focus-visible ~ .tabbed-labels > :first-child, -.md-typeset .tabbed-set > input:nth-child(10).focus-visible ~ .tabbed-labels > :nth-child(10), -.md-typeset .tabbed-set > input:nth-child(11).focus-visible ~ .tabbed-labels > :nth-child(11), -.md-typeset .tabbed-set > input:nth-child(12).focus-visible ~ .tabbed-labels > :nth-child(12), -.md-typeset .tabbed-set > input:nth-child(13).focus-visible ~ .tabbed-labels > :nth-child(13), -.md-typeset .tabbed-set > input:nth-child(14).focus-visible ~ .tabbed-labels > :nth-child(14), -.md-typeset .tabbed-set > input:nth-child(15).focus-visible ~ .tabbed-labels > :nth-child(15), -.md-typeset .tabbed-set > input:nth-child(16).focus-visible ~ .tabbed-labels > :nth-child(16), -.md-typeset .tabbed-set > input:nth-child(17).focus-visible ~ .tabbed-labels > :nth-child(17), -.md-typeset .tabbed-set > input:nth-child(18).focus-visible ~ .tabbed-labels > :nth-child(18), -.md-typeset .tabbed-set > input:nth-child(19).focus-visible ~ .tabbed-labels > :nth-child(19), -.md-typeset .tabbed-set > input:nth-child(2).focus-visible ~ .tabbed-labels > :nth-child(2), -.md-typeset .tabbed-set > input:nth-child(20).focus-visible ~ .tabbed-labels > :nth-child(20), -.md-typeset .tabbed-set > input:nth-child(3).focus-visible ~ .tabbed-labels > :nth-child(3), -.md-typeset .tabbed-set > input:nth-child(4).focus-visible ~ .tabbed-labels > :nth-child(4), -.md-typeset .tabbed-set > input:nth-child(5).focus-visible ~ .tabbed-labels > :nth-child(5), -.md-typeset .tabbed-set > input:nth-child(6).focus-visible ~ .tabbed-labels > :nth-child(6), -.md-typeset .tabbed-set > input:nth-child(7).focus-visible ~ .tabbed-labels > :nth-child(7), -.md-typeset .tabbed-set > input:nth-child(8).focus-visible ~ .tabbed-labels > :nth-child(8), -.md-typeset .tabbed-set > input:nth-child(9).focus-visible ~ .tabbed-labels > :nth-child(9) { - background-color: var(--darkreader-bg--md-accent-fg-color--transparent, #181a1b); -} -:root { - --darkreader-bgimg--md-tasklist-icon: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMSAxMkMxIDUuOTI1IDUuOTI1IDEgMTIgMXMxMSA0LjkyNSAxMSAxMS00LjkyNSAxMS0xMSAxMVMxIDE4LjA3NSAxIDEyem0xNi4yOC0yLjcyYS43NS43NSAwIDAgMC0xLjA2LTEuMDZsLTUuOTcgNS45Ny0yLjQ3LTIuNDdhLjc1Ljc1IDAgMCAwLTEuMDYgMS4wNmwzIDNhLjc1Ljc1IDAgMCAwIDEuMDYgMGw2LjUtNi41eiIvPjwvc3ZnPg=="); - --darkreader-bgimg--md-tasklist-icon--checked: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMSAxMkMxIDUuOTI1IDUuOTI1IDEgMTIgMXMxMSA0LjkyNSAxMSAxMS00LjkyNSAxMS0xMSAxMVMxIDE4LjA3NSAxIDEyem0xNi4yOC0yLjcyYS43NS43NSAwIDAgMC0xLjA2LTEuMDZsLTUuOTcgNS45Ny0yLjQ3LTIuNDdhLjc1Ljc1IDAgMCAwLTEuMDYgMS4wNmwzIDNhLjc1Ljc1IDAgMCAwIDEuMDYgMGw2LjUtNi41eiIvPjwvc3ZnPg=="); -} -.md-typeset .task-list-indicator::before { - background-color: var(--darkreader-bg--md-default-fg-color--lightest, #181a1b); -} -.md-typeset [type="checkbox"]:checked + .task-list-indicator::before { - background-color: rgb(43, 255, 152); -} -:root > * { - --md-mermaid-edge-color: var(--md-code-fg-color); - --md-mermaid-font-family: var(--md-text-font-family),sans-serif; - --md-mermaid-label-bg-color: var(--md-default-bg-color); - --md-mermaid-label-fg-color: var(--md-code-fg-color); - --md-mermaid-node-bg-color: var(--md-accent-fg-color--transparent); - --md-mermaid-node-fg-color: var(--md-accent-fg-color); -} -a { - color: rgb(94, 165, 234); -} -a:hover { - color: rgb(102, 177, 250); -} -a:active { - color: rgb(249, 146, 97); -} -* { - -webkit-tap-highlight-color: transparent; -} -table.sortable thead { - background-color: rgb(34, 37, 38); - color: rgb(168, 160, 149); -} -hr { - border-bottom-color: rgb(62, 68, 70); - border-left: 0px; - border-right: 0px; - border-top: 0px; -} -.dashed { - border-bottom-color: rgb(62, 68, 70); -} -.form-area { - background-color: rgb(27, 29, 30); - background-image: initial; - border-color: rgb(62, 68, 70); -} -footer { - color: rgb(152, 143, 129); -} -body { - background-color: rgb(27, 29, 30); - background-image: initial; - color: rgb(232, 230, 227); -} -header { - background-color: rgb(13, 14, 14); - background-image: initial; - color: rgb(178, 172, 162); -} -#user-links:hover { - border-color: rgb(140, 130, 115); - color: rgb(232, 230, 227); -} -#nav-shadow { - background-color: initial; - background-image: linear-gradient(rgb(49, 53, 55), rgba(0, 0, 0, 0)); -} -#nav-container { - background-color: rgb(24, 26, 27); - background-image: initial; -} -nav ul { - background-color: transparent; - background-image: initial; - list-style-image: initial; -} -nav ul li { - color: rgb(232, 230, 227); -} -nav ul li.home-nav-element a:hover { - border-bottom: none; -} -nav ul li a, -nav ul li button { - color: rgb(232, 230, 227); - text-decoration-color: initial; -} -nav ul li a:link, -nav ul li button:link { - color: rgb(232, 230, 227); -} -nav ul li a:hover, -nav ul li button:hover { - background-color: rgba(24, 26, 27, 0.25); - background-image: initial; - border-top-color: rgb(199, 70, 8); - color: rgb(232, 230, 227); -} -nav ul li a.active, -nav ul li button.active { - border-top-color: rgb(199, 70, 8); - color: rgb(249, 146, 97); -} -nav ul li ul { - background-color: rgb(24, 26, 27); - background-image: initial; - box-shadow: rgba(0, 0, 0, 0.4) 2px 2px 4px; - color: rgb(232, 230, 227); -} -nav ul li ul li:hover { - background-color: rgb(49, 53, 55); - background-image: initial; -} -nav ul li ul li a { - color: rgb(232, 230, 227) !important; -} -nav ul li ul li a, -nav ul li ul li button { - border-left-color: rgb(140, 130, 115); -} -nav ul li button { - background-color: initial; - background-image: none; - border-color: initial; - border-style: none; - border-width: initial; -} -nav ul li.home-nav-element a:hover { - background-color: transparent; - background-image: initial; - border-bottom: 0px; -} -hr { - color: rgba(232, 230, 227, 0.2); -} -#content .title { - color: rgb(199, 194, 187); -} -footer { - background-color: rgb(34, 37, 38); - background-image: initial; - border-top-color: rgb(62, 68, 70); -} -a { - text-decoration-color: initial; -} -noscript #noscript { - background-color: rgb(139, 0, 0); - background-image: initial; - color: rgb(232, 230, 227); -} -#announcement { - background-color: rgb(139, 0, 0); - background-image: initial; - color: rgb(232, 230, 227); -} -#announcement a { - color: rgb(255, 174, 26); -} -.time { - color: rgb(178, 172, 162); -} -#form-errors, -.form-errors { - background-color: rgba(204, 0, 0, 0.3); - background-image: initial; - border-color: rgb(179, 0, 0); -} -#nav-placeholder { - background-color: rgb(24, 26, 27); - background-image: initial; - border-left-color: rgb(62, 68, 70); - border-right-color: rgb(62, 68, 70); -} -#contest-info a { - color: rgb(232, 230, 227); -} -#contest-info-main { - background-color: rgba(0, 0, 0, 0.77); - background-image: initial; - border-left-color: rgb(48, 52, 54); - color: rgb(232, 230, 227); -} -.contest-info-toggle-mode-on { - background-color: rgba(0, 164, 0, 0.57); - background-image: initial; -} -.contest-info-toggle-mode-on:hover { - background-color: rgba(0, 164, 0, 0.97); - background-image: initial; -} -.contest-info-toggle-mode-off { - background-color: rgba(204, 0, 0, 0.57); - background-image: initial; -} -.contest-info-toggle-mode-off:hover { - background-color: rgba(204, 0, 0, 0.97); - background-image: initial; -} -#page-container { - border-left-color: rgb(62, 68, 70); - border-right-color: rgb(62, 68, 70); -} -@media (max-width: 1498px) { - #page-container { - border-left: none; - border-right: none; - } -} -@media (max-width: 799px) { - #navicon { - color: rgb(129, 175, 255); - } - #navicon.hover { - color: rgb(80, 184, 254); - text-shadow: rgb(24, 26, 27) 0px 0px 5px; - } - #nav-list { - background-color: rgb(24, 26, 27); - background-image: initial; - border-color: initial; - box-shadow: none; - } -} -#notification { - color: rgb(164, 192, 217); -} -#notification:hover { - color: rgb(178, 171, 161); -} -#chat-icon { - color: rgb(200, 196, 189); -} -#chat-icon:hover { - color: rgb(249, 146, 97); -} -#nav-lang-icon { - color: rgb(51, 125, 255); -} -#nav-lang-icon:hover { - color: rgb(121, 170, 255); -} -#nav-darkmode-icon:hover { - color: rgb(152, 143, 129); -} -.dropdown { - background-color: rgb(24, 26, 27); - box-shadow: rgba(0, 0, 0, 0.2) 0px 8px 16px 0px; -} -.dropdown a { - color: rgb(232, 230, 227); - text-decoration-color: initial; -} -.dropdown-item { - border-top-color: rgb(62, 68, 70); - color: rgb(232, 230, 227); -} -.dropdown-item:hover { - background-color: rgb(31, 31, 17); - color: rgb(249, 146, 97); -} -.popper-arrow, -.popper-arrow::before { - background-color: inherit; - background-image: inherit; -} -.unread_boxes { - background-color: rgb(204, 0, 0); - color: rgb(232, 230, 227); -} -.sub-lang { - color: rgb(232, 230, 227); -} -.notification-open #notification { - color: rgb(114, 255, 114) !important; -} -.title-row { - color: rgb(199, 194, 187); -} -.gray { - color: rgb(152, 143, 129); -} -.white { - color: rgb(232, 230, 227); -} -.black { - color: rgb(232, 230, 227); -} -.red { - color: rgb(255, 26, 26); -} -.green { - color: rgb(114, 255, 114); -} -.grayed { - color: rgb(168, 160, 149); -} -.darkcyan { - color: rgb(107, 255, 255); -} -.peru { - color: rgb(209, 144, 80); -} -.blue { - color: rgb(51, 125, 255); -} -.background-d6e8f7 { - background-color: rgb(38, 41, 43); -} -.background-bisque { - background-color: rgb(86, 47, 0); -} -.background-royalblue { - background-color: rgb(25, 58, 158) !important; -} -.background-green { - background-color: rgb(32, 134, 55) !important; -} -.background-red { - background-color: rgb(165, 29, 42) !important; -} -.background-footer { - color: rgb(152, 143, 129); -} -#loading-bar { - background-color: rgb(125, 44, 5); -} -.anon a { - color: rgb(232, 230, 227); -} -@media (min-width: 800px) { - #page-container { - background-color: rgb(32, 34, 36); - background-image: initial; - } - #content.wrapper { - background-color: rgb(24, 26, 27); - background-image: initial; - } -} -.colored-text { - color: rgb(232, 230, 227); -} -::-webkit-input-placeholder { - color: rgb(249, 146, 97); -} -::placeholder { - color: rgb(249, 146, 97); -} -input::-webkit-input-placeholder { - color: rgb(249, 146, 97); -} -input::placeholder { - color: rgb(249, 146, 97); -} -::-webkit-input-placeholder { - color: rgb(249, 146, 97); -} -.nav-fa-icon i { - color: rgb(232, 230, 227); -} -.nav-fa-icon-active i { - color: rgb(249, 146, 97); -} -.table { - background-color: rgba(0, 0, 0, 0.01); - background-image: initial; -} -.table.striped tr:nth-child(2n) { - background-color: rgb(29, 31, 32); - background-image: initial; -} -.table.striped tr:nth-child(2n+1) { - background-color: rgb(24, 26, 27); - background-image: initial; -} -.table.no-border td, -.table.no-border th { - border-color: initial; - border-style: none; - border-width: initial; -} -.table td:first-child { - border-color: rgb(62, 68, 70); -} -.table tr:last-child td { - border-color: rgb(62, 68, 70); -} -.table th { - background-color: rgb(174, 132, 26); - border-color: rgb(62, 68, 70); - color: rgb(232, 230, 227); -} -.table td { - border-color: rgb(62, 68, 70); -} -#users-table th a { - color: rgb(232, 230, 227); -} -.AB { - background-color: rgb(53, 57, 59); - color: rgb(232, 230, 227); -} -.AC { - background-color: rgb(0, 102, 0); - color: rgb(232, 230, 227); -} -._AC { - background-color: rgb(93, 132, 0); - color: rgb(232, 230, 227); -} -.WA { - background-color: rgb(204, 0, 0); - color: rgb(232, 230, 227); -} -.TLE, -.MLE { - background-color: rgb(53, 57, 59); - color: rgb(232, 230, 227); -} -.OLE, -.IR, -.RTE, -.OTH { - background-color: rgb(136, 94, 3); - color: rgb(232, 230, 227); -} -.CE { - background-color: rgb(53, 57, 59); - color: rgb(232, 230, 227); -} -.IE { - background-color: rgb(204, 0, 0); - color: rgb(232, 230, 227); -} -.QU, -.G { - background-color: rgb(24, 26, 27); - background-image: initial; - color: rgb(232, 230, 227); -} -.judge-online { - color: rgb(100, 196, 97); -} -.judge-offline { - color: rgb(225, 55, 55); -} -.middle-content .post { - border-color: rgb(76, 83, 86) rgb(84, 91, 94) rgb(84, 91, 94); -} -.middle-content .post .title a { - color: rgb(255, 114, 114) !important; -} -.middle-content .post .title a:hover { - color: rgb(255, 70, 70) !important; -} -.left-sidebar-item.active { - background-color: rgb(125, 44, 5); - color: rgb(232, 230, 227); -} -.left-sidebar-item.active .sidebar-icon { - color: rgb(232, 230, 227); -} -.blog-sidebox .contest { - border-bottom-color: rgb(62, 68, 70); -} -.blog-sidebox .contest:last-child { - border-bottom: none; -} -.blog-sidebox .contest .name a { - color: rgb(104, 149, 191) !important; -} -.blog-sidebox .contest .name a:hover { - color: rgb(102, 177, 250) !important; -} -.no-dot-blog-sidebox ul { - list-style-image: initial; -} -.blog-comment-count-link { - color: rgb(178, 172, 162); -} -.rssatom span { - background-color: initial; - background-image: linear-gradient(135deg, rgb(175, 79, 22) 0px, rgb(169, 90, 3) 47%, rgb(175, 79, 22) 100%); - border-color: rgb(174, 78, 16); - color: rgb(232, 230, 227); -} -.blog-box { - background-color: rgb(24, 26, 27); - border-bottom-color: rgb(60, 65, 68); - border-top-color: rgb(60, 65, 68); - box-shadow: rgba(0, 0, 0, 0.2) 0px 0px 5px; -} -.blog-box:hover, -.blog-box:not(.pre-expand-blog) { - border-color: rgb(81, 88, 91); - box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 2px; -} -.problem-feed-name a { - color: rgb(102, 177, 250); -} -.problem-feed-types { - color: rgb(152, 143, 129); -} -.left-sidebar-item { - color: rgb(232, 230, 227); -} -.left-sidebar-item:hover { - background-color: rgb(40, 43, 45); - color: rgb(232, 230, 227); -} -.left-sidebar-item.active:hover { - background-color: rgb(125, 44, 5); - color: rgb(232, 230, 227); -} -.sidebar-icon { - color: rgb(232, 230, 227); -} -.left-sidebar-header { - border-bottom-color: rgb(140, 130, 115); - color: rgb(232, 230, 227); -} -.show-more { - background-color: initial; - background-image: linear-gradient(rgba(0, 0, 0, 0), rgb(24, 26, 27)); - color: rgb(232, 230, 227); -} -.middle-right-content.wrapper { - background-color: rgb(24, 26, 27); - background-image: initial; -} -@media (max-width: 799px) { - .left-sidebar { - background-color: inherit; - background-image: inherit; - } -} -@media (min-width: 800px) { - .left-sidebar-item { - background-color: rgb(24, 26, 27); - border-color: rgb(60, 65, 68); - box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 5px; - } - .left-sidebar::-webkit-scrollbar { - background-color: transparent; - } - .blog-box { - border-left-color: rgb(60, 65, 68); - border-right-color: rgb(60, 65, 68); - } -} -#problem-table tr:hover { - background-color: rgb(36, 39, 40); - background-image: initial; -} -ul.problem-list { - list-style-image: initial; -} -.solved-problem-color { - color: rgb(100, 196, 97); -} -.unsolved-problem-color { - color: rgb(225, 55, 55); -} -.attempted-problem-color { - color: rgb(255, 174, 26); -} -.submissions-left { - color: rgb(232, 230, 227); -} -.no-submissions-left { - color: rgb(255, 26, 26); -} -.organization-tag { - background-color: rgb(53, 57, 59); - color: initial; -} -.organization-tag a { - color: rgb(232, 230, 227); -} -.pdf-icon .pdf-icon-logo { - color: rgb(242, 59, 63); -} -.pdf-icon .pdf-icon-bar { - background-color: rgb(170, 11, 15); - background-image: initial; -} -.license a { - color: rgb(152, 143, 129); - text-decoration-color: initial; -} -#problem_submit #result-version-info { - border-bottom-color: rgb(78, 85, 88); - color: rgb(158, 150, 137); -} -#problem_submit #language-select2 .select2-results__option { - background-color: rgb(24, 26, 27) !important; - background-image: initial !important; - color: rgb(158, 150, 137) !important; -} -#problem_submit #language-select2 .select2-results__option--highlighted { - text-decoration-color: initial; -} -#problem_submit #language-select2 .select2-results__option[aria-selected="true"] { - color: rgb(232, 230, 227) !important; -} -#problem-table th a { - color: inherit; -} -.problem-data-form .bad-file input, -.problem-data-form .bad-file .select2-selection { - border-color: rgb(179, 0, 0); -} -.problem-clarification { - border-bottom-color: rgb(62, 68, 70); -} -#clarification_header { - color: rgb(255, 26, 26); -} -#clarification_header:hover { - color: rgb(255, 174, 26); -} -#comment-announcement { - background-color: rgb(49, 53, 55); - color: rgb(166, 158, 146); -} -#comment-announcement:hover { - background-color: rgb(96, 104, 108); -} -.new-problem-info { - background-color: rgb(54, 39, 0); - border-color: rgb(140, 130, 115); -} -.admin a, -.admin { - color: rgb(232, 230, 227) !important; -} -svg.rate-box circle { - fill: none; -} -svg.rate-box.rate-newbie circle { - stroke: rgb(168, 160, 149); -} -svg.rate-box.rate-newbie path { - fill: rgb(168, 160, 149); -} -svg.rate-box.rate-amateur circle { - stroke: rgb(86, 255, 86); -} -svg.rate-box.rate-amateur path { - fill: rgb(86, 255, 86); -} -svg.rate-box.rate-specialist circle { - stroke: rgb(87, 252, 242); -} -svg.rate-box.rate-specialist path { - fill: rgb(87, 252, 242); -} -svg.rate-box.rate-expert circle { - stroke: rgb(97, 155, 255); -} -svg.rate-box.rate-expert path { - fill: rgb(97, 155, 255); -} -svg.rate-box.rate-candidate-master circle { - stroke: rgb(255, 97, 255); -} -svg.rate-box.rate-candidate-master path { - fill: rgb(255, 97, 255); -} -svg.rate-box.rate-master circle { - stroke: rgb(255, 239, 49); -} -svg.rate-box.rate-master path { - fill: rgb(255, 239, 49); -} -svg.rate-box.rate-grandmaster circle, -svg.rate-box.rate-target circle { - stroke: rgb(255, 37, 37); -} -svg.rate-box.rate-grandmaster path, -svg.rate-box.rate-target path { - fill: rgb(255, 37, 37); -} -svg.rate-box.rate-target circle:last-child { - fill: rgb(255, 37, 37); - stroke: none; -} -.rate-none, -.rate-none a { - color: rgb(232, 230, 227); -} -.rate-newbie, -.rate-newbie a { - color: rgb(168, 160, 149); -} -.rate-amateur, -.rate-amateur a { - color: rgb(86, 255, 86); -} -.rate-specialist, -.rate-specialist a { - color: rgb(107, 255, 255); -} -.rate-expert, -.rate-expert a { - color: rgb(51, 125, 255); -} -.rate-candidate-master, -.rate-candidate-master a { - color: rgb(255, 85, 255); -} -.rate-master, -.rate-master a { - color: rgb(255, 152, 26); -} -.rate-grandmaster, -.rate-grandmaster a, -.rate-target, -.rate-target a { - color: rgb(255, 37, 37); -} -.rate-group { - color: rgb(232, 230, 227); -} -#users-table th a, -#users-table th a:link, -#users-table th a:visited { - color: rgb(232, 230, 227); -} -#users-table th a:hover { - color: rgb(255, 211, 147); -} -#users-table tr:hover { - background-color: rgb(36, 39, 40); - background-image: initial; -} -#users-table tr.highlight { - background-color: rgb(85, 79, 0); - background-image: initial; -} -#users-table tr:target { - background-color: rgb(85, 79, 0); - background-image: initial; -} -#users-table .organization-column a { - color: rgb(152, 143, 129) !important; -} -#users-table .disqualified { - background-color: rgb(103, 0, 0) !important; -} -#users-table .frozen { - background-color: rgb(5, 77, 121) !important; - background-image: initial !important; -} -#users-table .full-score, -#users-table .full-score a { - color: rgb(114, 255, 114); -} -#users-table .partial-score, -#users-table .partial-score a { - color: rgb(114, 255, 114); -} -#users-table .failed-score, -#users-table .failed-score a { - color: rgb(255, 26, 26); -} -#users-table .pretest-full-score, -#users-table .pretest-full-score a { - color: rgb(84, 164, 217); -} -#users-table .pretest-partial-score, -#users-table .pretest-partial-score a { - color: rgb(84, 164, 217); -} -#users-table .pretest-failed-score, -#users-table .pretest-failed-score a { - color: rgb(255, 26, 26); -} -#users-table .user-points { - color: rgb(232, 230, 227); -} -#users-table .solving-time { - color: rgb(152, 143, 129); -} -#users-table .point-denominator { - border-top-color: rgb(84, 91, 94); -} -#users-table .fullname-column { - border-right: none !important; -} -#users-table .fullname-column span { - color: rgb(152, 143, 129) !important; -} -#search-form .select2-results__option--highlighted { - background-color: rgb(43, 46, 48) !important; -} -a.user-redirect { - color: rgb(84, 164, 217); -} -a.user-redirect:hover { - text-shadow: rgb(0, 0, 204) 0px 0px 2px; -} -.user-info-cell { - border-left-color: rgb(62, 68, 70); -} -.contest-history-cell { - border-left-color: rgb(62, 68, 70); -} -.hide-solved-problems > span::before { - background-color: rgba(0, 0, 0, 0.2); - background-image: initial; -} -.user-img { - background-color: rgb(43, 47, 49); -} -.pp-table .pp-weighted { - color: rgb(157, 148, 136); -} -.pp-table div.sub-pp { - border-left: none; -} -#pp-load-link-wrapper { - border-color: rgb(62, 68, 70); -} -#rating-tooltip { - background-color: rgba(0, 0, 0, 0.7); - background-image: initial; - color: rgb(232, 230, 227); -} -#rating-tooltip.rate-group { - color: rgb(232, 230, 227); -} -.follow { - background-color: rgb(0, 102, 0); - background-image: initial; - border-color: rgb(19, 122, 19); -} -.follow:hover { - background-color: rgb(0, 80, 0); - background-image: initial; -} -.unfollow { - background-color: rgb(204, 0, 0); - background-image: initial; - border-color: rgb(121, 0, 21); -} -.unfollow:hover { - background-color: rgb(111, 0, 0); - background-image: initial; -} -#submission-activity #submission-activity-actions #year { - color: rgb(189, 183, 175); -} -#submission-activity #submission-activity-display { - border-color: rgb(62, 68, 70); -} -#submission-activity #submission-activity-display .info-text { - color: rgb(189, 183, 175); -} -#submission-activity #submission-activity-display table td.activity-blank { - background-color: rgb(24, 26, 27); -} -#submission-activity #submission-activity-display table td.activity-0 { - background-color: rgb(43, 47, 49); -} -#submission-activity #submission-activity-display table td.activity-1 { - background-color: rgb(22, 102, 52); -} -#submission-activity #submission-activity-display table td.activity-2 { - background-color: rgb(47, 154, 95); -} -#submission-activity #submission-activity-display table td.activity-3 { - background-color: rgb(38, 125, 61); -} -#submission-activity #submission-activity-display table td.activity-4 { - background-color: rgb(26, 88, 46); -} -.user-info-header { - color: rgb(152, 143, 129); -} -.user-stat-header { - color: rgb(152, 143, 129); -} -.profile-card { - border-color: rgb(58, 62, 65); - box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 8px; -} -.profile-card:hover { - box-shadow: rgba(0, 0, 0, 0.2) 0px 8px 16px; -} -.profile-card .card-header { - background-color: rgb(29, 31, 32); -} -.profile-card .medal-count { - background-color: rgba(37, 40, 42, 0.7); - color: rgb(232, 230, 227); -} -.content-description pre, -.content-description code, -.content-description kbd, -.content-description samp, -.content-description span.code { - color: rgb(232, 230, 227); -} -.content-description code, -.content-description span.code { - background-color: var(--darkreader-bg--md-code-bg-color, #181a1b); - color: var(--darkreader-text--md-code-fg-color, #e8e6e3); -} -.content-description pre { - background-color: var(--darkreader-bg--md-code-bg-color, #181a1b); - color: var(--darkreader-text--md-code-fg-color, #e8e6e3); -} -.content-description pre code, -.content-description pre div.code { - background-color: transparent; - background-image: initial; - border-color: initial; - border-style: initial; - border-width: 0px; - color: var(--darkreader-text--md-code-fg-color, #e8e6e3); -} -.content-description pre.no-border { - background-color: inherit; - border-color: initial; - border-style: none; - border-width: initial; -} -.content-description ins { - background-color: rgb(84, 84, 0); - background-image: initial; - color: rgb(232, 230, 227); - text-decoration-color: initial; -} -.content-description mark { - background-color: rgb(153, 153, 0); - background-image: initial; - color: rgb(232, 230, 227); -} -.content-description img { - border-color: initial; - border-style: initial; - border-width: 0px; -} -.codehilitetable pre { - background-color: rgba(35, 38, 39, 0.5); -} -.codehilitetable .linenos pre { - background-color: rgba(0, 0, 0, 0.07); - border-right: 0px; - color: rgba(232, 230, 227, 0.26); -} -.info-float .fa { - color: rgb(232, 230, 227); -} -.tweet-this i { - color: rgb(90, 176, 238); -} -.facebook-this it { - color: rgb(132, 183, 237); -} -.gplus-this i { - color: rgb(224, 90, 72); -} -.button, -button, -input[type="submit"] { - background-color: rgb(125, 44, 5); - border-color: transparent; - box-shadow: rgba(0, 0, 0, 0.02) 0px 1px 3px 0px; - color: rgb(232, 230, 227) !important; - text-decoration-color: initial; -} -.button.disabled, -.button[disabled], -button.disabled, -button[disabled], -input[type="submit"].disabled, -input[type="submit"][disabled] { - background-color: initial !important; - background-image: linear-gradient(rgb(73, 79, 82) 0px, rgb(96, 104, 108) 100%) !important; - border-color: rgb(84, 91, 94) !important; -} -.button.btn-gray, -button.btn-gray, -input[type="submit"].btn-gray { - background-color: rgb(96, 104, 108); - background-image: initial; -} -.button.btn-hovergray:hover, -button.btn-hovergray:hover, -input[type="submit"].btn-hovergray:hover { - background-color: rgb(49, 53, 55); - background-image: initial; -} -.button.btn-green, -button.btn-green, -input[type="submit"].btn-green { - background-color: rgb(32, 134, 55); - background-image: initial; -} -.button.btn-green:hover, -button.btn-green:hover, -input[type="submit"].btn-green:hover { - background-color: rgb(0, 102, 0); - background-image: initial; -} -.button.btn-darkred, -button.btn-darkred, -input[type="submit"].btn-darkred { - background-color: rgb(111, 0, 0); - background-image: initial; -} -.button.btn-darkred:hover, -button.btn-darkred:hover, -input[type="submit"].btn-darkred:hover { - background-color: rgb(132, 34, 34); - background-image: initial; -} -.button.btn-midnightblue, -button.btn-midnightblue, -input[type="submit"].btn-midnightblue { - background-color: rgb(20, 20, 90); - background-image: initial; -} -.button.btn-midnightblue:hover, -button.btn-midnightblue:hover, -input[type="submit"].btn-midnightblue:hover { - background-color: rgb(0, 0, 111); - background-image: initial; -} -.button.btn-darkGreen, -button.btn-darkGreen, -input[type="submit"].btn-darkGreen { - background-color: rgb(125, 44, 5); - background-image: initial; -} -.button:hover, -button:hover, -input[type="submit"]:hover { - background-color: rgb(125, 44, 5); - box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px; -} -.button:focus, -button:focus, -input[type="submit"]:focus { - background-color: rgb(125, 44, 5); - box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px; -} -.button:active, -button:active, -input[type="submit"]:hover { - background-color: rgb(125, 44, 5); - box-shadow: rgba(0, 0, 0, 0.06) 0px 2px 4px; -} -input[type="text"], -input[type="password"], -input[type="email"], -input[type="number"], -input[type="datetime-local"], -input[type="date"] { - background-color: rgb(24, 26, 27); - background-image: none; - border-color: rgb(62, 68, 70); - box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 1px inset; - color: rgb(178, 172, 162); -} -textarea { - background-color: rgb(24, 26, 27); - background-image: none; - border-color: rgb(62, 68, 70); - box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 1px inset; -} -textarea:hover { - border-color: rgb(140, 130, 115); -} -input[type="text"]:hover, -input[type="password"]:hover { - border-color: rgba(16, 87, 144, 0.8); - box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 1px inset, rgba(16, 91, 150, 0.6) 0px 0px 4px; -} -textarea:focus { - border-color: rgb(140, 130, 115); - outline-color: initial; -} -input[type="text"]:focus, -input[type="password"]:focus { - border-color: rgba(16, 87, 144, 0.8); - box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 1px inset, rgba(16, 91, 150, 0.6) 0px 0px 8px; - outline-color: initial; -} -.btn-clipboard:hover { - background-color: rgb(24, 26, 27); - border-color: rgb(55, 60, 62); -} -.ul_tab_a_active, -.tabs > ul > li.active > a:focus, -.tabs > ul > li.active > span:focus, -.tabs > ul > li.active > a:hover, -.tabs > ul > li.active > span:hover, -.tabs > ul > li.active > a, -.tabs > ul > li.active > span { - background-color: transparent; - background-image: initial; - border-bottom-color: rgb(199, 70, 8); - color: rgb(249, 146, 97); -} -.tabs { - border-bottom-color: rgb(62, 68, 70); -} -.tabs .tab .tab-icon { - color: rgb(152, 143, 129); -} -.tabs .tab.active a, -.tabs .tab.active span { - border-top-color: rgb(48, 52, 54) !important; -} -.tabs .tab.active .tab-icon { - color: rgb(232, 230, 227); -} -.tabs h2 { - color: rgb(199, 194, 187); -} -.tabs > ul { - list-style-image: none; -} -.tabs > ul::-webkit-scrollbar { - background-color: transparent; -} -.tabs > ul::-webkit-scrollbar-thumb { - background-color: transparent; -} -.tabs > ul > li > a, -.tabs > ul > li > span { - color: rgb(178, 172, 162); - text-decoration-color: initial; -} -.tabs > ul > li > a:hover, -.tabs > ul > li > span:hover { - border-bottom-color: rgb(0, 217, 0); -} -ul.pagination a:hover { - background-color: rgb(163, 62, 18); - background-image: initial; - color: rgb(232, 230, 227); -} -ul.pagination > li > a, -ul.pagination > li > span { - background-color: rgb(24, 26, 27); - border-color: rgb(199, 70, 8); - color: rgb(249, 146, 97); - text-decoration-color: initial; -} -ul.pagination > .disabled-page > a { - background-color: rgb(137, 78, 57); - border-color: rgb(199, 68, 21); - color: rgb(223, 220, 215); -} -ul.pagination > .disabled-page > span { - background-color: rgb(137, 78, 57); - border-color: rgb(199, 68, 21); - color: rgb(223, 220, 215); -} -ul.pagination > .active-page > a { - background-color: rgb(125, 44, 5); - border-color: transparent; - color: rgb(232, 230, 227); -} -ul.pagination > .active-page > span { - background-color: rgb(24, 26, 27); - border-color: transparent; - color: rgb(232, 230, 227); -} -.alert { - border-color: transparent; -} -.alert-info { - background-color: rgb(14, 48, 65); - border-color: rgb(22, 90, 104); - color: rgb(117, 178, 208); -} -.alert-warning { - background-color: rgb(47, 40, 5); - border-color: rgb(108, 76, 11); - color: rgb(198, 171, 123); -} -.alert-danger { - background-color: rgb(56, 22, 22); - border-color: rgb(89, 35, 43); - color: rgb(194, 102, 100); -} -.alert-dismissable .close, -.alert-dismissible .close { - color: inherit; -} -.close { - color: rgb(232, 230, 227); - text-shadow: rgb(24, 26, 27) 0px 1px 0px; -} -a.close { - text-decoration-color: initial !important; -} -a.close:hover { - color: rgb(232, 230, 227) !important; -} -.close:focus, -.close:hover { - color: rgb(232, 230, 227); - text-decoration-color: initial; -} -.badge { - background-color: rgb(155, 19, 19); - color: rgb(232, 230, 227); -} -.form-submit-group { - border-top-color: rgb(53, 57, 59); -} -.sidebox h3 { - background-color: rgb(24, 26, 27); - background-image: initial; -} -.sidebox h3 .fa { - background-color: rgb(125, 44, 5); - background-image: initial; - color: rgb(232, 230, 227); -} -.sidebox-content { - background-color: rgb(24, 26, 27); - background-image: initial; - border-top: none; -} -.sidebox-content.sidebox-table { - border-color: initial; - border-style: none; - border-width: initial; -} -.sidebox { - box-shadow: rgba(0, 0, 0, 0.2) 0px 0px 5px; -} -.ws-closed { - background-color: rgb(139, 0, 0); - background-image: initial; -} -.ws-closed a { - color: rgb(232, 230, 227); -} -.messages li { - border-color: transparent; -} -.messages li.debug { - background-color: rgb(40, 43, 44); - border-color: rgb(59, 64, 66); - color: rgb(194, 188, 180); -} -.messages li.info { - background-color: rgb(20, 59, 67); - border-color: rgb(30, 89, 97); - color: rgb(142, 227, 241); -} -.messages li.success { - background-color: rgb(26, 62, 41); - border-color: rgb(37, 90, 50); - color: rgb(153, 230, 171); -} -.messages li.warning { - background-color: rgb(61, 46, 0); - border-color: rgb(123, 92, 0); - color: rgb(251, 215, 112); -} -.messages li.error { - background-color: rgb(67, 12, 17); - border-color: rgb(104, 18, 27); - color: rgb(225, 134, 143); -} -.spoiler-text { - background-color: rgb(34, 36, 38); - background-image: initial; - border-color: rgb(140, 130, 115); -} -.spoiler-summary { - text-decoration-color: initial; -} -.control-button { - border-color: initial; - border-style: initial; - border-width: 0px; - color: rgb(232, 230, 227) !important; -} -.control-button:hover { - background-color: rgb(96, 104, 108); - background-image: initial; -} -ul.errorlist { - color: rgb(255, 26, 26); - list-style-image: initial; -} -.registration-form .block-header { - color: rgb(178, 172, 162); -} -.registration-form .fullwidth-error input { - border-color: rgb(179, 0, 0); -} -.registration-form .form-field-error { - color: rgb(255, 26, 26); -} -.registration-form #edit-form { - background-color: unset; - background-image: unset; - border-color: unset; -} -#login-panel .google-icon i { - color: rgb(224, 90, 72); -} -#login-panel .facebook-icon i { - color: rgb(132, 183, 237); -} -#login-panel .github-icon i { - color: rgb(232, 230, 227); -} -.btn:hover { - color: rgb(209, 205, 199); - text-decoration-color: initial; -} -.link-row a { - color: inherit; - text-decoration-color: initial; -} -.link-row:hover { - background-color: rgb(31, 31, 17); - color: rgb(249, 146, 97); -} -button:hover, -button:focus { - box-shadow: none; - outline-color: initial; - text-decoration-color: initial; -} -.btn { - box-shadow: rgba(0, 0, 0, 0.12) 0px 10px 20px -6px; -} -.btn .icon { - background-color: rgb(24, 26, 27); - background-image: initial; -} -.btn:hover, -.btn:active, -.btn:focus { - outline-color: initial; -} -.btn.btn-primary { - color: rgb(232, 230, 227); -} -.btn.btn-primary .icon i { - color: rgb(97, 217, 124); -} -.btn.btn-disabled { - background-color: rgb(96, 104, 108); - background-image: initial; - border-color: rgb(84, 91, 94); - color: rgb(232, 230, 227); -} -a.upvote-link, -a.downvote-link { - color: rgb(232, 230, 227); -} -a.voted { - text-shadow: rgb(0, 0, 0) 0px 0px 4px, rgb(0, 0, 204) 0px 0px 9px; -} -.comment-area .featherlight-edit .featherlight-content { - background-color: rgb(27, 29, 30); - background-image: initial; - border-color: rgb(62, 68, 70); -} -.comment-area .new-comments .comment-display { - background-color: rgb(27, 29, 30); - background-image: initial; - border-color: rgb(62, 68, 70); -} -.comment-area .new-comments .comment .detail .header { - border-bottom-color: rgb(82, 88, 92); - color: rgb(157, 148, 136); -} -.comment-area .previous-revision, -.comment-area .next-revision { - color: rgb(189, 183, 175); -} -.comment-area .new-comments .header i { - color: rgb(157, 148, 136) !important; -} -.comment-area .new-comments .comment:target > .comment-display { - border-color: rgb(34, 106, 153); -} -.comment-author { - color: rgb(200, 195, 188); -} -.comment-header { - background-color: rgba(0, 0, 0, 0.1); - background-image: initial; - border-color: rgb(62, 68, 70); - color: rgb(231, 229, 226); -} -.comment-edits:not(:empty) { - color: rgb(189, 183, 175); -} -.comment-operation .fa { - color: rgb(189, 183, 175); -} -.comment-box { - background-color: rgba(0, 0, 0, 0.01); - background-image: initial; - border-color: rgb(62, 68, 70); -} -.comment { - list-style-image: none; -} -.comment:target > .comment-box { - border-left-color: rgb(48, 52, 54); -} -.actionbar .actionbar-button { - background-color: rgb(49, 53, 55); - background-image: initial; -} -.actionbar .actionbar-button:hover { - background-color: rgb(73, 79, 82); - background-image: initial; -} -.actionbar .dislike-button { - border-left: 0px; -} -.actionbar .like-button.voted { - color: rgb(51, 125, 255); -} -.actionbar .dislike-button.voted { - color: rgb(255, 26, 26); -} -.actionbar .bookmarked { - color: rgb(248, 248, 80); -} -.submission-row { - background-color: rgb(24, 26, 27); - background-image: initial; - box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 4px; -} -.submission-row .sub-result .language { - background-color: rgb(41, 44, 46); -} -.submission-row .sub-info .sub-problem:hover { - text-decoration-color: initial; -} -.submission-row .sub-testcase { - color: rgb(178, 172, 162); -} -#statistics-table tr:not(:first-child) td { - border-top-color: rgb(48, 52, 54) !important; -} -#statistics-table tr:not(:last-child) td:not(:last-child) { - border-right-color: rgb(48, 52, 54); -} -.submission-contest { - color: rgb(178, 172, 162); -} -.statistics-table .count { - color: rgb(232, 230, 227); -} -#test-cases .case-info { - border-color: rgb(34, 106, 153); - color: rgb(211, 207, 201); -} -#test-cases .case-output { - border-color: rgba(128, 119, 105, 0.15); - box-shadow: rgba(27, 29, 30, 0.15) 0px 1px 2px 0px; -} -#test-cases .testcases-table { - border-color: initial; -} -.overall-result-AC { - background-color: initial; - background-image: linear-gradient(45deg, rgb(68, 132, 0), rgb(0, 132, 102)); -} -.overall-result-WA { - background-color: initial; - background-image: linear-gradient(45deg, rgb(153, 153, 0), rgb(204, 0, 0)); -} -.overall-result-TLE { - background-color: initial; - background-image: linear-gradient(45deg, rgb(42, 45, 47), rgb(83, 91, 112)); -} -.overall-result-RTE, -.overall-result-MLE { - background-color: initial; - background-image: linear-gradient(45deg, rgb(67, 49, 3), rgb(198, 145, 0)); -} -.case-AC { - color: rgb(114, 255, 114); -} -.case-_AC { - color: rgb(255, 26, 26); -} -.case-WA { - color: rgb(255, 26, 26); -} -.case-TLE, -.case-SC { - color: rgb(152, 143, 129); -} -.case-MLE, -.case-OLE, -.case-RTE, -.case-IR { - color: rgb(255, 174, 26); -} -.source-wrap a:active .line .highlighter { - background-color: rgba(153, 127, 0, 0.48); - background-image: initial; -} -.submission-info .submission-date { - color: rgb(152, 143, 129); -} -.list-contest { - background-color: rgb(24, 26, 27); - background-image: initial; - box-shadow: rgb(49, 53, 55) 0px 1px 2px, rgb(49, 53, 55) 0px 1px 5px; -} -#contest-calendar th { - border-bottom-color: rgb(62, 68, 70); -} -#contest-calendar th.sun { - border-left-color: rgb(62, 68, 70); -} -#contest-calendar th.sun, -#contest-calendar th.mon, -#contest-calendar th.tue, -#contest-calendar th.wed, -#contest-calendar th.thu, -#contest-calendar th.fri, -#contest-calendar th.sat { - background-color: rgb(27, 29, 30); - background-image: initial; - border-right-color: rgb(62, 68, 70); -} -#contest-calendar td { - border-bottom-color: rgb(62, 68, 70); - border-right-color: rgb(62, 68, 70); - color: rgb(232, 230, 227); -} -#contest-calendar td .num { - border-bottom-color: rgb(62, 68, 70); -} -#contest-calendar td ul { - text-decoration-color: initial; -} -#contest-calendar td ul li i.fa { - color: rgb(255, 174, 26); -} -#contest-calendar td ul li a { - color: rgb(211, 207, 201); - text-decoration-color: initial; -} -#contest-calendar td ul li a:hover { - text-decoration-color: initial; -} -#contest-calendar td:hover { - background-color: rgba(0, 0, 204, 0.3); - background-image: initial; - color: rgb(232, 230, 227); -} -#contest-calendar .noday { - background-color: rgb(32, 35, 36); - background-image: initial; -} -#contest-calendar .today { - background-color: rgba(108, 108, 0, 0.5); - background-image: initial; -} -#contest-calendar tr td:first-child { - border-left-color: rgb(72, 78, 81); -} -#banner a.date { - text-decoration-color: initial; -} -#banner a.date:link, -#banner a.date:visited { - color: rgb(104, 149, 191); -} -#banner a.date:hover { - color: rgb(102, 177, 250); -} -#banner .time { - color: rgb(178, 172, 162); -} -.list-contest .contest-tag-hidden { - background-color: rgb(0, 0, 0); - color: rgb(232, 230, 227); -} -.first-solve { - background-color: rgb(0, 199, 129); - background-image: initial; -} -.contest-tag-edit { - background-color: rgb(0, 102, 0); -} -.contest-tag-private { - background-color: rgb(77, 83, 86); - color: rgb(232, 230, 227); -} -.contest-tag-org { - background-color: rgb(53, 57, 59); -} -.contest-tag-org a { - color: rgb(232, 230, 227); -} -.contest-tag-rated { - background-color: rgb(183, 61, 16); - color: rgb(232, 230, 227); -} -.contest-list-sort { - color: rgb(113, 195, 255); -} -.contest-participation-operation .fa { - color: rgb(189, 183, 175); -} -#add-clarification { - color: rgb(140, 255, 26); -} -#add-clarification:hover { - color: rgb(26, 255, 255); -} -#judge-versions .version-blank { - background-color: rgb(34, 36, 38); - background-image: initial; -} -#judge-versions .version-latest { - background-color: rgba(88, 125, 0, 0.9); - background-image: initial; -} -#judge-versions .version-outdated { - background-color: rgba(204, 0, 0, 0.8); - background-image: initial; - color: rgb(232, 230, 227); -} -.chat { - background-color: rgb(24, 26, 27); - background-image: initial; -} -#chat-online { - border-bottom: 0px; - border-right-color: rgb(62, 68, 70); -} -#chat-input { - border-color: rgb(140, 130, 115); - color: rgb(232, 230, 227); -} -#chat-input::-webkit-input-placeholder { - color: rgb(152, 143, 129); -} -#chat-input::placeholder { - color: rgb(152, 143, 129); -} -.selected-status-row { - background-color: rgb(49, 53, 55); -} -.status_last_message { - color: rgb(178, 171, 161); -} -@media (min-width: 800px) { - #chat-container { - border-bottom: 0px; - border-left-color: rgb(62, 68, 70); - border-right-color: rgb(62, 68, 70); - border-top-color: rgb(62, 68, 70); - } -} -#chat-info { - box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 3px; -} -.status-circle { - stroke: rgb(232, 230, 227); -} -.status-row:hover { - background-color: rgb(49, 53, 55); - background-image: initial; -} -.message-text-other { - background-color: rgb(34, 36, 38); - background-image: initial; - color: rgb(232, 230, 227); -} -.message-text-myself { - background-color: rgb(0, 106, 204); - background-image: initial; - color: rgb(232, 230, 227); -} -.chat-input-icon { - background-color: rgb(48, 104, 78); - color: rgb(232, 230, 227); -} -.chat-input-icon:hover { - background-color: rgb(62, 136, 112); - background-image: initial; -} -.chat .active-span { - color: rgb(169, 162, 151); -} -.chat .unread-count { - background-color: rgb(0, 111, 111); - color: rgb(232, 230, 227); -} -.chat .setting-content { - background-color: rgb(32, 35, 36); - box-shadow: rgba(0, 0, 0, 0.2) 0px 8px 16px 0px; -} -.chat .setting-content a { - text-decoration-color: initial; -} -.chat .setting-content a:hover { - background-color: rgb(43, 47, 49); -} -.leave-organization, -.leave-organization:hover { - color: rgb(255, 26, 26); -} -#control-list li { - border-bottom-color: rgb(140, 130, 115); -} -#pending-count-box { - background-color: rgb(204, 0, 0); - background-image: initial; - color: rgb(232, 230, 227); -} -.organization-card { - background-color: rgb(24, 26, 27); - border-color: rgb(58, 62, 65); - box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 4px; - color: inherit; - text-decoration-color: initial; -} -.organization-card:hover { - color: rgb(249, 146, 97); -} -.organization-card img.org-logo { - background-color: rgb(32, 35, 37); -} -.organization-row { - border-bottom-color: rgb(62, 68, 70); - border-top: none; - color: rgb(232, 230, 227); -} -.organization-row:hover { - background-color: rgb(31, 33, 35); -} -.org-help-text { - color: rgb(152, 143, 129); -} -.ticket-container #content > h2:first-child small { - color: rgb(168, 160, 149); -} -.ticket-container #content > h2:first-child .fa-check-circle { - color: rgb(86, 255, 86); -} -.ticket-container #content > h2:first-child .fa-exclamation-circle { - color: rgb(255, 107, 107); -} -.ticket-container .info-box { - border-color: rgb(77, 83, 86); -} -.ticket-container .info-title { - background-color: rgb(34, 36, 38); - background-image: initial; - border-bottom-color: rgb(77, 83, 86); -} -.ticket-container .info-empty { - color: rgb(168, 160, 149); -} -.ticket-container .close-ticket { - background-color: initial; - background-image: linear-gradient(rgb(60, 138, 0) 0%, rgb(31, 109, 14) 100%); - border-color: rgb(61, 193, 24); -} -.ticket-container .close-ticket:hover { - background-color: rgb(29, 90, 11); - background-image: initial; -} -.ticket-container .open-ticket { - background-color: initial; - background-image: linear-gradient(rgb(195, 3, 0), rgb(141, 49, 18)); - border-color: rgb(186, 67, 24); -} -.ticket-container .open-ticket:hover { - background-color: rgb(106, 38, 14); - background-image: initial; -} -.ticket-container .message .detail { - border-color: rgb(77, 83, 86); -} -.ticket-container .message .header { - background-color: rgb(34, 36, 38); - background-image: initial; - border-bottom-color: rgb(77, 83, 86); - color: rgb(157, 148, 136); -} -.wmd-button-bar { - background-color: rgb(24, 26, 27); -} -.wmd-input { - background-color: rgb(24, 26, 27); - background-image: initial; - border-color: rgb(72, 78, 81); -} -.wmd-preview { - background-color: initial; - background-image: none; -} -.wmd-button { - list-style-image: initial; -} -.wmd-bold-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iOCIgaGVpZ2h0PSI4Ij48ZGVmcz48ZmlsdGVyIGlkPSJkYXJrcmVhZGVyLWltYWdlLWZpbHRlciI+PGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAuMjQ5IC0wLjYxNCAtMC42NzIgMC4wMDAgMS4wMzUgLTAuNjQ2IDAuMjg4IC0wLjY2NCAwLjAwMCAxLjAyMCAtMC42MzYgLTAuNjA5IDAuMjUwIDAuMDAwIDAuOTk0IDAuMDAwIDAuMDAwIDAuMDAwIDEuMDAwIDAuMDAwIiAvPjwvZmlsdGVyPjwvZGVmcz48aW1hZ2Ugd2lkdGg9IjgiIGhlaWdodD0iOCIgZmlsdGVyPSJ1cmwoI2RhcmtyZWFkZXItaW1hZ2UtZmlsdGVyKSIgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCM2FXUjBhRDBpT0hCNElpQm9aV2xuYUhROUlqaHdlQ0lnZG1sbGQwSnZlRDBpTUNBd0lEZ2dPQ0lnZUcxc2JuTTlJbWgwZEhBNkx5OTNkM2N1ZHpNdWIzSm5Mekl3TURBdmMzWm5JajROQ2lBZ1BIQmhkR2dnWkQwaVRUQWdNSFl4WXk0MU5TQXdJREVnTGpRMUlERWdNWFkwWXpBZ0xqVTFMUzQwTlNBeExURWdNWFl4YURVdU5XTXhMak00SURBZ01pNDFMVEV1TVRJZ01pNDFMVEl1TlNBd0xURXRMalU1TFRFdU9EVXRNUzQwTkMweUxqSTFMakkzTFM0ek5DNDBOQzB1TnpndU5EUXRNUzR5TlNBd0xURXVNUzB1T0RrdE1pMHlMVEpvTFRWNmJUTWdNV2d4WXk0MU5TQXdJREVnTGpRMUlERWdNWE10TGpRMUlERXRNU0F4YUMweGRpMHllbTB3SUROb01TNDFZeTQ0TXlBd0lERXVOUzQyTnlBeExqVWdNUzQxY3kwdU5qY2dNUzQxTFRFdU5TQXhMalZvTFRFdU5YWXRNM29pSUM4K0RRbzhMM04yWno0PSIgLz48L3N2Zz4="); -} -.wmd-italic-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iNTI1IiBoZWlnaHQ9IjUyNSI+PGRlZnM+PGZpbHRlciBpZD0iZGFya3JlYWRlci1pbWFnZS1maWx0ZXIiPjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwLjI0OSAtMC42MTQgLTAuNjcyIDAuMDAwIDEuMDM1IC0wLjY0NiAwLjI4OCAtMC42NjQgMC4wMDAgMS4wMjAgLTAuNjM2IC0wLjYwOSAwLjI1MCAwLjAwMCAwLjk5NCAwLjAwMCAwLjAwMCAwLjAwMCAxLjAwMCAwLjAwMCIgLz48L2ZpbHRlcj48L2RlZnM+PGltYWdlIHdpZHRoPSI1MjUiIGhlaWdodD0iNTI1IiBmaWx0ZXI9InVybCgjZGFya3JlYWRlci1pbWFnZS1maWx0ZXIpIiB4bGluazpocmVmPSIiIC8+PC9zdmc+"); -} -.wmd-latex-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iNjQiIGhlaWdodD0iNjQiPjxkZWZzPjxmaWx0ZXIgaWQ9ImRhcmtyZWFkZXItaW1hZ2UtZmlsdGVyIj48ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMC4yNDkgLTAuNjE0IC0wLjY3MiAwLjAwMCAxLjAzNSAtMC42NDYgMC4yODggLTAuNjY0IDAuMDAwIDEuMDIwIC0wLjYzNiAtMC42MDkgMC4yNTAgMC4wMDAgMC45OTQgMC4wMDAgMC4wMDAgMC4wMDAgMS4wMDAgMC4wMDAiIC8+PC9maWx0ZXI+PC9kZWZzPjxpbWFnZSB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIGZpbHRlcj0idXJsKCNkYXJrcmVhZGVyLWltYWdlLWZpbHRlcikiIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QmxibUZpYkdVdFltRmphMmR5YjNWdVpEMGlibVYzSURBZ01DQTJOQ0EyTkNJZ2FHVnBaMmgwUFNJMk5IQjRJaUJwWkQwaVRHRjVaWEpmTVNJZ2RtVnljMmx2YmowaU1TNHhJaUIyYVdWM1FtOTRQU0l3SURBZ05qUWdOalFpSUhkcFpIUm9QU0kyTkhCNElpQjRiV3c2YzNCaFkyVTlJbkJ5WlhObGNuWmxJaUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGh0Ykc1ek9uaHNhVzVyUFNKb2RIUndPaTh2ZDNkM0xuY3pMbTl5Wnk4eE9UazVMM2hzYVc1cklqNDhjR0YwYUNCa1BTSk5ORFV1TnpNc05qUklNelF1TnpVeVl5MHdMamN4TlN3d0xURXVNamsxTFRBdU1UY3pMVEV1TnpNNExUQXVOVEp6TFRBdU56azBMVEF1TnpVNExURXVNRFUyTFRFdU1qTTBiQzA1TGpRNE5DMHhOaTQxTmpRZ0lHTXRNQzR4TnpRc01DNDBOemd0TUM0ek5qa3NNQzQ0T0RjdE1DNDFPRFFzTVM0eU16UnNMVGd1T0RNMUxERTFMak16WXkwd0xqTXdOQ3d3TGpRek5DMHdMalkyTVN3d0xqZ3pOQzB4TGpBM01pd3hMakl3TVVNeE1TNDFOeklzTmpNdU9ERTJMREV4TGpBME1TdzJOQ3d4TUM0ek9URXNOalJJTUM0eE1qY2dJRXd4TlM0ek5pd3pPUzQyTkRGTU1DNDNNVElzTVRZdU5qYzFTREV4TGpZNVl6QXVOekUwTERBc01TNHlOQ3d3TGpBNU15d3hMalUzTml3d0xqSTNObU13TGpNek5Td3dMakU0TlN3d0xqWXpOQ3d3TGpRNU15d3dMamc1TXl3d0xqa3lObXc1TGpReUxERTFMamswT0NBZ1l6QXVNVEE0TFRBdU1qZ3NNQzR5TXpJdE1DNDFOVElzTUM0ek56UXRNQzQ0TVRKak1DNHhOQzB3TGpJMk1pd3dMakk1Tnkwd0xqVXpNU3d3TGpRM01TMHdMamd4TTJ3NExqSTFMVEUwTGpFMk1XTXdMak13TlMwd0xqUTNOeXd3TGpZeU1pMHdMamd5TXl3d0xqazFPQzB4TGpBMElDQmpNQzR6TXpZdE1DNHlNVGNzTUM0M05USXRNQzR6TWpVc01TNHlOVEV0TUM0ek1qVm9NVEF1TlRJMFRETXdMalUyTWl3ek9TNHhPRFpNTkRVdU56TXNOalI2SWk4K1BIQmhkR2dnWkQwaVRUWXhMamcwTERJekxqZzJZekF1TmpZM0xEQXNNUzR4T1RNc01DNHhPRE1zTVM0MU9Dd3dMalUxWXpBdU16ZzNMREF1TXpZMkxEQXVOVGdzTUM0NE5Td3dMalU0TERFdU5EVjJNeTQyU0RRekxqVXlNWFl0TW1Nd0xUQXVNemczTERBdU1EYzVMVEF1T0N3d0xqSXpPUzB4TGpJMElDQmpNQzR4Tmkwd0xqUXpPU3d3TGpRMExUQXVPRFFzTUM0NE5DMHhMakpzT0M0MExUZ3VORFpqTUM0M01qRXRNQzQzTWl3eExqTTBOeTB4TGpRd05pd3hMamc0TVMweUxqQTJZekF1TlRNekxUQXVOalV6TERBdU9UY3pMVEV1TWprM0xERXVNekU1TFRFdU9UTWdJR013TGpNME55MHdMall6TkN3d0xqWXdOaTB4TGpJMk55d3dMamM0TFRFdU9XTXdMakUzTXkwd0xqWXpNeXd3TGpJMkxURXVNekEwTERBdU1qWXRNaTR3TVdNd0xURXVNVFl0TUM0eU56Y3RNaTR3TlMwd0xqZ3pMVEl1TmpjZ0lHTXRNQzQxTlRNdE1DNDJNaTB4TGpNNU5pMHdMamt6TFRJdU5USTVMVEF1T1ROakxUQXVPVElzTUMweExqWTVOeXd3TGpJME15MHlMak16TERBdU56TmpMVEF1TmpNMExEQXVORGczTFRFdU1EY3NNUzR3T1MweExqTXhNU3d4TGpneElDQmpMVEF1TWpjNUxEQXVOek16TFRBdU5qUTJMREV1TWpJdE1TNHhMREV1TkRaakxUQXVORFV6TERBdU1qUXRNUzR4TERBdU1qa3pMVEV1T1RRc01DNHhObXd0TXk0eU9DMHdMalU0WXpBdU1qRXpMVEV1TkRVekxEQXVOakl6TFRJdU56SXNNUzR5TXkwekxqZ2dJR013TGpZd05pMHhMakE0TERFdU16VTVMVEV1T1Rnc01pNHlOaTB5TGpkak1DNDVMVEF1TnpJc01TNDVNeTB4TGpJMU55d3pMakE1TFRFdU5qRkROVEV1TmpZc01DNHhOemNzTlRJdU9UQTJMREFzTlRRdU1qUXNNR014TGpRek9Td3dMREl1TnpNMkxEQXVNakVzTXk0NE9URXNNQzQyTXlBZ1l6RXVNVFV5TERBdU5ESXNNaTR4TXpjc01TNHdNRE1zTWk0NU5Ea3NNUzQzTldNd0xqZ3hNeXd3TGpjME55d3hMalF6T0N3eExqWXpOeXd4TGpnM0xESXVOamRETmpNdU16Z3pMRFl1TURnekxEWXpMallzTnk0eU1pdzJNeTQyTERndU5EWWdJR013TERFdU1EWTJMVEF1TVRRNUxESXVNRFUwTFRBdU5EUTVMREl1T1RaakxUQXVNekF4TERBdU9UQTNMVEF1TnpFc01TNDNOekV0TVM0eU15d3lMalU1WXkwd0xqVXlMREF1T0RJdE1TNHhNak1zTVM0Mk1UTXRNUzQ0TVN3eUxqTTRJQ0JqTFRBdU5qZzNMREF1TnpZNExURXVOREUzTERFdU5UUTBMVEl1TVRrc01pNHpNMnd0TlM0Mk9Ua3NOUzQ0TkdNd0xqY3pNaTB3TGpJeU55d3hMalExT1Mwd0xqTTVPU3d5TGpFNExUQXVOVEpqTUM0M01pMHdMakV5TERFdU16ZzNMVEF1TVRnc01pMHdMakU0U0RZeExqZzBlaUl2UGp3dmMzWm5QZz09IiAvPjwvc3ZnPg=="); -} -.wmd-latex-button-display { - background-image: url("http://127.0.0.1:8000/static/pagedown/resources/latex-display.svg"); -} -.wmd-link-button { - background-image: url("http://127.0.0.1:8000/static/pagedown/resources/link.svg"); -} -.wmd-user-reference-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiPjxkZWZzPjxmaWx0ZXIgaWQ9ImRhcmtyZWFkZXItaW1hZ2UtZmlsdGVyIj48ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMC4yNDkgLTAuNjE0IC0wLjY3MiAwLjAwMCAxLjAzNSAtMC42NDYgMC4yODggLTAuNjY0IDAuMDAwIDEuMDIwIC0wLjYzNiAtMC42MDkgMC4yNTAgMC4wMDAgMC45OTQgMC4wMDAgMC4wMDAgMC4wMDAgMS4wMDAgMC4wMDAiIC8+PC9maWx0ZXI+PC9kZWZzPjxpbWFnZSB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbHRlcj0idXJsKCNkYXJrcmVhZGVyLWltYWdlLWZpbHRlcikiIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEQ5NGJXd2dkbVZ5YzJsdmJqMGlNUzR3SWlBL1BqeHpkbWNnYUdWcFoyaDBQU0l5TkNJZ2RtVnljMmx2YmowaU1TNHhJaUIzYVdSMGFEMGlNalFpSUhodGJHNXpQU0pvZEhSd09pOHZkM2QzTG5jekxtOXlaeTh5TURBd0wzTjJaeUlnZUcxc2JuTTZZMk05SW1oMGRIQTZMeTlqY21WaGRHbDJaV052YlcxdmJuTXViM0puTDI1ekl5SWdlRzFzYm5NNlpHTTlJbWgwZEhBNkx5OXdkWEpzTG05eVp5OWtZeTlsYkdWdFpXNTBjeTh4TGpFdklpQjRiV3h1Y3pweVpHWTlJbWgwZEhBNkx5OTNkM2N1ZHpNdWIzSm5MekU1T1Rrdk1ESXZNakl0Y21SbUxYTjViblJoZUMxdWN5TWlQanhuSUhSeVlXNXpabTl5YlQwaWRISmhibk5zWVhSbEtEQWdMVEV3TWpndU5Da2lQanh3WVhSb0lHUTlJbTB4TWlBd1l5MHdMalF3TlNBd0xUQXVPREExSURBdU1EWXdNekkyTFRFdU1UZzRJREF1TVRVMk1qVXRNQzR5TWpRZ01DNHdOVFkzT0Mwd0xqUTBJREF1TVRNeE16VXRNQzQyTlRZZ01DNHlNVGczTlMwd0xqQTRNeUF3TGpBek5EQXhMVEF1TVRZM09TQXdMakExTlRNMExUQXVNalE1T0NBd0xqQTVNemMxTFRBdU1ETTBJREF1TURFMU9ETXRNQzR3TmlBd0xqQTBOVGswTFRBdU1Ea3pOeUF3TGpBMk1qVXRNQzR5TURNeUlEQXVNVEF3TlRndE1DNDBNREl4SURBdU1qRTNNRFF0TUM0MU9UTTNJREF1TXpRek56VXRNQzR3TWpjZ01DNHdNVGMwTFRBdU1EWTNNU0F3TGpBeE16TTVMVEF1TURrek9DQXdMakF6TVRJMUxUQXVNRFUyTXlBd0xqQXpPRFkwTFRBdU1UQXhJREF1TURnME1Ua3RNQzR4TlRZeUlEQXVNVEkwT1RVdE1DNHhOVFk1SURBdU1URXlOaTB3TGpNeU1UWWdNQzR5TVRZdE1DNDBOamc0SURBdU16UXpPQzB3TGpFek5ESWdNQzR4TWpBM0xUQXVNalE1TkNBd0xqSTNNalF0TUM0ek56VWdNQzQwTURZeUxUQXVOREkxTVNBd0xqUXpOVGt0TUM0M09UTTJJREF1T0RrM01TMHhMakE1TXpnZ01TNDBNemMyTFRBdU5URTFOQ0F3TGprd016UXRNQzQ1TURBeUlERXVPVEl3TlMweExqQTJNalFnTWk0NU5qZzNMVEF1TURjNE15MHdMakF4TmpVdE1DNHhOVEF4TFRBdU1ESXlOQzB3TGpJeE9EZ2dNQzB3TGpVeU5URWdNQzR4TnpFdE1DNDJOVFExSURFdU1UWTROUzB3TGpNeE1qVWdNaTR5TVRnM0lEQXVNakF3TnlBd0xqWXhOak1nTUM0MU16UTJJREV1TVRBeE5TQXdMamczTlNBeExqTTNOU0F3TGpRMU56TWdNUzQzTnpjNElERXVOREkxTnlBekxqSTFPVGdnTWk0Mk9EYzFJRFF1TVRnM09IWXhMakF6TVd3dE1TQXhMVElnTVdNdE1TNDJNVGN6SURBdU9EQXhMVE11TWpJNE5DQXhMall3TlMwMExqZzBNemdnTWk0ME1EWXRNQzQ0T1RVeE15QXdMalUwTFRFdU1qUXhOU0F4TGpZdE1TNHhOVFl5SURJdU5UazBJREF1TURReE5qWTBJREF1TmpJMkxUQXVNVGcwTkRnZ01TNDBNamNnTUM0ME16YzFJREV1T0RRMElEQXVOVGt3T1NBd0xqTXdOQ0F4TGpJNU5Ua2dNQzR4TURZZ01TNDVNemMxSURBdU1UVTJJREV1T0RjMk5pMHdMakF3TVNBekxqYzBPRFFnTUNBMUxqWXlOU0F3SURJdU5qWTVJREF1TURBeElEVXVNek14SURBZ09DQXdJREl1TXpZM0lEQWdOQzQzTWpjZ01DNHdNRFFnTnk0d09UUWdNQ0F3TGpjMk9DMHdMakExTkNBd0xqazRNUzB3TGpnMk5TQXdMamt3TmkweExqVWdNQzR3TVRRdE1DNDVNeklnTUM0d05qa3RNUzQ1TnpZdE1DNDJOVFl0TWk0Mk9EZ3RNQzQxT1RJdE1DNDJNREl0TVM0ME16UXRNQzQ0TkMweUxqRTFOaTB4TGpJMUxURXVNRFl4TFRBdU5USTFMVEl1TVRJNExURXVNRE0zTFRNdU1UZzRMVEV1TlRZeWJDMHlMVEV0TVMweGRpMHhMakF6TVdNeExqSTJNaTB3TGpreU9DQXlMakl6TFRJdU5ERWdNaTQyT0RndE5DNHhPRGM0SURBdU16UXRNQzR5TnpNMklEQXVOamMwTFRBdU56VTRPQ0F3TGpnM05DMHhMak0zTlNBd0xqTTBNaTB4TGpBMU1ESWdNQzR5TVRNdE1pNHdORGMzTFRBdU16RXlMVEl1TWpFNE55MHdMakEyT1Mwd0xqQXlNalF0TUM0eE5DMHdMakF4TmpVdE1DNHlNVGtnTUMwd0xqRTJNaTB4TGpBME9ESXRNQzQxTkRjdE1pNHdOalV6TFRFdU1EWXlMVEl1T1RZNE55MHdMak10TUM0MU5EQTFMVEF1TmpZNUxURXVNREF4TnkweExqQTVOQzB4TGpRek56WXRNQzR4TWpZdE1DNHhNek00TFRBdU1qUXhMVEF1TWpnMU5TMHdMak0zTlMwd0xqUXdOakl0TUM0d01EWXRNQzR3TURVMUxUQXVNREkxSURBdU1EQTFOUzB3TGpBek1TQXdMVEF1TXpreUxUQXVNelE1T1Mwd0xqZ3lOeTB3TGpZeE9EazBMVEV1TWpneExUQXVPRFF6TnpVdE1DNHhNVFV0TUM0d05UWXlNaTB3TGpJeU55MHdMakV3T0RVMExUQXVNelEwTFRBdU1UVTJNalV0TUM0d09EUXRNQzR3TXpRd01TMHdMakUyTlMwd0xqQTJOREkyTFRBdU1qVXRNQzR3T1RNM05TMHdMakkxTlMwd0xqQTRPRFE0TFRBdU5URTJMVEF1TVRjek5UWXRNQzQzT0RJdE1DNHlNVGczTlMwd0xqQXlMVEF1TURBek5EQTFMVEF1TURReUlEQXVNREF6TVRRNExUQXVNRFl5SURBdE1DNHlORGt0TUM0d016a3hORFF0TUM0ME9UVXRNQzR3TmpVeU5TMHdMamMxTFRBdU1EWXlOWG9pSUdacGJHdzlJaU16TkRRNU5XVWlJSFJ5WVc1elptOXliVDBpZEhKaGJuTnNZWFJsS0RBZ01UQXlPQzQwS1NJdlBqeHdZWFJvSUdROUltMHdJREV3TlRFdU5HTXdMakF5TmpReE9TQXdMak1nTUM0eE1qWTFNU0F3TGpZZ01DNDBNemMxSURBdU9DQXdMalU1TURrZ01DNHpJREV1TWprMU9TQXdMakVnTVM0NU16YzFJREF1TW1nMUxqWXlOU0E0SURjdU1EazBZekF1TlRjMkxUQXVNU0F3TGpnME1pMHdMalVnTUM0NU1EWXRNV2d0TWpSNklpQm1hV3hzUFNJak1tTXpaVFV3SWk4K1BDOW5Qand2YzNablBnPT0iIC8+PC9zdmc+"); -} -.wmd-quote-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iNDgiIGhlaWdodD0iNDgiPjxkZWZzPjxmaWx0ZXIgaWQ9ImRhcmtyZWFkZXItaW1hZ2UtZmlsdGVyIj48ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMC4yNDkgLTAuNjE0IC0wLjY3MiAwLjAwMCAxLjAzNSAtMC42NDYgMC4yODggLTAuNjY0IDAuMDAwIDEuMDIwIC0wLjYzNiAtMC42MDkgMC4yNTAgMC4wMDAgMC45OTQgMC4wMDAgMC4wMDAgMC4wMDAgMS4wMDAgMC4wMDAiIC8+PC9maWx0ZXI+PC9kZWZzPjxpbWFnZSB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIGZpbHRlcj0idXJsKCNkYXJrcmVhZGVyLWltYWdlLWZpbHRlcikiIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEQ5NGJXd2dkbVZ5YzJsdmJqMGlNUzR3SWlBL1Bqd2hSRTlEVkZsUVJTQnpkbWNnSUZCVlFreEpReUFuTFM4dlZ6TkRMeTlFVkVRZ1UxWkhJREV1TVM4dlJVNG5JQ0FuYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2UjNKaGNHaHBZM012VTFaSEx6RXVNUzlFVkVRdmMzWm5NVEV1WkhSa0p6NDhjM1puSUdWdVlXSnNaUzFpWVdOclozSnZkVzVrUFNKdVpYY2dNQ0F3SURRNElEUTRJaUJvWldsbmFIUTlJalE0Y0hnaUlHbGtQU0pNWVhsbGNsOHpJaUIyWlhKemFXOXVQU0l4TGpFaUlIWnBaWGRDYjNnOUlqQWdNQ0EwT0NBME9DSWdkMmxrZEdnOUlqUTRjSGdpSUhodGJEcHpjR0ZqWlQwaWNISmxjMlZ5ZG1VaUlIaHRiRzV6UFNKb2RIUndPaTh2ZDNkM0xuY3pMbTl5Wnk4eU1EQXdMM04yWnlJZ2VHMXNibk02ZUd4cGJtczlJbWgwZEhBNkx5OTNkM2N1ZHpNdWIzSm5MekU1T1RrdmVHeHBibXNpUGp4blBqeHdZWFJvSUdROUlrMHdMREk0TGpNM05XZ3hNaTQyTjJNdE1DNHdOaklzTXk0ek1TMHhMak16T0N3MkxqTXhNeTB6TGpRd015dzRMalpqTFRBdU9UUTFMREF1T0RreUxURXVPVGd5TERFdU5EQTRMVEl1T0RjNUxERXVOekEySUNBZ1l5MHhMakVzTUM0ek1qZ3RNaTR5Tml3d0xqTTVOaTB5TGpnNE15d3dMalF3T0Vnd2RqSXVNamcyYURndU56VjJMVEF1TURBell6Y3VNakExTFRBdU1ETXlMREV6TGpBME15MDFMamd4Tml3eE15NHhOemN0TVRJdU9UazNTREl5ZGkweU1rZ3dWakk0TGpNM05Yb2lJR1pwYkd3OUlpTXlOREZHTWpBaUx6NDhjR0YwYUNCa1BTSk5NallzTmk0ek56VjJNakpvTVRJdU56UmpNQ3d5TGprNU1TMHhMRFV1TnpReUxUSXVOamMwTERjdU9UVTJZeTB3TGpnek9Td3hMakE0T1MweExqZ3lPQ3d4TGpjNU1pMHlMamMyTVN3eUxqSXpNeUFnSUdNdE1DNHdNRGdzTUM0d01EUXRNQzR3TVRZc01DNHdNRGd0TUM0d01qTXNNQzR3TVRKakxUQXVNVE01TERBdU1EWTFMVEF1TWpjMExEQXVNVEl5TFRBdU5EQTVMREF1TVRjMll5MHhMalV4TkN3d0xqVTFOeTB6TGpreE5Td3dMalU0Tnkwekxqa3hOU3d3TGpVNE4wZ3lObll5TGpJNE5tZzRMamMxSUNBZ1l6Y3VNekUzTERBc01UTXVNalV0TlM0NU16TXNNVE11TWpVdE1UTXVNalYyTFRJeVNESTJlaUlnWm1sc2JEMGlJekkwTVVZeU1DSXZQand2Wno0OEwzTjJaejQ9IiAvPjwvc3ZnPg=="); -} -.wmd-code-button { - background-image: url("http://127.0.0.1:8000/static/pagedown/resources/code.svg"); -} -.wmd-image-button { - background-image: url("http://127.0.0.1:8000/static/pagedown/resources/image.svg"); -} -.wmd-olist-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiPjxkZWZzPjxmaWx0ZXIgaWQ9ImRhcmtyZWFkZXItaW1hZ2UtZmlsdGVyIj48ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMC4yNDkgLTAuNjE0IC0wLjY3MiAwLjAwMCAxLjAzNSAtMC42NDYgMC4yODggLTAuNjY0IDAuMDAwIDEuMDIwIC0wLjYzNiAtMC42MDkgMC4yNTAgMC4wMDAgMC45OTQgMC4wMDAgMC4wMDAgMC4wMDAgMS4wMDAgMC4wMDAiIC8+PC9maWx0ZXI+PC9kZWZzPjxpbWFnZSB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbHRlcj0idXJsKCNkYXJrcmVhZGVyLWltYWdlLWZpbHRlcikiIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEQ5NGJXd2dkbVZ5YzJsdmJqMGlNUzR3SWlBL1BqeHpkbWNnWm1sc2JEMGlibTl1WlNJZ2FHVnBaMmgwUFNJeU5DSWdkbWxsZDBKdmVEMGlNQ0F3SURJMElESTBJaUIzYVdSMGFEMGlNalFpSUhodGJHNXpQU0pvZEhSd09pOHZkM2QzTG5jekxtOXlaeTh5TURBd0wzTjJaeUkrUEhCaGRHZ2daRDBpVFRZdU1EQXdNU0F5TGpjMVF6WXVNREF3TVNBeUxqTTVOVGMzSURVdU56VXlNamNnTWk0d09EazROQ0ExTGpRd05UYzJJREl1TURFMk16TkROUzR3TmpBeE1TQXhMamswTXlBMExqY3hNRE1nTWk0eE1qQTROaUEwTGpVMk5UZ2dNaTQwTkRNd00wdzBMalUyTkRJMklESXVORFEyTXpkRE5DNDFOakl5TmlBeUxqUTFNRFkzSURRdU5UVTRORElnTWk0ME5UZzRNU0EwTGpVMU1qYzFJREl1TkRjd05ESkROQzQxTkRFMElESXVORGt6TmpZZ05DNDFNakk0TWlBeUxqVXpNRFUxSURRdU5EazNNRFFnTWk0MU56Z3dPRU0wTGpRME5USTNJREl1Tmpjek5UWWdOQzR6TmpVNE5DQXlMamd3T1RRMUlEUXVNalU1TkNBeUxqazJNalE0UXpRdU1EUXhOellnTXk0eU56VXpOeUF6TGpjek5UTTRJRE11TmpJME9EZ2dNeTR6TlRJM05TQXpMamcyTXpreFF6TXVNREF4TkRRZ05DNHdPRE16TmlBeUxqZzVORFUySURRdU5UUTJNRFVnTXk0eE1UUXdNU0EwTGpnNU56TTFRek11TXpNek5EWWdOUzR5TkRnMk5pQXpMamM1TmpFMUlEVXVNelUxTlRRZ05DNHhORGMwTmlBMUxqRXpOakE1UXpRdU1qY3lNRFVnTlM0d05UZ3lOaUEwTGpNNE9UVTJJRFF1T1RjME56TWdOQzQxTURBeElEUXVPRGczTmpWV055NHlOVU0wTGpVd01ERWdOeTQyTmpReU1TQTBMamd6TlRnNUlEZ2dOUzR5TlRBeElEaEROUzQyTmpRek1TQTRJRFl1TURBd01TQTNMalkyTkRJeElEWXVNREF3TVNBM0xqSTFWakl1TnpWYUlpQm1hV3hzUFNJak1qRXlNVEl4SWk4K1BIQmhkR2dnWkQwaVRURTVMalE1T1RVZ01UaElNVEF1T1RrNU5Vd3hNQzQ0T0RJNUlERTRMakF3TmpkRE1UQXVNemcxTmlBeE9DNHdOalExSURrdU9UazVOVEVnTVRndU5EZzNNaUE1TGprNU9UVXhJREU1UXprdU9UazVOVEVnTVRrdU5UVXlNeUF4TUM0ME5EY3lJREl3SURFd0xqazVPVFVnTWpCSU1Ua3VORGs1TlV3eE9TNDJNVFl4SURFNUxqazVNek5ETWpBdU1URXpOU0F4T1M0NU16VTFJREl3TGpRNU9UVWdNVGt1TlRFeU9DQXlNQzQwT1RrMUlERTVRekl3TGpRNU9UVWdNVGd1TkRRM055QXlNQzR3TlRFNElERTRJREU1TGpRNU9UVWdNVGhhSWlCbWFXeHNQU0lqTWpFeU1USXhJaTgrUEhCaGRHZ2daRDBpVFRFNUxqUTVPVFVnTVRFdU5VZ3hNQzQ1T1RrMVRERXdMamc0TWprZ01URXVOVEEyTjBNeE1DNHpPRFUySURFeExqVTJORFVnT1M0NU9UazFNU0F4TVM0NU9EY3lJRGt1T1RrNU5URWdNVEl1TlVNNUxqazVPVFV4SURFekxqQTFNak1nTVRBdU5EUTNNaUF4TXk0MUlERXdMams1T1RVZ01UTXVOVWd4T1M0ME9UazFUREU1TGpZeE5qRWdNVE11TkRrek0wTXlNQzR4TVRNMUlERXpMalF6TlRVZ01qQXVORGs1TlNBeE15NHdNVEk0SURJd0xqUTVPVFVnTVRJdU5VTXlNQzQwT1RrMUlERXhMamswTnpjZ01qQXVNRFV4T0NBeE1TNDFJREU1TGpRNU9UVWdNVEV1TlZvaUlHWnBiR3c5SWlNeU1USXhNakVpTHo0OGNHRjBhQ0JrUFNKTk1Ua3VORGs1TlNBMVNERXdMams1T1RWTU1UQXVPRGd5T1NBMUxqQXdOamN6UXpFd0xqTTROVFlnTlM0d05qUTBPU0E1TGprNU9UVXhJRFV1TkRnM01UWWdPUzQ1T1RrMU1TQTJRemt1T1RrNU5URWdOaTQxTlRJeU9DQXhNQzQwTkRjeUlEY2dNVEF1T1RrNU5TQTNTREU1TGpRNU9UVk1NVGt1TmpFMk1TQTJMams1TXpJM1F6SXdMakV4TXpVZ05pNDVNelUxTVNBeU1DNDBPVGsxSURZdU5URXlPRFFnTWpBdU5EazVOU0EyUXpJd0xqUTVPVFVnTlM0ME5EYzNNaUF5TUM0d05URTRJRFVnTVRrdU5EazVOU0ExV2lJZ1ptbHNiRDBpSXpJeE1qRXlNU0l2UGp4d1lYUm9JR1E5SWswMUxqRTFNRGt6SURFd0xqVXhPVGxETkM0NE5EazFOeUF4TUM0ME5qWTNJRFF1TkRjME5EUWdNVEF1TlRnMk15QTBMakk0TURNeElERXdMamM0TURSRE15NDVPRGMwTVNBeE1TNHdOek16SURNdU5URXlOVE1nTVRFdU1EY3pNeUF6TGpJeE9UWTFJREV3TGpjNE1EUkRNaTQ1TWpZM055QXhNQzQwT0RjMElESXVPVEkyTnpnZ01UQXVNREV5TmlBekxqSXhPVFk1SURrdU56RTVOamxETXk0M056VTFOaUE1TGpFMk16ZzFJRFF1TmpVd05EUWdPQzQ1TURnMElEVXVOREV4TlRjZ09TNHdOREkzUXpVdU9EQTVJRGt1TVRFeU9ETWdOaTR5TVRjeklEa3VNamszTVNBMkxqVXlOekl5SURrdU5qUTRNelJETmk0NE5ESTJJREV3TGpBd05UZ2dOeUF4TUM0ME56STNJRGNnTVRGRE55QXhNUzQyTVRrMUlEWXVOekk0T1RVZ01USXVNRGdnTmk0ek9UUXdOaUF4TWk0ME1qQTFRell1TVRFMk15QXhNaTQzTURJNUlEVXVOell6TVRVZ01USXVPVE14SURVdU5EZzRNallnTVRNdU1UQTROa3cxTGpRd09EYzBJREV6TGpFMk1ERkROUzR5TVRNME9DQXhNeTR5T0RjZ05TNHdOVFF5T0NBeE15NHpPVGNnTkM0NU1qWTROaUF4TXk0MVNEWXVNalZETmk0Mk5qUXlNU0F4TXk0MUlEY2dNVE11T0RNMU9DQTNJREUwTGpJMVF6Y2dNVFF1TmpZME1pQTJMalkyTkRJZ01UVWdOaTR5TkRrNU9TQXhOVWd6TGpjMVF6TXVNek0xTnprZ01UVWdNeUF4TkM0Mk5qUXlJRE1nTVRRdU1qVkRNeUF4TWk0NU16WTBJRE11T1Rnek9EVWdNVEl1TWprM01TQTBMalUzTlRBMUlERXhMamt4TTB3MExqWXpOVEl5SURFeExqZzNNemxETkM0NU5USTFPU0F4TVM0Mk5qYzNJRFV1TVRZNE5ETWdNVEV1TlRJM05TQTFMak15TkRZNUlERXhMak0yT0RaRE5TNDBOVGcxTlNBeE1TNHlNekkxSURVdU5TQXhNUzR4TXpBMklEVXVOU0F4TVVNMUxqVWdNVEF1TnpjM05DQTFMalF6T0RZMUlERXdMalk0TVRnZ05TNDBNREkwTnlBeE1DNDJOREE0UXpVdU16WXdPRE1nTVRBdU5Ua3pOaUExTGpJNE5EYzFJREV3TGpVME16VWdOUzR4TlRBNU15QXhNQzQxTVRrNVdpSWdabWxzYkQwaUl6SXhNakV5TVNJdlBqeHdZWFJvSUdROUlrMHlMamsyT1RZM0lESXhMakk0TURORE1pNDVOamsyTnlBeU1TNHlPREF6SURNdU1EWXlOVEVnTWpFdU16WTBNU0F5TGprM05ESTJJREl4TGpJNE5EbE1NaTQ1TnprMk1pQXlNUzR5T1RBeFRESXVPVGt5T1NBeU1TNHpNREk0UXpNdU1EQXlPRGNnTWpFdU16RXlNU0F6TGpBeE5UQTVJREl4TGpNeU16TWdNeTR3TWprMU5pQXlNUzR6TXpVNVF6TXVNRFU0TkRnZ01qRXVNell4TWlBekxqQTVOalV6SURJeExqTTVNamNnTXk0eE5ETTNOU0F5TVM0ME1qZ3hRek11TWpNNE1TQXlNUzQwT1RnNUlETXVNelk1T1RZZ01qRXVOVGcySURNdU5UTTVOVGtnTWpFdU5qY3dPRU16TGpnNE1UTXhJREl4TGpnME1UY2dOQzR6TnpBME15QXlNaUExSURJeVF6VXVOak01TWpFZ01qSWdOaTR4T1RVNU5pQXlNUzQ0TVRnMklEWXVOakF4TXpJZ01qRXVORFl3TlVNM0xqQXdPVEl5SURJeExqRXdNREVnTnk0eU1URTJNeUF5TUM0Mk1ETXlJRGN1TVRrMU5UTWdNakF1TVRBeFF6Y3VNVGd5TVRrZ01Ua3VOamcwTmlBM0xqQXlPVEF6SURFNUxqTXdOVE1nTmk0M05qazROaUF4T1VNM0xqQXlPVEF6SURFNExqWTVORGNnTnk0eE9ESXhPU0F4T0M0ek1UVTBJRGN1TVRrMU5UTWdNVGN1T0RrNVF6Y3VNakV4TmpNZ01UY3VNemsyT0NBM0xqQXdPVEl5SURFMkxqZzVPVGtnTmk0Mk1ERXpNaUF4Tmk0MU16azFRell1TVRrMU9UWWdNVFl1TVRneE5DQTFMall6T1RJeElERTJJRFVnTVRaRE5DNHpOekEwTXlBeE5pQXpMamc0TVRNeElERTJMakUxT0RNZ015NDFNemsxT1NBeE5pNHpNamt5UXpNdU16WTVPVFlnTVRZdU5ERTBJRE11TWpNNE1TQXhOaTQxTURFeElETXVNVFF6TnpVZ01UWXVOVGN4T1VNekxqQTVOalV6SURFMkxqWXdOek1nTXk0d05UZzBPQ0F4Tmk0Mk16ZzRJRE11TURJNU5UWWdNVFl1TmpZME1VTXpMakF4TlRBNUlERTJMalkzTmpjZ015NHdNREk0TnlBeE5pNDJPRGM1SURJdU9Ua3lPU0F4Tmk0Mk9UY3lUREl1T1RjNU5qSWdNVFl1TnpBNU9Vd3lMamszTkRJeklERTJMamN4TlRGTU1pNDVOekU0T1NBeE5pNDNNVGMxVERJdU9UY3dPVEVnTVRZdU56RTROVXd5TGprMk9UWTNJREUyTGpjeE9UZERNaTQyTnpZM09DQXhOeTR3TVRJMklESXVOamMyTnpnZ01UY3VORGczTkNBeUxqazJPVFkzSURFM0xqYzRNRE5ETXk0eU5UZzJJREU0TGpBMk9UTWdNeTQzTWpRMk1pQXhPQzR3TnpNeUlEUXVNREU0TXpRZ01UY3VOemt5TVVNMExqQXlNVGNnTVRjdU56ZzVNaUEwTGpBek1ERTRJREUzTGpjNE1pQTBMakEwTXpjMUlERTNMamMzTVRsRE5DNHdOelEwSURFM0xqYzBPRGtnTkM0eE16QXdOQ0F4Tnk0M01URWdOQzR5TVRBME1TQXhOeTQyTnpBNFF6UXVNelk0TmprZ01UY3VOVGt4TnlBMExqWXlPVFUzSURFM0xqVWdOU0F4Tnk0MVF6VXVNell3TnprZ01UY3VOU0ExTGpVek5qQXhJREUzTGpVNU9Ua2dOUzQyTURneE1pQXhOeTQyTmpNMlF6VXVOamMzTmpnZ01UY3VOekkxTVNBMUxqWTVPREl6SURFM0xqYzVNRGNnTlM0Mk9UWXpJREUzTGpnMU1VTTFMalk1TkRJeklERTNMamt4TlRVZ05TNDJOalF3TXlBeE9DNHdNREkzSURVdU5UY3lOemdnTVRndU1EZ3hPRU0xTGpRNE5EVWdNVGd1TVRVNE5DQTFMak13T1RZeUlERTRMakkxSURVZ01UZ3VNalZETkM0MU9EVTNPU0F4T0M0eU5TQTBMakkxSURFNExqVTROVGdnTkM0eU5TQXhPVU0wTGpJMUlERTVMalF4TkRJZ05DNDFPRFUzT1NBeE9TNDNOU0ExSURFNUxqYzFRelV1TXpBNU5qSWdNVGt1TnpVZ05TNDBPRFExSURFNUxqZzBNVFlnTlM0MU56STNPQ0F4T1M0NU1UZ3lRelV1TmpZME1ETWdNVGt1T1RrM015QTFMalk1TkRJeklESXdMakE0TkRVZ05TNDJPVFl6SURJd0xqRTBPVU0xTGpZNU9ESXpJREl3TGpJd09UTWdOUzQyTnpjMk9DQXlNQzR5TnpRNUlEVXVOakE0TVRJZ01qQXVNek0yTkVNMUxqVXpOakF4SURJd0xqUXdNREVnTlM0ek5qQTNPU0F5TUM0MUlEVWdNakF1TlVNMExqWXlPVFUzSURJd0xqVWdOQzR6TmpnMk9TQXlNQzQwTURneklEUXVNakV3TkRFZ01qQXVNekk1TWtNMExqRXpNREEwSURJd0xqSTRPU0EwTGpBM05EUWdNakF1TWpVeE1TQTBMakEwTXpjMUlESXdMakl5T0RGRE5DNHdNekF4T0NBeU1DNHlNVGdnTkM0d01qRTNJREl3TGpJeE1EZ2dOQzR3TVRnek5DQXlNQzR5TURjNVF6TXVOekkwTmpJZ01Ua3VPVEkyT0NBekxqSTFPRFlnTVRrdU9UTXdOeUF5TGprMk9UWTNJREl3TGpJeE9UZERNaTQyTnpZM09DQXlNQzQxTVRJMklESXVOamMyTnpnZ01qQXVPVGczTkNBeUxqazJPVFkzSURJeExqSTRNRE5hVFRJdU9UY3hPRGtnTVRZdU56RTNOVXd5TGprM01Ea3hJREUyTGpjeE9EVkRNaTQ1TnpjMklERTJMamN4TWprZ015NHhOekU0TWlBeE5pNDFOVEUxSURJdU9UY3hPRGtnTVRZdU56RTNOVm9pSUdacGJHdzlJaU15TVRJeE1qRWlMejQ4TDNOMlp6ND0iIC8+PC9zdmc+"); -} -.wmd-ulist-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiPjxkZWZzPjxmaWx0ZXIgaWQ9ImRhcmtyZWFkZXItaW1hZ2UtZmlsdGVyIj48ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMC4yNDkgLTAuNjE0IC0wLjY3MiAwLjAwMCAxLjAzNSAtMC42NDYgMC4yODggLTAuNjY0IDAuMDAwIDEuMDIwIC0wLjYzNiAtMC42MDkgMC4yNTAgMC4wMDAgMC45OTQgMC4wMDAgMC4wMDAgMC4wMDAgMS4wMDAgMC4wMDAiIC8+PC9maWx0ZXI+PC9kZWZzPjxpbWFnZSB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIGZpbHRlcj0idXJsKCNkYXJrcmVhZGVyLWltYWdlLWZpbHRlcikiIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEQ5NGJXd2dkbVZ5YzJsdmJqMGlNUzR3SWlBL1BqeHpkbWNnWm1sc2JEMGlibTl1WlNJZ2FHVnBaMmgwUFNJeU1DSWdkbWxsZDBKdmVEMGlNQ0F3SURJd0lESXdJaUIzYVdSMGFEMGlNakFpSUhodGJHNXpQU0pvZEhSd09pOHZkM2QzTG5jekxtOXlaeTh5TURBd0wzTjJaeUkrUEhCaGRHZ2daRDBpVFRNdU1qVWdOME16TGprME1ETTJJRGNnTkM0MUlEWXVORFF3TXpZZ05DNDFJRFV1TnpWRE5DNDFJRFV1TURVNU5qUWdNeTQ1TkRBek5pQTBMalVnTXk0eU5TQTBMalZETWk0MU5UazJOQ0EwTGpVZ01pQTFMakExT1RZMElESWdOUzQzTlVNeUlEWXVORFF3TXpZZ01pNDFOVGsyTkNBM0lETXVNalVnTjFwTk55QTFMamMxUXpjZ05TNHpNelUzT1NBM0xqTXpOVGM1SURVZ055NDNOU0ExU0RFM0xqSTFRekUzTGpZMk5ESWdOU0F4T0NBMUxqTXpOVGM1SURFNElEVXVOelZETVRnZ05pNHhOalF5TVNBeE55NDJOalF5SURZdU5TQXhOeTR5TlNBMkxqVklOeTQzTlVNM0xqTXpOVGM1SURZdU5TQTNJRFl1TVRZME1qRWdOeUExTGpjMVdrMDNMamMxSURFd1F6Y3VNek0xTnprZ01UQWdOeUF4TUM0ek16VTRJRGNnTVRBdU56VkROeUF4TVM0eE5qUXlJRGN1TXpNMU56a2dNVEV1TlNBM0xqYzFJREV4TGpWSU1UY3VNalZETVRjdU5qWTBNaUF4TVM0MUlERTRJREV4TGpFMk5ESWdNVGdnTVRBdU56VkRNVGdnTVRBdU16TTFPQ0F4Tnk0Mk5qUXlJREV3SURFM0xqSTFJREV3U0RjdU56VmFUVGN1TnpVZ01UVkROeTR6TXpVM09TQXhOU0EzSURFMUxqTXpOVGdnTnlBeE5TNDNOVU0zSURFMkxqRTJORElnTnk0ek16VTNPU0F4Tmk0MUlEY3VOelVnTVRZdU5VZ3hOeTR5TlVNeE55NDJOalF5SURFMkxqVWdNVGdnTVRZdU1UWTBNaUF4T0NBeE5TNDNOVU14T0NBeE5TNHpNelU0SURFM0xqWTJORElnTVRVZ01UY3VNalVnTVRWSU55NDNOVnBOTkM0MUlERXdMamMxUXpRdU5TQXhNUzQwTkRBMElETXVPVFF3TXpZZ01USWdNeTR5TlNBeE1rTXlMalUxT1RZMElERXlJRElnTVRFdU5EUXdOQ0F5SURFd0xqYzFReklnTVRBdU1EVTVOaUF5TGpVMU9UWTBJRGt1TlNBekxqSTFJRGt1TlVNekxqazBNRE0ySURrdU5TQTBMalVnTVRBdU1EVTVOaUEwTGpVZ01UQXVOelZhVFRNdU1qVWdNVGRETXk0NU5EQXpOaUF4TnlBMExqVWdNVFl1TkRRd05DQTBMalVnTVRVdU56VkROQzQxSURFMUxqQTFPVFlnTXk0NU5EQXpOaUF4TkM0MUlETXVNalVnTVRRdU5VTXlMalUxT1RZMElERTBMalVnTWlBeE5TNHdOVGsySURJZ01UVXVOelZETWlBeE5pNDBOREEwSURJdU5UVTVOalFnTVRjZ015NHlOU0F4TjFvaUlHWnBiR3c5SWlNeU1USXhNakVpTHo0OEwzTjJaejQ9IiAvPjwvc3ZnPg=="); -} -.wmd-heading-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIj48ZGVmcz48ZmlsdGVyIGlkPSJkYXJrcmVhZGVyLWltYWdlLWZpbHRlciI+PGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAuMjQ5IC0wLjYxNCAtMC42NzIgMC4wMDAgMS4wMzUgLTAuNjQ2IDAuMjg4IC0wLjY2NCAwLjAwMCAxLjAyMCAtMC42MzYgLTAuNjA5IDAuMjUwIDAuMDAwIDAuOTk0IDAuMDAwIDAuMDAwIDAuMDAwIDEuMDAwIDAuMDAwIiAvPjwvZmlsdGVyPjwvZGVmcz48aW1hZ2Ugd2lkdGg9IjE3OTIiIGhlaWdodD0iMTc5MiIgZmlsdGVyPSJ1cmwoI2RhcmtyZWFkZXItaW1hZ2UtZmlsdGVyKSIgeGxpbms6aHJlZj0iIiAvPjwvc3ZnPg=="); -} -.wmd-hr-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCI+PGRlZnM+PGZpbHRlciBpZD0iZGFya3JlYWRlci1pbWFnZS1maWx0ZXIiPjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwLjI0OSAtMC42MTQgLTAuNjcyIDAuMDAwIDEuMDM1IC0wLjY0NiAwLjI4OCAtMC42NjQgMC4wMDAgMS4wMjAgLTAuNjM2IC0wLjYwOSAwLjI1MCAwLjAwMCAwLjk5NCAwLjAwMCAwLjAwMCAwLjAwMCAxLjAwMCAwLjAwMCIgLz48L2ZpbHRlcj48L2RlZnM+PGltYWdlIHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiBmaWx0ZXI9InVybCgjZGFya3JlYWRlci1pbWFnZS1maWx0ZXIpIiB4bGluazpocmVmPSJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBEOTRiV3dnZG1WeWMybHZiajBpTVM0d0lpQS9Qanh6ZG1jZ2RtbGxkMEp2ZUQwaU1DQXdJREl3SURJd0lpQjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaVBqeHdZWFJvSUdROUlrMHhJREZvTW5ZeVNERldNWHB0TUNBMGFESjJNa2d4VmpWNmJUQWdOR2d4T0hZeVNERldPWHB0TUNBMGFESjJNa2d4ZGkweWVtMHdJRFJvTW5ZeVNERjJMVEo2VFRVZ01XZ3lkakpJTlZZeGVtMHdJREUyYURKMk1rZzFkaTB5ZWswNUlERm9Nbll5U0RsV01YcHRNQ0EwYURKMk1rZzVWalY2YlRBZ09HZ3lkakpJT1hZdE1ucHRNQ0EwYURKMk1rZzVkaTB5ZW0wMExURTJhREoyTW1ndE1sWXhlbTB3SURFMmFESjJNbWd0TW5ZdE1ucHROQzB4Tm1neWRqSm9MVEpXTVhwdE1DQTBhREoyTW1ndE1sWTFlbTB3SURob01uWXlhQzB5ZGkweWVtMHdJRFJvTW5ZeWFDMHlkaTB5ZWlJdlBqd3ZjM1puUGc9PSIgLz48L3N2Zz4="); -} -.wmd-undo-button { - background-image: url("http://127.0.0.1:8000/static/pagedown/resources/undo.svg"); -} -.wmd-redo-button { - background-image: url("http://127.0.0.1:8000/static/pagedown/resources/redo.svg"); -} -.wmd-admonition-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCI+PGRlZnM+PGZpbHRlciBpZD0iZGFya3JlYWRlci1pbWFnZS1maWx0ZXIiPjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwLjI0OSAtMC42MTQgLTAuNjcyIDAuMDAwIDEuMDM1IC0wLjY0NiAwLjI4OCAtMC42NjQgMC4wMDAgMS4wMjAgLTAuNjM2IC0wLjYwOSAwLjI1MCAwLjAwMCAwLjk5NCAwLjAwMCAwLjAwMCAwLjAwMCAxLjAwMCAwLjAwMCIgLz48L2ZpbHRlcj48L2RlZnM+PGltYWdlIHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiBmaWx0ZXI9InVybCgjZGFya3JlYWRlci1pbWFnZS1maWx0ZXIpIiB4bGluazpocmVmPSJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBEOTRiV3dnZG1WeWMybHZiajBpTVM0d0lpQS9QandoUkU5RFZGbFFSU0J6ZG1jZ0lGQlZRa3hKUXlBbkxTOHZWek5ETHk5RVZFUWdVMVpISURFdU1TOHZSVTRuSUNBbmFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdlIzSmhjR2hwWTNNdlUxWkhMekV1TVM5RVZFUXZjM1puTVRFdVpIUmtKejQ4YzNabklHVnVZV0pzWlMxaVlXTnJaM0p2ZFc1a1BTSnVaWGNnTUNBd0lEVXhNaUExTVRJaUlHbGtQU0pNWVhsbGNsOHhJaUIyWlhKemFXOXVQU0l4TGpFaUlIWnBaWGRDYjNnOUlqQWdNQ0ExTVRJZ05URXlJaUI0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhodGJHNXpPbmhzYVc1clBTSm9kSFJ3T2k4dmQzZDNMbmN6TG05eVp5OHhPVGs1TDNoc2FXNXJJajQ4Wno0OFp6NDhjR0YwYUNCa1BTSk5NelkxTERFMk55NDRTREl5T0M0eVl5MDFMamNzTUMweE1DNDBMVFF1TnkweE1DNDBMVEV3TGpSMkxUQXVPR013TFRVdU55dzBMamN0TVRBdU5Dd3hNQzQwTFRFd0xqUklNelkxWXpVdU55d3dMREV3TGpRc05DNDNMREV3TGpRc01UQXVOQ0FnSUNCMk1DNDRRek0zTlM0MUxERTJNeTR4TERNM01DNDRMREUyTnk0NExETTJOU3d4TmpjdU9Ib2lJR1pwYkd3OUlpTXpNRE5CTTBZaUx6NDhjR0YwYUNCa1BTSk5NelkxTERJeU9DNDRTREUxTXk0NVl5MDFMamNzTUMweE1DNDBMVFF1TnkweE1DNDBMVEV3TGpSMkxUQXVPR013TFRVdU55dzBMamN0TVRBdU5Dd3hNQzQwTFRFd0xqUklNelkxWXpVdU55d3dMREV3TGpRc05DNDNMREV3TGpRc01UQXVOQ0FnSUNCMk1DNDRRek0zTlM0MUxESXlOQzR4TERNM01DNDRMREl5T0M0NExETTJOU3d5TWpndU9Ib2lJR1pwYkd3OUlpTXpNRE5CTTBZaUx6NDhjR0YwYUNCa1BTSk5NelkxTERJNE9TNDRTREUxTXk0NVl5MDFMamNzTUMweE1DNDBMVFF1TnkweE1DNDBMVEV3TGpSMkxUQXVPR013TFRVdU55dzBMamN0TVRBdU5Dd3hNQzQwTFRFd0xqUklNelkxWXpVdU55d3dMREV3TGpRc05DNDNMREV3TGpRc01UQXVOQ0FnSUNCMk1DNDRRek0zTlM0MUxESTROUzR4TERNM01DNDRMREk0T1M0NExETTJOU3d5T0RrdU9Ib2lJR1pwYkd3OUlpTXpNRE5CTTBZaUx6NDhjR0YwYUNCa1BTSk5NelkxTERNMU1DNDVTREUxTXk0NVl5MDFMamNzTUMweE1DNDBMVFF1TnkweE1DNDBMVEV3TGpSMkxUQXVPR013TFRVdU55dzBMamN0TVRBdU5Dd3hNQzQwTFRFd0xqUklNelkxWXpVdU55d3dMREV3TGpRc05DNDNMREV3TGpRc01UQXVOQ0FnSUNCMk1DNDRRek0zTlM0MUxETTBOaTR5TERNM01DNDRMRE0xTUM0NUxETTJOU3d6TlRBdU9Yb2lJR1pwYkd3OUlpTXpNRE5CTTBZaUx6NDhjR0YwYUNCa1BTSk5NelkxTERReE1TNDVTREUxTXk0NVl5MDFMamNzTUMweE1DNDBMVFF1TnkweE1DNDBMVEV3TGpSMkxUQXVPR013TFRVdU55dzBMamN0TVRBdU5Dd3hNQzQwTFRFd0xqUklNelkxWXpVdU55d3dMREV3TGpRc05DNDNMREV3TGpRc01UQXVOQ0FnSUNCMk1DNDRRek0zTlM0MUxEUXdOeTR5TERNM01DNDRMRFF4TVM0NUxETTJOU3cwTVRFdU9Yb2lJR1pwYkd3OUlpTXpNRE5CTTBZaUx6NDhMMmMrUEhCaGRHZ2daRDBpVFRNNU1pdzBNeTQxU0RFM09TNHliRE00TGpZc05URXVOR2d4TkRRdU4yTXlOeTR5TERBc05Ea3VOU3d5TWk0ekxEUTVMalVzTkRrdU5YWXlOamt1TTJNd0xESTNMakl0TWpJdU15dzBPUzQxTFRRNUxqVXNORGt1TlVneE5UUXVOQ0FnSUdNdE1qY3VNaXd3TFRRNUxqVXRNakl1TXkwME9TNDFMVFE1TGpWV01UVXdMamxNTmpVdU15dzVPQzQwWXkwd0xqTXNNaTQzTFRBdU9DdzFMak10TUM0NExEaDJNelF5TGpOak1Dd3pOQzQyTERJNExqTXNOak1zTmpNc05qTklNemt5WXpNMExqWXNNQ3cyTXkweU9DNHpMRFl6TFRZelZqRXdOaTQwSUNBZ1F6UTFOQzQ1TERjeExqZ3NOREkyTGpZc05ETXVOU3d6T1RJc05ETXVOWG9pSUdacGJHdzlJaU16TUROQk0wWWlMejQ4Wno0OGNHRjBhQ0JrUFNKTk1USTFMalVzTVRRNUxqaHNOamd1TXl3eU5XTXpMallzTVM0ekxEWXVOUzB3TGpjc05pNHpMVFF1Tm13dE15NHlMVGN5TGpkakxUQXVNUzB4TGpVdE1DNDFMVEl1T1MweExqTXROQzR6YkRBdU1TMHdMakVnSUNBZ1l5MHdMakV0TUM0eUxUQXVNeTB3TGpRdE1DNDBMVEF1Tm13dE5EZ3VPQzAyTmk0MmJDMHhOaXd4TVM0M2JEUTBMakVzTmpBdU1tTXhMamdzTWk0MExERXVNaXcxTGpndE1TNHlMRGN1Tm13dE15NDRMREl1T0dNdE1pNDBMREV1T0MwMUxqZ3NNUzR5TFRjdU5pMHhMakpNTVRFNExEUTJMamtnSUNBZ2JDMHhOUzQ0TERFeExqWnNORFF1TkN3Mk1DNDNZekV1Tml3eUxqSXNNUzR4TERVdU1pMHhMakVzTmk0NGJDMDBMamNzTXk0MVl5MHlMaklzTVM0MkxUVXVNaXd4TGpFdE5pNDRMVEV1TVV3NE9TNDJMRFkzTGpaTU56SXVOaXc0TUd3ME55NHlMRFkwTGpRZ0lDQWdRekV5TVN3eE5EWXVPQ3d4TWpNc01UUTRMamtzTVRJMUxqVXNNVFE1TGpoNklFMHhNemdzTVRReExqbHNORGN1Tmkwek5DNDVZekl1T1MweUxqRXNOeTQyTERFdU15dzNMamdzTlM0NGJERXVNU3d6T1M0M1l5MHdMamNzTUM0eUxURXVOQ3d3TGpNdE1pd3dMamRzTFRFMExqSXNNVEF1TkNBZ0lDQmpMVEF1TVN3d0xqRXRNQzR6TERBdE1DNHpMREF1TVd3dE16WXVPQzB4TWk0MlF6RXpOeXd4TkRrdU5pd3hNelV1TVN3eE5EUXNNVE00TERFME1TNDVlaUlnWm1sc2JEMGlJek13TTBFelJpSXZQanh3WVhSb0lHUTlJazAyT0M0NUxEYzFMak5zTFRVdU55MDNMamRqTFRFd0xqVXRNVFF1TXkwM0xqTXRNelF1TlN3M0xUUTFiREl5TFRFMkxqRmpNVFF1TXkweE1DNDFMRE0wTGpVdE55NHpMRFExTERkc05TNDNMRGN1TjB3Mk9DNDVMRGMxTGpONklpQm1hV3hzUFNJak16QXpRVE5HSWk4K1BDOW5Qand2Wno0OEwzTjJaejQ9IiAvPjwvc3ZnPg=="); -} -.wmd-spoiler-button { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCI+PGRlZnM+PGZpbHRlciBpZD0iZGFya3JlYWRlci1pbWFnZS1maWx0ZXIiPjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwLjI0OSAtMC42MTQgLTAuNjcyIDAuMDAwIDEuMDM1IC0wLjY0NiAwLjI4OCAtMC42NjQgMC4wMDAgMS4wMjAgLTAuNjM2IC0wLjYwOSAwLjI1MCAwLjAwMCAwLjk5NCAwLjAwMCAwLjAwMCAwLjAwMCAxLjAwMCAwLjAwMCIgLz48L2ZpbHRlcj48L2RlZnM+PGltYWdlIHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiBmaWx0ZXI9InVybCgjZGFya3JlYWRlci1pbWFnZS1maWx0ZXIpIiB4bGluazpocmVmPSJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBEOTRiV3dnZG1WeWMybHZiajBpTVM0d0lpQS9Qanh6ZG1jZ1pHRjBZUzF1WVcxbFBTSk1ZWGxsY2lBeElpQnBaRDBpVEdGNVpYSmZNU0lnZG1sbGQwSnZlRDBpTUNBd0lETXlJRE15SWlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpUGp4MGFYUnNaUzgrUEhCaGRHZ2daRDBpVFRJM0xqTTNMREpJTVRRdU1EVmhPQzR3Tnl3NExqQTNMREFzTUN3eExESXVOemNzTmk0eExEY3VPVEVzTnk0NU1Td3dMREFzTVMwdU5qTXNNeTR4TW1nM0xqUTRZUzQ1TkM0NU5Dd3dMREVzTVN3d0xERXVPRGRJTVRVdU1XTXRMakUyTGpJdExqTTBMalF0TGpVeUxqVTVZVGd1TVRJc09DNHhNaXd3TERBc01TMDFMamcxTERJdU5URXNNaTR4Tnl3eUxqRTNMREFzTUN3eExTNHpOaXd3VmpJNFlUSXNNaXd3TERBc01Dd3lMREpvTVRkaE1pd3lMREFzTUN3d0xESXRNbFkwUVRJc01pd3dMREFzTUN3eU55NHpOeXd5V20wdE15NDNMREU0TGpjNGFDMDVMalpoTGprMExqazBMREFzTVN3eExEQXRNUzQ0TjJnNUxqWmhMamswTGprMExEQXNNU3d4TERBc01TNDROMXB0TUMwekxqZzFhQzA1TGpaaExqa3pMamt6TERBc01Td3hMREF0TVM0NE5tZzVMalpoTGprekxqa3pMREFzTVN3eExEQXNNUzQ0TmxvaUx6NDhjR0YwYUNCa1BTSk5PQzQzTXl3eE5DNHhPV0UyTGpBM0xEWXVNRGNzTUN3d0xEQXNOQzQwTVMweExqbEJOUzQyTVN3MUxqWXhMREFzTUN3d0xERTBMREV4TGpJellUWXNOaXd3TERBc01Dd3VPRGN0TXk0eE15dzJMakVzTmk0eExEQXNNQ3d3TFRVdE5tZ3dZVFlzTml3d0xEQXNNQzB4TFM0d09TdzJMakE1TERZdU1Ea3NNQ3d3TERBdExqTTJMREV5TGpFM1FUSXVNVGNzTWk0eE55d3dMREFzTUN3NExqY3pMREUwTGpFNVdtMHRNeTAyTGpBNVlURXNNU3d3TERBc01Td3hMVEZvTkdFeExERXNNQ3d3TERFc01Dd3lhQzAwUVRFc01Td3dMREFzTVN3MUxqY3pMRGd1TVZvaUx6NDhMM04yWno0PSIgLz48L3N2Zz4="); -} -.wmd-button-active:hover { - background-color: rgb(49, 53, 55); -} -.wmd-prompt-background { - background-color: rgb(0, 0, 0); -} -.wmd-prompt-dialog { - background-color: rgb(30, 32, 33); - border-color: rgb(77, 83, 86); -} -.wmd-prompt-dialog > form > input[type="text"] { - border-color: rgb(77, 83, 86); - color: rgb(232, 230, 227); -} -.wmd-prompt-dialog > form > input[type="button"] { - border-color: rgb(82, 88, 92); -} -.wmd-preview { - background-color: rgb(24, 26, 27); - background-image: initial; - border-color: rgb(72, 78, 81); -} -.pagedown-image-upload { - background-color: rgb(24, 26, 27); - background-image: initial; - box-shadow: rgba(0, 0, 0, 0.5) 2px 2px 10px 0px; -} -.pagedown-image-upload .submit-loading { - border-color: rgb(46, 91, 113) rgb(51, 56, 58) rgb(51, 56, 58); -} -div.dmmd-preview-update { - background-color: rgb(53, 57, 59); - background-image: initial; - color: rgb(200, 195, 188); -} -div.dmmd-preview-stale { - background-color: initial; - background-image: repeating-linear-gradient(-45deg, rgb(24, 26, 27), rgb(24, 26, 27) 10px, rgb(28, 30, 31) 10px, rgb(28, 30, 31) 20px); -} -.course-list { - list-style-image: initial; -} -.course-list .course-item { - background-color: rgb(24, 26, 27); - border-color: rgb(58, 62, 65); - box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px; -} -.course-list .course-item:hover { - box-shadow: rgba(0, 0, 0, 0.15) 0px 6px 12px; -} -.lesson-list { - list-style-image: initial; -} -.lesson-list li:hover { - background-color: rgb(52, 52, 0); - background-image: initial; - box-shadow: rgba(0, 0, 0, 0.15) 0px 6px 12px; -} -.lesson-list li { - background-color: rgb(24, 26, 27); - background-image: initial; - border-color: rgb(58, 62, 65); - box-shadow: rgb(53, 57, 59) 0px 2px 4px; -} -.lesson-list .lesson-title { - color: initial; -} -.lesson-list .lesson-title .lesson-points { - color: rgb(169, 162, 151); -} -.lesson-list .progress-container { - background-color: rgb(42, 45, 47); - background-image: initial; -} -.lesson-list .progress-bar { - background-color: rgb(27, 111, 27); - background-image: initial; - color: rgb(232, 230, 227); -} -.course-problem-list li { - border-bottom-color: rgb(53, 57, 59); -} -.course-problem-list li:hover { - background-color: rgb(42, 45, 47); - background-image: initial; -} -.course-problem-list a { - color: inherit; - text-decoration-color: initial; -} -.course-contest-card { - border-color: rgb(58, 62, 65); - box-shadow: rgba(0, 0, 0, 0.1) 2px 2px 10px; -} -.course-contest-card h5 { - color: rgb(200, 195, 188); -} -.course-contest-card p { - color: rgb(178, 172, 162); -} -.fa-border { - border: var(--darkreader-border--fa-border-width, .08em) var(--darkreader-border--fa-border-style, solid) var(--darkreader-border--fa-border-color, #35393b); -} -.fa-spin-reverse { - --fa-animation-direction: reverse; -} -.fa-inverse { - color: var(--darkreader-text--fa-inverse, #e8e6e3); -} -:host, -:root { - --fa-font-brands: normal 400 1em/1 "Font Awesome 6 Brands"; - --fa-style-family-brands: "Font Awesome 6 Brands"; -} -:host, -:root { - --fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free"; -} -:host, -:root { - --fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free"; - --fa-style-family-classic: "Font Awesome 6 Free"; -} -@media all { - .featherlight { - background-color: rgba(0, 0, 0, 0); - background-image: initial; - } - .featherlight:last-of-type { - background-color: rgba(0, 0, 0, 0.8); - background-image: initial; - } - .featherlight .featherlight-content { - background-color: rgb(24, 26, 27); - background-image: initial; - border-bottom-color: transparent; - } - .featherlight .featherlight-close-icon { - background-color: rgba(24, 26, 27, 0.3); - background-image: initial; - color: rgb(232, 230, 227); - } - .featherlight-iframe .featherlight-content { - border-bottom: 0px; - } - .featherlight iframe { - border-color: initial; - border-style: initial; - border-width: 0px; - } -} -@media only screen and (max-width: 1024px) { - .featherlight .featherlight-content { - border-bottom-color: transparent; - } -} -.tooltipped::after { - background-color: rgba(0, 0, 0, 0.8); - background-image: initial; - color: rgb(232, 230, 227); - text-decoration-color: initial; - text-shadow: none; -} -.tooltipped::before { - border-color: transparent; - color: rgba(232, 230, 227, 0.8); -} -.tooltipped:hover::before, -.tooltipped:hover::after, -.tooltipped:active::before, -.tooltipped:active::after, -.tooltipped:focus::before, -.tooltipped:focus::after { - text-decoration-color: initial; -} -.tooltipped-s::before, -.tooltipped-se::before, -.tooltipped-sw::before { - border-bottom-color: rgba(140, 130, 115, 0.8); -} -.tooltipped-n::before, -.tooltipped-ne::before, -.tooltipped-nw::before { - border-top-color: rgba(140, 130, 115, 0.8); -} -.tooltipped-w::before { - border-left-color: rgba(140, 130, 115, 0.8); -} -.tooltipped-e::before { - border-right-color: rgba(140, 130, 115, 0.8); -} -.select2-container .select2-search--inline .select2-search__field { - border-color: initial; - border-style: none; - border-width: initial; -} -.select2-dropdown { - background-color: rgb(24, 26, 27); - border-color: rgb(72, 78, 81); -} -.select2-results__options { - list-style-image: initial; -} -.select2-container--open .select2-dropdown--above { - border-bottom: none; -} -.select2-container--open .select2-dropdown--below { - border-top: none; -} -.select2-close-mask { - background-color: rgb(24, 26, 27); - border-color: initial; - border-style: initial; - border-width: 0px; -} -.select2-hidden-accessible { - border-color: initial !important; - border-style: initial !important; - border-width: 0px !important; -} -.select2-container--default .select2-selection--single { - background-color: rgb(24, 26, 27); - border-color: rgb(72, 78, 81); -} -.select2-container--default .select2-selection--single .select2-selection__rendered { - color: rgb(189, 183, 175); -} -.select2-container--default .select2-selection--single .select2-selection__placeholder { - color: rgb(168, 160, 149); -} -.select2-container--default .select2-selection--single .select2-selection__arrow b { - border-color: rgb(82, 88, 92) transparent transparent; -} -.select2-container--default.select2-container--disabled .select2-selection--single { - background-color: rgb(34, 36, 38); -} -.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { - border-color: transparent transparent rgb(82, 88, 92); -} -.select2-container--default .select2-selection--multiple { - background-color: rgb(24, 26, 27); - border-color: rgb(72, 78, 81); -} -.select2-container--default .select2-selection--multiple .select2-selection__rendered { - list-style-image: initial; -} -.select2-container--default .select2-selection--multiple .select2-selection__placeholder { - color: rgb(168, 160, 149); -} -.select2-container--default .select2-selection--multiple .select2-selection__choice { - background-color: rgb(39, 43, 44); - border-color: rgb(72, 78, 81); -} -.select2-container--default .select2-selection--multiple .select2-selection__choice__remove { - color: rgb(168, 160, 149); -} -.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { - color: rgb(200, 195, 188); -} -.select2-container--default.select2-container--focus .select2-selection--multiple { - border-color: rgb(140, 130, 115); - outline-color: initial; -} -.select2-container--default.select2-container--disabled .select2-selection--multiple { - background-color: rgb(34, 36, 38); -} -.select2-container--default .select2-search--dropdown .select2-search__field { - border-color: rgb(72, 78, 81); -} -.select2-container--default .select2-search--inline .select2-search__field { - background-color: transparent; - background-image: initial; - border-color: initial; - border-style: none; - border-width: initial; - box-shadow: none; - outline-color: initial; -} -.select2-container--default .select2-results__option[aria-disabled="true"] { - color: rgb(168, 160, 149); -} -.select2-container--default .select2-results__option[aria-selected="true"] { - background-color: rgb(43, 47, 49); -} -.select2-container--default .select2-results__option--highlighted[aria-selected] { - background-color: rgb(4, 60, 150); - color: rgb(232, 230, 227); -} -.select2-container--classic .select2-selection--single { - background-color: rgb(29, 31, 32); - background-image: linear-gradient(rgb(24, 26, 27) 50%, rgb(34, 36, 38) 100%); - border-color: rgb(72, 78, 81); - outline-color: initial; -} -.select2-container--classic .select2-selection--single:focus { - border-color: rgb(4, 60, 150); -} -.select2-container--classic .select2-selection--single .select2-selection__rendered { - color: rgb(189, 183, 175); -} -.select2-container--classic .select2-selection--single .select2-selection__placeholder { - color: rgb(168, 160, 149); -} -.select2-container--classic .select2-selection--single .select2-selection__arrow { - background-color: rgb(43, 47, 49); - background-image: linear-gradient(rgb(34, 36, 38) 50%, rgb(53, 57, 59) 100%); - border-bottom: none; - border-left-color: rgb(72, 78, 81); - border-right: none; - border-top: none; -} -.select2-container--classic .select2-selection--single .select2-selection__arrow b { - border-color: rgb(82, 88, 92) transparent transparent; -} -.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow { - border-bottom: none; - border-left: none; - border-right-color: rgb(72, 78, 81); - border-top: none; -} -.select2-container--classic.select2-container--open .select2-selection--single { - border-color: rgb(4, 60, 150); -} -.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow { - background-color: transparent; - background-image: initial; - border-color: initial; - border-style: none; - border-width: initial; -} -.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b { - border-color: transparent transparent rgb(82, 88, 92); -} -.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single { - background-image: linear-gradient(rgb(24, 26, 27) 0%, rgb(34, 36, 38) 50%); - border-top: none; -} -.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single { - background-image: linear-gradient(rgb(34, 36, 38) 50%, rgb(24, 26, 27) 100%); - border-bottom: none; -} -.select2-container--classic .select2-selection--multiple { - background-color: rgb(24, 26, 27); - border-color: rgb(72, 78, 81); - outline-color: initial; -} -.select2-container--classic .select2-selection--multiple:focus { - border-color: rgb(4, 60, 150); -} -.select2-container--classic .select2-selection--multiple .select2-selection__rendered { - list-style-image: initial; -} -.select2-container--classic .select2-selection--multiple .select2-selection__choice { - background-color: rgb(39, 43, 44); - border-color: rgb(72, 78, 81); -} -.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove { - color: rgb(157, 148, 136); -} -.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover { - color: rgb(178, 172, 162); -} -.select2-container--classic.select2-container--open .select2-selection--multiple { - border-color: rgb(4, 60, 150); -} -.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple { - border-top: none; -} -.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple { - border-bottom: none; -} -.select2-container--classic .select2-search--dropdown .select2-search__field { - border-color: rgb(72, 78, 81); - outline-color: initial; -} -.select2-container--classic .select2-search--inline .select2-search__field { - box-shadow: none; - outline-color: initial; -} -.select2-container--classic .select2-dropdown { - background-color: rgb(24, 26, 27); - border-color: transparent; -} -.select2-container--classic .select2-dropdown--above { - border-bottom: none; -} -.select2-container--classic .select2-dropdown--below { - border-top: none; -} -.select2-container--classic .select2-results__option[aria-disabled="true"] { - color: rgb(152, 143, 129); -} -.select2-container--classic .select2-results__option--highlighted[aria-selected] { - background-color: rgb(33, 82, 162); - color: rgb(232, 230, 227); -} -.select2-container--classic.select2-container--open .select2-dropdown { - border-color: rgb(4, 60, 150); -} -.icofont-border { - border-color: rgb(52, 56, 58); -} -.icofont-inverse { - color: rgb(232, 230, 227); -} -.sr-only { - border-color: initial; - border-style: initial; - border-width: 0px; -} -.katex * { - border-color: currentcolor; -} -.katex .katex-mathml { - border-color: initial; - border-style: initial; - border-width: 0px; -} -.katex .rule { - border-color: initial; - border-style: solid; - border-width: 0px; -} -.katex svg { - fill: currentcolor; - stroke: currentcolor; -} -.katex svg path { - stroke: none; -} -.katex .fbox, -.katex .fcolorbox { - border-color: initial; -} -.katex .angl { - border-right-color: initial; - border-top-color: initial; -} - -/* Override Style */ -.vimvixen-hint { - background-color: #684b00 !important; - border-color: #9e7e00 !important; - color: #d7d4cf !important; -} -#vimvixen-console-frame { - color-scheme: light !important; -} -::placeholder { - opacity: 0.5 !important; -} -#edge-translate-panel-body, -.MuiTypography-body1, -.nfe-quote-text { - color: var(--darkreader-neutral-text) !important; -} -gr-main-header { - background-color: #1b4958 !important; -} -.tou-z65h9k, -.tou-mignzq, -.tou-1b6i2ox, -.tou-lnqlqk { - background-color: var(--darkreader-neutral-background) !important; -} -.tou-75mvi { - background-color: #0f3a47 !important; -} -.tou-ta9e87, -.tou-1w3fhi0, -.tou-1b8t2us, -.tou-py7lfi, -.tou-1lpmd9d, -.tou-1frrtv8, -.tou-17ezmgn { - background-color: #1e2021 !important; -} -.tou-uknfeu { - background-color: #432c09 !important; -} -.tou-6i3zyv { - background-color: #245d70 !important; -} -div.mermaid-viewer-control-panel .btn { - background-color: var(--darkreader-neutral-background); - fill: var(--darkreader-neutral-text); -} -svg g rect.er { - fill: var(--darkreader-neutral-background) !important; -} -svg g rect.er.entityBox { - fill: var(--darkreader-neutral-background) !important; -} -svg g rect.er.attributeBoxOdd { - fill: var(--darkreader-neutral-background) !important; -} -svg g rect.er.attributeBoxEven { - fill: var(--darkreader-selection-background); - fill-opacity: 0.8 !important; -} -svg rect.er.relationshipLabelBox { - fill: var(--darkreader-neutral-background) !important; -} -svg g g.nodes rect, -svg g g.nodes polygon { - fill: var(--darkreader-neutral-background) !important; -} -svg g rect.task { - fill: var(--darkreader-selection-background) !important; -} -svg line.messageLine0, -svg line.messageLine1 { - stroke: var(--darkreader-neutral-text) !important; -} -div.mermaid .actor { - fill: var(--darkreader-neutral-background) !important; -} -mitid-authenticators-code-app > .code-app-container { - background-color: white !important; - padding-top: 1rem; -} -iframe#unpaywall[src$="unpaywall.html"] { - color-scheme: light !important; -} diff --git a/resources/datetime-picker/datetimepicker.full.min.js b/resources/datetime-picker/datetimepicker.full.min.js deleted file mode 100644 index a2a09c6..0000000 --- a/resources/datetime-picker/datetimepicker.full.min.js +++ /dev/null @@ -1 +0,0 @@ -var DateFormatter;!function(){"use strict";var e,t,a,r,n,o,i;o=864e5,i=3600,e=function(e,t){return"string"==typeof e&&"string"==typeof t&&e.toLowerCase()===t.toLowerCase()},t=function(e,a,r){var n=r||"0",o=e.toString();return o.lengths?"20":"19")+i):s,h=!0;break;case"m":case"n":case"M":case"F":if(isNaN(s)){if(!((u=m.getMonth(i))>0))return null;D.month=u}else{if(!(s>=1&&12>=s))return null;D.month=s}h=!0;break;case"d":case"j":if(!(s>=1&&31>=s))return null;D.day=s,h=!0;break;case"g":case"h":if(d=r.indexOf("a")>-1?r.indexOf("a"):r.indexOf("A")>-1?r.indexOf("A"):-1,c=n[d],d>-1)l=e(c,p.meridiem[0])?0:e(c,p.meridiem[1])?12:-1,s>=1&&12>=s&&l>-1?D.hour=s+l-1:s>=0&&23>=s&&(D.hour=s);else{if(!(s>=0&&23>=s))return null;D.hour=s}g=!0;break;case"G":case"H":if(!(s>=0&&23>=s))return null;D.hour=s,g=!0;break;case"i":if(!(s>=0&&59>=s))return null;D.min=s,g=!0;break;case"s":if(!(s>=0&&59>=s))return null;D.sec=s,g=!0}if(!0===h&&D.year&&D.month&&D.day)D.date=new Date(D.year,D.month-1,D.day,D.hour,D.min,D.sec,0);else{if(!0!==g)return null;D.date=new Date(0,0,0,D.hour,D.min,D.sec,0)}return D.date},guessDate:function(e,t){if("string"!=typeof e)return e;var a,r,n,o,i,s,u=this,d=e.replace(u.separators,"\0").split("\0"),l=/^[djmn]/g,f=t.match(u.validParts),c=new Date,m=0;if(!l.test(f[0]))return e;for(n=0;na?a:4,!(r=parseInt(4>a?r.toString().substr(0,4-a)+i:i.substr(0,4))))return null;c.setFullYear(r);break;case 3:c.setHours(s);break;case 4:c.setMinutes(s);break;case 5:c.setSeconds(s)}(o=i.substr(m)).length>0&&d.splice(n+1,0,o)}return c},parseFormat:function(e,a){var r,n=this,s=n.dateSettings,u=/\\?(.?)/gi,d=function(e,t){return r[e]?r[e]():t};return r={d:function(){return t(r.j(),2)},D:function(){return s.daysShort[r.w()]},j:function(){return a.getDate()},l:function(){return s.days[r.w()]},N:function(){return r.w()||7},w:function(){return a.getDay()},z:function(){var e=new Date(r.Y(),r.n()-1,r.j()),t=new Date(r.Y(),0,1);return Math.round((e-t)/o)},W:function(){var e=new Date(r.Y(),r.n()-1,r.j()-r.N()+3),a=new Date(e.getFullYear(),0,4);return t(1+Math.round((e-a)/o/7),2)},F:function(){return s.months[a.getMonth()]},m:function(){return t(r.n(),2)},M:function(){return s.monthsShort[a.getMonth()]},n:function(){return a.getMonth()+1},t:function(){return new Date(r.Y(),r.n(),0).getDate()},L:function(){var e=r.Y();return e%4==0&&e%100!=0||e%400==0?1:0},o:function(){var e=r.n(),t=r.W();return r.Y()+(12===e&&9>t?1:1===e&&t>9?-1:0)},Y:function(){return a.getFullYear()},y:function(){return r.Y().toString().slice(-2)},a:function(){return r.A().toLowerCase()},A:function(){var e=r.G()<12?0:1;return s.meridiem[e]},B:function(){var e=a.getUTCHours()*i,r=60*a.getUTCMinutes(),n=a.getUTCSeconds();return t(Math.floor((e+r+n+i)/86.4)%1e3,3)},g:function(){return r.G()%12||12},G:function(){return a.getHours()},h:function(){return t(r.g(),2)},H:function(){return t(r.G(),2)},i:function(){return t(a.getMinutes(),2)},s:function(){return t(a.getSeconds(),2)},u:function(){return t(1e3*a.getMilliseconds(),6)},e:function(){return/\((.*)\)/.exec(String(a))[1]||"Coordinated Universal Time"},I:function(){return new Date(r.Y(),0)-Date.UTC(r.Y(),0)!=new Date(r.Y(),6)-Date.UTC(r.Y(),6)?1:0},O:function(){var e=a.getTimezoneOffset(),r=Math.abs(e);return(e>0?"-":"+")+t(100*Math.floor(r/60)+r%60,4)},P:function(){var e=r.O();return e.substr(0,3)+":"+e.substr(3,2)},T:function(){return(String(a).match(n.tzParts)||[""]).pop().replace(n.tzClip,"")||"UTC"},Z:function(){return 60*-a.getTimezoneOffset()},c:function(){return"Y-m-d\\TH:i:sP".replace(u,d)},r:function(){return"D, d M Y H:i:s O".replace(u,d)},U:function(){return a.getTime()/1e3||0}},d(e,e)},formatDate:function(e,t){var a,r,n,o,i,s=this,u="";if("string"==typeof e&&!(e=s.parseDate(e,t)))return null;if(e instanceof Date){for(n=t.length,a=0;n>a;a++)"S"!==(i=t.charAt(a))&&"\\"!==i&&(a>0&&"\\"===t.charAt(a-1)?u+=i:(o=s.parseFormat(i,e),a!==n-1&&s.intParts.test(i)&&"S"===t.charAt(a+1)&&(r=parseInt(o)||0,o+=s.dateSettings.ordinal(r)),u+=o));return u}return""}}}();var datetimepickerFactory=function(e){"use strict";function t(e,t,a){this.date=e,this.desc=t,this.style=a}var a={i18n:{ar:{months:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],dayOfWeekShort:["ن","ث","ع","خ","ج","س","ح"],dayOfWeek:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت","الأحد"]},ro:{months:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],dayOfWeekShort:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],dayOfWeek:["Duminică","Luni","Marţi","Miercuri","Joi","Vineri","Sâmbătă"]},id:{months:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],dayOfWeekShort:["Min","Sen","Sel","Rab","Kam","Jum","Sab"],dayOfWeek:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"]},is:{months:["Janúar","Febrúar","Mars","Apríl","Maí","Júní","Júlí","Ágúst","September","Október","Nóvember","Desember"],dayOfWeekShort:["Sun","Mán","Þrið","Mið","Fim","Fös","Lau"],dayOfWeek:["Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur"]},bg:{months:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],dayOfWeekShort:["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],dayOfWeek:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"]},fa:{months:["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"],dayOfWeekShort:["یکشنبه","دوشنبه","سه شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],dayOfWeek:["یک‌شنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنج‌شنبه","جمعه","شنبه","یک‌شنبه"]},ru:{months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],dayOfWeekShort:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],dayOfWeek:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"]},uk:{months:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","Вересень","Жовтень","Листопад","Грудень"],dayOfWeekShort:["Ндл","Пнд","Втр","Срд","Чтв","Птн","Сбт"],dayOfWeek:["Неділя","Понеділок","Вівторок","Середа","Четвер","П'ятниця","Субота"]},en:{months:["January","February","March","April","May","June","July","August","September","October","November","December"],dayOfWeekShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayOfWeek:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},el:{months:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],dayOfWeekShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],dayOfWeek:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"]},de:{months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],dayOfWeekShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayOfWeek:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"]},nl:{months:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],dayOfWeekShort:["zo","ma","di","wo","do","vr","za"],dayOfWeek:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"]},tr:{months:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],dayOfWeekShort:["Paz","Pts","Sal","Çar","Per","Cum","Cts"],dayOfWeek:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"]},fr:{months:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],dayOfWeekShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],dayOfWeek:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"]},es:{months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],dayOfWeekShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"],dayOfWeek:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"]},th:{months:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],dayOfWeekShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayOfWeek:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัส","ศุกร์","เสาร์","อาทิตย์"]},pl:{months:["styczeń","luty","marzec","kwiecień","maj","czerwiec","lipiec","sierpień","wrzesień","październik","listopad","grudzień"],dayOfWeekShort:["nd","pn","wt","śr","cz","pt","sb"],dayOfWeek:["niedziela","poniedziałek","wtorek","środa","czwartek","piątek","sobota"]},pt:{months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],dayOfWeekShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sab"],dayOfWeek:["Domingo","Segunda","Terça","Quarta","Quinta","Sexta","Sábado"]},ch:{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeekShort:["日","一","二","三","四","五","六"]},se:{months:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],dayOfWeekShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"]},km:{months:["មករា​","កុម្ភៈ","មិនា​","មេសា​","ឧសភា​","មិថុនា​","កក្កដា​","សីហា​","កញ្ញា​","តុលា​","វិច្ឆិកា","ធ្នូ​"],dayOfWeekShort:["អាទិ​","ច័ន្ទ​","អង្គារ​","ពុធ​","ព្រហ​​","សុក្រ​","សៅរ៍"],dayOfWeek:["អាទិត្យ​","ច័ន្ទ​","អង្គារ​","ពុធ​","ព្រហស្បតិ៍​","សុក្រ​","សៅរ៍"]},kr:{months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayOfWeekShort:["일","월","화","수","목","금","토"],dayOfWeek:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"]},it:{months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],dayOfWeekShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayOfWeek:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"]},da:{months:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],dayOfWeekShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayOfWeek:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"]},no:{months:["Januar","Februar","Mars","April","Mai","Juni","Juli","August","September","Oktober","November","Desember"],dayOfWeekShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayOfWeek:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"]},ja:{months:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayOfWeekShort:["日","月","火","水","木","金","土"],dayOfWeek:["日曜","月曜","火曜","水曜","木曜","金曜","土曜"]},vi:{months:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],dayOfWeekShort:["CN","T2","T3","T4","T5","T6","T7"],dayOfWeek:["Chủ nhật","Thứ hai","Thứ ba","Thứ tư","Thứ năm","Thứ sáu","Thứ bảy"]},sl:{months:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],dayOfWeekShort:["Ned","Pon","Tor","Sre","Čet","Pet","Sob"],dayOfWeek:["Nedelja","Ponedeljek","Torek","Sreda","Četrtek","Petek","Sobota"]},cs:{months:["Leden","Únor","Březen","Duben","Květen","Červen","Červenec","Srpen","Září","Říjen","Listopad","Prosinec"],dayOfWeekShort:["Ne","Po","Út","St","Čt","Pá","So"]},hu:{months:["Január","Február","Március","Április","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],dayOfWeekShort:["Va","Hé","Ke","Sze","Cs","Pé","Szo"],dayOfWeek:["vasárnap","hétfő","kedd","szerda","csütörtök","péntek","szombat"]},az:{months:["Yanvar","Fevral","Mart","Aprel","May","Iyun","Iyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],dayOfWeekShort:["B","Be","Ça","Ç","Ca","C","Ş"],dayOfWeek:["Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə"]},bs:{months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],dayOfWeekShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],dayOfWeek:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"]},ca:{months:["Gener","Febrer","Març","Abril","Maig","Juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"],dayOfWeekShort:["Dg","Dl","Dt","Dc","Dj","Dv","Ds"],dayOfWeek:["Diumenge","Dilluns","Dimarts","Dimecres","Dijous","Divendres","Dissabte"]},"en-GB":{months:["January","February","March","April","May","June","July","August","September","October","November","December"],dayOfWeekShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayOfWeek:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},et:{months:["Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni","Juuli","August","September","Oktoober","November","Detsember"],dayOfWeekShort:["P","E","T","K","N","R","L"],dayOfWeek:["Pühapäev","Esmaspäev","Teisipäev","Kolmapäev","Neljapäev","Reede","Laupäev"]},eu:{months:["Urtarrila","Otsaila","Martxoa","Apirila","Maiatza","Ekaina","Uztaila","Abuztua","Iraila","Urria","Azaroa","Abendua"],dayOfWeekShort:["Ig.","Al.","Ar.","Az.","Og.","Or.","La."],dayOfWeek:["Igandea","Astelehena","Asteartea","Asteazkena","Osteguna","Ostirala","Larunbata"]},fi:{months:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],dayOfWeekShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayOfWeek:["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"]},gl:{months:["Xan","Feb","Maz","Abr","Mai","Xun","Xul","Ago","Set","Out","Nov","Dec"],dayOfWeekShort:["Dom","Lun","Mar","Mer","Xov","Ven","Sab"],dayOfWeek:["Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado"]},hr:{months:["Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"],dayOfWeekShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],dayOfWeek:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"]},ko:{months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayOfWeekShort:["일","월","화","수","목","금","토"],dayOfWeek:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"]},lt:{months:["Sausio","Vasario","Kovo","Balandžio","Gegužės","Birželio","Liepos","Rugpjūčio","Rugsėjo","Spalio","Lapkričio","Gruodžio"],dayOfWeekShort:["Sek","Pir","Ant","Tre","Ket","Pen","Šeš"],dayOfWeek:["Sekmadienis","Pirmadienis","Antradienis","Trečiadienis","Ketvirtadienis","Penktadienis","Šeštadienis"]},lv:{months:["Janvāris","Februāris","Marts","Aprīlis ","Maijs","Jūnijs","Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],dayOfWeekShort:["Sv","Pr","Ot","Tr","Ct","Pk","St"],dayOfWeek:["Svētdiena","Pirmdiena","Otrdiena","Trešdiena","Ceturtdiena","Piektdiena","Sestdiena"]},mk:{months:["јануари","февруари","март","април","мај","јуни","јули","август","септември","октомври","ноември","декември"],dayOfWeekShort:["нед","пон","вто","сре","чет","пет","саб"],dayOfWeek:["Недела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота"]},mn:{months:["1-р сар","2-р сар","3-р сар","4-р сар","5-р сар","6-р сар","7-р сар","8-р сар","9-р сар","10-р сар","11-р сар","12-р сар"],dayOfWeekShort:["Дав","Мяг","Лха","Пүр","Бсн","Бям","Ням"],dayOfWeek:["Даваа","Мягмар","Лхагва","Пүрэв","Баасан","Бямба","Ням"]},"pt-BR":{months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],dayOfWeekShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayOfWeek:["Domingo","Segunda","Terça","Quarta","Quinta","Sexta","Sábado"]},sk:{months:["Január","Február","Marec","Apríl","Máj","Jún","Júl","August","September","Október","November","December"],dayOfWeekShort:["Ne","Po","Ut","St","Št","Pi","So"],dayOfWeek:["Nedeľa","Pondelok","Utorok","Streda","Štvrtok","Piatok","Sobota"]},sq:{months:["Janar","Shkurt","Mars","Prill","Maj","Qershor","Korrik","Gusht","Shtator","Tetor","Nëntor","Dhjetor"],dayOfWeekShort:["Die","Hën","Mar","Mër","Enj","Pre","Shtu"],dayOfWeek:["E Diel","E Hënë","E Martē","E Mërkurë","E Enjte","E Premte","E Shtunë"]},"sr-YU":{months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],dayOfWeekShort:["Ned","Pon","Uto","Sre","čet","Pet","Sub"],dayOfWeek:["Nedelja","Ponedeljak","Utorak","Sreda","Četvrtak","Petak","Subota"]},sr:{months:["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],dayOfWeekShort:["нед","пон","уто","сре","чет","пет","суб"],dayOfWeek:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"]},sv:{months:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],dayOfWeekShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"],dayOfWeek:["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"]},"zh-TW":{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeekShort:["日","一","二","三","四","五","六"],dayOfWeek:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]},zh:{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeekShort:["日","一","二","三","四","五","六"],dayOfWeek:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]},ug:{months:["1-ئاي","2-ئاي","3-ئاي","4-ئاي","5-ئاي","6-ئاي","7-ئاي","8-ئاي","9-ئاي","10-ئاي","11-ئاي","12-ئاي"],dayOfWeek:["يەكشەنبە","دۈشەنبە","سەيشەنبە","چارشەنبە","پەيشەنبە","جۈمە","شەنبە"]},he:{months:["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],dayOfWeekShort:["א'","ב'","ג'","ד'","ה'","ו'","שבת"],dayOfWeek:["ראשון","שני","שלישי","רביעי","חמישי","שישי","שבת","ראשון"]},hy:{months:["Հունվար","Փետրվար","Մարտ","Ապրիլ","Մայիս","Հունիս","Հուլիս","Օգոստոս","Սեպտեմբեր","Հոկտեմբեր","Նոյեմբեր","Դեկտեմբեր"],dayOfWeekShort:["Կի","Երկ","Երք","Չոր","Հնգ","Ուրբ","Շբթ"],dayOfWeek:["Կիրակի","Երկուշաբթի","Երեքշաբթի","Չորեքշաբթի","Հինգշաբթի","Ուրբաթ","Շաբաթ"]},kg:{months:["Үчтүн айы","Бирдин айы","Жалган Куран","Чын Куран","Бугу","Кулжа","Теке","Баш Оона","Аяк Оона","Тогуздун айы","Жетинин айы","Бештин айы"],dayOfWeekShort:["Жек","Дүй","Шей","Шар","Бей","Жум","Ише"],dayOfWeek:["Жекшемб","Дүйшөмб","Шейшемб","Шаршемб","Бейшемби","Жума","Ишенб"]},rm:{months:["Schaner","Favrer","Mars","Avrigl","Matg","Zercladur","Fanadur","Avust","Settember","October","November","December"],dayOfWeekShort:["Du","Gli","Ma","Me","Gie","Ve","So"],dayOfWeek:["Dumengia","Glindesdi","Mardi","Mesemna","Gievgia","Venderdi","Sonda"]},ka:{months:["იანვარი","თებერვალი","მარტი","აპრილი","მაისი","ივნისი","ივლისი","აგვისტო","სექტემბერი","ოქტომბერი","ნოემბერი","დეკემბერი"],dayOfWeekShort:["კვ","ორშ","სამშ","ოთხ","ხუთ","პარ","შაბ"],dayOfWeek:["კვირა","ორშაბათი","სამშაბათი","ოთხშაბათი","ხუთშაბათი","პარასკევი","შაბათი"]}},ownerDocument:document,contentWindow:window,value:"",rtl:!1,format:"Y/m/d H:i",formatTime:"H:i",formatDate:"Y/m/d",startDate:!1,step:60,monthChangeSpinner:!0,closeOnDateSelect:!1,closeOnTimeSelect:!0,closeOnWithoutClick:!0,closeOnInputClick:!0,openOnFocus:!0,timepicker:!0,datepicker:!0,weeks:!1,defaultTime:!1,defaultDate:!1,minDate:!1,maxDate:!1,minTime:!1,maxTime:!1,minDateTime:!1,maxDateTime:!1,allowTimes:[],opened:!1,initTime:!0,inline:!1,theme:"",touchMovedThreshold:5,onSelectDate:function(){},onSelectTime:function(){},onChangeMonth:function(){},onGetWeekOfYear:function(){},onChangeYear:function(){},onChangeDateTime:function(){},onShow:function(){},onClose:function(){},onGenerate:function(){},withoutCopyright:!0,inverseButton:!1,hours12:!1,next:"xdsoft_next",prev:"xdsoft_prev",dayOfWeekStart:0,parentID:"body",timeHeightInTimePicker:25,timepickerScrollbar:!0,todayButton:!0,prevButton:!0,nextButton:!0,defaultSelect:!0,scrollMonth:!0,scrollTime:!0,scrollInput:!0,lazyInit:!1,mask:!1,validateOnBlur:!0,allowBlank:!0,yearStart:1950,yearEnd:2050,monthStart:0,monthEnd:11,style:"",id:"",fixed:!1,roundTime:"round",className:"",weekends:[],highlightedDates:[],highlightedPeriods:[],allowDates:[],allowDateRe:null,disabledDates:[],disabledWeekDays:[],yearOffset:0,beforeShowDay:null,enterLikeTab:!0,showApplyButton:!1},r=null,n=null,o="en",i={meridiem:["AM","PM"]},s=function(){var t=a.i18n[o],s={days:t.dayOfWeek,daysShort:t.dayOfWeekShort,months:t.months,monthsShort:e.map(t.months,function(e){return e.substring(0,3)})};"function"==typeof DateFormatter&&(r=n=new DateFormatter({dateSettings:e.extend({},i,s)}))},u={moment:{default_options:{format:"YYYY/MM/DD HH:mm",formatDate:"YYYY/MM/DD",formatTime:"HH:mm"},formatter:{parseDate:function(e,t){if(l(t))return n.parseDate(e,t);var a=moment(e,t);return!!a.isValid()&&a.toDate()},formatDate:function(e,t){return l(t)?n.formatDate(e,t):moment(e).format(t)},formatMask:function(e){return e.replace(/Y{4}/g,"9999").replace(/Y{2}/g,"99").replace(/M{2}/g,"19").replace(/D{2}/g,"39").replace(/H{2}/g,"29").replace(/m{2}/g,"59").replace(/s{2}/g,"59")}}}};e.datetimepicker={setLocale:function(e){var t=a.i18n[e]?e:"en";o!==t&&(o=t,s())},setDateFormatter:function(t){if("string"==typeof t&&u.hasOwnProperty(t)){var n=u[t];e.extend(a,n.default_options),r=n.formatter}else r=t}};var d={RFC_2822:"D, d M Y H:i:s O",ATOM:"Y-m-dTH:i:sP",ISO_8601:"Y-m-dTH:i:sO",RFC_822:"D, d M y H:i:s O",RFC_850:"l, d-M-y H:i:s T",RFC_1036:"D, d M y H:i:s O",RFC_1123:"D, d M Y H:i:s O",RSS:"D, d M Y H:i:s O",W3C:"Y-m-dTH:i:sP"},l=function(e){return-1!==Object.values(d).indexOf(e)};e.extend(e.datetimepicker,d),s(),window.getComputedStyle||(window.getComputedStyle=function(e){return this.el=e,this.getPropertyValue=function(t){var a=/(-([a-z]))/g;return"float"===t&&(t="styleFloat"),a.test(t)&&(t=t.replace(a,function(e,t,a){return a.toUpperCase()})),e.currentStyle[t]||null},this}),Array.prototype.indexOf||(Array.prototype.indexOf=function(e,t){var a,r;for(a=t||0,r=this.length;a
'),s=e('
'),i.append(s),u.addClass("xdsoft_scroller_box").append(i),D=function(e){var t=d(e).y-c+p;t<0&&(t=0),t+s[0].offsetHeight>h&&(t=h-s[0].offsetHeight),u.trigger("scroll_element.xdsoft_scroller",[l?t/l:0])},s.on("touchstart.xdsoft_scroller mousedown.xdsoft_scroller",function(r){n||u.trigger("resize_scroll.xdsoft_scroller",[a]),c=d(r).y,p=parseInt(s.css("margin-top"),10),h=i[0].offsetHeight,"mousedown"===r.type||"touchstart"===r.type?(t.ownerDocument&&e(t.ownerDocument.body).addClass("xdsoft_noselect"),e([t.ownerDocument.body,t.contentWindow]).on("touchend mouseup.xdsoft_scroller",function a(){e([t.ownerDocument.body,t.contentWindow]).off("touchend mouseup.xdsoft_scroller",a).off("mousemove.xdsoft_scroller",D).removeClass("xdsoft_noselect")}),e(t.ownerDocument.body).on("mousemove.xdsoft_scroller",D)):(g=!0,r.stopPropagation(),r.preventDefault())}).on("touchmove",function(e){g&&(e.preventDefault(),D(e))}).on("touchend touchcancel",function(){g=!1,p=0}),u.on("scroll_element.xdsoft_scroller",function(e,t){n||u.trigger("resize_scroll.xdsoft_scroller",[t,!0]),t=t>1?1:t<0||isNaN(t)?0:t,s.css("margin-top",l*t),setTimeout(function(){r.css("marginTop",-parseInt((r[0].offsetHeight-n)*t,10))},10)}).on("resize_scroll.xdsoft_scroller",function(e,t,a){var d,f;n=u[0].clientHeight,o=r[0].offsetHeight,f=(d=n/o)*i[0].offsetHeight,d>1?s.hide():(s.show(),s.css("height",parseInt(f>10?f:10,10)),l=i[0].offsetHeight-s[0].offsetHeight,!0!==a&&u.trigger("scroll_element.xdsoft_scroller",[t||Math.abs(parseInt(r.css("marginTop"),10))/(o-n)]))}),u.on("mousewheel",function(e){var t=Math.abs(parseInt(r.css("marginTop"),10));return(t-=20*e.deltaY)<0&&(t=0),u.trigger("scroll_element.xdsoft_scroller",[t/(o-n)]),e.stopPropagation(),!1}),u.on("touchstart",function(e){f=d(e),m=Math.abs(parseInt(r.css("marginTop"),10))}),u.on("touchmove",function(e){if(f){e.preventDefault();var t=d(e);u.trigger("scroll_element.xdsoft_scroller",[(m-(t.y-f.y))/(o-n)])}}),u.on("touchend touchcancel",function(){f=!1,m=0})),u.trigger("resize_scroll.xdsoft_scroller",[a])):u.find(".xdsoft_scrollbar").hide()})},e.fn.datetimepicker=function(n,i){var s,u,d=this,l=48,f=57,c=96,m=105,h=17,g=46,p=13,D=27,v=8,y=37,b=38,k=39,x=40,T=9,S=116,M=65,w=67,O=86,W=90,_=89,F=!1,C=e.isPlainObject(n)||!n?e.extend(!0,{},a,n):e.extend(!0,{},a),P=0,Y=function(e){e.on("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",function t(){e.is(":disabled")||e.data("xdsoft_datetimepicker")||(clearTimeout(P),P=setTimeout(function(){e.data("xdsoft_datetimepicker")||s(e),e.off("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",t).trigger("open.xdsoft")},100))})};return s=function(a){function i(){var e,t=!1;return C.startDate?t=A.strToDate(C.startDate):(t=C.value||(a&&a.val&&a.val()?a.val():""))?(t=A.strToDateTime(t),C.yearOffset&&(t=new Date(t.getFullYear()-C.yearOffset,t.getMonth(),t.getDate(),t.getHours(),t.getMinutes(),t.getSeconds(),t.getMilliseconds()))):C.defaultDate&&(t=A.strToDateTime(C.defaultDate),C.defaultTime&&(e=A.strtotime(C.defaultTime),t.setHours(e.getHours()),t.setMinutes(e.getMinutes()))),t&&A.isValidDate(t)?j.data("changed",!0):t="",t||0}function s(t){var n=function(e,t){var a=e.replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g,"\\$1").replace(/_/g,"{digit+}").replace(/([0-9]{1})/g,"{digit$1}").replace(/\{digit([0-9]{1})\}/g,"[0-$1_]{1}").replace(/\{digit[\+]\}/g,"[0-9_]{1}");return new RegExp(a).test(t)},o=function(e,a){if(!(e="string"==typeof e||e instanceof String?t.ownerDocument.getElementById(e):e))return!1;if(e.createTextRange){var r=e.createTextRange();return r.collapse(!0),r.moveEnd("character",a),r.moveStart("character",a),r.select(),!0}return!!e.setSelectionRange&&(e.setSelectionRange(a,a),!0)};t.mask&&a.off("keydown.xdsoft"),!0===t.mask&&(r.formatMask?t.mask=r.formatMask(t.format):t.mask=t.format.replace(/Y/g,"9999").replace(/F/g,"9999").replace(/m/g,"19").replace(/d/g,"39").replace(/H/g,"29").replace(/i/g,"59").replace(/s/g,"59")),"string"===e.type(t.mask)&&(n(t.mask,a.val())||(a.val(t.mask.replace(/[0-9]/g,"_")),o(a[0],0)),a.on("paste.xdsoft",function(r){var i=(r.clipboardData||r.originalEvent.clipboardData||window.clipboardData).getData("text"),s=this.value,u=this.selectionStart;return s=s.substr(0,u)+i+s.substr(u+i.length),u+=i.length,n(t.mask,s)?(this.value=s,o(this,u)):""===e.trim(s)?this.value=t.mask.replace(/[0-9]/g,"_"):a.trigger("error_input.xdsoft"),r.preventDefault(),!1}),a.on("keydown.xdsoft",function(r){var i,s=this.value,u=r.which,d=this.selectionStart,C=this.selectionEnd,P=d!==C;if(u>=l&&u<=f||u>=c&&u<=m||u===v||u===g){for(i=u===v||u===g?"_":String.fromCharCode(c<=u&&u<=m?u-l:u),u===v&&d&&!P&&(d-=1);;){var Y=t.mask.substr(d,1),A=d0;if(!(/[^0-9_]/.test(Y)&&A&&H))break;d+=u!==v||P?1:-1}if(P){var j=C-d,J=t.mask.replace(/[0-9]/g,"_"),z=J.substr(d,j).substr(1);s=s.substr(0,d)+(i+z)+s.substr(d+j)}else s=s.substr(0,d)+i+s.substr(d+1);if(""===e.trim(s))s=J;else if(d===t.mask.length)return r.preventDefault(),!1;for(d+=u===v?0:1;/[^0-9_]/.test(t.mask.substr(d,1))&&d0;)d+=u===v?0:1;n(t.mask,s)?(this.value=s,o(this,d)):""===e.trim(s)?this.value=t.mask.replace(/[0-9]/g,"_"):a.trigger("error_input.xdsoft")}else if(-1!==[M,w,O,W,_].indexOf(u)&&F||-1!==[D,b,x,y,k,S,h,T,p].indexOf(u))return!0;return r.preventDefault(),!1}))}var u,d,P,Y,A,H,j=e('
'),J=e(''),z=e('
'),I=e('
'),N=e('
'),L=e('
'),E=L.find(".xdsoft_time_box").eq(0),R=e('
'),V=e(''),B=e('
'),G=e('
'),U=!1,q=0;C.id&&j.attr("id",C.id),C.style&&j.attr("style",C.style),C.weeks&&j.addClass("xdsoft_showweeks"),C.rtl&&j.addClass("xdsoft_rtl"),j.addClass("xdsoft_"+C.theme),j.addClass(C.className),I.find(".xdsoft_month span").after(B),I.find(".xdsoft_year span").after(G),I.find(".xdsoft_month,.xdsoft_year").on("touchstart mousedown.xdsoft",function(t){var a,r,n=e(this).find(".xdsoft_select").eq(0),o=0,i=0,s=n.is(":visible");for(I.find(".xdsoft_select").hide(),A.currentTime&&(o=A.currentTime[e(this).hasClass("xdsoft_month")?"getMonth":"getFullYear"]()),n[s?"hide":"show"](),a=n.find("div.xdsoft_option"),r=0;rC.touchMovedThreshold&&(this.touchMoved=!0)};I.find(".xdsoft_select").xdsoftScroller(C).on("touchstart mousedown.xdsoft",function(e){var t=e.originalEvent;this.touchMoved=!1,this.touchStartPosition=t.touches?t.touches[0]:t,e.stopPropagation(),e.preventDefault()}).on("touchmove",".xdsoft_option",X).on("touchend mousedown.xdsoft",".xdsoft_option",function(){if(!this.touchMoved){void 0!==A.currentTime&&null!==A.currentTime||(A.currentTime=A.now());var t=A.currentTime.getFullYear();A&&A.currentTime&&A.currentTime[e(this).parent().parent().hasClass("xdsoft_monthselect")?"setMonth":"setFullYear"](e(this).data("value")),e(this).parent().parent().hide(),j.trigger("xchange.xdsoft"),C.onChangeMonth&&e.isFunction(C.onChangeMonth)&&C.onChangeMonth.call(j,A.currentTime,j.data("input")),t!==A.currentTime.getFullYear()&&e.isFunction(C.onChangeYear)&&C.onChangeYear.call(j,A.currentTime,j.data("input"))}}),j.getValue=function(){return A.getCurrentTime()},j.setOptions=function(n){var o={};C=e.extend(!0,{},C,n),n.allowTimes&&e.isArray(n.allowTimes)&&n.allowTimes.length&&(C.allowTimes=e.extend(!0,[],n.allowTimes)),n.weekends&&e.isArray(n.weekends)&&n.weekends.length&&(C.weekends=e.extend(!0,[],n.weekends)),n.allowDates&&e.isArray(n.allowDates)&&n.allowDates.length&&(C.allowDates=e.extend(!0,[],n.allowDates)),n.allowDateRe&&"[object String]"===Object.prototype.toString.call(n.allowDateRe)&&(C.allowDateRe=new RegExp(n.allowDateRe)),n.highlightedDates&&e.isArray(n.highlightedDates)&&n.highlightedDates.length&&(e.each(n.highlightedDates,function(a,n){var i,s=e.map(n.split(","),e.trim),u=new t(r.parseDate(s[0],C.formatDate),s[1],s[2]),d=r.formatDate(u.date,C.formatDate);void 0!==o[d]?(i=o[d].desc)&&i.length&&u.desc&&u.desc.length&&(o[d].desc=i+"\n"+u.desc):o[d]=u}),C.highlightedDates=e.extend(!0,[],o)),n.highlightedPeriods&&e.isArray(n.highlightedPeriods)&&n.highlightedPeriods.length&&(o=e.extend(!0,[],C.highlightedDates),e.each(n.highlightedPeriods,function(a,n){var i,s,u,d,l,f,c;if(e.isArray(n))i=n[0],s=n[1],u=n[2],c=n[3];else{var m=e.map(n.split(","),e.trim);i=r.parseDate(m[0],C.formatDate),s=r.parseDate(m[1],C.formatDate),u=m[2],c=m[3]}for(;i<=s;)d=new t(i,u,c),l=r.formatDate(i,C.formatDate),i.setDate(i.getDate()+1),void 0!==o[l]?(f=o[l].desc)&&f.length&&d.desc&&d.desc.length&&(o[l].desc=f+"\n"+d.desc):o[l]=d}),C.highlightedDates=e.extend(!0,[],o)),n.disabledDates&&e.isArray(n.disabledDates)&&n.disabledDates.length&&(C.disabledDates=e.extend(!0,[],n.disabledDates)),n.disabledWeekDays&&e.isArray(n.disabledWeekDays)&&n.disabledWeekDays.length&&(C.disabledWeekDays=e.extend(!0,[],n.disabledWeekDays)),!C.open&&!C.opened||C.inline||a.trigger("open.xdsoft"),C.inline&&(U=!0,j.addClass("xdsoft_inline"),a.after(j).hide()),C.inverseButton&&(C.next="xdsoft_prev",C.prev="xdsoft_next"),C.datepicker?z.addClass("active"):z.removeClass("active"),C.timepicker?L.addClass("active"):L.removeClass("active"),C.value&&(A.setCurrentTime(C.value),a&&a.val&&a.val(A.str)),isNaN(C.dayOfWeekStart)?C.dayOfWeekStart=0:C.dayOfWeekStart=parseInt(C.dayOfWeekStart,10)%7,C.timepickerScrollbar||E.xdsoftScroller(C,"hide"),C.minDate&&/^[\+\-](.*)$/.test(C.minDate)&&(C.minDate=r.formatDate(A.strToDateTime(C.minDate),C.formatDate)),C.maxDate&&/^[\+\-](.*)$/.test(C.maxDate)&&(C.maxDate=r.formatDate(A.strToDateTime(C.maxDate),C.formatDate)),C.minDateTime&&/^\+(.*)$/.test(C.minDateTime)&&(C.minDateTime=A.strToDateTime(C.minDateTime).dateFormat(C.formatDate)),C.maxDateTime&&/^\+(.*)$/.test(C.maxDateTime)&&(C.maxDateTime=A.strToDateTime(C.maxDateTime).dateFormat(C.formatDate)),V.toggle(C.showApplyButton),I.find(".xdsoft_today_button").css("visibility",C.todayButton?"visible":"hidden"),I.find("."+C.prev).css("visibility",C.prevButton?"visible":"hidden"),I.find("."+C.next).css("visibility",C.nextButton?"visible":"hidden"),s(C),C.validateOnBlur&&a.off("blur.xdsoft").on("blur.xdsoft",function(){if(C.allowBlank&&(!e.trim(e(this).val()).length||"string"==typeof C.mask&&e.trim(e(this).val())===C.mask.replace(/[0-9]/g,"_")))e(this).val(null),j.data("xdsoft_datetime").empty();else{var t=r.parseDate(e(this).val(),C.format);if(t)e(this).val(r.formatDate(t,C.format));else{var a=+[e(this).val()[0],e(this).val()[1]].join(""),n=+[e(this).val()[2],e(this).val()[3]].join("");!C.datepicker&&C.timepicker&&a>=0&&a<24&&n>=0&&n<60?e(this).val([a,n].map(function(e){return e>9?e:"0"+e}).join(":")):e(this).val(r.formatDate(A.now(),C.format))}j.data("xdsoft_datetime").setCurrentTime(e(this).val())}j.trigger("changedatetime.xdsoft"),j.trigger("close.xdsoft")}),C.dayOfWeekStartPrev=0===C.dayOfWeekStart?6:C.dayOfWeekStart-1,j.trigger("xchange.xdsoft").trigger("afterOpen.xdsoft")},j.data("options",C).on("touchstart mousedown.xdsoft",function(e){return e.stopPropagation(),e.preventDefault(),G.hide(),B.hide(),!1}),E.append(R),E.xdsoftScroller(C),j.on("afterOpen.xdsoft",function(){E.xdsoftScroller(C)}),j.append(z).append(L),!0!==C.withoutCopyright&&j.append(J),z.append(I).append(N).append(V),e(C.parentID).append(j),A=new function(){var t=this;t.now=function(e){var a,r,n=new Date;return!e&&C.defaultDate&&(a=t.strToDateTime(C.defaultDate),n.setFullYear(a.getFullYear()),n.setMonth(a.getMonth()),n.setDate(a.getDate())),n.setFullYear(n.getFullYear()),!e&&C.defaultTime&&(r=t.strtotime(C.defaultTime),n.setHours(r.getHours()),n.setMinutes(r.getMinutes()),n.setSeconds(r.getSeconds()),n.setMilliseconds(r.getMilliseconds())),n},t.isValidDate=function(e){return"[object Date]"===Object.prototype.toString.call(e)&&!isNaN(e.getTime())},t.setCurrentTime=function(e,a){"string"==typeof e?t.currentTime=t.strToDateTime(e):t.isValidDate(e)?t.currentTime=e:e||a||!C.allowBlank||C.inline?t.currentTime=t.now():t.currentTime=null,j.trigger("xchange.xdsoft")},t.empty=function(){t.currentTime=null},t.getCurrentTime=function(){return t.currentTime},t.nextMonth=function(){void 0!==t.currentTime&&null!==t.currentTime||(t.currentTime=t.now());var a,r=t.currentTime.getMonth()+1;return 12===r&&(t.currentTime.setFullYear(t.currentTime.getFullYear()+1),r=0),a=t.currentTime.getFullYear(),t.currentTime.setDate(Math.min(new Date(t.currentTime.getFullYear(),r+1,0).getDate(),t.currentTime.getDate())),t.currentTime.setMonth(r),C.onChangeMonth&&e.isFunction(C.onChangeMonth)&&C.onChangeMonth.call(j,A.currentTime,j.data("input")),a!==t.currentTime.getFullYear()&&e.isFunction(C.onChangeYear)&&C.onChangeYear.call(j,A.currentTime,j.data("input")),j.trigger("xchange.xdsoft"),r},t.prevMonth=function(){void 0!==t.currentTime&&null!==t.currentTime||(t.currentTime=t.now());var a=t.currentTime.getMonth()-1;return-1===a&&(t.currentTime.setFullYear(t.currentTime.getFullYear()-1),a=11),t.currentTime.setDate(Math.min(new Date(t.currentTime.getFullYear(),a+1,0).getDate(),t.currentTime.getDate())),t.currentTime.setMonth(a),C.onChangeMonth&&e.isFunction(C.onChangeMonth)&&C.onChangeMonth.call(j,A.currentTime,j.data("input")),j.trigger("xchange.xdsoft"),a},t.getWeekOfYear=function(t){if(C.onGetWeekOfYear&&e.isFunction(C.onGetWeekOfYear)){var a=C.onGetWeekOfYear.call(j,t);if(void 0!==a)return a}var r=new Date(t.getFullYear(),0,1);return 4!==r.getDay()&&r.setMonth(0,1+(4-r.getDay()+7)%7),Math.ceil(((t-r)/864e5+r.getDay()+1)/7)},t.strToDateTime=function(e){var a,n,o=[];return e&&e instanceof Date&&t.isValidDate(e)?e:((o=/^([+-]{1})(.*)$/.exec(e))&&(o[2]=r.parseDate(o[2],C.formatDate)),o&&o[2]?(a=o[2].getTime()-6e4*o[2].getTimezoneOffset(),n=new Date(t.now(!0).getTime()+parseInt(o[1]+"1",10)*a)):n=e?r.parseDate(e,C.format):t.now(),t.isValidDate(n)||(n=t.now()),n)},t.strToDate=function(e){if(e&&e instanceof Date&&t.isValidDate(e))return e;var a=e?r.parseDate(e,C.formatDate):t.now(!0);return t.isValidDate(a)||(a=t.now(!0)),a},t.strtotime=function(e){if(e&&e instanceof Date&&t.isValidDate(e))return e;var a=e?r.parseDate(e,C.formatTime):t.now(!0);return t.isValidDate(a)||(a=t.now(!0)),a},t.str=function(){var e=C.format;return C.yearOffset&&(e=(e=e.replace("Y",t.currentTime.getFullYear()+C.yearOffset)).replace("y",String(t.currentTime.getFullYear()+C.yearOffset).substring(2,4))),r.formatDate(t.currentTime,e)},t.currentTime=this.now()},V.on("touchend click",function(e){e.preventDefault(),j.data("changed",!0),A.setCurrentTime(i()),a.val(A.str()),j.trigger("close.xdsoft")}),I.find(".xdsoft_today_button").on("touchend mousedown.xdsoft",function(){j.data("changed",!0),A.setCurrentTime(0,!0),j.trigger("afterOpen.xdsoft")}).on("dblclick.xdsoft",function(){var e,t,r=A.getCurrentTime();r=new Date(r.getFullYear(),r.getMonth(),r.getDate()),e=A.strToDate(C.minDate),r<(e=new Date(e.getFullYear(),e.getMonth(),e.getDate()))||(t=A.strToDate(C.maxDate),r>(t=new Date(t.getFullYear(),t.getMonth(),t.getDate()))||(a.val(A.str()),a.trigger("change"),j.trigger("close.xdsoft")))}),I.find(".xdsoft_prev,.xdsoft_next").on("touchend mousedown.xdsoft",function(){var t=e(this),a=0,r=!1;!function e(n){t.hasClass(C.next)?A.nextMonth():t.hasClass(C.prev)&&A.prevMonth(),C.monthChangeSpinner&&(r||(a=setTimeout(e,n||100)))}(500),e([C.ownerDocument.body,C.contentWindow]).on("touchend mouseup.xdsoft",function t(){clearTimeout(a),r=!0,e([C.ownerDocument.body,C.contentWindow]).off("touchend mouseup.xdsoft",t)})}),L.find(".xdsoft_prev,.xdsoft_next").on("touchend mousedown.xdsoft",function(){var t=e(this),a=0,r=!1,n=110;!function e(o){var i=E[0].clientHeight,s=R[0].offsetHeight,u=Math.abs(parseInt(R.css("marginTop"),10));t.hasClass(C.next)&&s-i-C.timeHeightInTimePicker>=u?R.css("marginTop","-"+(u+C.timeHeightInTimePicker)+"px"):t.hasClass(C.prev)&&u-C.timeHeightInTimePicker>=0&&R.css("marginTop","-"+(u-C.timeHeightInTimePicker)+"px"),E.trigger("scroll_element.xdsoft_scroller",[Math.abs(parseInt(R[0].style.marginTop,10)/(s-i))]),n=n>10?10:n-10,r||(a=setTimeout(e,o||n))}(500),e([C.ownerDocument.body,C.contentWindow]).on("touchend mouseup.xdsoft",function t(){clearTimeout(a),r=!0,e([C.ownerDocument.body,C.contentWindow]).off("touchend mouseup.xdsoft",t)})}),u=0,j.on("xchange.xdsoft",function(t){clearTimeout(u),u=setTimeout(function(){void 0!==A.currentTime&&null!==A.currentTime||(A.currentTime=A.now());for(var t,i,s,u,d,l,f,c,m,h,g="",p=new Date(A.currentTime.getFullYear(),A.currentTime.getMonth(),1,12,0,0),D=0,v=A.now(),y=!1,b=!1,k=!1,x=!1,T=[],S=!0,M="";p.getDay()!==C.dayOfWeekStart;)p.setDate(p.getDate()-1);for(g+="",C.weeks&&(g+=""),t=0;t<7;t+=1)g+="";g+="",g+="",!1!==C.maxDate&&(y=A.strToDate(C.maxDate),y=new Date(y.getFullYear(),y.getMonth(),y.getDate(),23,59,59,999)),!1!==C.minDate&&(b=A.strToDate(C.minDate),b=new Date(b.getFullYear(),b.getMonth(),b.getDate())),!1!==C.minDateTime&&(k=A.strToDate(C.minDateTime),k=new Date(k.getFullYear(),k.getMonth(),k.getDate(),k.getHours(),k.getMinutes(),k.getSeconds())),!1!==C.maxDateTime&&(x=A.strToDate(C.maxDateTime),x=new Date(x.getFullYear(),x.getMonth(),x.getDate(),x.getHours(),x.getMinutes(),x.getSeconds()));var w;for(!1!==x&&(w=31*(12*x.getFullYear()+x.getMonth())+x.getDate());D0&&-1===C.allowDates.indexOf(r.formatDate(p,C.formatDate))&&T.push("xdsoft_disabled");var O=31*(12*p.getFullYear()+p.getMonth())+p.getDate();(!1!==y&&p>y||!1!==k&&pw||c&&!1===c[0])&&T.push("xdsoft_disabled"),-1!==C.disabledDates.indexOf(r.formatDate(p,C.formatDate))&&T.push("xdsoft_disabled"),-1!==C.disabledWeekDays.indexOf(s)&&T.push("xdsoft_disabled"),a.is("[disabled]")&&T.push("xdsoft_disabled"),c&&""!==c[1]&&T.push(c[1]),A.currentTime.getMonth()!==l&&T.push("xdsoft_other_month"),(C.defaultSelect||j.data("changed"))&&r.formatDate(A.currentTime,C.formatDate)===r.formatDate(p,C.formatDate)&&T.push("xdsoft_current"),r.formatDate(v,C.formatDate)===r.formatDate(p,C.formatDate)&&T.push("xdsoft_today"),0!==p.getDay()&&6!==p.getDay()&&-1===C.weekends.indexOf(r.formatDate(p,C.formatDate))||T.push("xdsoft_weekend"),void 0!==C.highlightedDates[r.formatDate(p,C.formatDate)]&&(i=C.highlightedDates[r.formatDate(p,C.formatDate)],T.push(void 0===i.style?"xdsoft_highlighted_default":i.style),h=void 0===i.desc?"":i.desc),C.beforeShowDay&&e.isFunction(C.beforeShowDay)&&T.push(C.beforeShowDay(p)),S&&(g+="",S=!1,C.weeks&&(g+="")),g+='",p.getDay()===C.dayOfWeekStartPrev&&(g+="",S=!0),p.setDate(u+1)}g+="
"+C.i18n[o].dayOfWeekShort[(t+C.dayOfWeekStart)%7]+"
"+f+"
'+u+"
",N.html(g),I.find(".xdsoft_label span").eq(0).text(C.i18n[o].months[A.currentTime.getMonth()]),I.find(".xdsoft_label span").eq(1).text(A.currentTime.getFullYear()+C.yearOffset),M="",l="";var W=0;if(!1!==C.minTime){F=A.strtotime(C.minTime);W=60*F.getHours()+F.getMinutes()}var _=1440;if(!1!==C.maxTime){F=A.strtotime(C.maxTime);_=60*F.getHours()+F.getMinutes()}if(!1!==C.minDateTime){F=A.strToDateTime(C.minDateTime);r.formatDate(A.currentTime,C.formatDate)===r.formatDate(F,C.formatDate)&&(l=60*F.getHours()+F.getMinutes())>W&&(W=l)}if(!1!==C.maxDateTime){var F=A.strToDateTime(C.maxDateTime);r.formatDate(A.currentTime,C.formatDate)===r.formatDate(F,C.formatDate)&&(l=60*F.getHours()+F.getMinutes())<_&&(_=l)}if(m=function(t,n){var o,i=A.now(),s=C.allowTimes&&e.isArray(C.allowTimes)&&C.allowTimes.length;i.setHours(t),t=parseInt(i.getHours(),10),i.setMinutes(n),n=parseInt(i.getMinutes(),10),T=[];var u=60*t+n;(a.is("[disabled]")||u>=_||u59||o.getMinutes()===parseInt(n,10))&&(C.defaultSelect||j.data("changed")?T.push("xdsoft_current"):C.initTime&&T.push("xdsoft_init_time")),parseInt(v.getHours(),10)===parseInt(t,10)&&parseInt(v.getMinutes(),10)===parseInt(n,10)&&T.push("xdsoft_today"),M+='
'+r.formatDate(i,C.formatTime)+"
"},C.allowTimes&&e.isArray(C.allowTimes)&&C.allowTimes.length)for(D=0;D=_||m((D<10?"0":"")+D,l=(t<10?"0":"")+t))}for(R.html(M),n="",D=parseInt(C.yearStart,10);D<=parseInt(C.yearEnd,10);D+=1)n+='
'+(D+C.yearOffset)+"
";for(G.children().eq(0).html(n),D=parseInt(C.monthStart,10),n="";D<=parseInt(C.monthEnd,10);D+=1)n+='
'+C.i18n[o].months[D]+"
";B.children().eq(0).html(n),e(j).trigger("generate.xdsoft")},10),t.stopPropagation()}).on("afterOpen.xdsoft",function(){if(C.timepicker){var e,t,a,r;R.find(".xdsoft_current").length?e=".xdsoft_current":R.find(".xdsoft_init_time").length&&(e=".xdsoft_init_time"),e?(t=E[0].clientHeight,(a=R[0].offsetHeight)-t<(r=R.find(e).index()*C.timeHeightInTimePicker+1)&&(r=a-t),E.trigger("scroll_element.xdsoft_scroller",[parseInt(r,10)/(a-t)])):E.trigger("scroll_element.xdsoft_scroller",[0])}}),d=0,N.on("touchend click.xdsoft","td",function(t){t.stopPropagation(),d+=1;var r=e(this),n=A.currentTime;if(void 0!==n&&null!==n||(A.currentTime=A.now(),n=A.currentTime),r.hasClass("xdsoft_disabled"))return!1;n.setDate(1),n.setFullYear(r.data("year")),n.setMonth(r.data("month")),n.setDate(r.data("date")),j.trigger("select.xdsoft",[n]),a.val(A.str()),C.onSelectDate&&e.isFunction(C.onSelectDate)&&C.onSelectDate.call(j,A.currentTime,j.data("input"),t),j.data("changed",!0),j.trigger("xchange.xdsoft"),j.trigger("changedatetime.xdsoft"),(d>1||!0===C.closeOnDateSelect||!1===C.closeOnDateSelect&&!C.timepicker)&&!C.inline&&j.trigger("close.xdsoft"),setTimeout(function(){d=0},200)}),R.on("touchstart","div",function(e){this.touchMoved=!1}).on("touchmove","div",X).on("touchend click.xdsoft","div",function(t){if(!this.touchMoved){t.stopPropagation();var a=e(this),r=A.currentTime;if(void 0!==r&&null!==r||(A.currentTime=A.now(),r=A.currentTime),a.hasClass("xdsoft_disabled"))return!1;r.setHours(a.data("hour")),r.setMinutes(a.data("minute")),j.trigger("select.xdsoft",[r]),j.data("input").val(A.str()),C.onSelectTime&&e.isFunction(C.onSelectTime)&&C.onSelectTime.call(j,A.currentTime,j.data("input"),t),j.data("changed",!0),j.trigger("xchange.xdsoft"),j.trigger("changedatetime.xdsoft"),!0!==C.inline&&!0===C.closeOnTimeSelect&&j.trigger("close.xdsoft")}}),z.on("mousewheel.xdsoft",function(e){return!C.scrollMonth||(e.deltaY<0?A.nextMonth():A.prevMonth(),!1)}),a.on("mousewheel.xdsoft",function(e){return!C.scrollInput||(!C.datepicker&&C.timepicker?((P=R.find(".xdsoft_current").length?R.find(".xdsoft_current").eq(0).index():0)+e.deltaY>=0&&P+e.deltaYc+m?(l="bottom",r=c+m-t.top):r-=m):r+j[0].offsetHeight>c+m&&(r=t.top-j[0].offsetHeight+1),r<0&&(r=0),n+a.offsetWidth>d&&(n=d-a.offsetWidth)),i=j[0],H(i,function(e){if("relative"===C.contentWindow.getComputedStyle(e).getPropertyValue("position")&&d>=e.offsetWidth)return n-=(d-e.offsetWidth)/2,!1}),(f={position:o,left:n,top:"",bottom:""})[l]=r,j.css(f)},j.on("open.xdsoft",function(t){var a=!0;C.onShow&&e.isFunction(C.onShow)&&(a=C.onShow.call(j,A.currentTime,j.data("input"),t)),!1!==a&&(j.show(),Y(),e(C.contentWindow).off("resize.xdsoft",Y).on("resize.xdsoft",Y),C.closeOnWithoutClick&&e([C.ownerDocument.body,C.contentWindow]).on("touchstart mousedown.xdsoft",function t(){j.trigger("close.xdsoft"),e([C.ownerDocument.body,C.contentWindow]).off("touchstart mousedown.xdsoft",t)}))}).on("close.xdsoft",function(t){var a=!0;I.find(".xdsoft_month,.xdsoft_year").find(".xdsoft_select").hide(),C.onClose&&e.isFunction(C.onClose)&&(a=C.onClose.call(j,A.currentTime,j.data("input"),t)),!1===a||C.opened||C.inline||j.hide(),t.stopPropagation()}).on("toggle.xdsoft",function(){j.is(":visible")?j.trigger("close.xdsoft"):j.trigger("open.xdsoft")}).data("input",a),q=0,j.data("xdsoft_datetime",A),j.setOptions(C),A.setCurrentTime(i()),a.data("xdsoft_datetimepicker",j).on("open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart",function(){a.is(":disabled")||a.data("xdsoft_datetimepicker").is(":visible")&&C.closeOnInputClick||C.openOnFocus&&(clearTimeout(q),q=setTimeout(function(){a.is(":disabled")||(U=!0,A.setCurrentTime(i(),!0),C.mask&&s(C),j.trigger("open.xdsoft"))},100))}).on("keydown.xdsoft",function(t){var a,r=t.which;return-1!==[p].indexOf(r)&&C.enterLikeTab?(a=e("input:visible,textarea:visible,button:visible,a:visible"),j.trigger("close.xdsoft"),a.eq(a.index(this)+1).focus(),!1):-1!==[T].indexOf(r)?(j.trigger("close.xdsoft"),!0):void 0}).on("blur.xdsoft",function(){j.trigger("close.xdsoft")})},u=function(t){var a=t.data("xdsoft_datetimepicker");a&&(a.data("xdsoft_datetime",null),a.remove(),t.data("xdsoft_datetimepicker",null).off(".xdsoft"),e(C.contentWindow).off("resize.xdsoft"),e([C.contentWindow,C.ownerDocument.body]).off("mousedown.xdsoft touchstart"),t.unmousewheel&&t.unmousewheel())},e(C.ownerDocument).off("keydown.xdsoftctrl keyup.xdsoftctrl").on("keydown.xdsoftctrl",function(e){e.keyCode===h&&(F=!0)}).on("keyup.xdsoftctrl",function(e){e.keyCode===h&&(F=!1)}),this.each(function(){var t=e(this).data("xdsoft_datetimepicker");if(t){if("string"===e.type(n))switch(n){case"show":e(this).select().focus(),t.trigger("open.xdsoft");break;case"hide":t.trigger("close.xdsoft");break;case"toggle":t.trigger("toggle.xdsoft");break;case"destroy":u(e(this));break;case"reset":this.value=this.defaultValue,this.value&&t.data("xdsoft_datetime").isValidDate(r.parseDate(this.value,C.format))||t.data("changed",!1),t.data("xdsoft_datetime").setCurrentTime(this.value);break;case"validate":t.data("input").trigger("blur.xdsoft");break;default:t[n]&&e.isFunction(t[n])&&(d=t[n](i))}else t.setOptions(n);return 0}"string"!==e.type(n)&&(!C.lazyInit||C.open||C.inline?s(e(this)):Y(e(this)))}),d},e.fn.datetimepicker.defaults=a};!function(e){"function"==typeof define&&define.amd?define(["jquery","jquery-mousewheel"],e):"object"==typeof exports?module.exports=e(require("jquery")):e(jQuery)}(datetimepickerFactory),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof exports?module.exports=e:e(jQuery)}(function(e){function t(t){var i=t||window.event,s=u.call(arguments,1),d=0,f=0,c=0,m=0,h=0,g=0;if(t=e.event.fix(i),t.type="mousewheel","detail"in i&&(c=-1*i.detail),"wheelDelta"in i&&(c=i.wheelDelta),"wheelDeltaY"in i&&(c=i.wheelDeltaY),"wheelDeltaX"in i&&(f=-1*i.wheelDeltaX),"axis"in i&&i.axis===i.HORIZONTAL_AXIS&&(f=-1*c,c=0),d=0===c?f:c,"deltaY"in i&&(d=c=-1*i.deltaY),"deltaX"in i&&(f=i.deltaX,0===c&&(d=-1*f)),0!==c||0!==f){if(1===i.deltaMode){var p=e.data(this,"mousewheel-line-height");d*=p,c*=p,f*=p}else if(2===i.deltaMode){var D=e.data(this,"mousewheel-page-height");d*=D,c*=D,f*=D}if(m=Math.max(Math.abs(c),Math.abs(f)),(!o||m=1?"floor":"ceil"](d/o),f=Math[f>=1?"floor":"ceil"](f/o),c=Math[c>=1?"floor":"ceil"](c/o),l.settings.normalizeOffset&&this.getBoundingClientRect){var v=this.getBoundingClientRect();h=t.clientX-v.left,g=t.clientY-v.top}return t.deltaX=f,t.deltaY=c,t.deltaFactor=o,t.offsetX=h,t.offsetY=g,t.deltaMode=0,s.unshift(t,d,f,c),n&&clearTimeout(n),n=setTimeout(a,200),(e.event.dispatch||e.event.handle).apply(this,s)}}function a(){o=null}function r(e,t){return l.settings.adjustOldDeltas&&"mousewheel"===e.type&&t%120==0}var n,o,i=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],s="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],u=Array.prototype.slice;if(e.event.fixHooks)for(var d=i.length;d;)e.event.fixHooks[i[--d]]=e.event.mouseHooks;var l=e.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var a=s.length;a;)this.addEventListener(s[--a],t,!1);else this.onmousewheel=t;e.data(this,"mousewheel-line-height",l.getLineHeight(this)),e.data(this,"mousewheel-page-height",l.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var a=s.length;a;)this.removeEventListener(s[--a],t,!1);else this.onmousewheel=null;e.removeData(this,"mousewheel-line-height"),e.removeData(this,"mousewheel-page-height")},getLineHeight:function(t){var a=e(t),r=a["offsetParent"in e.fn?"offsetParent":"parent"]();return r.length||(r=e("body")),parseInt(r.css("fontSize"),10)||parseInt(a.css("fontSize"),10)||16},getPageHeight:function(t){return e(t).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};e.fn.extend({mousewheel:function(e){return e?this.bind("mousewheel",e):this.trigger("mousewheel")},unmousewheel:function(e){return this.unbind("mousewheel",e)}})}); \ No newline at end of file diff --git a/resources/datetime-picker/datetimepicker.min.css b/resources/datetime-picker/datetimepicker.min.css deleted file mode 100644 index 14a08a1..0000000 --- a/resources/datetime-picker/datetimepicker.min.css +++ /dev/null @@ -1 +0,0 @@ -.xdsoft_datetimepicker{box-shadow:0 5px 15px -5px rgba(0,0,0,0.506);background:#fff;border-bottom:1px solid #bbb;border-left:1px solid #ccc;border-right:1px solid #ccc;border-top:1px solid #ccc;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;padding:8px;padding-left:0;padding-top:2px;position:absolute;z-index:9999;-moz-box-sizing:border-box;box-sizing:border-box;display:none}.xdsoft_datetimepicker.xdsoft_rtl{padding:8px 0 8px 8px}.xdsoft_datetimepicker iframe{position:absolute;left:0;top:0;width:75px;height:210px;background:transparent;border:0}.xdsoft_datetimepicker button{border:none !important}.xdsoft_noselect{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.xdsoft_noselect::selection{background:transparent}.xdsoft_noselect::-moz-selection{background:transparent}.xdsoft_datetimepicker.xdsoft_inline{display:inline-block;position:static;box-shadow:none}.xdsoft_datetimepicker *{-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin:0}.xdsoft_datetimepicker .xdsoft_datepicker,.xdsoft_datetimepicker .xdsoft_timepicker{display:none}.xdsoft_datetimepicker .xdsoft_datepicker.active,.xdsoft_datetimepicker .xdsoft_timepicker.active{display:block}.xdsoft_datetimepicker .xdsoft_datepicker{width:224px;float:left;margin-left:8px}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_datepicker{float:right;margin-right:8px;margin-left:0}.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_datepicker{width:256px}.xdsoft_datetimepicker .xdsoft_timepicker{width:58px;float:left;text-align:center;margin-left:8px;margin-top:0}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker{float:right;margin-right:8px;margin-left:0}.xdsoft_datetimepicker .xdsoft_datepicker.active+.xdsoft_timepicker{margin-top:8px;margin-bottom:3px}.xdsoft_datetimepicker .xdsoft_monthpicker{position:relative;text-align:center}.xdsoft_datetimepicker .xdsoft_label i,.xdsoft_datetimepicker .xdsoft_prev,.xdsoft_datetimepicker .xdsoft_next,.xdsoft_datetimepicker .xdsoft_today_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0NBRjI1NjM0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0NBRjI1NjQ0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQ0FGMjU2MTQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQ0FGMjU2MjQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoNEP54AAAIOSURBVHja7Jq9TsMwEMcxrZD4WpBYeKUCe+kTMCACHZh4BFfHO/AAIHZGFhYkBBsSEqxsLCAgXKhbXYOTxh9pfJVP+qutnZ5s/5Lz2Y5I03QhWji2GIcgAokWgfCxNvcOCCGKqiSqhUp0laHOne05vdEyGMfkdxJDVjgwDlEQgYQBgx+ULJaWSXXS6r/ER5FBVR8VfGftTKcITNs+a1XpcFoExREIDF14AVIFxgQUS+h520cdud6wNkC0UBw6BCO/HoCYwBhD8QCkQ/x1mwDyD4plh4D6DDV0TAGyo4HcawLIBBSLDkHeH0Mg2yVP3l4TQMZQDDsEOl/MgHQqhMNuE0D+oBh0CIr8MAKyazBH9WyBuKxDWgbXfjNf32TZ1KWm/Ap1oSk/R53UtQ5xTh3LUlMmT8gt6g51Q9p+SobxgJQ/qmsfZhWywGFSl0yBjCLJCMgXail3b7+rumdVJ2YRss4cN+r6qAHDkPWjPjdJCF4n9RmAD/V9A/Wp4NQassDjwlB6XBiCxcJQWmZZb8THFilfy/lfrTvLghq2TqTHrRMTKNJ0sIhdo15RT+RpyWwFdY96UZ/LdQKBGjcXpcc1AlSFEfLmouD+1knuxBDUVrvOBmoOC/rEcN7OQxKVeJTCiAdUzUJhA2Oez9QTkp72OTVcxDcXY8iKNkxGAJXmJCOQwOa6dhyXsOa6XwEGAKdeb5ET3rQdAAAAAElFTkSuQmCC)}.xdsoft_datetimepicker .xdsoft_label i{opacity:.5;background-position:-92px -19px;display:inline-block;width:9px;height:20px;vertical-align:middle}.xdsoft_datetimepicker .xdsoft_prev{float:left;background-position:-20px 0}.xdsoft_datetimepicker .xdsoft_today_button{float:left;background-position:-70px 0;margin-left:5px}.xdsoft_datetimepicker .xdsoft_next{float:right;background-position:0 0}.xdsoft_datetimepicker .xdsoft_next,.xdsoft_datetimepicker .xdsoft_prev,.xdsoft_datetimepicker .xdsoft_today_button{background-color:transparent;background-repeat:no-repeat;border:0 none;cursor:pointer;display:block;height:30px;opacity:.5;-ms-filter:"alpha(opacity=50)";outline:medium none;overflow:hidden;padding:0;position:relative;text-indent:100%;white-space:nowrap;width:20px;min-width:0}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_next{float:none;background-position:-40px -15px;height:15px;width:30px;display:block;margin-left:14px;margin-top:7px}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker .xdsoft_prev,.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker .xdsoft_next{float:none;margin-left:0;margin-right:14px}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev{background-position:-40px 0;margin-bottom:7px;margin-top:0}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box{height:151px;overflow:hidden;border-bottom:1px solid #ddd}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div{background:#f5f5f5;border-top:1px solid #ddd;color:#666;font-size:12px;text-align:center;border-collapse:collapse;cursor:pointer;border-bottom-width:0;height:25px;line-height:25px}.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div:first-child{border-top-width:0}.xdsoft_datetimepicker .xdsoft_today_button:hover,.xdsoft_datetimepicker .xdsoft_next:hover,.xdsoft_datetimepicker .xdsoft_prev:hover{opacity:1;-ms-filter:"alpha(opacity=100)"}.xdsoft_datetimepicker .xdsoft_label{display:inline;position:relative;z-index:9999;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:bold;background-color:#fff;float:left;width:182px;text-align:center;cursor:pointer}.xdsoft_datetimepicker .xdsoft_label:hover>span{text-decoration:underline}.xdsoft_datetimepicker .xdsoft_label:hover i{opacity:1.0}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select{border:1px solid #ccc;position:absolute;right:0;top:30px;z-index:101;display:none;background:#fff;max-height:160px;overflow-y:hidden}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select.xdsoft_monthselect{right:-7px}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select.xdsoft_yearselect{right:2px}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option:hover{color:#fff;background:#ff8000}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option{padding:2px 10px 2px 5px;text-decoration:none !important}.xdsoft_datetimepicker .xdsoft_label>.xdsoft_select>div>.xdsoft_option.xdsoft_current{background:#3af;box-shadow:#178fe5 0 1px 3px 0 inset;color:#fff;font-weight:700}.xdsoft_datetimepicker .xdsoft_month{width:100px;text-align:right}.xdsoft_datetimepicker .xdsoft_calendar{clear:both}.xdsoft_datetimepicker .xdsoft_year{width:48px;margin-left:5px}.xdsoft_datetimepicker .xdsoft_calendar table{border-collapse:collapse;width:100%}.xdsoft_datetimepicker .xdsoft_calendar td>div{padding-right:5px}.xdsoft_datetimepicker .xdsoft_calendar th{height:25px}.xdsoft_datetimepicker .xdsoft_calendar td,.xdsoft_datetimepicker .xdsoft_calendar th{width:14.2857142%;background:#f5f5f5;border:1px solid #ddd;color:#666;font-size:12px;text-align:right;vertical-align:middle;padding:0;border-collapse:collapse;cursor:pointer;height:25px}.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar th{width:12.5%}.xdsoft_datetimepicker .xdsoft_calendar th{background:#f1f1f1}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today{color:#3af}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_default{background:#ffe9d2;box-shadow:#ffb871 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_mint{background:#c1ffc9;box-shadow:#00dd1c 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current{background:#3af;box-shadow:#178fe5 0 1px 3px 0 inset;color:#fff;font-weight:700}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month,.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled,.xdsoft_datetimepicker .xdsoft_time_box>div>div.xdsoft_disabled{opacity:.5;-ms-filter:"alpha(opacity=50)";cursor:default}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled{opacity:.2;-ms-filter:"alpha(opacity=20)"}.xdsoft_datetimepicker .xdsoft_calendar td:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div:hover{color:#fff !important;background:#ff8000 !important;box-shadow:none !important}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current.xdsoft_disabled:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current.xdsoft_disabled:hover{background:#3af !important;box-shadow:#178fe5 0 1px 3px 0 inset !important;color:#fff !important}.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled:hover,.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_disabled:hover{color:inherit !important;background:inherit !important;box-shadow:inherit !important}.xdsoft_datetimepicker .xdsoft_calendar th{font-weight:700;text-align:center;color:#999;cursor:default}.xdsoft_datetimepicker .xdsoft_copyright{color:#ccc !important;font-size:10px;clear:both;float:none;margin-left:8px}.xdsoft_datetimepicker .xdsoft_copyright a{color:#eee !important}.xdsoft_datetimepicker .xdsoft_copyright a:hover{color:#aaa !important}.xdsoft_time_box{position:relative;border:1px solid #ccc}.xdsoft_scrollbar>.xdsoft_scroller{background:#ccc !important;height:20px;border-radius:3px}.xdsoft_scrollbar{position:absolute;width:7px;right:0;top:0;bottom:0;cursor:pointer}.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_scrollbar{left:0;right:auto}.xdsoft_scroller_box{position:relative}.xdsoft_datetimepicker.xdsoft_dark{box-shadow:0 5px 15px -5px rgba(255,255,255,0.506);background:#000;border-bottom:1px solid #444;border-left:1px solid #333;border-right:1px solid #333;border-top:1px solid #333;color:#ccc}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box{border-bottom:1px solid #222}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div{background:#0a0a0a;border-top:1px solid #222;color:#999}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label{background-color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select{border:1px solid #333;background:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select>div>.xdsoft_option:hover{color:#000;background:#007fff}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label>.xdsoft_select>div>.xdsoft_option.xdsoft_current{background:#c50;box-shadow:#b03e00 0 1px 3px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label i,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_prev,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_next,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_today_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUExQUUzOTA0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUExQUUzOTE0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQTFBRTM4RTQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQTFBRTM4RjQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp0VxGEAAAIASURBVHja7JrNSgMxEMebtgh+3MSLr1T1Xn2CHoSKB08+QmR8Bx9A8e7RixdB9CKCoNdexIugxFlJa7rNZneTbLIpM/CnNLsdMvNjM8l0mRCiQ9Ye61IKCAgZAUnH+mU3MMZaHYChBnJUDzWOFZdVfc5+ZFLbrWDeXPwbxIqrLLfaeS0hEBVGIRQCEiZoHQwtlGSByCCdYBl8g8egTTAWoKQMRBRBcZxYlhzhKegqMOageErsCHVkk3hXIFooDgHB1KkHIHVgzKB4ADJQ/A1jAFmAYhkQqA5TOBtocrKrgXwQA8gcFIuAIO8sQSA7hidvPwaQGZSaAYHOUWJABhWWw2EMIH9QagQERU4SArJXo0ZZL18uvaxejXt/Em8xjVBXmvFr1KVm/AJ10tRe2XnraNqaJvKE3KHuUbfK1E+VHB0q40/y3sdQSxY4FHWeKJCunP8UyDdqJZenT3ntVV5jIYCAh20vT7ioP8tpf6E2lfEMwERe+whV1MHjwZB7PBiCxcGQWwKZKD62lfGNnP/1poFAA60T7rF1UgcKd2id3KDeUS+oLWV8DfWAepOfq00CgQabi9zjcgJVYVD7PVzQUAUGAQkbNJTBICDhgwYTjDYD6XeW08ZKh+A4pYkzenOxXUbvZcWz7E8ykRMnIHGX1XPl+1m2vPYpL+2qdb8CDAARlKFEz/ZVkAAAAABJRU5ErkJggg==)}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{background:#0a0a0a;border:1px solid #222;color:#999}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{background:#0e0e0e}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_today{color:#c50}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_default{background:#ffe9d2;box-shadow:#ffb871 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_mint{background:#c1ffc9;box-shadow:#00dd1c 0 1px 4px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_default,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_current,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current{background:#c50;box-shadow:#b03e00 0 1px 3px 0 inset;color:#000}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td:hover,.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box>div>div:hover{color:#000 !important;background:#007fff !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{color:#666}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright{color:#333 !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a{color:#111 !important}.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a:hover{color:#555 !important}.xdsoft_dark .xdsoft_time_box{border:1px solid #333}.xdsoft_dark .xdsoft_scrollbar>.xdsoft_scroller{background:#333 !important}.xdsoft_datetimepicker .xdsoft_save_selected{display:block;border:1px solid #ddd !important;margin-top:5px;width:100%;color:#454551;font-size:13px}.xdsoft_datetimepicker .blue-gradient-button{font-family:"museo-sans","Book Antiqua",sans-serif;font-size:12px;font-weight:300;color:#82878c;height:28px;position:relative;padding:4px 17px 4px 33px;border:1px solid #d7d8da;background:-moz-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(73%,#f4f8fa));background:-webkit-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-o-linear-gradient(top,#fff 0,#f4f8fa 73%);background:-ms-linear-gradient(top,#fff 0,#f4f8fa 73%);background:linear-gradient(to bottom,#fff 0,#f4f8fa 73%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff',endColorstr='#f4f8fa',GradientType=0)}.xdsoft_datetimepicker .blue-gradient-button:hover,.xdsoft_datetimepicker .blue-gradient-button:focus,.xdsoft_datetimepicker .blue-gradient-button:hover span,.xdsoft_datetimepicker .blue-gradient-button:focus span{color:#454551;background:-moz-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#f4f8fa),color-stop(73%,#FFF));background:-webkit-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-o-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:-ms-linear-gradient(top,#f4f8fa 0,#FFF 73%);background:linear-gradient(to bottom,#f4f8fa 0,#FFF 73%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f4f8fa',endColorstr='#FFF',GradientType=0)} \ No newline at end of file diff --git a/resources/dmmd-preview.css b/resources/dmmd-preview.css index c768a0e..a3236b7 100644 --- a/resources/dmmd-preview.css +++ b/resources/dmmd-preview.css @@ -1,44 +1,41 @@ div.dmmd-preview { - padding: 0; + padding: 0; } div.dmmd-preview-update { - background: #ccc; - color: #333; - text-align: center; - cursor: pointer; - border-radius: 4px; - height: 2em; - line-height: 2em; + background: #ccc; + color: #333; + text-align: center; + cursor: pointer; + border-radius: 4px; + height: 2em; + line-height: 2em; } div.dmmd-preview-content { - padding: 0 8px; + padding: 0 7px; } div.dmmd-preview.dmmd-preview-has-content div.dmmd-preview-update { - border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; } div.dmmd-preview-has-content div.dmmd-preview-content { - padding-bottom: 8px; - padding-top: 8px; + padding-bottom: 7px; } div.dmmd-no-button div.dmmd-preview-update { - display: none; + display: none; } div.dmmd-no-button div.dmmd-preview-content { - padding-bottom: 0; + padding-bottom: 0; } div.dmmd-no-button:not(.dmmd-preview-has-content) { - display: none; + display: none; } div.dmmd-preview-stale { - background: repeating-linear-gradient(-45deg, #fff, #fff 10px, #f8f8f8 10px, #f8f8f8 20px); + background: repeating-linear-gradient(-45deg, #fff, #fff 10px, #f8f8f8 10px, #f8f8f8 20px); } - -/*# sourceMappingURL=dmmd-preview.css.map */ diff --git a/resources/dmmd-preview.js b/resources/dmmd-preview.js index 2646503..fa6dc87 100644 --- a/resources/dmmd-preview.js +++ b/resources/dmmd-preview.js @@ -24,7 +24,37 @@ $(function () { }, function (result) { $content.html(result); $preview.addClass('dmmd-preview-has-content').removeClass('dmmd-preview-stale'); - renderKatex($content[0]); + + var $jax = $content.find('.require-mathjax-support'); + if ($jax.length) { + if (!('MathJax' in window)) { + $.ajax({ + type: 'GET', + url: $jax.attr('data-config'), + dataType: 'script', + cache: true, + success: function () { + $.ajax({ + type: 'GET', + url: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML', + dataType: 'script', + cache: true, + success: function () { + MathJax.Hub.Queue(function () { + $content.find('.tex-image').hide(); + $content.find('.tex-text').show(); + }); + } + }); + } + }); + } else { + MathJax.Hub.Queue(['Typeset', MathJax.Hub, $content[0]], function () { + $content.find('.tex-image').hide(); + $content.find('.tex-text').show(); + }); + } + } }); } else { $content.empty(); diff --git a/resources/dmmd-preview.scss b/resources/dmmd-preview.scss deleted file mode 100644 index 98cdbaa..0000000 --- a/resources/dmmd-preview.scss +++ /dev/null @@ -1,41 +0,0 @@ -div.dmmd-preview { - padding: 0; -} - -div.dmmd-preview-update { - background: #ccc; - color: #333; - text-align: center; - cursor: pointer; - border-radius: 4px; - height: 2em; - line-height: 2em; -} - -div.dmmd-preview-content { - padding: 0 7px; -} - -div.dmmd-preview.dmmd-preview-has-content div.dmmd-preview-update { - border-radius: 4px 4px 0 0; -} - -div.dmmd-preview-has-content div.dmmd-preview-content { - padding-bottom: 7px; -} - -div.dmmd-no-button div.dmmd-preview-update { - display: none; -} - -div.dmmd-no-button div.dmmd-preview-content { - padding-bottom: 0; -} - -div.dmmd-no-button:not(.dmmd-preview-has-content) { - display: none; -} - -div.dmmd-preview-stale { - background: repeating-linear-gradient(-45deg, #fff, #fff 10px, #f8f8f8 10px, #f8f8f8 20px); -} diff --git a/resources/download.js b/resources/download.js new file mode 100644 index 0000000..9312aec --- /dev/null +++ b/resources/download.js @@ -0,0 +1,132 @@ +//download.js v3.0, by dandavis; 2008-2014. [CCBY2] see http://danml.com/download.html for tests/usage +// v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime +// v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs +// v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support + +// data can be a string, Blob, File, or dataURL + + + + +function download(data, strFileName, strMimeType) { + + var self = window, // this script is only for browsers anyway... + u = "application/octet-stream", // this default mime also triggers iframe downloads + m = strMimeType || u, + x = data, + D = document, + a = D.createElement("a"), + z = function(a){return String(a);}, + + + B = self.Blob || self.MozBlob || self.WebKitBlob || z, + BB = self.MSBlobBuilder || self.WebKitBlobBuilder || self.BlobBuilder, + fn = strFileName || "download", + blob, + b, + ua, + fr; + + //if(typeof B.bind === 'function' ){ B=B.bind(self); } + + if(String(this)==="true"){ //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback + x=[x, m]; + m=x[0]; + x=x[1]; + } + + + + //go ahead and download dataURLs right away + if(String(x).match(/^data\:[\w+\-]+\/[\w+\-]+[,;]/)){ + return navigator.msSaveBlob ? // IE10 can't do a[download], only Blobs: + navigator.msSaveBlob(d2b(x), fn) : + saver(x) ; // everyone else can save dataURLs un-processed + }//end if dataURL passed? + + try{ + + blob = x instanceof B ? + x : + new B([x], {type: m}) ; + }catch(y){ + if(BB){ + b = new BB(); + b.append([x]); + blob = b.getBlob(m); // the blob + } + + } + + + + function d2b(u) { + var p= u.split(/[:;,]/), + t= p[1], + dec= p[2] == "base64" ? atob : decodeURIComponent, + bin= dec(p.pop()), + mx= bin.length, + i= 0, + uia= new Uint8Array(mx); + + for(i;i diff --git a/resources/fine-uploader/templates/gallery.html b/resources/fine-uploader/templates/gallery.html index 44827a6..3c7d69e 100644 --- a/resources/fine-uploader/templates/gallery.html +++ b/resources/fine-uploader/templates/gallery.html @@ -5,78 +5,78 @@ on how to customize this template. --> diff --git a/resources/fine-uploader/templates/simple-thumbnails.html b/resources/fine-uploader/templates/simple-thumbnails.html index 767d2d1..668c0c0 100644 --- a/resources/fine-uploader/templates/simple-thumbnails.html +++ b/resources/fine-uploader/templates/simple-thumbnails.html @@ -5,60 +5,60 @@ on how to customize this template. --> diff --git a/resources/fontawesome/css/all.css b/resources/fontawesome/css/all.css deleted file mode 100644 index 7e4dfe1..0000000 --- a/resources/fontawesome/css/all.css +++ /dev/null @@ -1,8030 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -.fa { - font-family: var(--fa-style-family, "Font Awesome 6 Free"); - font-weight: var(--fa-style, 900); } - -.fa, -.fa-classic, -.fa-sharp, -.fas, -.fa-solid, -.far, -.fa-regular, -.fab, -.fa-brands { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - display: var(--fa-display, inline-block); - font-style: normal; - font-variant: normal; - line-height: 1; - text-rendering: auto; } - -.fas, -.fa-classic, -.fa-solid, -.far, -.fa-regular { - font-family: 'Font Awesome 6 Free'; } - -.fab, -.fa-brands { - font-family: 'Font Awesome 6 Brands'; } - -.fa-1x { - font-size: 1em; } - -.fa-2x { - font-size: 2em; } - -.fa-3x { - font-size: 3em; } - -.fa-4x { - font-size: 4em; } - -.fa-5x { - font-size: 5em; } - -.fa-6x { - font-size: 6em; } - -.fa-7x { - font-size: 7em; } - -.fa-8x { - font-size: 8em; } - -.fa-9x { - font-size: 9em; } - -.fa-10x { - font-size: 10em; } - -.fa-2xs { - font-size: 0.625em; - line-height: 0.1em; - vertical-align: 0.225em; } - -.fa-xs { - font-size: 0.75em; - line-height: 0.08333em; - vertical-align: 0.125em; } - -.fa-sm { - font-size: 0.875em; - line-height: 0.07143em; - vertical-align: 0.05357em; } - -.fa-lg { - font-size: 1.25em; - line-height: 0.05em; - vertical-align: -0.075em; } - -.fa-xl { - font-size: 1.5em; - line-height: 0.04167em; - vertical-align: -0.125em; } - -.fa-2xl { - font-size: 2em; - line-height: 0.03125em; - vertical-align: -0.1875em; } - -.fa-fw { - text-align: center; - width: 1.25em; } - -.fa-ul { - list-style-type: none; - margin-left: var(--fa-li-margin, 2.5em); - padding-left: 0; } - .fa-ul > li { - position: relative; } - -.fa-li { - left: calc(var(--fa-li-width, 2em) * -1); - position: absolute; - text-align: center; - width: var(--fa-li-width, 2em); - line-height: inherit; } - -.fa-border { - border-color: var(--fa-border-color, #eee); - border-radius: var(--fa-border-radius, 0.1em); - border-style: var(--fa-border-style, solid); - border-width: var(--fa-border-width, 0.08em); - padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); } - -.fa-pull-left { - float: left; - margin-right: var(--fa-pull-margin, 0.3em); } - -.fa-pull-right { - float: right; - margin-left: var(--fa-pull-margin, 0.3em); } - -.fa-beat { - -webkit-animation-name: fa-beat; - animation-name: fa-beat; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); - animation-timing-function: var(--fa-animation-timing, ease-in-out); } - -.fa-bounce { - -webkit-animation-name: fa-bounce; - animation-name: fa-bounce; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); } - -.fa-fade { - -webkit-animation-name: fa-fade; - animation-name: fa-fade; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } - -.fa-beat-fade { - -webkit-animation-name: fa-beat-fade; - animation-name: fa-beat-fade; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } - -.fa-flip { - -webkit-animation-name: fa-flip; - animation-name: fa-flip; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); - animation-timing-function: var(--fa-animation-timing, ease-in-out); } - -.fa-shake { - -webkit-animation-name: fa-shake; - animation-name: fa-shake; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, linear); - animation-timing-function: var(--fa-animation-timing, linear); } - -.fa-spin { - -webkit-animation-name: fa-spin; - animation-name: fa-spin; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 2s); - animation-duration: var(--fa-animation-duration, 2s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, linear); - animation-timing-function: var(--fa-animation-timing, linear); } - -.fa-spin-reverse { - --fa-animation-direction: reverse; } - -.fa-pulse, -.fa-spin-pulse { - -webkit-animation-name: fa-spin; - animation-name: fa-spin; - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); - animation-timing-function: var(--fa-animation-timing, steps(8)); } - -@media (prefers-reduced-motion: reduce) { - .fa-beat, - .fa-bounce, - .fa-fade, - .fa-beat-fade, - .fa-flip, - .fa-pulse, - .fa-shake, - .fa-spin, - .fa-spin-pulse { - -webkit-animation-delay: -1ms; - animation-delay: -1ms; - -webkit-animation-duration: 1ms; - animation-duration: 1ms; - -webkit-animation-iteration-count: 1; - animation-iteration-count: 1; - -webkit-transition-delay: 0s; - transition-delay: 0s; - -webkit-transition-duration: 0s; - transition-duration: 0s; } } - -@-webkit-keyframes fa-beat { - 0%, 90% { - -webkit-transform: scale(1); - transform: scale(1); } - 45% { - -webkit-transform: scale(var(--fa-beat-scale, 1.25)); - transform: scale(var(--fa-beat-scale, 1.25)); } } - -@keyframes fa-beat { - 0%, 90% { - -webkit-transform: scale(1); - transform: scale(1); } - 45% { - -webkit-transform: scale(var(--fa-beat-scale, 1.25)); - transform: scale(var(--fa-beat-scale, 1.25)); } } - -@-webkit-keyframes fa-bounce { - 0% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 10% { - -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); - transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } - 30% { - -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); - transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } - 50% { - -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); - transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } - 57% { - -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); - transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } - 64% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 100% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } } - -@keyframes fa-bounce { - 0% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 10% { - -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); - transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } - 30% { - -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); - transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } - 50% { - -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); - transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } - 57% { - -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); - transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } - 64% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 100% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } } - -@-webkit-keyframes fa-fade { - 50% { - opacity: var(--fa-fade-opacity, 0.4); } } - -@keyframes fa-fade { - 50% { - opacity: var(--fa-fade-opacity, 0.4); } } - -@-webkit-keyframes fa-beat-fade { - 0%, 100% { - opacity: var(--fa-beat-fade-opacity, 0.4); - -webkit-transform: scale(1); - transform: scale(1); } - 50% { - opacity: 1; - -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); - transform: scale(var(--fa-beat-fade-scale, 1.125)); } } - -@keyframes fa-beat-fade { - 0%, 100% { - opacity: var(--fa-beat-fade-opacity, 0.4); - -webkit-transform: scale(1); - transform: scale(1); } - 50% { - opacity: 1; - -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); - transform: scale(var(--fa-beat-fade-scale, 1.125)); } } - -@-webkit-keyframes fa-flip { - 50% { - -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); - transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } - -@keyframes fa-flip { - 50% { - -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); - transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } - -@-webkit-keyframes fa-shake { - 0% { - -webkit-transform: rotate(-15deg); - transform: rotate(-15deg); } - 4% { - -webkit-transform: rotate(15deg); - transform: rotate(15deg); } - 8%, 24% { - -webkit-transform: rotate(-18deg); - transform: rotate(-18deg); } - 12%, 28% { - -webkit-transform: rotate(18deg); - transform: rotate(18deg); } - 16% { - -webkit-transform: rotate(-22deg); - transform: rotate(-22deg); } - 20% { - -webkit-transform: rotate(22deg); - transform: rotate(22deg); } - 32% { - -webkit-transform: rotate(-12deg); - transform: rotate(-12deg); } - 36% { - -webkit-transform: rotate(12deg); - transform: rotate(12deg); } - 40%, 100% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } } - -@keyframes fa-shake { - 0% { - -webkit-transform: rotate(-15deg); - transform: rotate(-15deg); } - 4% { - -webkit-transform: rotate(15deg); - transform: rotate(15deg); } - 8%, 24% { - -webkit-transform: rotate(-18deg); - transform: rotate(-18deg); } - 12%, 28% { - -webkit-transform: rotate(18deg); - transform: rotate(18deg); } - 16% { - -webkit-transform: rotate(-22deg); - transform: rotate(-22deg); } - 20% { - -webkit-transform: rotate(22deg); - transform: rotate(22deg); } - 32% { - -webkit-transform: rotate(-12deg); - transform: rotate(-12deg); } - 36% { - -webkit-transform: rotate(12deg); - transform: rotate(12deg); } - 40%, 100% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } } - -@-webkit-keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -@keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -.fa-rotate-90 { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); } - -.fa-rotate-180 { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); } - -.fa-rotate-270 { - -webkit-transform: rotate(270deg); - transform: rotate(270deg); } - -.fa-flip-horizontal { - -webkit-transform: scale(-1, 1); - transform: scale(-1, 1); } - -.fa-flip-vertical { - -webkit-transform: scale(1, -1); - transform: scale(1, -1); } - -.fa-flip-both, -.fa-flip-horizontal.fa-flip-vertical { - -webkit-transform: scale(-1, -1); - transform: scale(-1, -1); } - -.fa-rotate-by { - -webkit-transform: rotate(var(--fa-rotate-angle, 0)); - transform: rotate(var(--fa-rotate-angle, 0)); } - -.fa-stack { - display: inline-block; - height: 2em; - line-height: 2em; - position: relative; - vertical-align: middle; - width: 2.5em; } - -.fa-stack-1x, -.fa-stack-2x { - left: 0; - position: absolute; - text-align: center; - width: 100%; - z-index: var(--fa-stack-z-index, auto); } - -.fa-stack-1x { - line-height: inherit; } - -.fa-stack-2x { - font-size: 2em; } - -.fa-inverse { - color: var(--fa-inverse, #fff); } - -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen -readers do not read off random characters that represent icons */ - -.fa-0::before { - content: "\30"; } - -.fa-1::before { - content: "\31"; } - -.fa-2::before { - content: "\32"; } - -.fa-3::before { - content: "\33"; } - -.fa-4::before { - content: "\34"; } - -.fa-5::before { - content: "\35"; } - -.fa-6::before { - content: "\36"; } - -.fa-7::before { - content: "\37"; } - -.fa-8::before { - content: "\38"; } - -.fa-9::before { - content: "\39"; } - -.fa-fill-drip::before { - content: "\f576"; } - -.fa-arrows-to-circle::before { - content: "\e4bd"; } - -.fa-circle-chevron-right::before { - content: "\f138"; } - -.fa-chevron-circle-right::before { - content: "\f138"; } - -.fa-at::before { - content: "\40"; } - -.fa-trash-can::before { - content: "\f2ed"; } - -.fa-trash-alt::before { - content: "\f2ed"; } - -.fa-text-height::before { - content: "\f034"; } - -.fa-user-xmark::before { - content: "\f235"; } - -.fa-user-times::before { - content: "\f235"; } - -.fa-stethoscope::before { - content: "\f0f1"; } - -.fa-message::before { - content: "\f27a"; } - -.fa-comment-alt::before { - content: "\f27a"; } - -.fa-info::before { - content: "\f129"; } - -.fa-down-left-and-up-right-to-center::before { - content: "\f422"; } - -.fa-compress-alt::before { - content: "\f422"; } - -.fa-explosion::before { - content: "\e4e9"; } - -.fa-file-lines::before { - content: "\f15c"; } - -.fa-file-alt::before { - content: "\f15c"; } - -.fa-file-text::before { - content: "\f15c"; } - -.fa-wave-square::before { - content: "\f83e"; } - -.fa-ring::before { - content: "\f70b"; } - -.fa-building-un::before { - content: "\e4d9"; } - -.fa-dice-three::before { - content: "\f527"; } - -.fa-calendar-days::before { - content: "\f073"; } - -.fa-calendar-alt::before { - content: "\f073"; } - -.fa-anchor-circle-check::before { - content: "\e4aa"; } - -.fa-building-circle-arrow-right::before { - content: "\e4d1"; } - -.fa-volleyball::before { - content: "\f45f"; } - -.fa-volleyball-ball::before { - content: "\f45f"; } - -.fa-arrows-up-to-line::before { - content: "\e4c2"; } - -.fa-sort-down::before { - content: "\f0dd"; } - -.fa-sort-desc::before { - content: "\f0dd"; } - -.fa-circle-minus::before { - content: "\f056"; } - -.fa-minus-circle::before { - content: "\f056"; } - -.fa-door-open::before { - content: "\f52b"; } - -.fa-right-from-bracket::before { - content: "\f2f5"; } - -.fa-sign-out-alt::before { - content: "\f2f5"; } - -.fa-atom::before { - content: "\f5d2"; } - -.fa-soap::before { - content: "\e06e"; } - -.fa-icons::before { - content: "\f86d"; } - -.fa-heart-music-camera-bolt::before { - content: "\f86d"; } - -.fa-microphone-lines-slash::before { - content: "\f539"; } - -.fa-microphone-alt-slash::before { - content: "\f539"; } - -.fa-bridge-circle-check::before { - content: "\e4c9"; } - -.fa-pump-medical::before { - content: "\e06a"; } - -.fa-fingerprint::before { - content: "\f577"; } - -.fa-hand-point-right::before { - content: "\f0a4"; } - -.fa-magnifying-glass-location::before { - content: "\f689"; } - -.fa-search-location::before { - content: "\f689"; } - -.fa-forward-step::before { - content: "\f051"; } - -.fa-step-forward::before { - content: "\f051"; } - -.fa-face-smile-beam::before { - content: "\f5b8"; } - -.fa-smile-beam::before { - content: "\f5b8"; } - -.fa-flag-checkered::before { - content: "\f11e"; } - -.fa-football::before { - content: "\f44e"; } - -.fa-football-ball::before { - content: "\f44e"; } - -.fa-school-circle-exclamation::before { - content: "\e56c"; } - -.fa-crop::before { - content: "\f125"; } - -.fa-angles-down::before { - content: "\f103"; } - -.fa-angle-double-down::before { - content: "\f103"; } - -.fa-users-rectangle::before { - content: "\e594"; } - -.fa-people-roof::before { - content: "\e537"; } - -.fa-people-line::before { - content: "\e534"; } - -.fa-beer-mug-empty::before { - content: "\f0fc"; } - -.fa-beer::before { - content: "\f0fc"; } - -.fa-diagram-predecessor::before { - content: "\e477"; } - -.fa-arrow-up-long::before { - content: "\f176"; } - -.fa-long-arrow-up::before { - content: "\f176"; } - -.fa-fire-flame-simple::before { - content: "\f46a"; } - -.fa-burn::before { - content: "\f46a"; } - -.fa-person::before { - content: "\f183"; } - -.fa-male::before { - content: "\f183"; } - -.fa-laptop::before { - content: "\f109"; } - -.fa-file-csv::before { - content: "\f6dd"; } - -.fa-menorah::before { - content: "\f676"; } - -.fa-truck-plane::before { - content: "\e58f"; } - -.fa-record-vinyl::before { - content: "\f8d9"; } - -.fa-face-grin-stars::before { - content: "\f587"; } - -.fa-grin-stars::before { - content: "\f587"; } - -.fa-bong::before { - content: "\f55c"; } - -.fa-spaghetti-monster-flying::before { - content: "\f67b"; } - -.fa-pastafarianism::before { - content: "\f67b"; } - -.fa-arrow-down-up-across-line::before { - content: "\e4af"; } - -.fa-spoon::before { - content: "\f2e5"; } - -.fa-utensil-spoon::before { - content: "\f2e5"; } - -.fa-jar-wheat::before { - content: "\e517"; } - -.fa-envelopes-bulk::before { - content: "\f674"; } - -.fa-mail-bulk::before { - content: "\f674"; } - -.fa-file-circle-exclamation::before { - content: "\e4eb"; } - -.fa-circle-h::before { - content: "\f47e"; } - -.fa-hospital-symbol::before { - content: "\f47e"; } - -.fa-pager::before { - content: "\f815"; } - -.fa-address-book::before { - content: "\f2b9"; } - -.fa-contact-book::before { - content: "\f2b9"; } - -.fa-strikethrough::before { - content: "\f0cc"; } - -.fa-k::before { - content: "\4b"; } - -.fa-landmark-flag::before { - content: "\e51c"; } - -.fa-pencil::before { - content: "\f303"; } - -.fa-pencil-alt::before { - content: "\f303"; } - -.fa-backward::before { - content: "\f04a"; } - -.fa-caret-right::before { - content: "\f0da"; } - -.fa-comments::before { - content: "\f086"; } - -.fa-paste::before { - content: "\f0ea"; } - -.fa-file-clipboard::before { - content: "\f0ea"; } - -.fa-code-pull-request::before { - content: "\e13c"; } - -.fa-clipboard-list::before { - content: "\f46d"; } - -.fa-truck-ramp-box::before { - content: "\f4de"; } - -.fa-truck-loading::before { - content: "\f4de"; } - -.fa-user-check::before { - content: "\f4fc"; } - -.fa-vial-virus::before { - content: "\e597"; } - -.fa-sheet-plastic::before { - content: "\e571"; } - -.fa-blog::before { - content: "\f781"; } - -.fa-user-ninja::before { - content: "\f504"; } - -.fa-person-arrow-up-from-line::before { - content: "\e539"; } - -.fa-scroll-torah::before { - content: "\f6a0"; } - -.fa-torah::before { - content: "\f6a0"; } - -.fa-broom-ball::before { - content: "\f458"; } - -.fa-quidditch::before { - content: "\f458"; } - -.fa-quidditch-broom-ball::before { - content: "\f458"; } - -.fa-toggle-off::before { - content: "\f204"; } - -.fa-box-archive::before { - content: "\f187"; } - -.fa-archive::before { - content: "\f187"; } - -.fa-person-drowning::before { - content: "\e545"; } - -.fa-arrow-down-9-1::before { - content: "\f886"; } - -.fa-sort-numeric-desc::before { - content: "\f886"; } - -.fa-sort-numeric-down-alt::before { - content: "\f886"; } - -.fa-face-grin-tongue-squint::before { - content: "\f58a"; } - -.fa-grin-tongue-squint::before { - content: "\f58a"; } - -.fa-spray-can::before { - content: "\f5bd"; } - -.fa-truck-monster::before { - content: "\f63b"; } - -.fa-w::before { - content: "\57"; } - -.fa-earth-africa::before { - content: "\f57c"; } - -.fa-globe-africa::before { - content: "\f57c"; } - -.fa-rainbow::before { - content: "\f75b"; } - -.fa-circle-notch::before { - content: "\f1ce"; } - -.fa-tablet-screen-button::before { - content: "\f3fa"; } - -.fa-tablet-alt::before { - content: "\f3fa"; } - -.fa-paw::before { - content: "\f1b0"; } - -.fa-cloud::before { - content: "\f0c2"; } - -.fa-trowel-bricks::before { - content: "\e58a"; } - -.fa-face-flushed::before { - content: "\f579"; } - -.fa-flushed::before { - content: "\f579"; } - -.fa-hospital-user::before { - content: "\f80d"; } - -.fa-tent-arrow-left-right::before { - content: "\e57f"; } - -.fa-gavel::before { - content: "\f0e3"; } - -.fa-legal::before { - content: "\f0e3"; } - -.fa-binoculars::before { - content: "\f1e5"; } - -.fa-microphone-slash::before { - content: "\f131"; } - -.fa-box-tissue::before { - content: "\e05b"; } - -.fa-motorcycle::before { - content: "\f21c"; } - -.fa-bell-concierge::before { - content: "\f562"; } - -.fa-concierge-bell::before { - content: "\f562"; } - -.fa-pen-ruler::before { - content: "\f5ae"; } - -.fa-pencil-ruler::before { - content: "\f5ae"; } - -.fa-people-arrows::before { - content: "\e068"; } - -.fa-people-arrows-left-right::before { - content: "\e068"; } - -.fa-mars-and-venus-burst::before { - content: "\e523"; } - -.fa-square-caret-right::before { - content: "\f152"; } - -.fa-caret-square-right::before { - content: "\f152"; } - -.fa-scissors::before { - content: "\f0c4"; } - -.fa-cut::before { - content: "\f0c4"; } - -.fa-sun-plant-wilt::before { - content: "\e57a"; } - -.fa-toilets-portable::before { - content: "\e584"; } - -.fa-hockey-puck::before { - content: "\f453"; } - -.fa-table::before { - content: "\f0ce"; } - -.fa-magnifying-glass-arrow-right::before { - content: "\e521"; } - -.fa-tachograph-digital::before { - content: "\f566"; } - -.fa-digital-tachograph::before { - content: "\f566"; } - -.fa-users-slash::before { - content: "\e073"; } - -.fa-clover::before { - content: "\e139"; } - -.fa-reply::before { - content: "\f3e5"; } - -.fa-mail-reply::before { - content: "\f3e5"; } - -.fa-star-and-crescent::before { - content: "\f699"; } - -.fa-house-fire::before { - content: "\e50c"; } - -.fa-square-minus::before { - content: "\f146"; } - -.fa-minus-square::before { - content: "\f146"; } - -.fa-helicopter::before { - content: "\f533"; } - -.fa-compass::before { - content: "\f14e"; } - -.fa-square-caret-down::before { - content: "\f150"; } - -.fa-caret-square-down::before { - content: "\f150"; } - -.fa-file-circle-question::before { - content: "\e4ef"; } - -.fa-laptop-code::before { - content: "\f5fc"; } - -.fa-swatchbook::before { - content: "\f5c3"; } - -.fa-prescription-bottle::before { - content: "\f485"; } - -.fa-bars::before { - content: "\f0c9"; } - -.fa-navicon::before { - content: "\f0c9"; } - -.fa-people-group::before { - content: "\e533"; } - -.fa-hourglass-end::before { - content: "\f253"; } - -.fa-hourglass-3::before { - content: "\f253"; } - -.fa-heart-crack::before { - content: "\f7a9"; } - -.fa-heart-broken::before { - content: "\f7a9"; } - -.fa-square-up-right::before { - content: "\f360"; } - -.fa-external-link-square-alt::before { - content: "\f360"; } - -.fa-face-kiss-beam::before { - content: "\f597"; } - -.fa-kiss-beam::before { - content: "\f597"; } - -.fa-film::before { - content: "\f008"; } - -.fa-ruler-horizontal::before { - content: "\f547"; } - -.fa-people-robbery::before { - content: "\e536"; } - -.fa-lightbulb::before { - content: "\f0eb"; } - -.fa-caret-left::before { - content: "\f0d9"; } - -.fa-circle-exclamation::before { - content: "\f06a"; } - -.fa-exclamation-circle::before { - content: "\f06a"; } - -.fa-school-circle-xmark::before { - content: "\e56d"; } - -.fa-arrow-right-from-bracket::before { - content: "\f08b"; } - -.fa-sign-out::before { - content: "\f08b"; } - -.fa-circle-chevron-down::before { - content: "\f13a"; } - -.fa-chevron-circle-down::before { - content: "\f13a"; } - -.fa-unlock-keyhole::before { - content: "\f13e"; } - -.fa-unlock-alt::before { - content: "\f13e"; } - -.fa-cloud-showers-heavy::before { - content: "\f740"; } - -.fa-headphones-simple::before { - content: "\f58f"; } - -.fa-headphones-alt::before { - content: "\f58f"; } - -.fa-sitemap::before { - content: "\f0e8"; } - -.fa-circle-dollar-to-slot::before { - content: "\f4b9"; } - -.fa-donate::before { - content: "\f4b9"; } - -.fa-memory::before { - content: "\f538"; } - -.fa-road-spikes::before { - content: "\e568"; } - -.fa-fire-burner::before { - content: "\e4f1"; } - -.fa-flag::before { - content: "\f024"; } - -.fa-hanukiah::before { - content: "\f6e6"; } - -.fa-feather::before { - content: "\f52d"; } - -.fa-volume-low::before { - content: "\f027"; } - -.fa-volume-down::before { - content: "\f027"; } - -.fa-comment-slash::before { - content: "\f4b3"; } - -.fa-cloud-sun-rain::before { - content: "\f743"; } - -.fa-compress::before { - content: "\f066"; } - -.fa-wheat-awn::before { - content: "\e2cd"; } - -.fa-wheat-alt::before { - content: "\e2cd"; } - -.fa-ankh::before { - content: "\f644"; } - -.fa-hands-holding-child::before { - content: "\e4fa"; } - -.fa-asterisk::before { - content: "\2a"; } - -.fa-square-check::before { - content: "\f14a"; } - -.fa-check-square::before { - content: "\f14a"; } - -.fa-peseta-sign::before { - content: "\e221"; } - -.fa-heading::before { - content: "\f1dc"; } - -.fa-header::before { - content: "\f1dc"; } - -.fa-ghost::before { - content: "\f6e2"; } - -.fa-list::before { - content: "\f03a"; } - -.fa-list-squares::before { - content: "\f03a"; } - -.fa-square-phone-flip::before { - content: "\f87b"; } - -.fa-phone-square-alt::before { - content: "\f87b"; } - -.fa-cart-plus::before { - content: "\f217"; } - -.fa-gamepad::before { - content: "\f11b"; } - -.fa-circle-dot::before { - content: "\f192"; } - -.fa-dot-circle::before { - content: "\f192"; } - -.fa-face-dizzy::before { - content: "\f567"; } - -.fa-dizzy::before { - content: "\f567"; } - -.fa-egg::before { - content: "\f7fb"; } - -.fa-house-medical-circle-xmark::before { - content: "\e513"; } - -.fa-campground::before { - content: "\f6bb"; } - -.fa-folder-plus::before { - content: "\f65e"; } - -.fa-futbol::before { - content: "\f1e3"; } - -.fa-futbol-ball::before { - content: "\f1e3"; } - -.fa-soccer-ball::before { - content: "\f1e3"; } - -.fa-paintbrush::before { - content: "\f1fc"; } - -.fa-paint-brush::before { - content: "\f1fc"; } - -.fa-lock::before { - content: "\f023"; } - -.fa-gas-pump::before { - content: "\f52f"; } - -.fa-hot-tub-person::before { - content: "\f593"; } - -.fa-hot-tub::before { - content: "\f593"; } - -.fa-map-location::before { - content: "\f59f"; } - -.fa-map-marked::before { - content: "\f59f"; } - -.fa-house-flood-water::before { - content: "\e50e"; } - -.fa-tree::before { - content: "\f1bb"; } - -.fa-bridge-lock::before { - content: "\e4cc"; } - -.fa-sack-dollar::before { - content: "\f81d"; } - -.fa-pen-to-square::before { - content: "\f044"; } - -.fa-edit::before { - content: "\f044"; } - -.fa-car-side::before { - content: "\f5e4"; } - -.fa-share-nodes::before { - content: "\f1e0"; } - -.fa-share-alt::before { - content: "\f1e0"; } - -.fa-heart-circle-minus::before { - content: "\e4ff"; } - -.fa-hourglass-half::before { - content: "\f252"; } - -.fa-hourglass-2::before { - content: "\f252"; } - -.fa-microscope::before { - content: "\f610"; } - -.fa-sink::before { - content: "\e06d"; } - -.fa-bag-shopping::before { - content: "\f290"; } - -.fa-shopping-bag::before { - content: "\f290"; } - -.fa-arrow-down-z-a::before { - content: "\f881"; } - -.fa-sort-alpha-desc::before { - content: "\f881"; } - -.fa-sort-alpha-down-alt::before { - content: "\f881"; } - -.fa-mitten::before { - content: "\f7b5"; } - -.fa-person-rays::before { - content: "\e54d"; } - -.fa-users::before { - content: "\f0c0"; } - -.fa-eye-slash::before { - content: "\f070"; } - -.fa-flask-vial::before { - content: "\e4f3"; } - -.fa-hand::before { - content: "\f256"; } - -.fa-hand-paper::before { - content: "\f256"; } - -.fa-om::before { - content: "\f679"; } - -.fa-worm::before { - content: "\e599"; } - -.fa-house-circle-xmark::before { - content: "\e50b"; } - -.fa-plug::before { - content: "\f1e6"; } - -.fa-chevron-up::before { - content: "\f077"; } - -.fa-hand-spock::before { - content: "\f259"; } - -.fa-stopwatch::before { - content: "\f2f2"; } - -.fa-face-kiss::before { - content: "\f596"; } - -.fa-kiss::before { - content: "\f596"; } - -.fa-bridge-circle-xmark::before { - content: "\e4cb"; } - -.fa-face-grin-tongue::before { - content: "\f589"; } - -.fa-grin-tongue::before { - content: "\f589"; } - -.fa-chess-bishop::before { - content: "\f43a"; } - -.fa-face-grin-wink::before { - content: "\f58c"; } - -.fa-grin-wink::before { - content: "\f58c"; } - -.fa-ear-deaf::before { - content: "\f2a4"; } - -.fa-deaf::before { - content: "\f2a4"; } - -.fa-deafness::before { - content: "\f2a4"; } - -.fa-hard-of-hearing::before { - content: "\f2a4"; } - -.fa-road-circle-check::before { - content: "\e564"; } - -.fa-dice-five::before { - content: "\f523"; } - -.fa-square-rss::before { - content: "\f143"; } - -.fa-rss-square::before { - content: "\f143"; } - -.fa-land-mine-on::before { - content: "\e51b"; } - -.fa-i-cursor::before { - content: "\f246"; } - -.fa-stamp::before { - content: "\f5bf"; } - -.fa-stairs::before { - content: "\e289"; } - -.fa-i::before { - content: "\49"; } - -.fa-hryvnia-sign::before { - content: "\f6f2"; } - -.fa-hryvnia::before { - content: "\f6f2"; } - -.fa-pills::before { - content: "\f484"; } - -.fa-face-grin-wide::before { - content: "\f581"; } - -.fa-grin-alt::before { - content: "\f581"; } - -.fa-tooth::before { - content: "\f5c9"; } - -.fa-v::before { - content: "\56"; } - -.fa-bangladeshi-taka-sign::before { - content: "\e2e6"; } - -.fa-bicycle::before { - content: "\f206"; } - -.fa-staff-snake::before { - content: "\e579"; } - -.fa-rod-asclepius::before { - content: "\e579"; } - -.fa-rod-snake::before { - content: "\e579"; } - -.fa-staff-aesculapius::before { - content: "\e579"; } - -.fa-head-side-cough-slash::before { - content: "\e062"; } - -.fa-truck-medical::before { - content: "\f0f9"; } - -.fa-ambulance::before { - content: "\f0f9"; } - -.fa-wheat-awn-circle-exclamation::before { - content: "\e598"; } - -.fa-snowman::before { - content: "\f7d0"; } - -.fa-mortar-pestle::before { - content: "\f5a7"; } - -.fa-road-barrier::before { - content: "\e562"; } - -.fa-school::before { - content: "\f549"; } - -.fa-igloo::before { - content: "\f7ae"; } - -.fa-joint::before { - content: "\f595"; } - -.fa-angle-right::before { - content: "\f105"; } - -.fa-horse::before { - content: "\f6f0"; } - -.fa-q::before { - content: "\51"; } - -.fa-g::before { - content: "\47"; } - -.fa-notes-medical::before { - content: "\f481"; } - -.fa-temperature-half::before { - content: "\f2c9"; } - -.fa-temperature-2::before { - content: "\f2c9"; } - -.fa-thermometer-2::before { - content: "\f2c9"; } - -.fa-thermometer-half::before { - content: "\f2c9"; } - -.fa-dong-sign::before { - content: "\e169"; } - -.fa-capsules::before { - content: "\f46b"; } - -.fa-poo-storm::before { - content: "\f75a"; } - -.fa-poo-bolt::before { - content: "\f75a"; } - -.fa-face-frown-open::before { - content: "\f57a"; } - -.fa-frown-open::before { - content: "\f57a"; } - -.fa-hand-point-up::before { - content: "\f0a6"; } - -.fa-money-bill::before { - content: "\f0d6"; } - -.fa-bookmark::before { - content: "\f02e"; } - -.fa-align-justify::before { - content: "\f039"; } - -.fa-umbrella-beach::before { - content: "\f5ca"; } - -.fa-helmet-un::before { - content: "\e503"; } - -.fa-bullseye::before { - content: "\f140"; } - -.fa-bacon::before { - content: "\f7e5"; } - -.fa-hand-point-down::before { - content: "\f0a7"; } - -.fa-arrow-up-from-bracket::before { - content: "\e09a"; } - -.fa-folder::before { - content: "\f07b"; } - -.fa-folder-blank::before { - content: "\f07b"; } - -.fa-file-waveform::before { - content: "\f478"; } - -.fa-file-medical-alt::before { - content: "\f478"; } - -.fa-radiation::before { - content: "\f7b9"; } - -.fa-chart-simple::before { - content: "\e473"; } - -.fa-mars-stroke::before { - content: "\f229"; } - -.fa-vial::before { - content: "\f492"; } - -.fa-gauge::before { - content: "\f624"; } - -.fa-dashboard::before { - content: "\f624"; } - -.fa-gauge-med::before { - content: "\f624"; } - -.fa-tachometer-alt-average::before { - content: "\f624"; } - -.fa-wand-magic-sparkles::before { - content: "\e2ca"; } - -.fa-magic-wand-sparkles::before { - content: "\e2ca"; } - -.fa-e::before { - content: "\45"; } - -.fa-pen-clip::before { - content: "\f305"; } - -.fa-pen-alt::before { - content: "\f305"; } - -.fa-bridge-circle-exclamation::before { - content: "\e4ca"; } - -.fa-user::before { - content: "\f007"; } - -.fa-school-circle-check::before { - content: "\e56b"; } - -.fa-dumpster::before { - content: "\f793"; } - -.fa-van-shuttle::before { - content: "\f5b6"; } - -.fa-shuttle-van::before { - content: "\f5b6"; } - -.fa-building-user::before { - content: "\e4da"; } - -.fa-square-caret-left::before { - content: "\f191"; } - -.fa-caret-square-left::before { - content: "\f191"; } - -.fa-highlighter::before { - content: "\f591"; } - -.fa-key::before { - content: "\f084"; } - -.fa-bullhorn::before { - content: "\f0a1"; } - -.fa-globe::before { - content: "\f0ac"; } - -.fa-synagogue::before { - content: "\f69b"; } - -.fa-person-half-dress::before { - content: "\e548"; } - -.fa-road-bridge::before { - content: "\e563"; } - -.fa-location-arrow::before { - content: "\f124"; } - -.fa-c::before { - content: "\43"; } - -.fa-tablet-button::before { - content: "\f10a"; } - -.fa-building-lock::before { - content: "\e4d6"; } - -.fa-pizza-slice::before { - content: "\f818"; } - -.fa-money-bill-wave::before { - content: "\f53a"; } - -.fa-chart-area::before { - content: "\f1fe"; } - -.fa-area-chart::before { - content: "\f1fe"; } - -.fa-house-flag::before { - content: "\e50d"; } - -.fa-person-circle-minus::before { - content: "\e540"; } - -.fa-ban::before { - content: "\f05e"; } - -.fa-cancel::before { - content: "\f05e"; } - -.fa-camera-rotate::before { - content: "\e0d8"; } - -.fa-spray-can-sparkles::before { - content: "\f5d0"; } - -.fa-air-freshener::before { - content: "\f5d0"; } - -.fa-star::before { - content: "\f005"; } - -.fa-repeat::before { - content: "\f363"; } - -.fa-cross::before { - content: "\f654"; } - -.fa-box::before { - content: "\f466"; } - -.fa-venus-mars::before { - content: "\f228"; } - -.fa-arrow-pointer::before { - content: "\f245"; } - -.fa-mouse-pointer::before { - content: "\f245"; } - -.fa-maximize::before { - content: "\f31e"; } - -.fa-expand-arrows-alt::before { - content: "\f31e"; } - -.fa-charging-station::before { - content: "\f5e7"; } - -.fa-shapes::before { - content: "\f61f"; } - -.fa-triangle-circle-square::before { - content: "\f61f"; } - -.fa-shuffle::before { - content: "\f074"; } - -.fa-random::before { - content: "\f074"; } - -.fa-person-running::before { - content: "\f70c"; } - -.fa-running::before { - content: "\f70c"; } - -.fa-mobile-retro::before { - content: "\e527"; } - -.fa-grip-lines-vertical::before { - content: "\f7a5"; } - -.fa-spider::before { - content: "\f717"; } - -.fa-hands-bound::before { - content: "\e4f9"; } - -.fa-file-invoice-dollar::before { - content: "\f571"; } - -.fa-plane-circle-exclamation::before { - content: "\e556"; } - -.fa-x-ray::before { - content: "\f497"; } - -.fa-spell-check::before { - content: "\f891"; } - -.fa-slash::before { - content: "\f715"; } - -.fa-computer-mouse::before { - content: "\f8cc"; } - -.fa-mouse::before { - content: "\f8cc"; } - -.fa-arrow-right-to-bracket::before { - content: "\f090"; } - -.fa-sign-in::before { - content: "\f090"; } - -.fa-shop-slash::before { - content: "\e070"; } - -.fa-store-alt-slash::before { - content: "\e070"; } - -.fa-server::before { - content: "\f233"; } - -.fa-virus-covid-slash::before { - content: "\e4a9"; } - -.fa-shop-lock::before { - content: "\e4a5"; } - -.fa-hourglass-start::before { - content: "\f251"; } - -.fa-hourglass-1::before { - content: "\f251"; } - -.fa-blender-phone::before { - content: "\f6b6"; } - -.fa-building-wheat::before { - content: "\e4db"; } - -.fa-person-breastfeeding::before { - content: "\e53a"; } - -.fa-right-to-bracket::before { - content: "\f2f6"; } - -.fa-sign-in-alt::before { - content: "\f2f6"; } - -.fa-venus::before { - content: "\f221"; } - -.fa-passport::before { - content: "\f5ab"; } - -.fa-heart-pulse::before { - content: "\f21e"; } - -.fa-heartbeat::before { - content: "\f21e"; } - -.fa-people-carry-box::before { - content: "\f4ce"; } - -.fa-people-carry::before { - content: "\f4ce"; } - -.fa-temperature-high::before { - content: "\f769"; } - -.fa-microchip::before { - content: "\f2db"; } - -.fa-crown::before { - content: "\f521"; } - -.fa-weight-hanging::before { - content: "\f5cd"; } - -.fa-xmarks-lines::before { - content: "\e59a"; } - -.fa-file-prescription::before { - content: "\f572"; } - -.fa-weight-scale::before { - content: "\f496"; } - -.fa-weight::before { - content: "\f496"; } - -.fa-user-group::before { - content: "\f500"; } - -.fa-user-friends::before { - content: "\f500"; } - -.fa-arrow-up-a-z::before { - content: "\f15e"; } - -.fa-sort-alpha-up::before { - content: "\f15e"; } - -.fa-chess-knight::before { - content: "\f441"; } - -.fa-face-laugh-squint::before { - content: "\f59b"; } - -.fa-laugh-squint::before { - content: "\f59b"; } - -.fa-wheelchair::before { - content: "\f193"; } - -.fa-circle-arrow-up::before { - content: "\f0aa"; } - -.fa-arrow-circle-up::before { - content: "\f0aa"; } - -.fa-toggle-on::before { - content: "\f205"; } - -.fa-person-walking::before { - content: "\f554"; } - -.fa-walking::before { - content: "\f554"; } - -.fa-l::before { - content: "\4c"; } - -.fa-fire::before { - content: "\f06d"; } - -.fa-bed-pulse::before { - content: "\f487"; } - -.fa-procedures::before { - content: "\f487"; } - -.fa-shuttle-space::before { - content: "\f197"; } - -.fa-space-shuttle::before { - content: "\f197"; } - -.fa-face-laugh::before { - content: "\f599"; } - -.fa-laugh::before { - content: "\f599"; } - -.fa-folder-open::before { - content: "\f07c"; } - -.fa-heart-circle-plus::before { - content: "\e500"; } - -.fa-code-fork::before { - content: "\e13b"; } - -.fa-city::before { - content: "\f64f"; } - -.fa-microphone-lines::before { - content: "\f3c9"; } - -.fa-microphone-alt::before { - content: "\f3c9"; } - -.fa-pepper-hot::before { - content: "\f816"; } - -.fa-unlock::before { - content: "\f09c"; } - -.fa-colon-sign::before { - content: "\e140"; } - -.fa-headset::before { - content: "\f590"; } - -.fa-store-slash::before { - content: "\e071"; } - -.fa-road-circle-xmark::before { - content: "\e566"; } - -.fa-user-minus::before { - content: "\f503"; } - -.fa-mars-stroke-up::before { - content: "\f22a"; } - -.fa-mars-stroke-v::before { - content: "\f22a"; } - -.fa-champagne-glasses::before { - content: "\f79f"; } - -.fa-glass-cheers::before { - content: "\f79f"; } - -.fa-clipboard::before { - content: "\f328"; } - -.fa-house-circle-exclamation::before { - content: "\e50a"; } - -.fa-file-arrow-up::before { - content: "\f574"; } - -.fa-file-upload::before { - content: "\f574"; } - -.fa-wifi::before { - content: "\f1eb"; } - -.fa-wifi-3::before { - content: "\f1eb"; } - -.fa-wifi-strong::before { - content: "\f1eb"; } - -.fa-bath::before { - content: "\f2cd"; } - -.fa-bathtub::before { - content: "\f2cd"; } - -.fa-underline::before { - content: "\f0cd"; } - -.fa-user-pen::before { - content: "\f4ff"; } - -.fa-user-edit::before { - content: "\f4ff"; } - -.fa-signature::before { - content: "\f5b7"; } - -.fa-stroopwafel::before { - content: "\f551"; } - -.fa-bold::before { - content: "\f032"; } - -.fa-anchor-lock::before { - content: "\e4ad"; } - -.fa-building-ngo::before { - content: "\e4d7"; } - -.fa-manat-sign::before { - content: "\e1d5"; } - -.fa-not-equal::before { - content: "\f53e"; } - -.fa-border-top-left::before { - content: "\f853"; } - -.fa-border-style::before { - content: "\f853"; } - -.fa-map-location-dot::before { - content: "\f5a0"; } - -.fa-map-marked-alt::before { - content: "\f5a0"; } - -.fa-jedi::before { - content: "\f669"; } - -.fa-square-poll-vertical::before { - content: "\f681"; } - -.fa-poll::before { - content: "\f681"; } - -.fa-mug-hot::before { - content: "\f7b6"; } - -.fa-car-battery::before { - content: "\f5df"; } - -.fa-battery-car::before { - content: "\f5df"; } - -.fa-gift::before { - content: "\f06b"; } - -.fa-dice-two::before { - content: "\f528"; } - -.fa-chess-queen::before { - content: "\f445"; } - -.fa-glasses::before { - content: "\f530"; } - -.fa-chess-board::before { - content: "\f43c"; } - -.fa-building-circle-check::before { - content: "\e4d2"; } - -.fa-person-chalkboard::before { - content: "\e53d"; } - -.fa-mars-stroke-right::before { - content: "\f22b"; } - -.fa-mars-stroke-h::before { - content: "\f22b"; } - -.fa-hand-back-fist::before { - content: "\f255"; } - -.fa-hand-rock::before { - content: "\f255"; } - -.fa-square-caret-up::before { - content: "\f151"; } - -.fa-caret-square-up::before { - content: "\f151"; } - -.fa-cloud-showers-water::before { - content: "\e4e4"; } - -.fa-chart-bar::before { - content: "\f080"; } - -.fa-bar-chart::before { - content: "\f080"; } - -.fa-hands-bubbles::before { - content: "\e05e"; } - -.fa-hands-wash::before { - content: "\e05e"; } - -.fa-less-than-equal::before { - content: "\f537"; } - -.fa-train::before { - content: "\f238"; } - -.fa-eye-low-vision::before { - content: "\f2a8"; } - -.fa-low-vision::before { - content: "\f2a8"; } - -.fa-crow::before { - content: "\f520"; } - -.fa-sailboat::before { - content: "\e445"; } - -.fa-window-restore::before { - content: "\f2d2"; } - -.fa-square-plus::before { - content: "\f0fe"; } - -.fa-plus-square::before { - content: "\f0fe"; } - -.fa-torii-gate::before { - content: "\f6a1"; } - -.fa-frog::before { - content: "\f52e"; } - -.fa-bucket::before { - content: "\e4cf"; } - -.fa-image::before { - content: "\f03e"; } - -.fa-microphone::before { - content: "\f130"; } - -.fa-cow::before { - content: "\f6c8"; } - -.fa-caret-up::before { - content: "\f0d8"; } - -.fa-screwdriver::before { - content: "\f54a"; } - -.fa-folder-closed::before { - content: "\e185"; } - -.fa-house-tsunami::before { - content: "\e515"; } - -.fa-square-nfi::before { - content: "\e576"; } - -.fa-arrow-up-from-ground-water::before { - content: "\e4b5"; } - -.fa-martini-glass::before { - content: "\f57b"; } - -.fa-glass-martini-alt::before { - content: "\f57b"; } - -.fa-rotate-left::before { - content: "\f2ea"; } - -.fa-rotate-back::before { - content: "\f2ea"; } - -.fa-rotate-backward::before { - content: "\f2ea"; } - -.fa-undo-alt::before { - content: "\f2ea"; } - -.fa-table-columns::before { - content: "\f0db"; } - -.fa-columns::before { - content: "\f0db"; } - -.fa-lemon::before { - content: "\f094"; } - -.fa-head-side-mask::before { - content: "\e063"; } - -.fa-handshake::before { - content: "\f2b5"; } - -.fa-gem::before { - content: "\f3a5"; } - -.fa-dolly::before { - content: "\f472"; } - -.fa-dolly-box::before { - content: "\f472"; } - -.fa-smoking::before { - content: "\f48d"; } - -.fa-minimize::before { - content: "\f78c"; } - -.fa-compress-arrows-alt::before { - content: "\f78c"; } - -.fa-monument::before { - content: "\f5a6"; } - -.fa-snowplow::before { - content: "\f7d2"; } - -.fa-angles-right::before { - content: "\f101"; } - -.fa-angle-double-right::before { - content: "\f101"; } - -.fa-cannabis::before { - content: "\f55f"; } - -.fa-circle-play::before { - content: "\f144"; } - -.fa-play-circle::before { - content: "\f144"; } - -.fa-tablets::before { - content: "\f490"; } - -.fa-ethernet::before { - content: "\f796"; } - -.fa-euro-sign::before { - content: "\f153"; } - -.fa-eur::before { - content: "\f153"; } - -.fa-euro::before { - content: "\f153"; } - -.fa-chair::before { - content: "\f6c0"; } - -.fa-circle-check::before { - content: "\f058"; } - -.fa-check-circle::before { - content: "\f058"; } - -.fa-circle-stop::before { - content: "\f28d"; } - -.fa-stop-circle::before { - content: "\f28d"; } - -.fa-compass-drafting::before { - content: "\f568"; } - -.fa-drafting-compass::before { - content: "\f568"; } - -.fa-plate-wheat::before { - content: "\e55a"; } - -.fa-icicles::before { - content: "\f7ad"; } - -.fa-person-shelter::before { - content: "\e54f"; } - -.fa-neuter::before { - content: "\f22c"; } - -.fa-id-badge::before { - content: "\f2c1"; } - -.fa-marker::before { - content: "\f5a1"; } - -.fa-face-laugh-beam::before { - content: "\f59a"; } - -.fa-laugh-beam::before { - content: "\f59a"; } - -.fa-helicopter-symbol::before { - content: "\e502"; } - -.fa-universal-access::before { - content: "\f29a"; } - -.fa-circle-chevron-up::before { - content: "\f139"; } - -.fa-chevron-circle-up::before { - content: "\f139"; } - -.fa-lari-sign::before { - content: "\e1c8"; } - -.fa-volcano::before { - content: "\f770"; } - -.fa-person-walking-dashed-line-arrow-right::before { - content: "\e553"; } - -.fa-sterling-sign::before { - content: "\f154"; } - -.fa-gbp::before { - content: "\f154"; } - -.fa-pound-sign::before { - content: "\f154"; } - -.fa-viruses::before { - content: "\e076"; } - -.fa-square-person-confined::before { - content: "\e577"; } - -.fa-user-tie::before { - content: "\f508"; } - -.fa-arrow-down-long::before { - content: "\f175"; } - -.fa-long-arrow-down::before { - content: "\f175"; } - -.fa-tent-arrow-down-to-line::before { - content: "\e57e"; } - -.fa-certificate::before { - content: "\f0a3"; } - -.fa-reply-all::before { - content: "\f122"; } - -.fa-mail-reply-all::before { - content: "\f122"; } - -.fa-suitcase::before { - content: "\f0f2"; } - -.fa-person-skating::before { - content: "\f7c5"; } - -.fa-skating::before { - content: "\f7c5"; } - -.fa-filter-circle-dollar::before { - content: "\f662"; } - -.fa-funnel-dollar::before { - content: "\f662"; } - -.fa-camera-retro::before { - content: "\f083"; } - -.fa-circle-arrow-down::before { - content: "\f0ab"; } - -.fa-arrow-circle-down::before { - content: "\f0ab"; } - -.fa-file-import::before { - content: "\f56f"; } - -.fa-arrow-right-to-file::before { - content: "\f56f"; } - -.fa-square-arrow-up-right::before { - content: "\f14c"; } - -.fa-external-link-square::before { - content: "\f14c"; } - -.fa-box-open::before { - content: "\f49e"; } - -.fa-scroll::before { - content: "\f70e"; } - -.fa-spa::before { - content: "\f5bb"; } - -.fa-location-pin-lock::before { - content: "\e51f"; } - -.fa-pause::before { - content: "\f04c"; } - -.fa-hill-avalanche::before { - content: "\e507"; } - -.fa-temperature-empty::before { - content: "\f2cb"; } - -.fa-temperature-0::before { - content: "\f2cb"; } - -.fa-thermometer-0::before { - content: "\f2cb"; } - -.fa-thermometer-empty::before { - content: "\f2cb"; } - -.fa-bomb::before { - content: "\f1e2"; } - -.fa-registered::before { - content: "\f25d"; } - -.fa-address-card::before { - content: "\f2bb"; } - -.fa-contact-card::before { - content: "\f2bb"; } - -.fa-vcard::before { - content: "\f2bb"; } - -.fa-scale-unbalanced-flip::before { - content: "\f516"; } - -.fa-balance-scale-right::before { - content: "\f516"; } - -.fa-subscript::before { - content: "\f12c"; } - -.fa-diamond-turn-right::before { - content: "\f5eb"; } - -.fa-directions::before { - content: "\f5eb"; } - -.fa-burst::before { - content: "\e4dc"; } - -.fa-house-laptop::before { - content: "\e066"; } - -.fa-laptop-house::before { - content: "\e066"; } - -.fa-face-tired::before { - content: "\f5c8"; } - -.fa-tired::before { - content: "\f5c8"; } - -.fa-money-bills::before { - content: "\e1f3"; } - -.fa-smog::before { - content: "\f75f"; } - -.fa-crutch::before { - content: "\f7f7"; } - -.fa-cloud-arrow-up::before { - content: "\f0ee"; } - -.fa-cloud-upload::before { - content: "\f0ee"; } - -.fa-cloud-upload-alt::before { - content: "\f0ee"; } - -.fa-palette::before { - content: "\f53f"; } - -.fa-arrows-turn-right::before { - content: "\e4c0"; } - -.fa-vest::before { - content: "\e085"; } - -.fa-ferry::before { - content: "\e4ea"; } - -.fa-arrows-down-to-people::before { - content: "\e4b9"; } - -.fa-seedling::before { - content: "\f4d8"; } - -.fa-sprout::before { - content: "\f4d8"; } - -.fa-left-right::before { - content: "\f337"; } - -.fa-arrows-alt-h::before { - content: "\f337"; } - -.fa-boxes-packing::before { - content: "\e4c7"; } - -.fa-circle-arrow-left::before { - content: "\f0a8"; } - -.fa-arrow-circle-left::before { - content: "\f0a8"; } - -.fa-group-arrows-rotate::before { - content: "\e4f6"; } - -.fa-bowl-food::before { - content: "\e4c6"; } - -.fa-candy-cane::before { - content: "\f786"; } - -.fa-arrow-down-wide-short::before { - content: "\f160"; } - -.fa-sort-amount-asc::before { - content: "\f160"; } - -.fa-sort-amount-down::before { - content: "\f160"; } - -.fa-cloud-bolt::before { - content: "\f76c"; } - -.fa-thunderstorm::before { - content: "\f76c"; } - -.fa-text-slash::before { - content: "\f87d"; } - -.fa-remove-format::before { - content: "\f87d"; } - -.fa-face-smile-wink::before { - content: "\f4da"; } - -.fa-smile-wink::before { - content: "\f4da"; } - -.fa-file-word::before { - content: "\f1c2"; } - -.fa-file-powerpoint::before { - content: "\f1c4"; } - -.fa-arrows-left-right::before { - content: "\f07e"; } - -.fa-arrows-h::before { - content: "\f07e"; } - -.fa-house-lock::before { - content: "\e510"; } - -.fa-cloud-arrow-down::before { - content: "\f0ed"; } - -.fa-cloud-download::before { - content: "\f0ed"; } - -.fa-cloud-download-alt::before { - content: "\f0ed"; } - -.fa-children::before { - content: "\e4e1"; } - -.fa-chalkboard::before { - content: "\f51b"; } - -.fa-blackboard::before { - content: "\f51b"; } - -.fa-user-large-slash::before { - content: "\f4fa"; } - -.fa-user-alt-slash::before { - content: "\f4fa"; } - -.fa-envelope-open::before { - content: "\f2b6"; } - -.fa-handshake-simple-slash::before { - content: "\e05f"; } - -.fa-handshake-alt-slash::before { - content: "\e05f"; } - -.fa-mattress-pillow::before { - content: "\e525"; } - -.fa-guarani-sign::before { - content: "\e19a"; } - -.fa-arrows-rotate::before { - content: "\f021"; } - -.fa-refresh::before { - content: "\f021"; } - -.fa-sync::before { - content: "\f021"; } - -.fa-fire-extinguisher::before { - content: "\f134"; } - -.fa-cruzeiro-sign::before { - content: "\e152"; } - -.fa-greater-than-equal::before { - content: "\f532"; } - -.fa-shield-halved::before { - content: "\f3ed"; } - -.fa-shield-alt::before { - content: "\f3ed"; } - -.fa-book-atlas::before { - content: "\f558"; } - -.fa-atlas::before { - content: "\f558"; } - -.fa-virus::before { - content: "\e074"; } - -.fa-envelope-circle-check::before { - content: "\e4e8"; } - -.fa-layer-group::before { - content: "\f5fd"; } - -.fa-arrows-to-dot::before { - content: "\e4be"; } - -.fa-archway::before { - content: "\f557"; } - -.fa-heart-circle-check::before { - content: "\e4fd"; } - -.fa-house-chimney-crack::before { - content: "\f6f1"; } - -.fa-house-damage::before { - content: "\f6f1"; } - -.fa-file-zipper::before { - content: "\f1c6"; } - -.fa-file-archive::before { - content: "\f1c6"; } - -.fa-square::before { - content: "\f0c8"; } - -.fa-martini-glass-empty::before { - content: "\f000"; } - -.fa-glass-martini::before { - content: "\f000"; } - -.fa-couch::before { - content: "\f4b8"; } - -.fa-cedi-sign::before { - content: "\e0df"; } - -.fa-italic::before { - content: "\f033"; } - -.fa-table-cells-column-lock::before { - content: "\e678"; } - -.fa-church::before { - content: "\f51d"; } - -.fa-comments-dollar::before { - content: "\f653"; } - -.fa-democrat::before { - content: "\f747"; } - -.fa-z::before { - content: "\5a"; } - -.fa-person-skiing::before { - content: "\f7c9"; } - -.fa-skiing::before { - content: "\f7c9"; } - -.fa-road-lock::before { - content: "\e567"; } - -.fa-a::before { - content: "\41"; } - -.fa-temperature-arrow-down::before { - content: "\e03f"; } - -.fa-temperature-down::before { - content: "\e03f"; } - -.fa-feather-pointed::before { - content: "\f56b"; } - -.fa-feather-alt::before { - content: "\f56b"; } - -.fa-p::before { - content: "\50"; } - -.fa-snowflake::before { - content: "\f2dc"; } - -.fa-newspaper::before { - content: "\f1ea"; } - -.fa-rectangle-ad::before { - content: "\f641"; } - -.fa-ad::before { - content: "\f641"; } - -.fa-circle-arrow-right::before { - content: "\f0a9"; } - -.fa-arrow-circle-right::before { - content: "\f0a9"; } - -.fa-filter-circle-xmark::before { - content: "\e17b"; } - -.fa-locust::before { - content: "\e520"; } - -.fa-sort::before { - content: "\f0dc"; } - -.fa-unsorted::before { - content: "\f0dc"; } - -.fa-list-ol::before { - content: "\f0cb"; } - -.fa-list-1-2::before { - content: "\f0cb"; } - -.fa-list-numeric::before { - content: "\f0cb"; } - -.fa-person-dress-burst::before { - content: "\e544"; } - -.fa-money-check-dollar::before { - content: "\f53d"; } - -.fa-money-check-alt::before { - content: "\f53d"; } - -.fa-vector-square::before { - content: "\f5cb"; } - -.fa-bread-slice::before { - content: "\f7ec"; } - -.fa-language::before { - content: "\f1ab"; } - -.fa-face-kiss-wink-heart::before { - content: "\f598"; } - -.fa-kiss-wink-heart::before { - content: "\f598"; } - -.fa-filter::before { - content: "\f0b0"; } - -.fa-question::before { - content: "\3f"; } - -.fa-file-signature::before { - content: "\f573"; } - -.fa-up-down-left-right::before { - content: "\f0b2"; } - -.fa-arrows-alt::before { - content: "\f0b2"; } - -.fa-house-chimney-user::before { - content: "\e065"; } - -.fa-hand-holding-heart::before { - content: "\f4be"; } - -.fa-puzzle-piece::before { - content: "\f12e"; } - -.fa-money-check::before { - content: "\f53c"; } - -.fa-star-half-stroke::before { - content: "\f5c0"; } - -.fa-star-half-alt::before { - content: "\f5c0"; } - -.fa-code::before { - content: "\f121"; } - -.fa-whiskey-glass::before { - content: "\f7a0"; } - -.fa-glass-whiskey::before { - content: "\f7a0"; } - -.fa-building-circle-exclamation::before { - content: "\e4d3"; } - -.fa-magnifying-glass-chart::before { - content: "\e522"; } - -.fa-arrow-up-right-from-square::before { - content: "\f08e"; } - -.fa-external-link::before { - content: "\f08e"; } - -.fa-cubes-stacked::before { - content: "\e4e6"; } - -.fa-won-sign::before { - content: "\f159"; } - -.fa-krw::before { - content: "\f159"; } - -.fa-won::before { - content: "\f159"; } - -.fa-virus-covid::before { - content: "\e4a8"; } - -.fa-austral-sign::before { - content: "\e0a9"; } - -.fa-f::before { - content: "\46"; } - -.fa-leaf::before { - content: "\f06c"; } - -.fa-road::before { - content: "\f018"; } - -.fa-taxi::before { - content: "\f1ba"; } - -.fa-cab::before { - content: "\f1ba"; } - -.fa-person-circle-plus::before { - content: "\e541"; } - -.fa-chart-pie::before { - content: "\f200"; } - -.fa-pie-chart::before { - content: "\f200"; } - -.fa-bolt-lightning::before { - content: "\e0b7"; } - -.fa-sack-xmark::before { - content: "\e56a"; } - -.fa-file-excel::before { - content: "\f1c3"; } - -.fa-file-contract::before { - content: "\f56c"; } - -.fa-fish-fins::before { - content: "\e4f2"; } - -.fa-building-flag::before { - content: "\e4d5"; } - -.fa-face-grin-beam::before { - content: "\f582"; } - -.fa-grin-beam::before { - content: "\f582"; } - -.fa-object-ungroup::before { - content: "\f248"; } - -.fa-poop::before { - content: "\f619"; } - -.fa-location-pin::before { - content: "\f041"; } - -.fa-map-marker::before { - content: "\f041"; } - -.fa-kaaba::before { - content: "\f66b"; } - -.fa-toilet-paper::before { - content: "\f71e"; } - -.fa-helmet-safety::before { - content: "\f807"; } - -.fa-hard-hat::before { - content: "\f807"; } - -.fa-hat-hard::before { - content: "\f807"; } - -.fa-eject::before { - content: "\f052"; } - -.fa-circle-right::before { - content: "\f35a"; } - -.fa-arrow-alt-circle-right::before { - content: "\f35a"; } - -.fa-plane-circle-check::before { - content: "\e555"; } - -.fa-face-rolling-eyes::before { - content: "\f5a5"; } - -.fa-meh-rolling-eyes::before { - content: "\f5a5"; } - -.fa-object-group::before { - content: "\f247"; } - -.fa-chart-line::before { - content: "\f201"; } - -.fa-line-chart::before { - content: "\f201"; } - -.fa-mask-ventilator::before { - content: "\e524"; } - -.fa-arrow-right::before { - content: "\f061"; } - -.fa-signs-post::before { - content: "\f277"; } - -.fa-map-signs::before { - content: "\f277"; } - -.fa-cash-register::before { - content: "\f788"; } - -.fa-person-circle-question::before { - content: "\e542"; } - -.fa-h::before { - content: "\48"; } - -.fa-tarp::before { - content: "\e57b"; } - -.fa-screwdriver-wrench::before { - content: "\f7d9"; } - -.fa-tools::before { - content: "\f7d9"; } - -.fa-arrows-to-eye::before { - content: "\e4bf"; } - -.fa-plug-circle-bolt::before { - content: "\e55b"; } - -.fa-heart::before { - content: "\f004"; } - -.fa-mars-and-venus::before { - content: "\f224"; } - -.fa-house-user::before { - content: "\e1b0"; } - -.fa-home-user::before { - content: "\e1b0"; } - -.fa-dumpster-fire::before { - content: "\f794"; } - -.fa-house-crack::before { - content: "\e3b1"; } - -.fa-martini-glass-citrus::before { - content: "\f561"; } - -.fa-cocktail::before { - content: "\f561"; } - -.fa-face-surprise::before { - content: "\f5c2"; } - -.fa-surprise::before { - content: "\f5c2"; } - -.fa-bottle-water::before { - content: "\e4c5"; } - -.fa-circle-pause::before { - content: "\f28b"; } - -.fa-pause-circle::before { - content: "\f28b"; } - -.fa-toilet-paper-slash::before { - content: "\e072"; } - -.fa-apple-whole::before { - content: "\f5d1"; } - -.fa-apple-alt::before { - content: "\f5d1"; } - -.fa-kitchen-set::before { - content: "\e51a"; } - -.fa-r::before { - content: "\52"; } - -.fa-temperature-quarter::before { - content: "\f2ca"; } - -.fa-temperature-1::before { - content: "\f2ca"; } - -.fa-thermometer-1::before { - content: "\f2ca"; } - -.fa-thermometer-quarter::before { - content: "\f2ca"; } - -.fa-cube::before { - content: "\f1b2"; } - -.fa-bitcoin-sign::before { - content: "\e0b4"; } - -.fa-shield-dog::before { - content: "\e573"; } - -.fa-solar-panel::before { - content: "\f5ba"; } - -.fa-lock-open::before { - content: "\f3c1"; } - -.fa-elevator::before { - content: "\e16d"; } - -.fa-money-bill-transfer::before { - content: "\e528"; } - -.fa-money-bill-trend-up::before { - content: "\e529"; } - -.fa-house-flood-water-circle-arrow-right::before { - content: "\e50f"; } - -.fa-square-poll-horizontal::before { - content: "\f682"; } - -.fa-poll-h::before { - content: "\f682"; } - -.fa-circle::before { - content: "\f111"; } - -.fa-backward-fast::before { - content: "\f049"; } - -.fa-fast-backward::before { - content: "\f049"; } - -.fa-recycle::before { - content: "\f1b8"; } - -.fa-user-astronaut::before { - content: "\f4fb"; } - -.fa-plane-slash::before { - content: "\e069"; } - -.fa-trademark::before { - content: "\f25c"; } - -.fa-basketball::before { - content: "\f434"; } - -.fa-basketball-ball::before { - content: "\f434"; } - -.fa-satellite-dish::before { - content: "\f7c0"; } - -.fa-circle-up::before { - content: "\f35b"; } - -.fa-arrow-alt-circle-up::before { - content: "\f35b"; } - -.fa-mobile-screen-button::before { - content: "\f3cd"; } - -.fa-mobile-alt::before { - content: "\f3cd"; } - -.fa-volume-high::before { - content: "\f028"; } - -.fa-volume-up::before { - content: "\f028"; } - -.fa-users-rays::before { - content: "\e593"; } - -.fa-wallet::before { - content: "\f555"; } - -.fa-clipboard-check::before { - content: "\f46c"; } - -.fa-file-audio::before { - content: "\f1c7"; } - -.fa-burger::before { - content: "\f805"; } - -.fa-hamburger::before { - content: "\f805"; } - -.fa-wrench::before { - content: "\f0ad"; } - -.fa-bugs::before { - content: "\e4d0"; } - -.fa-rupee-sign::before { - content: "\f156"; } - -.fa-rupee::before { - content: "\f156"; } - -.fa-file-image::before { - content: "\f1c5"; } - -.fa-circle-question::before { - content: "\f059"; } - -.fa-question-circle::before { - content: "\f059"; } - -.fa-plane-departure::before { - content: "\f5b0"; } - -.fa-handshake-slash::before { - content: "\e060"; } - -.fa-book-bookmark::before { - content: "\e0bb"; } - -.fa-code-branch::before { - content: "\f126"; } - -.fa-hat-cowboy::before { - content: "\f8c0"; } - -.fa-bridge::before { - content: "\e4c8"; } - -.fa-phone-flip::before { - content: "\f879"; } - -.fa-phone-alt::before { - content: "\f879"; } - -.fa-truck-front::before { - content: "\e2b7"; } - -.fa-cat::before { - content: "\f6be"; } - -.fa-anchor-circle-exclamation::before { - content: "\e4ab"; } - -.fa-truck-field::before { - content: "\e58d"; } - -.fa-route::before { - content: "\f4d7"; } - -.fa-clipboard-question::before { - content: "\e4e3"; } - -.fa-panorama::before { - content: "\e209"; } - -.fa-comment-medical::before { - content: "\f7f5"; } - -.fa-teeth-open::before { - content: "\f62f"; } - -.fa-file-circle-minus::before { - content: "\e4ed"; } - -.fa-tags::before { - content: "\f02c"; } - -.fa-wine-glass::before { - content: "\f4e3"; } - -.fa-forward-fast::before { - content: "\f050"; } - -.fa-fast-forward::before { - content: "\f050"; } - -.fa-face-meh-blank::before { - content: "\f5a4"; } - -.fa-meh-blank::before { - content: "\f5a4"; } - -.fa-square-parking::before { - content: "\f540"; } - -.fa-parking::before { - content: "\f540"; } - -.fa-house-signal::before { - content: "\e012"; } - -.fa-bars-progress::before { - content: "\f828"; } - -.fa-tasks-alt::before { - content: "\f828"; } - -.fa-faucet-drip::before { - content: "\e006"; } - -.fa-cart-flatbed::before { - content: "\f474"; } - -.fa-dolly-flatbed::before { - content: "\f474"; } - -.fa-ban-smoking::before { - content: "\f54d"; } - -.fa-smoking-ban::before { - content: "\f54d"; } - -.fa-terminal::before { - content: "\f120"; } - -.fa-mobile-button::before { - content: "\f10b"; } - -.fa-house-medical-flag::before { - content: "\e514"; } - -.fa-basket-shopping::before { - content: "\f291"; } - -.fa-shopping-basket::before { - content: "\f291"; } - -.fa-tape::before { - content: "\f4db"; } - -.fa-bus-simple::before { - content: "\f55e"; } - -.fa-bus-alt::before { - content: "\f55e"; } - -.fa-eye::before { - content: "\f06e"; } - -.fa-face-sad-cry::before { - content: "\f5b3"; } - -.fa-sad-cry::before { - content: "\f5b3"; } - -.fa-audio-description::before { - content: "\f29e"; } - -.fa-person-military-to-person::before { - content: "\e54c"; } - -.fa-file-shield::before { - content: "\e4f0"; } - -.fa-user-slash::before { - content: "\f506"; } - -.fa-pen::before { - content: "\f304"; } - -.fa-tower-observation::before { - content: "\e586"; } - -.fa-file-code::before { - content: "\f1c9"; } - -.fa-signal::before { - content: "\f012"; } - -.fa-signal-5::before { - content: "\f012"; } - -.fa-signal-perfect::before { - content: "\f012"; } - -.fa-bus::before { - content: "\f207"; } - -.fa-heart-circle-xmark::before { - content: "\e501"; } - -.fa-house-chimney::before { - content: "\e3af"; } - -.fa-home-lg::before { - content: "\e3af"; } - -.fa-window-maximize::before { - content: "\f2d0"; } - -.fa-face-frown::before { - content: "\f119"; } - -.fa-frown::before { - content: "\f119"; } - -.fa-prescription::before { - content: "\f5b1"; } - -.fa-shop::before { - content: "\f54f"; } - -.fa-store-alt::before { - content: "\f54f"; } - -.fa-floppy-disk::before { - content: "\f0c7"; } - -.fa-save::before { - content: "\f0c7"; } - -.fa-vihara::before { - content: "\f6a7"; } - -.fa-scale-unbalanced::before { - content: "\f515"; } - -.fa-balance-scale-left::before { - content: "\f515"; } - -.fa-sort-up::before { - content: "\f0de"; } - -.fa-sort-asc::before { - content: "\f0de"; } - -.fa-comment-dots::before { - content: "\f4ad"; } - -.fa-commenting::before { - content: "\f4ad"; } - -.fa-plant-wilt::before { - content: "\e5aa"; } - -.fa-diamond::before { - content: "\f219"; } - -.fa-face-grin-squint::before { - content: "\f585"; } - -.fa-grin-squint::before { - content: "\f585"; } - -.fa-hand-holding-dollar::before { - content: "\f4c0"; } - -.fa-hand-holding-usd::before { - content: "\f4c0"; } - -.fa-bacterium::before { - content: "\e05a"; } - -.fa-hand-pointer::before { - content: "\f25a"; } - -.fa-drum-steelpan::before { - content: "\f56a"; } - -.fa-hand-scissors::before { - content: "\f257"; } - -.fa-hands-praying::before { - content: "\f684"; } - -.fa-praying-hands::before { - content: "\f684"; } - -.fa-arrow-rotate-right::before { - content: "\f01e"; } - -.fa-arrow-right-rotate::before { - content: "\f01e"; } - -.fa-arrow-rotate-forward::before { - content: "\f01e"; } - -.fa-redo::before { - content: "\f01e"; } - -.fa-biohazard::before { - content: "\f780"; } - -.fa-location-crosshairs::before { - content: "\f601"; } - -.fa-location::before { - content: "\f601"; } - -.fa-mars-double::before { - content: "\f227"; } - -.fa-child-dress::before { - content: "\e59c"; } - -.fa-users-between-lines::before { - content: "\e591"; } - -.fa-lungs-virus::before { - content: "\e067"; } - -.fa-face-grin-tears::before { - content: "\f588"; } - -.fa-grin-tears::before { - content: "\f588"; } - -.fa-phone::before { - content: "\f095"; } - -.fa-calendar-xmark::before { - content: "\f273"; } - -.fa-calendar-times::before { - content: "\f273"; } - -.fa-child-reaching::before { - content: "\e59d"; } - -.fa-head-side-virus::before { - content: "\e064"; } - -.fa-user-gear::before { - content: "\f4fe"; } - -.fa-user-cog::before { - content: "\f4fe"; } - -.fa-arrow-up-1-9::before { - content: "\f163"; } - -.fa-sort-numeric-up::before { - content: "\f163"; } - -.fa-door-closed::before { - content: "\f52a"; } - -.fa-shield-virus::before { - content: "\e06c"; } - -.fa-dice-six::before { - content: "\f526"; } - -.fa-mosquito-net::before { - content: "\e52c"; } - -.fa-bridge-water::before { - content: "\e4ce"; } - -.fa-person-booth::before { - content: "\f756"; } - -.fa-text-width::before { - content: "\f035"; } - -.fa-hat-wizard::before { - content: "\f6e8"; } - -.fa-pen-fancy::before { - content: "\f5ac"; } - -.fa-person-digging::before { - content: "\f85e"; } - -.fa-digging::before { - content: "\f85e"; } - -.fa-trash::before { - content: "\f1f8"; } - -.fa-gauge-simple::before { - content: "\f629"; } - -.fa-gauge-simple-med::before { - content: "\f629"; } - -.fa-tachometer-average::before { - content: "\f629"; } - -.fa-book-medical::before { - content: "\f7e6"; } - -.fa-poo::before { - content: "\f2fe"; } - -.fa-quote-right::before { - content: "\f10e"; } - -.fa-quote-right-alt::before { - content: "\f10e"; } - -.fa-shirt::before { - content: "\f553"; } - -.fa-t-shirt::before { - content: "\f553"; } - -.fa-tshirt::before { - content: "\f553"; } - -.fa-cubes::before { - content: "\f1b3"; } - -.fa-divide::before { - content: "\f529"; } - -.fa-tenge-sign::before { - content: "\f7d7"; } - -.fa-tenge::before { - content: "\f7d7"; } - -.fa-headphones::before { - content: "\f025"; } - -.fa-hands-holding::before { - content: "\f4c2"; } - -.fa-hands-clapping::before { - content: "\e1a8"; } - -.fa-republican::before { - content: "\f75e"; } - -.fa-arrow-left::before { - content: "\f060"; } - -.fa-person-circle-xmark::before { - content: "\e543"; } - -.fa-ruler::before { - content: "\f545"; } - -.fa-align-left::before { - content: "\f036"; } - -.fa-dice-d6::before { - content: "\f6d1"; } - -.fa-restroom::before { - content: "\f7bd"; } - -.fa-j::before { - content: "\4a"; } - -.fa-users-viewfinder::before { - content: "\e595"; } - -.fa-file-video::before { - content: "\f1c8"; } - -.fa-up-right-from-square::before { - content: "\f35d"; } - -.fa-external-link-alt::before { - content: "\f35d"; } - -.fa-table-cells::before { - content: "\f00a"; } - -.fa-th::before { - content: "\f00a"; } - -.fa-file-pdf::before { - content: "\f1c1"; } - -.fa-book-bible::before { - content: "\f647"; } - -.fa-bible::before { - content: "\f647"; } - -.fa-o::before { - content: "\4f"; } - -.fa-suitcase-medical::before { - content: "\f0fa"; } - -.fa-medkit::before { - content: "\f0fa"; } - -.fa-user-secret::before { - content: "\f21b"; } - -.fa-otter::before { - content: "\f700"; } - -.fa-person-dress::before { - content: "\f182"; } - -.fa-female::before { - content: "\f182"; } - -.fa-comment-dollar::before { - content: "\f651"; } - -.fa-business-time::before { - content: "\f64a"; } - -.fa-briefcase-clock::before { - content: "\f64a"; } - -.fa-table-cells-large::before { - content: "\f009"; } - -.fa-th-large::before { - content: "\f009"; } - -.fa-book-tanakh::before { - content: "\f827"; } - -.fa-tanakh::before { - content: "\f827"; } - -.fa-phone-volume::before { - content: "\f2a0"; } - -.fa-volume-control-phone::before { - content: "\f2a0"; } - -.fa-hat-cowboy-side::before { - content: "\f8c1"; } - -.fa-clipboard-user::before { - content: "\f7f3"; } - -.fa-child::before { - content: "\f1ae"; } - -.fa-lira-sign::before { - content: "\f195"; } - -.fa-satellite::before { - content: "\f7bf"; } - -.fa-plane-lock::before { - content: "\e558"; } - -.fa-tag::before { - content: "\f02b"; } - -.fa-comment::before { - content: "\f075"; } - -.fa-cake-candles::before { - content: "\f1fd"; } - -.fa-birthday-cake::before { - content: "\f1fd"; } - -.fa-cake::before { - content: "\f1fd"; } - -.fa-envelope::before { - content: "\f0e0"; } - -.fa-angles-up::before { - content: "\f102"; } - -.fa-angle-double-up::before { - content: "\f102"; } - -.fa-paperclip::before { - content: "\f0c6"; } - -.fa-arrow-right-to-city::before { - content: "\e4b3"; } - -.fa-ribbon::before { - content: "\f4d6"; } - -.fa-lungs::before { - content: "\f604"; } - -.fa-arrow-up-9-1::before { - content: "\f887"; } - -.fa-sort-numeric-up-alt::before { - content: "\f887"; } - -.fa-litecoin-sign::before { - content: "\e1d3"; } - -.fa-border-none::before { - content: "\f850"; } - -.fa-circle-nodes::before { - content: "\e4e2"; } - -.fa-parachute-box::before { - content: "\f4cd"; } - -.fa-indent::before { - content: "\f03c"; } - -.fa-truck-field-un::before { - content: "\e58e"; } - -.fa-hourglass::before { - content: "\f254"; } - -.fa-hourglass-empty::before { - content: "\f254"; } - -.fa-mountain::before { - content: "\f6fc"; } - -.fa-user-doctor::before { - content: "\f0f0"; } - -.fa-user-md::before { - content: "\f0f0"; } - -.fa-circle-info::before { - content: "\f05a"; } - -.fa-info-circle::before { - content: "\f05a"; } - -.fa-cloud-meatball::before { - content: "\f73b"; } - -.fa-camera::before { - content: "\f030"; } - -.fa-camera-alt::before { - content: "\f030"; } - -.fa-square-virus::before { - content: "\e578"; } - -.fa-meteor::before { - content: "\f753"; } - -.fa-car-on::before { - content: "\e4dd"; } - -.fa-sleigh::before { - content: "\f7cc"; } - -.fa-arrow-down-1-9::before { - content: "\f162"; } - -.fa-sort-numeric-asc::before { - content: "\f162"; } - -.fa-sort-numeric-down::before { - content: "\f162"; } - -.fa-hand-holding-droplet::before { - content: "\f4c1"; } - -.fa-hand-holding-water::before { - content: "\f4c1"; } - -.fa-water::before { - content: "\f773"; } - -.fa-calendar-check::before { - content: "\f274"; } - -.fa-braille::before { - content: "\f2a1"; } - -.fa-prescription-bottle-medical::before { - content: "\f486"; } - -.fa-prescription-bottle-alt::before { - content: "\f486"; } - -.fa-landmark::before { - content: "\f66f"; } - -.fa-truck::before { - content: "\f0d1"; } - -.fa-crosshairs::before { - content: "\f05b"; } - -.fa-person-cane::before { - content: "\e53c"; } - -.fa-tent::before { - content: "\e57d"; } - -.fa-vest-patches::before { - content: "\e086"; } - -.fa-check-double::before { - content: "\f560"; } - -.fa-arrow-down-a-z::before { - content: "\f15d"; } - -.fa-sort-alpha-asc::before { - content: "\f15d"; } - -.fa-sort-alpha-down::before { - content: "\f15d"; } - -.fa-money-bill-wheat::before { - content: "\e52a"; } - -.fa-cookie::before { - content: "\f563"; } - -.fa-arrow-rotate-left::before { - content: "\f0e2"; } - -.fa-arrow-left-rotate::before { - content: "\f0e2"; } - -.fa-arrow-rotate-back::before { - content: "\f0e2"; } - -.fa-arrow-rotate-backward::before { - content: "\f0e2"; } - -.fa-undo::before { - content: "\f0e2"; } - -.fa-hard-drive::before { - content: "\f0a0"; } - -.fa-hdd::before { - content: "\f0a0"; } - -.fa-face-grin-squint-tears::before { - content: "\f586"; } - -.fa-grin-squint-tears::before { - content: "\f586"; } - -.fa-dumbbell::before { - content: "\f44b"; } - -.fa-rectangle-list::before { - content: "\f022"; } - -.fa-list-alt::before { - content: "\f022"; } - -.fa-tarp-droplet::before { - content: "\e57c"; } - -.fa-house-medical-circle-check::before { - content: "\e511"; } - -.fa-person-skiing-nordic::before { - content: "\f7ca"; } - -.fa-skiing-nordic::before { - content: "\f7ca"; } - -.fa-calendar-plus::before { - content: "\f271"; } - -.fa-plane-arrival::before { - content: "\f5af"; } - -.fa-circle-left::before { - content: "\f359"; } - -.fa-arrow-alt-circle-left::before { - content: "\f359"; } - -.fa-train-subway::before { - content: "\f239"; } - -.fa-subway::before { - content: "\f239"; } - -.fa-chart-gantt::before { - content: "\e0e4"; } - -.fa-indian-rupee-sign::before { - content: "\e1bc"; } - -.fa-indian-rupee::before { - content: "\e1bc"; } - -.fa-inr::before { - content: "\e1bc"; } - -.fa-crop-simple::before { - content: "\f565"; } - -.fa-crop-alt::before { - content: "\f565"; } - -.fa-money-bill-1::before { - content: "\f3d1"; } - -.fa-money-bill-alt::before { - content: "\f3d1"; } - -.fa-left-long::before { - content: "\f30a"; } - -.fa-long-arrow-alt-left::before { - content: "\f30a"; } - -.fa-dna::before { - content: "\f471"; } - -.fa-virus-slash::before { - content: "\e075"; } - -.fa-minus::before { - content: "\f068"; } - -.fa-subtract::before { - content: "\f068"; } - -.fa-chess::before { - content: "\f439"; } - -.fa-arrow-left-long::before { - content: "\f177"; } - -.fa-long-arrow-left::before { - content: "\f177"; } - -.fa-plug-circle-check::before { - content: "\e55c"; } - -.fa-street-view::before { - content: "\f21d"; } - -.fa-franc-sign::before { - content: "\e18f"; } - -.fa-volume-off::before { - content: "\f026"; } - -.fa-hands-asl-interpreting::before { - content: "\f2a3"; } - -.fa-american-sign-language-interpreting::before { - content: "\f2a3"; } - -.fa-asl-interpreting::before { - content: "\f2a3"; } - -.fa-hands-american-sign-language-interpreting::before { - content: "\f2a3"; } - -.fa-gear::before { - content: "\f013"; } - -.fa-cog::before { - content: "\f013"; } - -.fa-droplet-slash::before { - content: "\f5c7"; } - -.fa-tint-slash::before { - content: "\f5c7"; } - -.fa-mosque::before { - content: "\f678"; } - -.fa-mosquito::before { - content: "\e52b"; } - -.fa-star-of-david::before { - content: "\f69a"; } - -.fa-person-military-rifle::before { - content: "\e54b"; } - -.fa-cart-shopping::before { - content: "\f07a"; } - -.fa-shopping-cart::before { - content: "\f07a"; } - -.fa-vials::before { - content: "\f493"; } - -.fa-plug-circle-plus::before { - content: "\e55f"; } - -.fa-place-of-worship::before { - content: "\f67f"; } - -.fa-grip-vertical::before { - content: "\f58e"; } - -.fa-arrow-turn-up::before { - content: "\f148"; } - -.fa-level-up::before { - content: "\f148"; } - -.fa-u::before { - content: "\55"; } - -.fa-square-root-variable::before { - content: "\f698"; } - -.fa-square-root-alt::before { - content: "\f698"; } - -.fa-clock::before { - content: "\f017"; } - -.fa-clock-four::before { - content: "\f017"; } - -.fa-backward-step::before { - content: "\f048"; } - -.fa-step-backward::before { - content: "\f048"; } - -.fa-pallet::before { - content: "\f482"; } - -.fa-faucet::before { - content: "\e005"; } - -.fa-baseball-bat-ball::before { - content: "\f432"; } - -.fa-s::before { - content: "\53"; } - -.fa-timeline::before { - content: "\e29c"; } - -.fa-keyboard::before { - content: "\f11c"; } - -.fa-caret-down::before { - content: "\f0d7"; } - -.fa-house-chimney-medical::before { - content: "\f7f2"; } - -.fa-clinic-medical::before { - content: "\f7f2"; } - -.fa-temperature-three-quarters::before { - content: "\f2c8"; } - -.fa-temperature-3::before { - content: "\f2c8"; } - -.fa-thermometer-3::before { - content: "\f2c8"; } - -.fa-thermometer-three-quarters::before { - content: "\f2c8"; } - -.fa-mobile-screen::before { - content: "\f3cf"; } - -.fa-mobile-android-alt::before { - content: "\f3cf"; } - -.fa-plane-up::before { - content: "\e22d"; } - -.fa-piggy-bank::before { - content: "\f4d3"; } - -.fa-battery-half::before { - content: "\f242"; } - -.fa-battery-3::before { - content: "\f242"; } - -.fa-mountain-city::before { - content: "\e52e"; } - -.fa-coins::before { - content: "\f51e"; } - -.fa-khanda::before { - content: "\f66d"; } - -.fa-sliders::before { - content: "\f1de"; } - -.fa-sliders-h::before { - content: "\f1de"; } - -.fa-folder-tree::before { - content: "\f802"; } - -.fa-network-wired::before { - content: "\f6ff"; } - -.fa-map-pin::before { - content: "\f276"; } - -.fa-hamsa::before { - content: "\f665"; } - -.fa-cent-sign::before { - content: "\e3f5"; } - -.fa-flask::before { - content: "\f0c3"; } - -.fa-person-pregnant::before { - content: "\e31e"; } - -.fa-wand-sparkles::before { - content: "\f72b"; } - -.fa-ellipsis-vertical::before { - content: "\f142"; } - -.fa-ellipsis-v::before { - content: "\f142"; } - -.fa-ticket::before { - content: "\f145"; } - -.fa-power-off::before { - content: "\f011"; } - -.fa-right-long::before { - content: "\f30b"; } - -.fa-long-arrow-alt-right::before { - content: "\f30b"; } - -.fa-flag-usa::before { - content: "\f74d"; } - -.fa-laptop-file::before { - content: "\e51d"; } - -.fa-tty::before { - content: "\f1e4"; } - -.fa-teletype::before { - content: "\f1e4"; } - -.fa-diagram-next::before { - content: "\e476"; } - -.fa-person-rifle::before { - content: "\e54e"; } - -.fa-house-medical-circle-exclamation::before { - content: "\e512"; } - -.fa-closed-captioning::before { - content: "\f20a"; } - -.fa-person-hiking::before { - content: "\f6ec"; } - -.fa-hiking::before { - content: "\f6ec"; } - -.fa-venus-double::before { - content: "\f226"; } - -.fa-images::before { - content: "\f302"; } - -.fa-calculator::before { - content: "\f1ec"; } - -.fa-people-pulling::before { - content: "\e535"; } - -.fa-n::before { - content: "\4e"; } - -.fa-cable-car::before { - content: "\f7da"; } - -.fa-tram::before { - content: "\f7da"; } - -.fa-cloud-rain::before { - content: "\f73d"; } - -.fa-building-circle-xmark::before { - content: "\e4d4"; } - -.fa-ship::before { - content: "\f21a"; } - -.fa-arrows-down-to-line::before { - content: "\e4b8"; } - -.fa-download::before { - content: "\f019"; } - -.fa-face-grin::before { - content: "\f580"; } - -.fa-grin::before { - content: "\f580"; } - -.fa-delete-left::before { - content: "\f55a"; } - -.fa-backspace::before { - content: "\f55a"; } - -.fa-eye-dropper::before { - content: "\f1fb"; } - -.fa-eye-dropper-empty::before { - content: "\f1fb"; } - -.fa-eyedropper::before { - content: "\f1fb"; } - -.fa-file-circle-check::before { - content: "\e5a0"; } - -.fa-forward::before { - content: "\f04e"; } - -.fa-mobile::before { - content: "\f3ce"; } - -.fa-mobile-android::before { - content: "\f3ce"; } - -.fa-mobile-phone::before { - content: "\f3ce"; } - -.fa-face-meh::before { - content: "\f11a"; } - -.fa-meh::before { - content: "\f11a"; } - -.fa-align-center::before { - content: "\f037"; } - -.fa-book-skull::before { - content: "\f6b7"; } - -.fa-book-dead::before { - content: "\f6b7"; } - -.fa-id-card::before { - content: "\f2c2"; } - -.fa-drivers-license::before { - content: "\f2c2"; } - -.fa-outdent::before { - content: "\f03b"; } - -.fa-dedent::before { - content: "\f03b"; } - -.fa-heart-circle-exclamation::before { - content: "\e4fe"; } - -.fa-house::before { - content: "\f015"; } - -.fa-home::before { - content: "\f015"; } - -.fa-home-alt::before { - content: "\f015"; } - -.fa-home-lg-alt::before { - content: "\f015"; } - -.fa-calendar-week::before { - content: "\f784"; } - -.fa-laptop-medical::before { - content: "\f812"; } - -.fa-b::before { - content: "\42"; } - -.fa-file-medical::before { - content: "\f477"; } - -.fa-dice-one::before { - content: "\f525"; } - -.fa-kiwi-bird::before { - content: "\f535"; } - -.fa-arrow-right-arrow-left::before { - content: "\f0ec"; } - -.fa-exchange::before { - content: "\f0ec"; } - -.fa-rotate-right::before { - content: "\f2f9"; } - -.fa-redo-alt::before { - content: "\f2f9"; } - -.fa-rotate-forward::before { - content: "\f2f9"; } - -.fa-utensils::before { - content: "\f2e7"; } - -.fa-cutlery::before { - content: "\f2e7"; } - -.fa-arrow-up-wide-short::before { - content: "\f161"; } - -.fa-sort-amount-up::before { - content: "\f161"; } - -.fa-mill-sign::before { - content: "\e1ed"; } - -.fa-bowl-rice::before { - content: "\e2eb"; } - -.fa-skull::before { - content: "\f54c"; } - -.fa-tower-broadcast::before { - content: "\f519"; } - -.fa-broadcast-tower::before { - content: "\f519"; } - -.fa-truck-pickup::before { - content: "\f63c"; } - -.fa-up-long::before { - content: "\f30c"; } - -.fa-long-arrow-alt-up::before { - content: "\f30c"; } - -.fa-stop::before { - content: "\f04d"; } - -.fa-code-merge::before { - content: "\f387"; } - -.fa-upload::before { - content: "\f093"; } - -.fa-hurricane::before { - content: "\f751"; } - -.fa-mound::before { - content: "\e52d"; } - -.fa-toilet-portable::before { - content: "\e583"; } - -.fa-compact-disc::before { - content: "\f51f"; } - -.fa-file-arrow-down::before { - content: "\f56d"; } - -.fa-file-download::before { - content: "\f56d"; } - -.fa-caravan::before { - content: "\f8ff"; } - -.fa-shield-cat::before { - content: "\e572"; } - -.fa-bolt::before { - content: "\f0e7"; } - -.fa-zap::before { - content: "\f0e7"; } - -.fa-glass-water::before { - content: "\e4f4"; } - -.fa-oil-well::before { - content: "\e532"; } - -.fa-vault::before { - content: "\e2c5"; } - -.fa-mars::before { - content: "\f222"; } - -.fa-toilet::before { - content: "\f7d8"; } - -.fa-plane-circle-xmark::before { - content: "\e557"; } - -.fa-yen-sign::before { - content: "\f157"; } - -.fa-cny::before { - content: "\f157"; } - -.fa-jpy::before { - content: "\f157"; } - -.fa-rmb::before { - content: "\f157"; } - -.fa-yen::before { - content: "\f157"; } - -.fa-ruble-sign::before { - content: "\f158"; } - -.fa-rouble::before { - content: "\f158"; } - -.fa-rub::before { - content: "\f158"; } - -.fa-ruble::before { - content: "\f158"; } - -.fa-sun::before { - content: "\f185"; } - -.fa-guitar::before { - content: "\f7a6"; } - -.fa-face-laugh-wink::before { - content: "\f59c"; } - -.fa-laugh-wink::before { - content: "\f59c"; } - -.fa-horse-head::before { - content: "\f7ab"; } - -.fa-bore-hole::before { - content: "\e4c3"; } - -.fa-industry::before { - content: "\f275"; } - -.fa-circle-down::before { - content: "\f358"; } - -.fa-arrow-alt-circle-down::before { - content: "\f358"; } - -.fa-arrows-turn-to-dots::before { - content: "\e4c1"; } - -.fa-florin-sign::before { - content: "\e184"; } - -.fa-arrow-down-short-wide::before { - content: "\f884"; } - -.fa-sort-amount-desc::before { - content: "\f884"; } - -.fa-sort-amount-down-alt::before { - content: "\f884"; } - -.fa-less-than::before { - content: "\3c"; } - -.fa-angle-down::before { - content: "\f107"; } - -.fa-car-tunnel::before { - content: "\e4de"; } - -.fa-head-side-cough::before { - content: "\e061"; } - -.fa-grip-lines::before { - content: "\f7a4"; } - -.fa-thumbs-down::before { - content: "\f165"; } - -.fa-user-lock::before { - content: "\f502"; } - -.fa-arrow-right-long::before { - content: "\f178"; } - -.fa-long-arrow-right::before { - content: "\f178"; } - -.fa-anchor-circle-xmark::before { - content: "\e4ac"; } - -.fa-ellipsis::before { - content: "\f141"; } - -.fa-ellipsis-h::before { - content: "\f141"; } - -.fa-chess-pawn::before { - content: "\f443"; } - -.fa-kit-medical::before { - content: "\f479"; } - -.fa-first-aid::before { - content: "\f479"; } - -.fa-person-through-window::before { - content: "\e5a9"; } - -.fa-toolbox::before { - content: "\f552"; } - -.fa-hands-holding-circle::before { - content: "\e4fb"; } - -.fa-bug::before { - content: "\f188"; } - -.fa-credit-card::before { - content: "\f09d"; } - -.fa-credit-card-alt::before { - content: "\f09d"; } - -.fa-car::before { - content: "\f1b9"; } - -.fa-automobile::before { - content: "\f1b9"; } - -.fa-hand-holding-hand::before { - content: "\e4f7"; } - -.fa-book-open-reader::before { - content: "\f5da"; } - -.fa-book-reader::before { - content: "\f5da"; } - -.fa-mountain-sun::before { - content: "\e52f"; } - -.fa-arrows-left-right-to-line::before { - content: "\e4ba"; } - -.fa-dice-d20::before { - content: "\f6cf"; } - -.fa-truck-droplet::before { - content: "\e58c"; } - -.fa-file-circle-xmark::before { - content: "\e5a1"; } - -.fa-temperature-arrow-up::before { - content: "\e040"; } - -.fa-temperature-up::before { - content: "\e040"; } - -.fa-medal::before { - content: "\f5a2"; } - -.fa-bed::before { - content: "\f236"; } - -.fa-square-h::before { - content: "\f0fd"; } - -.fa-h-square::before { - content: "\f0fd"; } - -.fa-podcast::before { - content: "\f2ce"; } - -.fa-temperature-full::before { - content: "\f2c7"; } - -.fa-temperature-4::before { - content: "\f2c7"; } - -.fa-thermometer-4::before { - content: "\f2c7"; } - -.fa-thermometer-full::before { - content: "\f2c7"; } - -.fa-bell::before { - content: "\f0f3"; } - -.fa-superscript::before { - content: "\f12b"; } - -.fa-plug-circle-xmark::before { - content: "\e560"; } - -.fa-star-of-life::before { - content: "\f621"; } - -.fa-phone-slash::before { - content: "\f3dd"; } - -.fa-paint-roller::before { - content: "\f5aa"; } - -.fa-handshake-angle::before { - content: "\f4c4"; } - -.fa-hands-helping::before { - content: "\f4c4"; } - -.fa-location-dot::before { - content: "\f3c5"; } - -.fa-map-marker-alt::before { - content: "\f3c5"; } - -.fa-file::before { - content: "\f15b"; } - -.fa-greater-than::before { - content: "\3e"; } - -.fa-person-swimming::before { - content: "\f5c4"; } - -.fa-swimmer::before { - content: "\f5c4"; } - -.fa-arrow-down::before { - content: "\f063"; } - -.fa-droplet::before { - content: "\f043"; } - -.fa-tint::before { - content: "\f043"; } - -.fa-eraser::before { - content: "\f12d"; } - -.fa-earth-americas::before { - content: "\f57d"; } - -.fa-earth::before { - content: "\f57d"; } - -.fa-earth-america::before { - content: "\f57d"; } - -.fa-globe-americas::before { - content: "\f57d"; } - -.fa-person-burst::before { - content: "\e53b"; } - -.fa-dove::before { - content: "\f4ba"; } - -.fa-battery-empty::before { - content: "\f244"; } - -.fa-battery-0::before { - content: "\f244"; } - -.fa-socks::before { - content: "\f696"; } - -.fa-inbox::before { - content: "\f01c"; } - -.fa-section::before { - content: "\e447"; } - -.fa-gauge-high::before { - content: "\f625"; } - -.fa-tachometer-alt::before { - content: "\f625"; } - -.fa-tachometer-alt-fast::before { - content: "\f625"; } - -.fa-envelope-open-text::before { - content: "\f658"; } - -.fa-hospital::before { - content: "\f0f8"; } - -.fa-hospital-alt::before { - content: "\f0f8"; } - -.fa-hospital-wide::before { - content: "\f0f8"; } - -.fa-wine-bottle::before { - content: "\f72f"; } - -.fa-chess-rook::before { - content: "\f447"; } - -.fa-bars-staggered::before { - content: "\f550"; } - -.fa-reorder::before { - content: "\f550"; } - -.fa-stream::before { - content: "\f550"; } - -.fa-dharmachakra::before { - content: "\f655"; } - -.fa-hotdog::before { - content: "\f80f"; } - -.fa-person-walking-with-cane::before { - content: "\f29d"; } - -.fa-blind::before { - content: "\f29d"; } - -.fa-drum::before { - content: "\f569"; } - -.fa-ice-cream::before { - content: "\f810"; } - -.fa-heart-circle-bolt::before { - content: "\e4fc"; } - -.fa-fax::before { - content: "\f1ac"; } - -.fa-paragraph::before { - content: "\f1dd"; } - -.fa-check-to-slot::before { - content: "\f772"; } - -.fa-vote-yea::before { - content: "\f772"; } - -.fa-star-half::before { - content: "\f089"; } - -.fa-boxes-stacked::before { - content: "\f468"; } - -.fa-boxes::before { - content: "\f468"; } - -.fa-boxes-alt::before { - content: "\f468"; } - -.fa-link::before { - content: "\f0c1"; } - -.fa-chain::before { - content: "\f0c1"; } - -.fa-ear-listen::before { - content: "\f2a2"; } - -.fa-assistive-listening-systems::before { - content: "\f2a2"; } - -.fa-tree-city::before { - content: "\e587"; } - -.fa-play::before { - content: "\f04b"; } - -.fa-font::before { - content: "\f031"; } - -.fa-table-cells-row-lock::before { - content: "\e67a"; } - -.fa-rupiah-sign::before { - content: "\e23d"; } - -.fa-magnifying-glass::before { - content: "\f002"; } - -.fa-search::before { - content: "\f002"; } - -.fa-table-tennis-paddle-ball::before { - content: "\f45d"; } - -.fa-ping-pong-paddle-ball::before { - content: "\f45d"; } - -.fa-table-tennis::before { - content: "\f45d"; } - -.fa-person-dots-from-line::before { - content: "\f470"; } - -.fa-diagnoses::before { - content: "\f470"; } - -.fa-trash-can-arrow-up::before { - content: "\f82a"; } - -.fa-trash-restore-alt::before { - content: "\f82a"; } - -.fa-naira-sign::before { - content: "\e1f6"; } - -.fa-cart-arrow-down::before { - content: "\f218"; } - -.fa-walkie-talkie::before { - content: "\f8ef"; } - -.fa-file-pen::before { - content: "\f31c"; } - -.fa-file-edit::before { - content: "\f31c"; } - -.fa-receipt::before { - content: "\f543"; } - -.fa-square-pen::before { - content: "\f14b"; } - -.fa-pen-square::before { - content: "\f14b"; } - -.fa-pencil-square::before { - content: "\f14b"; } - -.fa-suitcase-rolling::before { - content: "\f5c1"; } - -.fa-person-circle-exclamation::before { - content: "\e53f"; } - -.fa-chevron-down::before { - content: "\f078"; } - -.fa-battery-full::before { - content: "\f240"; } - -.fa-battery::before { - content: "\f240"; } - -.fa-battery-5::before { - content: "\f240"; } - -.fa-skull-crossbones::before { - content: "\f714"; } - -.fa-code-compare::before { - content: "\e13a"; } - -.fa-list-ul::before { - content: "\f0ca"; } - -.fa-list-dots::before { - content: "\f0ca"; } - -.fa-school-lock::before { - content: "\e56f"; } - -.fa-tower-cell::before { - content: "\e585"; } - -.fa-down-long::before { - content: "\f309"; } - -.fa-long-arrow-alt-down::before { - content: "\f309"; } - -.fa-ranking-star::before { - content: "\e561"; } - -.fa-chess-king::before { - content: "\f43f"; } - -.fa-person-harassing::before { - content: "\e549"; } - -.fa-brazilian-real-sign::before { - content: "\e46c"; } - -.fa-landmark-dome::before { - content: "\f752"; } - -.fa-landmark-alt::before { - content: "\f752"; } - -.fa-arrow-up::before { - content: "\f062"; } - -.fa-tv::before { - content: "\f26c"; } - -.fa-television::before { - content: "\f26c"; } - -.fa-tv-alt::before { - content: "\f26c"; } - -.fa-shrimp::before { - content: "\e448"; } - -.fa-list-check::before { - content: "\f0ae"; } - -.fa-tasks::before { - content: "\f0ae"; } - -.fa-jug-detergent::before { - content: "\e519"; } - -.fa-circle-user::before { - content: "\f2bd"; } - -.fa-user-circle::before { - content: "\f2bd"; } - -.fa-user-shield::before { - content: "\f505"; } - -.fa-wind::before { - content: "\f72e"; } - -.fa-car-burst::before { - content: "\f5e1"; } - -.fa-car-crash::before { - content: "\f5e1"; } - -.fa-y::before { - content: "\59"; } - -.fa-person-snowboarding::before { - content: "\f7ce"; } - -.fa-snowboarding::before { - content: "\f7ce"; } - -.fa-truck-fast::before { - content: "\f48b"; } - -.fa-shipping-fast::before { - content: "\f48b"; } - -.fa-fish::before { - content: "\f578"; } - -.fa-user-graduate::before { - content: "\f501"; } - -.fa-circle-half-stroke::before { - content: "\f042"; } - -.fa-adjust::before { - content: "\f042"; } - -.fa-clapperboard::before { - content: "\e131"; } - -.fa-circle-radiation::before { - content: "\f7ba"; } - -.fa-radiation-alt::before { - content: "\f7ba"; } - -.fa-baseball::before { - content: "\f433"; } - -.fa-baseball-ball::before { - content: "\f433"; } - -.fa-jet-fighter-up::before { - content: "\e518"; } - -.fa-diagram-project::before { - content: "\f542"; } - -.fa-project-diagram::before { - content: "\f542"; } - -.fa-copy::before { - content: "\f0c5"; } - -.fa-volume-xmark::before { - content: "\f6a9"; } - -.fa-volume-mute::before { - content: "\f6a9"; } - -.fa-volume-times::before { - content: "\f6a9"; } - -.fa-hand-sparkles::before { - content: "\e05d"; } - -.fa-grip::before { - content: "\f58d"; } - -.fa-grip-horizontal::before { - content: "\f58d"; } - -.fa-share-from-square::before { - content: "\f14d"; } - -.fa-share-square::before { - content: "\f14d"; } - -.fa-child-combatant::before { - content: "\e4e0"; } - -.fa-child-rifle::before { - content: "\e4e0"; } - -.fa-gun::before { - content: "\e19b"; } - -.fa-square-phone::before { - content: "\f098"; } - -.fa-phone-square::before { - content: "\f098"; } - -.fa-plus::before { - content: "\2b"; } - -.fa-add::before { - content: "\2b"; } - -.fa-expand::before { - content: "\f065"; } - -.fa-computer::before { - content: "\e4e5"; } - -.fa-xmark::before { - content: "\f00d"; } - -.fa-close::before { - content: "\f00d"; } - -.fa-multiply::before { - content: "\f00d"; } - -.fa-remove::before { - content: "\f00d"; } - -.fa-times::before { - content: "\f00d"; } - -.fa-arrows-up-down-left-right::before { - content: "\f047"; } - -.fa-arrows::before { - content: "\f047"; } - -.fa-chalkboard-user::before { - content: "\f51c"; } - -.fa-chalkboard-teacher::before { - content: "\f51c"; } - -.fa-peso-sign::before { - content: "\e222"; } - -.fa-building-shield::before { - content: "\e4d8"; } - -.fa-baby::before { - content: "\f77c"; } - -.fa-users-line::before { - content: "\e592"; } - -.fa-quote-left::before { - content: "\f10d"; } - -.fa-quote-left-alt::before { - content: "\f10d"; } - -.fa-tractor::before { - content: "\f722"; } - -.fa-trash-arrow-up::before { - content: "\f829"; } - -.fa-trash-restore::before { - content: "\f829"; } - -.fa-arrow-down-up-lock::before { - content: "\e4b0"; } - -.fa-lines-leaning::before { - content: "\e51e"; } - -.fa-ruler-combined::before { - content: "\f546"; } - -.fa-copyright::before { - content: "\f1f9"; } - -.fa-equals::before { - content: "\3d"; } - -.fa-blender::before { - content: "\f517"; } - -.fa-teeth::before { - content: "\f62e"; } - -.fa-shekel-sign::before { - content: "\f20b"; } - -.fa-ils::before { - content: "\f20b"; } - -.fa-shekel::before { - content: "\f20b"; } - -.fa-sheqel::before { - content: "\f20b"; } - -.fa-sheqel-sign::before { - content: "\f20b"; } - -.fa-map::before { - content: "\f279"; } - -.fa-rocket::before { - content: "\f135"; } - -.fa-photo-film::before { - content: "\f87c"; } - -.fa-photo-video::before { - content: "\f87c"; } - -.fa-folder-minus::before { - content: "\f65d"; } - -.fa-store::before { - content: "\f54e"; } - -.fa-arrow-trend-up::before { - content: "\e098"; } - -.fa-plug-circle-minus::before { - content: "\e55e"; } - -.fa-sign-hanging::before { - content: "\f4d9"; } - -.fa-sign::before { - content: "\f4d9"; } - -.fa-bezier-curve::before { - content: "\f55b"; } - -.fa-bell-slash::before { - content: "\f1f6"; } - -.fa-tablet::before { - content: "\f3fb"; } - -.fa-tablet-android::before { - content: "\f3fb"; } - -.fa-school-flag::before { - content: "\e56e"; } - -.fa-fill::before { - content: "\f575"; } - -.fa-angle-up::before { - content: "\f106"; } - -.fa-drumstick-bite::before { - content: "\f6d7"; } - -.fa-holly-berry::before { - content: "\f7aa"; } - -.fa-chevron-left::before { - content: "\f053"; } - -.fa-bacteria::before { - content: "\e059"; } - -.fa-hand-lizard::before { - content: "\f258"; } - -.fa-notdef::before { - content: "\e1fe"; } - -.fa-disease::before { - content: "\f7fa"; } - -.fa-briefcase-medical::before { - content: "\f469"; } - -.fa-genderless::before { - content: "\f22d"; } - -.fa-chevron-right::before { - content: "\f054"; } - -.fa-retweet::before { - content: "\f079"; } - -.fa-car-rear::before { - content: "\f5de"; } - -.fa-car-alt::before { - content: "\f5de"; } - -.fa-pump-soap::before { - content: "\e06b"; } - -.fa-video-slash::before { - content: "\f4e2"; } - -.fa-battery-quarter::before { - content: "\f243"; } - -.fa-battery-2::before { - content: "\f243"; } - -.fa-radio::before { - content: "\f8d7"; } - -.fa-baby-carriage::before { - content: "\f77d"; } - -.fa-carriage-baby::before { - content: "\f77d"; } - -.fa-traffic-light::before { - content: "\f637"; } - -.fa-thermometer::before { - content: "\f491"; } - -.fa-vr-cardboard::before { - content: "\f729"; } - -.fa-hand-middle-finger::before { - content: "\f806"; } - -.fa-percent::before { - content: "\25"; } - -.fa-percentage::before { - content: "\25"; } - -.fa-truck-moving::before { - content: "\f4df"; } - -.fa-glass-water-droplet::before { - content: "\e4f5"; } - -.fa-display::before { - content: "\e163"; } - -.fa-face-smile::before { - content: "\f118"; } - -.fa-smile::before { - content: "\f118"; } - -.fa-thumbtack::before { - content: "\f08d"; } - -.fa-thumb-tack::before { - content: "\f08d"; } - -.fa-trophy::before { - content: "\f091"; } - -.fa-person-praying::before { - content: "\f683"; } - -.fa-pray::before { - content: "\f683"; } - -.fa-hammer::before { - content: "\f6e3"; } - -.fa-hand-peace::before { - content: "\f25b"; } - -.fa-rotate::before { - content: "\f2f1"; } - -.fa-sync-alt::before { - content: "\f2f1"; } - -.fa-spinner::before { - content: "\f110"; } - -.fa-robot::before { - content: "\f544"; } - -.fa-peace::before { - content: "\f67c"; } - -.fa-gears::before { - content: "\f085"; } - -.fa-cogs::before { - content: "\f085"; } - -.fa-warehouse::before { - content: "\f494"; } - -.fa-arrow-up-right-dots::before { - content: "\e4b7"; } - -.fa-splotch::before { - content: "\f5bc"; } - -.fa-face-grin-hearts::before { - content: "\f584"; } - -.fa-grin-hearts::before { - content: "\f584"; } - -.fa-dice-four::before { - content: "\f524"; } - -.fa-sim-card::before { - content: "\f7c4"; } - -.fa-transgender::before { - content: "\f225"; } - -.fa-transgender-alt::before { - content: "\f225"; } - -.fa-mercury::before { - content: "\f223"; } - -.fa-arrow-turn-down::before { - content: "\f149"; } - -.fa-level-down::before { - content: "\f149"; } - -.fa-person-falling-burst::before { - content: "\e547"; } - -.fa-award::before { - content: "\f559"; } - -.fa-ticket-simple::before { - content: "\f3ff"; } - -.fa-ticket-alt::before { - content: "\f3ff"; } - -.fa-building::before { - content: "\f1ad"; } - -.fa-angles-left::before { - content: "\f100"; } - -.fa-angle-double-left::before { - content: "\f100"; } - -.fa-qrcode::before { - content: "\f029"; } - -.fa-clock-rotate-left::before { - content: "\f1da"; } - -.fa-history::before { - content: "\f1da"; } - -.fa-face-grin-beam-sweat::before { - content: "\f583"; } - -.fa-grin-beam-sweat::before { - content: "\f583"; } - -.fa-file-export::before { - content: "\f56e"; } - -.fa-arrow-right-from-file::before { - content: "\f56e"; } - -.fa-shield::before { - content: "\f132"; } - -.fa-shield-blank::before { - content: "\f132"; } - -.fa-arrow-up-short-wide::before { - content: "\f885"; } - -.fa-sort-amount-up-alt::before { - content: "\f885"; } - -.fa-house-medical::before { - content: "\e3b2"; } - -.fa-golf-ball-tee::before { - content: "\f450"; } - -.fa-golf-ball::before { - content: "\f450"; } - -.fa-circle-chevron-left::before { - content: "\f137"; } - -.fa-chevron-circle-left::before { - content: "\f137"; } - -.fa-house-chimney-window::before { - content: "\e00d"; } - -.fa-pen-nib::before { - content: "\f5ad"; } - -.fa-tent-arrow-turn-left::before { - content: "\e580"; } - -.fa-tents::before { - content: "\e582"; } - -.fa-wand-magic::before { - content: "\f0d0"; } - -.fa-magic::before { - content: "\f0d0"; } - -.fa-dog::before { - content: "\f6d3"; } - -.fa-carrot::before { - content: "\f787"; } - -.fa-moon::before { - content: "\f186"; } - -.fa-wine-glass-empty::before { - content: "\f5ce"; } - -.fa-wine-glass-alt::before { - content: "\f5ce"; } - -.fa-cheese::before { - content: "\f7ef"; } - -.fa-yin-yang::before { - content: "\f6ad"; } - -.fa-music::before { - content: "\f001"; } - -.fa-code-commit::before { - content: "\f386"; } - -.fa-temperature-low::before { - content: "\f76b"; } - -.fa-person-biking::before { - content: "\f84a"; } - -.fa-biking::before { - content: "\f84a"; } - -.fa-broom::before { - content: "\f51a"; } - -.fa-shield-heart::before { - content: "\e574"; } - -.fa-gopuram::before { - content: "\f664"; } - -.fa-earth-oceania::before { - content: "\e47b"; } - -.fa-globe-oceania::before { - content: "\e47b"; } - -.fa-square-xmark::before { - content: "\f2d3"; } - -.fa-times-square::before { - content: "\f2d3"; } - -.fa-xmark-square::before { - content: "\f2d3"; } - -.fa-hashtag::before { - content: "\23"; } - -.fa-up-right-and-down-left-from-center::before { - content: "\f424"; } - -.fa-expand-alt::before { - content: "\f424"; } - -.fa-oil-can::before { - content: "\f613"; } - -.fa-t::before { - content: "\54"; } - -.fa-hippo::before { - content: "\f6ed"; } - -.fa-chart-column::before { - content: "\e0e3"; } - -.fa-infinity::before { - content: "\f534"; } - -.fa-vial-circle-check::before { - content: "\e596"; } - -.fa-person-arrow-down-to-line::before { - content: "\e538"; } - -.fa-voicemail::before { - content: "\f897"; } - -.fa-fan::before { - content: "\f863"; } - -.fa-person-walking-luggage::before { - content: "\e554"; } - -.fa-up-down::before { - content: "\f338"; } - -.fa-arrows-alt-v::before { - content: "\f338"; } - -.fa-cloud-moon-rain::before { - content: "\f73c"; } - -.fa-calendar::before { - content: "\f133"; } - -.fa-trailer::before { - content: "\e041"; } - -.fa-bahai::before { - content: "\f666"; } - -.fa-haykal::before { - content: "\f666"; } - -.fa-sd-card::before { - content: "\f7c2"; } - -.fa-dragon::before { - content: "\f6d5"; } - -.fa-shoe-prints::before { - content: "\f54b"; } - -.fa-circle-plus::before { - content: "\f055"; } - -.fa-plus-circle::before { - content: "\f055"; } - -.fa-face-grin-tongue-wink::before { - content: "\f58b"; } - -.fa-grin-tongue-wink::before { - content: "\f58b"; } - -.fa-hand-holding::before { - content: "\f4bd"; } - -.fa-plug-circle-exclamation::before { - content: "\e55d"; } - -.fa-link-slash::before { - content: "\f127"; } - -.fa-chain-broken::before { - content: "\f127"; } - -.fa-chain-slash::before { - content: "\f127"; } - -.fa-unlink::before { - content: "\f127"; } - -.fa-clone::before { - content: "\f24d"; } - -.fa-person-walking-arrow-loop-left::before { - content: "\e551"; } - -.fa-arrow-up-z-a::before { - content: "\f882"; } - -.fa-sort-alpha-up-alt::before { - content: "\f882"; } - -.fa-fire-flame-curved::before { - content: "\f7e4"; } - -.fa-fire-alt::before { - content: "\f7e4"; } - -.fa-tornado::before { - content: "\f76f"; } - -.fa-file-circle-plus::before { - content: "\e494"; } - -.fa-book-quran::before { - content: "\f687"; } - -.fa-quran::before { - content: "\f687"; } - -.fa-anchor::before { - content: "\f13d"; } - -.fa-border-all::before { - content: "\f84c"; } - -.fa-face-angry::before { - content: "\f556"; } - -.fa-angry::before { - content: "\f556"; } - -.fa-cookie-bite::before { - content: "\f564"; } - -.fa-arrow-trend-down::before { - content: "\e097"; } - -.fa-rss::before { - content: "\f09e"; } - -.fa-feed::before { - content: "\f09e"; } - -.fa-draw-polygon::before { - content: "\f5ee"; } - -.fa-scale-balanced::before { - content: "\f24e"; } - -.fa-balance-scale::before { - content: "\f24e"; } - -.fa-gauge-simple-high::before { - content: "\f62a"; } - -.fa-tachometer::before { - content: "\f62a"; } - -.fa-tachometer-fast::before { - content: "\f62a"; } - -.fa-shower::before { - content: "\f2cc"; } - -.fa-desktop::before { - content: "\f390"; } - -.fa-desktop-alt::before { - content: "\f390"; } - -.fa-m::before { - content: "\4d"; } - -.fa-table-list::before { - content: "\f00b"; } - -.fa-th-list::before { - content: "\f00b"; } - -.fa-comment-sms::before { - content: "\f7cd"; } - -.fa-sms::before { - content: "\f7cd"; } - -.fa-book::before { - content: "\f02d"; } - -.fa-user-plus::before { - content: "\f234"; } - -.fa-check::before { - content: "\f00c"; } - -.fa-battery-three-quarters::before { - content: "\f241"; } - -.fa-battery-4::before { - content: "\f241"; } - -.fa-house-circle-check::before { - content: "\e509"; } - -.fa-angle-left::before { - content: "\f104"; } - -.fa-diagram-successor::before { - content: "\e47a"; } - -.fa-truck-arrow-right::before { - content: "\e58b"; } - -.fa-arrows-split-up-and-left::before { - content: "\e4bc"; } - -.fa-hand-fist::before { - content: "\f6de"; } - -.fa-fist-raised::before { - content: "\f6de"; } - -.fa-cloud-moon::before { - content: "\f6c3"; } - -.fa-briefcase::before { - content: "\f0b1"; } - -.fa-person-falling::before { - content: "\e546"; } - -.fa-image-portrait::before { - content: "\f3e0"; } - -.fa-portrait::before { - content: "\f3e0"; } - -.fa-user-tag::before { - content: "\f507"; } - -.fa-rug::before { - content: "\e569"; } - -.fa-earth-europe::before { - content: "\f7a2"; } - -.fa-globe-europe::before { - content: "\f7a2"; } - -.fa-cart-flatbed-suitcase::before { - content: "\f59d"; } - -.fa-luggage-cart::before { - content: "\f59d"; } - -.fa-rectangle-xmark::before { - content: "\f410"; } - -.fa-rectangle-times::before { - content: "\f410"; } - -.fa-times-rectangle::before { - content: "\f410"; } - -.fa-window-close::before { - content: "\f410"; } - -.fa-baht-sign::before { - content: "\e0ac"; } - -.fa-book-open::before { - content: "\f518"; } - -.fa-book-journal-whills::before { - content: "\f66a"; } - -.fa-journal-whills::before { - content: "\f66a"; } - -.fa-handcuffs::before { - content: "\e4f8"; } - -.fa-triangle-exclamation::before { - content: "\f071"; } - -.fa-exclamation-triangle::before { - content: "\f071"; } - -.fa-warning::before { - content: "\f071"; } - -.fa-database::before { - content: "\f1c0"; } - -.fa-share::before { - content: "\f064"; } - -.fa-mail-forward::before { - content: "\f064"; } - -.fa-bottle-droplet::before { - content: "\e4c4"; } - -.fa-mask-face::before { - content: "\e1d7"; } - -.fa-hill-rockslide::before { - content: "\e508"; } - -.fa-right-left::before { - content: "\f362"; } - -.fa-exchange-alt::before { - content: "\f362"; } - -.fa-paper-plane::before { - content: "\f1d8"; } - -.fa-road-circle-exclamation::before { - content: "\e565"; } - -.fa-dungeon::before { - content: "\f6d9"; } - -.fa-align-right::before { - content: "\f038"; } - -.fa-money-bill-1-wave::before { - content: "\f53b"; } - -.fa-money-bill-wave-alt::before { - content: "\f53b"; } - -.fa-life-ring::before { - content: "\f1cd"; } - -.fa-hands::before { - content: "\f2a7"; } - -.fa-sign-language::before { - content: "\f2a7"; } - -.fa-signing::before { - content: "\f2a7"; } - -.fa-calendar-day::before { - content: "\f783"; } - -.fa-water-ladder::before { - content: "\f5c5"; } - -.fa-ladder-water::before { - content: "\f5c5"; } - -.fa-swimming-pool::before { - content: "\f5c5"; } - -.fa-arrows-up-down::before { - content: "\f07d"; } - -.fa-arrows-v::before { - content: "\f07d"; } - -.fa-face-grimace::before { - content: "\f57f"; } - -.fa-grimace::before { - content: "\f57f"; } - -.fa-wheelchair-move::before { - content: "\e2ce"; } - -.fa-wheelchair-alt::before { - content: "\e2ce"; } - -.fa-turn-down::before { - content: "\f3be"; } - -.fa-level-down-alt::before { - content: "\f3be"; } - -.fa-person-walking-arrow-right::before { - content: "\e552"; } - -.fa-square-envelope::before { - content: "\f199"; } - -.fa-envelope-square::before { - content: "\f199"; } - -.fa-dice::before { - content: "\f522"; } - -.fa-bowling-ball::before { - content: "\f436"; } - -.fa-brain::before { - content: "\f5dc"; } - -.fa-bandage::before { - content: "\f462"; } - -.fa-band-aid::before { - content: "\f462"; } - -.fa-calendar-minus::before { - content: "\f272"; } - -.fa-circle-xmark::before { - content: "\f057"; } - -.fa-times-circle::before { - content: "\f057"; } - -.fa-xmark-circle::before { - content: "\f057"; } - -.fa-gifts::before { - content: "\f79c"; } - -.fa-hotel::before { - content: "\f594"; } - -.fa-earth-asia::before { - content: "\f57e"; } - -.fa-globe-asia::before { - content: "\f57e"; } - -.fa-id-card-clip::before { - content: "\f47f"; } - -.fa-id-card-alt::before { - content: "\f47f"; } - -.fa-magnifying-glass-plus::before { - content: "\f00e"; } - -.fa-search-plus::before { - content: "\f00e"; } - -.fa-thumbs-up::before { - content: "\f164"; } - -.fa-user-clock::before { - content: "\f4fd"; } - -.fa-hand-dots::before { - content: "\f461"; } - -.fa-allergies::before { - content: "\f461"; } - -.fa-file-invoice::before { - content: "\f570"; } - -.fa-window-minimize::before { - content: "\f2d1"; } - -.fa-mug-saucer::before { - content: "\f0f4"; } - -.fa-coffee::before { - content: "\f0f4"; } - -.fa-brush::before { - content: "\f55d"; } - -.fa-mask::before { - content: "\f6fa"; } - -.fa-magnifying-glass-minus::before { - content: "\f010"; } - -.fa-search-minus::before { - content: "\f010"; } - -.fa-ruler-vertical::before { - content: "\f548"; } - -.fa-user-large::before { - content: "\f406"; } - -.fa-user-alt::before { - content: "\f406"; } - -.fa-train-tram::before { - content: "\e5b4"; } - -.fa-user-nurse::before { - content: "\f82f"; } - -.fa-syringe::before { - content: "\f48e"; } - -.fa-cloud-sun::before { - content: "\f6c4"; } - -.fa-stopwatch-20::before { - content: "\e06f"; } - -.fa-square-full::before { - content: "\f45c"; } - -.fa-magnet::before { - content: "\f076"; } - -.fa-jar::before { - content: "\e516"; } - -.fa-note-sticky::before { - content: "\f249"; } - -.fa-sticky-note::before { - content: "\f249"; } - -.fa-bug-slash::before { - content: "\e490"; } - -.fa-arrow-up-from-water-pump::before { - content: "\e4b6"; } - -.fa-bone::before { - content: "\f5d7"; } - -.fa-user-injured::before { - content: "\f728"; } - -.fa-face-sad-tear::before { - content: "\f5b4"; } - -.fa-sad-tear::before { - content: "\f5b4"; } - -.fa-plane::before { - content: "\f072"; } - -.fa-tent-arrows-down::before { - content: "\e581"; } - -.fa-exclamation::before { - content: "\21"; } - -.fa-arrows-spin::before { - content: "\e4bb"; } - -.fa-print::before { - content: "\f02f"; } - -.fa-turkish-lira-sign::before { - content: "\e2bb"; } - -.fa-try::before { - content: "\e2bb"; } - -.fa-turkish-lira::before { - content: "\e2bb"; } - -.fa-dollar-sign::before { - content: "\24"; } - -.fa-dollar::before { - content: "\24"; } - -.fa-usd::before { - content: "\24"; } - -.fa-x::before { - content: "\58"; } - -.fa-magnifying-glass-dollar::before { - content: "\f688"; } - -.fa-search-dollar::before { - content: "\f688"; } - -.fa-users-gear::before { - content: "\f509"; } - -.fa-users-cog::before { - content: "\f509"; } - -.fa-person-military-pointing::before { - content: "\e54a"; } - -.fa-building-columns::before { - content: "\f19c"; } - -.fa-bank::before { - content: "\f19c"; } - -.fa-institution::before { - content: "\f19c"; } - -.fa-museum::before { - content: "\f19c"; } - -.fa-university::before { - content: "\f19c"; } - -.fa-umbrella::before { - content: "\f0e9"; } - -.fa-trowel::before { - content: "\e589"; } - -.fa-d::before { - content: "\44"; } - -.fa-stapler::before { - content: "\e5af"; } - -.fa-masks-theater::before { - content: "\f630"; } - -.fa-theater-masks::before { - content: "\f630"; } - -.fa-kip-sign::before { - content: "\e1c4"; } - -.fa-hand-point-left::before { - content: "\f0a5"; } - -.fa-handshake-simple::before { - content: "\f4c6"; } - -.fa-handshake-alt::before { - content: "\f4c6"; } - -.fa-jet-fighter::before { - content: "\f0fb"; } - -.fa-fighter-jet::before { - content: "\f0fb"; } - -.fa-square-share-nodes::before { - content: "\f1e1"; } - -.fa-share-alt-square::before { - content: "\f1e1"; } - -.fa-barcode::before { - content: "\f02a"; } - -.fa-plus-minus::before { - content: "\e43c"; } - -.fa-video::before { - content: "\f03d"; } - -.fa-video-camera::before { - content: "\f03d"; } - -.fa-graduation-cap::before { - content: "\f19d"; } - -.fa-mortar-board::before { - content: "\f19d"; } - -.fa-hand-holding-medical::before { - content: "\e05c"; } - -.fa-person-circle-check::before { - content: "\e53e"; } - -.fa-turn-up::before { - content: "\f3bf"; } - -.fa-level-up-alt::before { - content: "\f3bf"; } - -.sr-only, -.fa-sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; } - -.sr-only-focusable:not(:focus), -.fa-sr-only-focusable:not(:focus) { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; } -:root, :host { - --fa-style-family-brands: 'Font Awesome 6 Brands'; - --fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; } - -@font-face { - font-family: 'Font Awesome 6 Brands'; - font-style: normal; - font-weight: 400; - font-display: block; - src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } - -.fab, -.fa-brands { - font-weight: 400; } - -.fa-monero:before { - content: "\f3d0"; } - -.fa-hooli:before { - content: "\f427"; } - -.fa-yelp:before { - content: "\f1e9"; } - -.fa-cc-visa:before { - content: "\f1f0"; } - -.fa-lastfm:before { - content: "\f202"; } - -.fa-shopware:before { - content: "\f5b5"; } - -.fa-creative-commons-nc:before { - content: "\f4e8"; } - -.fa-aws:before { - content: "\f375"; } - -.fa-redhat:before { - content: "\f7bc"; } - -.fa-yoast:before { - content: "\f2b1"; } - -.fa-cloudflare:before { - content: "\e07d"; } - -.fa-ups:before { - content: "\f7e0"; } - -.fa-pixiv:before { - content: "\e640"; } - -.fa-wpexplorer:before { - content: "\f2de"; } - -.fa-dyalog:before { - content: "\f399"; } - -.fa-bity:before { - content: "\f37a"; } - -.fa-stackpath:before { - content: "\f842"; } - -.fa-buysellads:before { - content: "\f20d"; } - -.fa-first-order:before { - content: "\f2b0"; } - -.fa-modx:before { - content: "\f285"; } - -.fa-guilded:before { - content: "\e07e"; } - -.fa-vnv:before { - content: "\f40b"; } - -.fa-square-js:before { - content: "\f3b9"; } - -.fa-js-square:before { - content: "\f3b9"; } - -.fa-microsoft:before { - content: "\f3ca"; } - -.fa-qq:before { - content: "\f1d6"; } - -.fa-orcid:before { - content: "\f8d2"; } - -.fa-java:before { - content: "\f4e4"; } - -.fa-invision:before { - content: "\f7b0"; } - -.fa-creative-commons-pd-alt:before { - content: "\f4ed"; } - -.fa-centercode:before { - content: "\f380"; } - -.fa-glide-g:before { - content: "\f2a6"; } - -.fa-drupal:before { - content: "\f1a9"; } - -.fa-jxl:before { - content: "\e67b"; } - -.fa-hire-a-helper:before { - content: "\f3b0"; } - -.fa-creative-commons-by:before { - content: "\f4e7"; } - -.fa-unity:before { - content: "\e049"; } - -.fa-whmcs:before { - content: "\f40d"; } - -.fa-rocketchat:before { - content: "\f3e8"; } - -.fa-vk:before { - content: "\f189"; } - -.fa-untappd:before { - content: "\f405"; } - -.fa-mailchimp:before { - content: "\f59e"; } - -.fa-css3-alt:before { - content: "\f38b"; } - -.fa-square-reddit:before { - content: "\f1a2"; } - -.fa-reddit-square:before { - content: "\f1a2"; } - -.fa-vimeo-v:before { - content: "\f27d"; } - -.fa-contao:before { - content: "\f26d"; } - -.fa-square-font-awesome:before { - content: "\e5ad"; } - -.fa-deskpro:before { - content: "\f38f"; } - -.fa-brave:before { - content: "\e63c"; } - -.fa-sistrix:before { - content: "\f3ee"; } - -.fa-square-instagram:before { - content: "\e055"; } - -.fa-instagram-square:before { - content: "\e055"; } - -.fa-battle-net:before { - content: "\f835"; } - -.fa-the-red-yeti:before { - content: "\f69d"; } - -.fa-square-hacker-news:before { - content: "\f3af"; } - -.fa-hacker-news-square:before { - content: "\f3af"; } - -.fa-edge:before { - content: "\f282"; } - -.fa-threads:before { - content: "\e618"; } - -.fa-napster:before { - content: "\f3d2"; } - -.fa-square-snapchat:before { - content: "\f2ad"; } - -.fa-snapchat-square:before { - content: "\f2ad"; } - -.fa-google-plus-g:before { - content: "\f0d5"; } - -.fa-artstation:before { - content: "\f77a"; } - -.fa-markdown:before { - content: "\f60f"; } - -.fa-sourcetree:before { - content: "\f7d3"; } - -.fa-google-plus:before { - content: "\f2b3"; } - -.fa-diaspora:before { - content: "\f791"; } - -.fa-foursquare:before { - content: "\f180"; } - -.fa-stack-overflow:before { - content: "\f16c"; } - -.fa-github-alt:before { - content: "\f113"; } - -.fa-phoenix-squadron:before { - content: "\f511"; } - -.fa-pagelines:before { - content: "\f18c"; } - -.fa-algolia:before { - content: "\f36c"; } - -.fa-red-river:before { - content: "\f3e3"; } - -.fa-creative-commons-sa:before { - content: "\f4ef"; } - -.fa-safari:before { - content: "\f267"; } - -.fa-google:before { - content: "\f1a0"; } - -.fa-square-font-awesome-stroke:before { - content: "\f35c"; } - -.fa-font-awesome-alt:before { - content: "\f35c"; } - -.fa-atlassian:before { - content: "\f77b"; } - -.fa-linkedin-in:before { - content: "\f0e1"; } - -.fa-digital-ocean:before { - content: "\f391"; } - -.fa-nimblr:before { - content: "\f5a8"; } - -.fa-chromecast:before { - content: "\f838"; } - -.fa-evernote:before { - content: "\f839"; } - -.fa-hacker-news:before { - content: "\f1d4"; } - -.fa-creative-commons-sampling:before { - content: "\f4f0"; } - -.fa-adversal:before { - content: "\f36a"; } - -.fa-creative-commons:before { - content: "\f25e"; } - -.fa-watchman-monitoring:before { - content: "\e087"; } - -.fa-fonticons:before { - content: "\f280"; } - -.fa-weixin:before { - content: "\f1d7"; } - -.fa-shirtsinbulk:before { - content: "\f214"; } - -.fa-codepen:before { - content: "\f1cb"; } - -.fa-git-alt:before { - content: "\f841"; } - -.fa-lyft:before { - content: "\f3c3"; } - -.fa-rev:before { - content: "\f5b2"; } - -.fa-windows:before { - content: "\f17a"; } - -.fa-wizards-of-the-coast:before { - content: "\f730"; } - -.fa-square-viadeo:before { - content: "\f2aa"; } - -.fa-viadeo-square:before { - content: "\f2aa"; } - -.fa-meetup:before { - content: "\f2e0"; } - -.fa-centos:before { - content: "\f789"; } - -.fa-adn:before { - content: "\f170"; } - -.fa-cloudsmith:before { - content: "\f384"; } - -.fa-opensuse:before { - content: "\e62b"; } - -.fa-pied-piper-alt:before { - content: "\f1a8"; } - -.fa-square-dribbble:before { - content: "\f397"; } - -.fa-dribbble-square:before { - content: "\f397"; } - -.fa-codiepie:before { - content: "\f284"; } - -.fa-node:before { - content: "\f419"; } - -.fa-mix:before { - content: "\f3cb"; } - -.fa-steam:before { - content: "\f1b6"; } - -.fa-cc-apple-pay:before { - content: "\f416"; } - -.fa-scribd:before { - content: "\f28a"; } - -.fa-debian:before { - content: "\e60b"; } - -.fa-openid:before { - content: "\f19b"; } - -.fa-instalod:before { - content: "\e081"; } - -.fa-expeditedssl:before { - content: "\f23e"; } - -.fa-sellcast:before { - content: "\f2da"; } - -.fa-square-twitter:before { - content: "\f081"; } - -.fa-twitter-square:before { - content: "\f081"; } - -.fa-r-project:before { - content: "\f4f7"; } - -.fa-delicious:before { - content: "\f1a5"; } - -.fa-freebsd:before { - content: "\f3a4"; } - -.fa-vuejs:before { - content: "\f41f"; } - -.fa-accusoft:before { - content: "\f369"; } - -.fa-ioxhost:before { - content: "\f208"; } - -.fa-fonticons-fi:before { - content: "\f3a2"; } - -.fa-app-store:before { - content: "\f36f"; } - -.fa-cc-mastercard:before { - content: "\f1f1"; } - -.fa-itunes-note:before { - content: "\f3b5"; } - -.fa-golang:before { - content: "\e40f"; } - -.fa-kickstarter:before { - content: "\f3bb"; } - -.fa-square-kickstarter:before { - content: "\f3bb"; } - -.fa-grav:before { - content: "\f2d6"; } - -.fa-weibo:before { - content: "\f18a"; } - -.fa-uncharted:before { - content: "\e084"; } - -.fa-firstdraft:before { - content: "\f3a1"; } - -.fa-square-youtube:before { - content: "\f431"; } - -.fa-youtube-square:before { - content: "\f431"; } - -.fa-wikipedia-w:before { - content: "\f266"; } - -.fa-wpressr:before { - content: "\f3e4"; } - -.fa-rendact:before { - content: "\f3e4"; } - -.fa-angellist:before { - content: "\f209"; } - -.fa-galactic-republic:before { - content: "\f50c"; } - -.fa-nfc-directional:before { - content: "\e530"; } - -.fa-skype:before { - content: "\f17e"; } - -.fa-joget:before { - content: "\f3b7"; } - -.fa-fedora:before { - content: "\f798"; } - -.fa-stripe-s:before { - content: "\f42a"; } - -.fa-meta:before { - content: "\e49b"; } - -.fa-laravel:before { - content: "\f3bd"; } - -.fa-hotjar:before { - content: "\f3b1"; } - -.fa-bluetooth-b:before { - content: "\f294"; } - -.fa-square-letterboxd:before { - content: "\e62e"; } - -.fa-sticker-mule:before { - content: "\f3f7"; } - -.fa-creative-commons-zero:before { - content: "\f4f3"; } - -.fa-hips:before { - content: "\f452"; } - -.fa-behance:before { - content: "\f1b4"; } - -.fa-reddit:before { - content: "\f1a1"; } - -.fa-discord:before { - content: "\f392"; } - -.fa-chrome:before { - content: "\f268"; } - -.fa-app-store-ios:before { - content: "\f370"; } - -.fa-cc-discover:before { - content: "\f1f2"; } - -.fa-wpbeginner:before { - content: "\f297"; } - -.fa-confluence:before { - content: "\f78d"; } - -.fa-shoelace:before { - content: "\e60c"; } - -.fa-mdb:before { - content: "\f8ca"; } - -.fa-dochub:before { - content: "\f394"; } - -.fa-accessible-icon:before { - content: "\f368"; } - -.fa-ebay:before { - content: "\f4f4"; } - -.fa-amazon:before { - content: "\f270"; } - -.fa-unsplash:before { - content: "\e07c"; } - -.fa-yarn:before { - content: "\f7e3"; } - -.fa-square-steam:before { - content: "\f1b7"; } - -.fa-steam-square:before { - content: "\f1b7"; } - -.fa-500px:before { - content: "\f26e"; } - -.fa-square-vimeo:before { - content: "\f194"; } - -.fa-vimeo-square:before { - content: "\f194"; } - -.fa-asymmetrik:before { - content: "\f372"; } - -.fa-font-awesome:before { - content: "\f2b4"; } - -.fa-font-awesome-flag:before { - content: "\f2b4"; } - -.fa-font-awesome-logo-full:before { - content: "\f2b4"; } - -.fa-gratipay:before { - content: "\f184"; } - -.fa-apple:before { - content: "\f179"; } - -.fa-hive:before { - content: "\e07f"; } - -.fa-gitkraken:before { - content: "\f3a6"; } - -.fa-keybase:before { - content: "\f4f5"; } - -.fa-apple-pay:before { - content: "\f415"; } - -.fa-padlet:before { - content: "\e4a0"; } - -.fa-amazon-pay:before { - content: "\f42c"; } - -.fa-square-github:before { - content: "\f092"; } - -.fa-github-square:before { - content: "\f092"; } - -.fa-stumbleupon:before { - content: "\f1a4"; } - -.fa-fedex:before { - content: "\f797"; } - -.fa-phoenix-framework:before { - content: "\f3dc"; } - -.fa-shopify:before { - content: "\e057"; } - -.fa-neos:before { - content: "\f612"; } - -.fa-square-threads:before { - content: "\e619"; } - -.fa-hackerrank:before { - content: "\f5f7"; } - -.fa-researchgate:before { - content: "\f4f8"; } - -.fa-swift:before { - content: "\f8e1"; } - -.fa-angular:before { - content: "\f420"; } - -.fa-speakap:before { - content: "\f3f3"; } - -.fa-angrycreative:before { - content: "\f36e"; } - -.fa-y-combinator:before { - content: "\f23b"; } - -.fa-empire:before { - content: "\f1d1"; } - -.fa-envira:before { - content: "\f299"; } - -.fa-google-scholar:before { - content: "\e63b"; } - -.fa-square-gitlab:before { - content: "\e5ae"; } - -.fa-gitlab-square:before { - content: "\e5ae"; } - -.fa-studiovinari:before { - content: "\f3f8"; } - -.fa-pied-piper:before { - content: "\f2ae"; } - -.fa-wordpress:before { - content: "\f19a"; } - -.fa-product-hunt:before { - content: "\f288"; } - -.fa-firefox:before { - content: "\f269"; } - -.fa-linode:before { - content: "\f2b8"; } - -.fa-goodreads:before { - content: "\f3a8"; } - -.fa-square-odnoklassniki:before { - content: "\f264"; } - -.fa-odnoklassniki-square:before { - content: "\f264"; } - -.fa-jsfiddle:before { - content: "\f1cc"; } - -.fa-sith:before { - content: "\f512"; } - -.fa-themeisle:before { - content: "\f2b2"; } - -.fa-page4:before { - content: "\f3d7"; } - -.fa-hashnode:before { - content: "\e499"; } - -.fa-react:before { - content: "\f41b"; } - -.fa-cc-paypal:before { - content: "\f1f4"; } - -.fa-squarespace:before { - content: "\f5be"; } - -.fa-cc-stripe:before { - content: "\f1f5"; } - -.fa-creative-commons-share:before { - content: "\f4f2"; } - -.fa-bitcoin:before { - content: "\f379"; } - -.fa-keycdn:before { - content: "\f3ba"; } - -.fa-opera:before { - content: "\f26a"; } - -.fa-itch-io:before { - content: "\f83a"; } - -.fa-umbraco:before { - content: "\f8e8"; } - -.fa-galactic-senate:before { - content: "\f50d"; } - -.fa-ubuntu:before { - content: "\f7df"; } - -.fa-draft2digital:before { - content: "\f396"; } - -.fa-stripe:before { - content: "\f429"; } - -.fa-houzz:before { - content: "\f27c"; } - -.fa-gg:before { - content: "\f260"; } - -.fa-dhl:before { - content: "\f790"; } - -.fa-square-pinterest:before { - content: "\f0d3"; } - -.fa-pinterest-square:before { - content: "\f0d3"; } - -.fa-xing:before { - content: "\f168"; } - -.fa-blackberry:before { - content: "\f37b"; } - -.fa-creative-commons-pd:before { - content: "\f4ec"; } - -.fa-playstation:before { - content: "\f3df"; } - -.fa-quinscape:before { - content: "\f459"; } - -.fa-less:before { - content: "\f41d"; } - -.fa-blogger-b:before { - content: "\f37d"; } - -.fa-opencart:before { - content: "\f23d"; } - -.fa-vine:before { - content: "\f1ca"; } - -.fa-signal-messenger:before { - content: "\e663"; } - -.fa-paypal:before { - content: "\f1ed"; } - -.fa-gitlab:before { - content: "\f296"; } - -.fa-typo3:before { - content: "\f42b"; } - -.fa-reddit-alien:before { - content: "\f281"; } - -.fa-yahoo:before { - content: "\f19e"; } - -.fa-dailymotion:before { - content: "\e052"; } - -.fa-affiliatetheme:before { - content: "\f36b"; } - -.fa-pied-piper-pp:before { - content: "\f1a7"; } - -.fa-bootstrap:before { - content: "\f836"; } - -.fa-odnoklassniki:before { - content: "\f263"; } - -.fa-nfc-symbol:before { - content: "\e531"; } - -.fa-mintbit:before { - content: "\e62f"; } - -.fa-ethereum:before { - content: "\f42e"; } - -.fa-speaker-deck:before { - content: "\f83c"; } - -.fa-creative-commons-nc-eu:before { - content: "\f4e9"; } - -.fa-patreon:before { - content: "\f3d9"; } - -.fa-avianex:before { - content: "\f374"; } - -.fa-ello:before { - content: "\f5f1"; } - -.fa-gofore:before { - content: "\f3a7"; } - -.fa-bimobject:before { - content: "\f378"; } - -.fa-brave-reverse:before { - content: "\e63d"; } - -.fa-facebook-f:before { - content: "\f39e"; } - -.fa-square-google-plus:before { - content: "\f0d4"; } - -.fa-google-plus-square:before { - content: "\f0d4"; } - -.fa-web-awesome:before { - content: "\e682"; } - -.fa-mandalorian:before { - content: "\f50f"; } - -.fa-first-order-alt:before { - content: "\f50a"; } - -.fa-osi:before { - content: "\f41a"; } - -.fa-google-wallet:before { - content: "\f1ee"; } - -.fa-d-and-d-beyond:before { - content: "\f6ca"; } - -.fa-periscope:before { - content: "\f3da"; } - -.fa-fulcrum:before { - content: "\f50b"; } - -.fa-cloudscale:before { - content: "\f383"; } - -.fa-forumbee:before { - content: "\f211"; } - -.fa-mizuni:before { - content: "\f3cc"; } - -.fa-schlix:before { - content: "\f3ea"; } - -.fa-square-xing:before { - content: "\f169"; } - -.fa-xing-square:before { - content: "\f169"; } - -.fa-bandcamp:before { - content: "\f2d5"; } - -.fa-wpforms:before { - content: "\f298"; } - -.fa-cloudversify:before { - content: "\f385"; } - -.fa-usps:before { - content: "\f7e1"; } - -.fa-megaport:before { - content: "\f5a3"; } - -.fa-magento:before { - content: "\f3c4"; } - -.fa-spotify:before { - content: "\f1bc"; } - -.fa-optin-monster:before { - content: "\f23c"; } - -.fa-fly:before { - content: "\f417"; } - -.fa-aviato:before { - content: "\f421"; } - -.fa-itunes:before { - content: "\f3b4"; } - -.fa-cuttlefish:before { - content: "\f38c"; } - -.fa-blogger:before { - content: "\f37c"; } - -.fa-flickr:before { - content: "\f16e"; } - -.fa-viber:before { - content: "\f409"; } - -.fa-soundcloud:before { - content: "\f1be"; } - -.fa-digg:before { - content: "\f1a6"; } - -.fa-tencent-weibo:before { - content: "\f1d5"; } - -.fa-letterboxd:before { - content: "\e62d"; } - -.fa-symfony:before { - content: "\f83d"; } - -.fa-maxcdn:before { - content: "\f136"; } - -.fa-etsy:before { - content: "\f2d7"; } - -.fa-facebook-messenger:before { - content: "\f39f"; } - -.fa-audible:before { - content: "\f373"; } - -.fa-think-peaks:before { - content: "\f731"; } - -.fa-bilibili:before { - content: "\e3d9"; } - -.fa-erlang:before { - content: "\f39d"; } - -.fa-x-twitter:before { - content: "\e61b"; } - -.fa-cotton-bureau:before { - content: "\f89e"; } - -.fa-dashcube:before { - content: "\f210"; } - -.fa-42-group:before { - content: "\e080"; } - -.fa-innosoft:before { - content: "\e080"; } - -.fa-stack-exchange:before { - content: "\f18d"; } - -.fa-elementor:before { - content: "\f430"; } - -.fa-square-pied-piper:before { - content: "\e01e"; } - -.fa-pied-piper-square:before { - content: "\e01e"; } - -.fa-creative-commons-nd:before { - content: "\f4eb"; } - -.fa-palfed:before { - content: "\f3d8"; } - -.fa-superpowers:before { - content: "\f2dd"; } - -.fa-resolving:before { - content: "\f3e7"; } - -.fa-xbox:before { - content: "\f412"; } - -.fa-square-web-awesome-stroke:before { - content: "\e684"; } - -.fa-searchengin:before { - content: "\f3eb"; } - -.fa-tiktok:before { - content: "\e07b"; } - -.fa-square-facebook:before { - content: "\f082"; } - -.fa-facebook-square:before { - content: "\f082"; } - -.fa-renren:before { - content: "\f18b"; } - -.fa-linux:before { - content: "\f17c"; } - -.fa-glide:before { - content: "\f2a5"; } - -.fa-linkedin:before { - content: "\f08c"; } - -.fa-hubspot:before { - content: "\f3b2"; } - -.fa-deploydog:before { - content: "\f38e"; } - -.fa-twitch:before { - content: "\f1e8"; } - -.fa-ravelry:before { - content: "\f2d9"; } - -.fa-mixer:before { - content: "\e056"; } - -.fa-square-lastfm:before { - content: "\f203"; } - -.fa-lastfm-square:before { - content: "\f203"; } - -.fa-vimeo:before { - content: "\f40a"; } - -.fa-mendeley:before { - content: "\f7b3"; } - -.fa-uniregistry:before { - content: "\f404"; } - -.fa-figma:before { - content: "\f799"; } - -.fa-creative-commons-remix:before { - content: "\f4ee"; } - -.fa-cc-amazon-pay:before { - content: "\f42d"; } - -.fa-dropbox:before { - content: "\f16b"; } - -.fa-instagram:before { - content: "\f16d"; } - -.fa-cmplid:before { - content: "\e360"; } - -.fa-upwork:before { - content: "\e641"; } - -.fa-facebook:before { - content: "\f09a"; } - -.fa-gripfire:before { - content: "\f3ac"; } - -.fa-jedi-order:before { - content: "\f50e"; } - -.fa-uikit:before { - content: "\f403"; } - -.fa-fort-awesome-alt:before { - content: "\f3a3"; } - -.fa-phabricator:before { - content: "\f3db"; } - -.fa-ussunnah:before { - content: "\f407"; } - -.fa-earlybirds:before { - content: "\f39a"; } - -.fa-trade-federation:before { - content: "\f513"; } - -.fa-autoprefixer:before { - content: "\f41c"; } - -.fa-whatsapp:before { - content: "\f232"; } - -.fa-square-upwork:before { - content: "\e67c"; } - -.fa-slideshare:before { - content: "\f1e7"; } - -.fa-google-play:before { - content: "\f3ab"; } - -.fa-viadeo:before { - content: "\f2a9"; } - -.fa-line:before { - content: "\f3c0"; } - -.fa-google-drive:before { - content: "\f3aa"; } - -.fa-servicestack:before { - content: "\f3ec"; } - -.fa-simplybuilt:before { - content: "\f215"; } - -.fa-bitbucket:before { - content: "\f171"; } - -.fa-imdb:before { - content: "\f2d8"; } - -.fa-deezer:before { - content: "\e077"; } - -.fa-raspberry-pi:before { - content: "\f7bb"; } - -.fa-jira:before { - content: "\f7b1"; } - -.fa-docker:before { - content: "\f395"; } - -.fa-screenpal:before { - content: "\e570"; } - -.fa-bluetooth:before { - content: "\f293"; } - -.fa-gitter:before { - content: "\f426"; } - -.fa-d-and-d:before { - content: "\f38d"; } - -.fa-microblog:before { - content: "\e01a"; } - -.fa-cc-diners-club:before { - content: "\f24c"; } - -.fa-gg-circle:before { - content: "\f261"; } - -.fa-pied-piper-hat:before { - content: "\f4e5"; } - -.fa-kickstarter-k:before { - content: "\f3bc"; } - -.fa-yandex:before { - content: "\f413"; } - -.fa-readme:before { - content: "\f4d5"; } - -.fa-html5:before { - content: "\f13b"; } - -.fa-sellsy:before { - content: "\f213"; } - -.fa-square-web-awesome:before { - content: "\e683"; } - -.fa-sass:before { - content: "\f41e"; } - -.fa-wirsindhandwerk:before { - content: "\e2d0"; } - -.fa-wsh:before { - content: "\e2d0"; } - -.fa-buromobelexperte:before { - content: "\f37f"; } - -.fa-salesforce:before { - content: "\f83b"; } - -.fa-octopus-deploy:before { - content: "\e082"; } - -.fa-medapps:before { - content: "\f3c6"; } - -.fa-ns8:before { - content: "\f3d5"; } - -.fa-pinterest-p:before { - content: "\f231"; } - -.fa-apper:before { - content: "\f371"; } - -.fa-fort-awesome:before { - content: "\f286"; } - -.fa-waze:before { - content: "\f83f"; } - -.fa-bluesky:before { - content: "\e671"; } - -.fa-cc-jcb:before { - content: "\f24b"; } - -.fa-snapchat:before { - content: "\f2ab"; } - -.fa-snapchat-ghost:before { - content: "\f2ab"; } - -.fa-fantasy-flight-games:before { - content: "\f6dc"; } - -.fa-rust:before { - content: "\e07a"; } - -.fa-wix:before { - content: "\f5cf"; } - -.fa-square-behance:before { - content: "\f1b5"; } - -.fa-behance-square:before { - content: "\f1b5"; } - -.fa-supple:before { - content: "\f3f9"; } - -.fa-webflow:before { - content: "\e65c"; } - -.fa-rebel:before { - content: "\f1d0"; } - -.fa-css3:before { - content: "\f13c"; } - -.fa-staylinked:before { - content: "\f3f5"; } - -.fa-kaggle:before { - content: "\f5fa"; } - -.fa-space-awesome:before { - content: "\e5ac"; } - -.fa-deviantart:before { - content: "\f1bd"; } - -.fa-cpanel:before { - content: "\f388"; } - -.fa-goodreads-g:before { - content: "\f3a9"; } - -.fa-square-git:before { - content: "\f1d2"; } - -.fa-git-square:before { - content: "\f1d2"; } - -.fa-square-tumblr:before { - content: "\f174"; } - -.fa-tumblr-square:before { - content: "\f174"; } - -.fa-trello:before { - content: "\f181"; } - -.fa-creative-commons-nc-jp:before { - content: "\f4ea"; } - -.fa-get-pocket:before { - content: "\f265"; } - -.fa-perbyte:before { - content: "\e083"; } - -.fa-grunt:before { - content: "\f3ad"; } - -.fa-weebly:before { - content: "\f5cc"; } - -.fa-connectdevelop:before { - content: "\f20e"; } - -.fa-leanpub:before { - content: "\f212"; } - -.fa-black-tie:before { - content: "\f27e"; } - -.fa-themeco:before { - content: "\f5c6"; } - -.fa-python:before { - content: "\f3e2"; } - -.fa-android:before { - content: "\f17b"; } - -.fa-bots:before { - content: "\e340"; } - -.fa-free-code-camp:before { - content: "\f2c5"; } - -.fa-hornbill:before { - content: "\f592"; } - -.fa-js:before { - content: "\f3b8"; } - -.fa-ideal:before { - content: "\e013"; } - -.fa-git:before { - content: "\f1d3"; } - -.fa-dev:before { - content: "\f6cc"; } - -.fa-sketch:before { - content: "\f7c6"; } - -.fa-yandex-international:before { - content: "\f414"; } - -.fa-cc-amex:before { - content: "\f1f3"; } - -.fa-uber:before { - content: "\f402"; } - -.fa-github:before { - content: "\f09b"; } - -.fa-php:before { - content: "\f457"; } - -.fa-alipay:before { - content: "\f642"; } - -.fa-youtube:before { - content: "\f167"; } - -.fa-skyatlas:before { - content: "\f216"; } - -.fa-firefox-browser:before { - content: "\e007"; } - -.fa-replyd:before { - content: "\f3e6"; } - -.fa-suse:before { - content: "\f7d6"; } - -.fa-jenkins:before { - content: "\f3b6"; } - -.fa-twitter:before { - content: "\f099"; } - -.fa-rockrms:before { - content: "\f3e9"; } - -.fa-pinterest:before { - content: "\f0d2"; } - -.fa-buffer:before { - content: "\f837"; } - -.fa-npm:before { - content: "\f3d4"; } - -.fa-yammer:before { - content: "\f840"; } - -.fa-btc:before { - content: "\f15a"; } - -.fa-dribbble:before { - content: "\f17d"; } - -.fa-stumbleupon-circle:before { - content: "\f1a3"; } - -.fa-internet-explorer:before { - content: "\f26b"; } - -.fa-stubber:before { - content: "\e5c7"; } - -.fa-telegram:before { - content: "\f2c6"; } - -.fa-telegram-plane:before { - content: "\f2c6"; } - -.fa-old-republic:before { - content: "\f510"; } - -.fa-odysee:before { - content: "\e5c6"; } - -.fa-square-whatsapp:before { - content: "\f40c"; } - -.fa-whatsapp-square:before { - content: "\f40c"; } - -.fa-node-js:before { - content: "\f3d3"; } - -.fa-edge-legacy:before { - content: "\e078"; } - -.fa-slack:before { - content: "\f198"; } - -.fa-slack-hash:before { - content: "\f198"; } - -.fa-medrt:before { - content: "\f3c8"; } - -.fa-usb:before { - content: "\f287"; } - -.fa-tumblr:before { - content: "\f173"; } - -.fa-vaadin:before { - content: "\f408"; } - -.fa-quora:before { - content: "\f2c4"; } - -.fa-square-x-twitter:before { - content: "\e61a"; } - -.fa-reacteurope:before { - content: "\f75d"; } - -.fa-medium:before { - content: "\f23a"; } - -.fa-medium-m:before { - content: "\f23a"; } - -.fa-amilia:before { - content: "\f36d"; } - -.fa-mixcloud:before { - content: "\f289"; } - -.fa-flipboard:before { - content: "\f44d"; } - -.fa-viacoin:before { - content: "\f237"; } - -.fa-critical-role:before { - content: "\f6c9"; } - -.fa-sitrox:before { - content: "\e44a"; } - -.fa-discourse:before { - content: "\f393"; } - -.fa-joomla:before { - content: "\f1aa"; } - -.fa-mastodon:before { - content: "\f4f6"; } - -.fa-airbnb:before { - content: "\f834"; } - -.fa-wolf-pack-battalion:before { - content: "\f514"; } - -.fa-buy-n-large:before { - content: "\f8a6"; } - -.fa-gulp:before { - content: "\f3ae"; } - -.fa-creative-commons-sampling-plus:before { - content: "\f4f1"; } - -.fa-strava:before { - content: "\f428"; } - -.fa-ember:before { - content: "\f423"; } - -.fa-canadian-maple-leaf:before { - content: "\f785"; } - -.fa-teamspeak:before { - content: "\f4f9"; } - -.fa-pushed:before { - content: "\f3e1"; } - -.fa-wordpress-simple:before { - content: "\f411"; } - -.fa-nutritionix:before { - content: "\f3d6"; } - -.fa-wodu:before { - content: "\e088"; } - -.fa-google-pay:before { - content: "\e079"; } - -.fa-intercom:before { - content: "\f7af"; } - -.fa-zhihu:before { - content: "\f63f"; } - -.fa-korvue:before { - content: "\f42f"; } - -.fa-pix:before { - content: "\e43a"; } - -.fa-steam-symbol:before { - content: "\f3f6"; } -:root, :host { - --fa-style-family-classic: 'Font Awesome 6 Free'; - --fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free'; } - -@font-face { - font-family: 'Font Awesome 6 Free'; - font-style: normal; - font-weight: 400; - font-display: block; - src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } - -.far, -.fa-regular { - font-weight: 400; } -:root, :host { - --fa-style-family-classic: 'Font Awesome 6 Free'; - --fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; } - -@font-face { - font-family: 'Font Awesome 6 Free'; - font-style: normal; - font-weight: 900; - font-display: block; - src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } - -.fas, -.fa-solid { - font-weight: 900; } -@font-face { - font-family: 'Font Awesome 5 Brands'; - font-display: block; - font-weight: 400; - src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } - -@font-face { - font-family: 'Font Awesome 5 Free'; - font-display: block; - font-weight: 900; - src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } - -@font-face { - font-family: 'Font Awesome 5 Free'; - font-display: block; - font-weight: 400; - src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } -@font-face { - font-family: 'FontAwesome'; - font-display: block; - src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } - -@font-face { - font-family: 'FontAwesome'; - font-display: block; - src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } - -@font-face { - font-family: 'FontAwesome'; - font-display: block; - src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); - unicode-range: U+F003,U+F006,U+F014,U+F016-F017,U+F01A-F01B,U+F01D,U+F022,U+F03E,U+F044,U+F046,U+F05C-F05D,U+F06E,U+F070,U+F087-F088,U+F08A,U+F094,U+F096-F097,U+F09D,U+F0A0,U+F0A2,U+F0A4-F0A7,U+F0C5,U+F0C7,U+F0E5-F0E6,U+F0EB,U+F0F6-F0F8,U+F10C,U+F114-F115,U+F118-F11A,U+F11C-F11D,U+F133,U+F147,U+F14E,U+F150-F152,U+F185-F186,U+F18E,U+F190-F192,U+F196,U+F1C1-F1C9,U+F1D9,U+F1DB,U+F1E3,U+F1EA,U+F1F7,U+F1F9,U+F20A,U+F247-F248,U+F24A,U+F24D,U+F255-F25B,U+F25D,U+F271-F274,U+F278,U+F27B,U+F28C,U+F28E,U+F29C,U+F2B5,U+F2B7,U+F2BA,U+F2BC,U+F2BE,U+F2C0-F2C1,U+F2C3,U+F2D0,U+F2D2,U+F2D4,U+F2DC; } - -@font-face { - font-family: 'FontAwesome'; - font-display: block; - src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype"); - unicode-range: U+F041,U+F047,U+F065-F066,U+F07D-F07E,U+F080,U+F08B,U+F08E,U+F090,U+F09A,U+F0AC,U+F0AE,U+F0B2,U+F0D0,U+F0D6,U+F0E4,U+F0EC,U+F10A-F10B,U+F123,U+F13E,U+F148-F149,U+F14C,U+F156,U+F15E,U+F160-F161,U+F163,U+F175-F178,U+F195,U+F1F8,U+F219,U+F27A; } diff --git a/resources/fontawesome/css/all.min.css b/resources/fontawesome/css/all.min.css deleted file mode 100644 index 45072b3..0000000 --- a/resources/fontawesome/css/all.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-classic,.fa-regular,.fa-sharp,.fa-solid,.fab,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-classic,.fa-regular,.fa-solid,.far,.fas{font-family:"Font Awesome 6 Free"}.fa-brands,.fab{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,0));transform:rotate(var(--fa-rotate-angle,0))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} - -.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-fill-drip:before{content:"\f576"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-at:before{content:"\40"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-text-height:before{content:"\f034"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-stethoscope:before{content:"\f0f1"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-info:before{content:"\f129"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-explosion:before{content:"\e4e9"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-wave-square:before{content:"\f83e"}.fa-ring:before{content:"\f70b"}.fa-building-un:before{content:"\e4d9"}.fa-dice-three:before{content:"\f527"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-door-open:before{content:"\f52b"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-atom:before{content:"\f5d2"}.fa-soap:before{content:"\e06e"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-pump-medical:before{content:"\e06a"}.fa-fingerprint:before{content:"\f577"}.fa-hand-point-right:before{content:"\f0a4"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-flag-checkered:before{content:"\f11e"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-crop:before{content:"\f125"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-users-rectangle:before{content:"\e594"}.fa-people-roof:before{content:"\e537"}.fa-people-line:before{content:"\e534"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-diagram-predecessor:before{content:"\e477"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-laptop:before{content:"\f109"}.fa-file-csv:before{content:"\f6dd"}.fa-menorah:before{content:"\f676"}.fa-truck-plane:before{content:"\e58f"}.fa-record-vinyl:before{content:"\f8d9"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-bong:before{content:"\f55c"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-jar-wheat:before{content:"\e517"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-pager:before{content:"\f815"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-strikethrough:before{content:"\f0cc"}.fa-k:before{content:"\4b"}.fa-landmark-flag:before{content:"\e51c"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-backward:before{content:"\f04a"}.fa-caret-right:before{content:"\f0da"}.fa-comments:before{content:"\f086"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-code-pull-request:before{content:"\e13c"}.fa-clipboard-list:before{content:"\f46d"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-user-check:before{content:"\f4fc"}.fa-vial-virus:before{content:"\e597"}.fa-sheet-plastic:before{content:"\e571"}.fa-blog:before{content:"\f781"}.fa-user-ninja:before{content:"\f504"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-toggle-off:before{content:"\f204"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-person-drowning:before{content:"\e545"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-spray-can:before{content:"\f5bd"}.fa-truck-monster:before{content:"\f63b"}.fa-w:before{content:"\57"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-rainbow:before{content:"\f75b"}.fa-circle-notch:before{content:"\f1ce"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-paw:before{content:"\f1b0"}.fa-cloud:before{content:"\f0c2"}.fa-trowel-bricks:before{content:"\e58a"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-hospital-user:before{content:"\f80d"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-binoculars:before{content:"\f1e5"}.fa-microphone-slash:before{content:"\f131"}.fa-box-tissue:before{content:"\e05b"}.fa-motorcycle:before{content:"\f21c"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-toilets-portable:before{content:"\e584"}.fa-hockey-puck:before{content:"\f453"}.fa-table:before{content:"\f0ce"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-users-slash:before{content:"\e073"}.fa-clover:before{content:"\e139"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-star-and-crescent:before{content:"\f699"}.fa-house-fire:before{content:"\e50c"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-helicopter:before{content:"\f533"}.fa-compass:before{content:"\f14e"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-file-circle-question:before{content:"\e4ef"}.fa-laptop-code:before{content:"\f5fc"}.fa-swatchbook:before{content:"\f5c3"}.fa-prescription-bottle:before{content:"\f485"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-people-group:before{content:"\e533"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-film:before{content:"\f008"}.fa-ruler-horizontal:before{content:"\f547"}.fa-people-robbery:before{content:"\e536"}.fa-lightbulb:before{content:"\f0eb"}.fa-caret-left:before{content:"\f0d9"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-sitemap:before{content:"\f0e8"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-memory:before{content:"\f538"}.fa-road-spikes:before{content:"\e568"}.fa-fire-burner:before{content:"\e4f1"}.fa-flag:before{content:"\f024"}.fa-hanukiah:before{content:"\f6e6"}.fa-feather:before{content:"\f52d"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-comment-slash:before{content:"\f4b3"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-compress:before{content:"\f066"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-ankh:before{content:"\f644"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-asterisk:before{content:"\2a"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-peseta-sign:before{content:"\e221"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-ghost:before{content:"\f6e2"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-cart-plus:before{content:"\f217"}.fa-gamepad:before{content:"\f11b"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-egg:before{content:"\f7fb"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-campground:before{content:"\f6bb"}.fa-folder-plus:before{content:"\f65e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-lock:before{content:"\f023"}.fa-gas-pump:before{content:"\f52f"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-house-flood-water:before{content:"\e50e"}.fa-tree:before{content:"\f1bb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-sack-dollar:before{content:"\f81d"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-car-side:before{content:"\f5e4"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-microscope:before{content:"\f610"}.fa-sink:before{content:"\e06d"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-mitten:before{content:"\f7b5"}.fa-person-rays:before{content:"\e54d"}.fa-users:before{content:"\f0c0"}.fa-eye-slash:before{content:"\f070"}.fa-flask-vial:before{content:"\e4f3"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-om:before{content:"\f679"}.fa-worm:before{content:"\e599"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-plug:before{content:"\f1e6"}.fa-chevron-up:before{content:"\f077"}.fa-hand-spock:before{content:"\f259"}.fa-stopwatch:before{content:"\f2f2"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-chess-bishop:before{content:"\f43a"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-road-circle-check:before{content:"\e564"}.fa-dice-five:before{content:"\f523"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-land-mine-on:before{content:"\e51b"}.fa-i-cursor:before{content:"\f246"}.fa-stamp:before{content:"\f5bf"}.fa-stairs:before{content:"\e289"}.fa-i:before{content:"\49"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-pills:before{content:"\f484"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-tooth:before{content:"\f5c9"}.fa-v:before{content:"\56"}.fa-bangladeshi-taka-sign:before{content:"\e2e6"}.fa-bicycle:before{content:"\f206"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-snowman:before{content:"\f7d0"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-road-barrier:before{content:"\e562"}.fa-school:before{content:"\f549"}.fa-igloo:before{content:"\f7ae"}.fa-joint:before{content:"\f595"}.fa-angle-right:before{content:"\f105"}.fa-horse:before{content:"\f6f0"}.fa-q:before{content:"\51"}.fa-g:before{content:"\47"}.fa-notes-medical:before{content:"\f481"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-dong-sign:before{content:"\e169"}.fa-capsules:before{content:"\f46b"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-hand-point-up:before{content:"\f0a6"}.fa-money-bill:before{content:"\f0d6"}.fa-bookmark:before{content:"\f02e"}.fa-align-justify:before{content:"\f039"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-helmet-un:before{content:"\e503"}.fa-bullseye:before{content:"\f140"}.fa-bacon:before{content:"\f7e5"}.fa-hand-point-down:before{content:"\f0a7"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-radiation:before{content:"\f7b9"}.fa-chart-simple:before{content:"\e473"}.fa-mars-stroke:before{content:"\f229"}.fa-vial:before{content:"\f492"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-e:before{content:"\45"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-user:before{content:"\f007"}.fa-school-circle-check:before{content:"\e56b"}.fa-dumpster:before{content:"\f793"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-building-user:before{content:"\e4da"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-highlighter:before{content:"\f591"}.fa-key:before{content:"\f084"}.fa-bullhorn:before{content:"\f0a1"}.fa-globe:before{content:"\f0ac"}.fa-synagogue:before{content:"\f69b"}.fa-person-half-dress:before{content:"\e548"}.fa-road-bridge:before{content:"\e563"}.fa-location-arrow:before{content:"\f124"}.fa-c:before{content:"\43"}.fa-tablet-button:before{content:"\f10a"}.fa-building-lock:before{content:"\e4d6"}.fa-pizza-slice:before{content:"\f818"}.fa-money-bill-wave:before{content:"\f53a"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-house-flag:before{content:"\e50d"}.fa-person-circle-minus:before{content:"\e540"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-camera-rotate:before{content:"\e0d8"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-star:before{content:"\f005"}.fa-repeat:before{content:"\f363"}.fa-cross:before{content:"\f654"}.fa-box:before{content:"\f466"}.fa-venus-mars:before{content:"\f228"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-charging-station:before{content:"\f5e7"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-mobile-retro:before{content:"\e527"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-spider:before{content:"\f717"}.fa-hands-bound:before{content:"\e4f9"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-x-ray:before{content:"\f497"}.fa-spell-check:before{content:"\f891"}.fa-slash:before{content:"\f715"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-server:before{content:"\f233"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-shop-lock:before{content:"\e4a5"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-blender-phone:before{content:"\f6b6"}.fa-building-wheat:before{content:"\e4db"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-venus:before{content:"\f221"}.fa-passport:before{content:"\f5ab"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-temperature-high:before{content:"\f769"}.fa-microchip:before{content:"\f2db"}.fa-crown:before{content:"\f521"}.fa-weight-hanging:before{content:"\f5cd"}.fa-xmarks-lines:before{content:"\e59a"}.fa-file-prescription:before{content:"\f572"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-chess-knight:before{content:"\f441"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-wheelchair:before{content:"\f193"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-toggle-on:before{content:"\f205"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-l:before{content:"\4c"}.fa-fire:before{content:"\f06d"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-folder-open:before{content:"\f07c"}.fa-heart-circle-plus:before{content:"\e500"}.fa-code-fork:before{content:"\e13b"}.fa-city:before{content:"\f64f"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-pepper-hot:before{content:"\f816"}.fa-unlock:before{content:"\f09c"}.fa-colon-sign:before{content:"\e140"}.fa-headset:before{content:"\f590"}.fa-store-slash:before{content:"\e071"}.fa-road-circle-xmark:before{content:"\e566"}.fa-user-minus:before{content:"\f503"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-clipboard:before{content:"\f328"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-underline:before{content:"\f0cd"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-signature:before{content:"\f5b7"}.fa-stroopwafel:before{content:"\f551"}.fa-bold:before{content:"\f032"}.fa-anchor-lock:before{content:"\e4ad"}.fa-building-ngo:before{content:"\e4d7"}.fa-manat-sign:before{content:"\e1d5"}.fa-not-equal:before{content:"\f53e"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-jedi:before{content:"\f669"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-mug-hot:before{content:"\f7b6"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-gift:before{content:"\f06b"}.fa-dice-two:before{content:"\f528"}.fa-chess-queen:before{content:"\f445"}.fa-glasses:before{content:"\f530"}.fa-chess-board:before{content:"\f43c"}.fa-building-circle-check:before{content:"\e4d2"}.fa-person-chalkboard:before{content:"\e53d"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-less-than-equal:before{content:"\f537"}.fa-train:before{content:"\f238"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-crow:before{content:"\f520"}.fa-sailboat:before{content:"\e445"}.fa-window-restore:before{content:"\f2d2"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-torii-gate:before{content:"\f6a1"}.fa-frog:before{content:"\f52e"}.fa-bucket:before{content:"\e4cf"}.fa-image:before{content:"\f03e"}.fa-microphone:before{content:"\f130"}.fa-cow:before{content:"\f6c8"}.fa-caret-up:before{content:"\f0d8"}.fa-screwdriver:before{content:"\f54a"}.fa-folder-closed:before{content:"\e185"}.fa-house-tsunami:before{content:"\e515"}.fa-square-nfi:before{content:"\e576"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-lemon:before{content:"\f094"}.fa-head-side-mask:before{content:"\e063"}.fa-handshake:before{content:"\f2b5"}.fa-gem:before{content:"\f3a5"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-smoking:before{content:"\f48d"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-monument:before{content:"\f5a6"}.fa-snowplow:before{content:"\f7d2"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-cannabis:before{content:"\f55f"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-tablets:before{content:"\f490"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-chair:before{content:"\f6c0"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-plate-wheat:before{content:"\e55a"}.fa-icicles:before{content:"\f7ad"}.fa-person-shelter:before{content:"\e54f"}.fa-neuter:before{content:"\f22c"}.fa-id-badge:before{content:"\f2c1"}.fa-marker:before{content:"\f5a1"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-helicopter-symbol:before{content:"\e502"}.fa-universal-access:before{content:"\f29a"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-lari-sign:before{content:"\e1c8"}.fa-volcano:before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-viruses:before{content:"\e076"}.fa-square-person-confined:before{content:"\e577"}.fa-user-tie:before{content:"\f508"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-certificate:before{content:"\f0a3"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-suitcase:before{content:"\f0f2"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-camera-retro:before{content:"\f083"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-box-open:before{content:"\f49e"}.fa-scroll:before{content:"\f70e"}.fa-spa:before{content:"\f5bb"}.fa-location-pin-lock:before{content:"\e51f"}.fa-pause:before{content:"\f04c"}.fa-hill-avalanche:before{content:"\e507"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-bomb:before{content:"\f1e2"}.fa-registered:before{content:"\f25d"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-subscript:before{content:"\f12c"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-burst:before{content:"\e4dc"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-money-bills:before{content:"\e1f3"}.fa-smog:before{content:"\f75f"}.fa-crutch:before{content:"\f7f7"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-palette:before{content:"\f53f"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-vest:before{content:"\e085"}.fa-ferry:before{content:"\e4ea"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-boxes-packing:before{content:"\e4c7"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-bowl-food:before{content:"\e4c6"}.fa-candy-cane:before{content:"\f786"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-file-word:before{content:"\f1c2"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-house-lock:before{content:"\e510"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-children:before{content:"\e4e1"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-envelope-open:before{content:"\f2b6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-mattress-pillow:before{content:"\e525"}.fa-guarani-sign:before{content:"\e19a"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-fire-extinguisher:before{content:"\f134"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-greater-than-equal:before{content:"\f532"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-virus:before{content:"\e074"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-layer-group:before{content:"\f5fd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-archway:before{content:"\f557"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-square:before{content:"\f0c8"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-couch:before{content:"\f4b8"}.fa-cedi-sign:before{content:"\e0df"}.fa-italic:before{content:"\f033"}.fa-table-cells-column-lock:before{content:"\e678"}.fa-church:before{content:"\f51d"}.fa-comments-dollar:before{content:"\f653"}.fa-democrat:before{content:"\f747"}.fa-z:before{content:"\5a"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-road-lock:before{content:"\e567"}.fa-a:before{content:"\41"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-p:before{content:"\50"}.fa-snowflake:before{content:"\f2dc"}.fa-newspaper:before{content:"\f1ea"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-locust:before{content:"\e520"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-person-dress-burst:before{content:"\e544"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-vector-square:before{content:"\f5cb"}.fa-bread-slice:before{content:"\f7ec"}.fa-language:before{content:"\f1ab"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-filter:before{content:"\f0b0"}.fa-question:before{content:"\3f"}.fa-file-signature:before{content:"\f573"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-house-chimney-user:before{content:"\e065"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-puzzle-piece:before{content:"\f12e"}.fa-money-check:before{content:"\f53c"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-code:before{content:"\f121"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-virus-covid:before{content:"\e4a8"}.fa-austral-sign:before{content:"\e0a9"}.fa-f:before{content:"\46"}.fa-leaf:before{content:"\f06c"}.fa-road:before{content:"\f018"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-person-circle-plus:before{content:"\e541"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-sack-xmark:before{content:"\e56a"}.fa-file-excel:before{content:"\f1c3"}.fa-file-contract:before{content:"\f56c"}.fa-fish-fins:before{content:"\e4f2"}.fa-building-flag:before{content:"\e4d5"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-object-ungroup:before{content:"\f248"}.fa-poop:before{content:"\f619"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-kaaba:before{content:"\f66b"}.fa-toilet-paper:before{content:"\f71e"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-eject:before{content:"\f052"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-plane-circle-check:before{content:"\e555"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-object-group:before{content:"\f247"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-mask-ventilator:before{content:"\e524"}.fa-arrow-right:before{content:"\f061"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-cash-register:before{content:"\f788"}.fa-person-circle-question:before{content:"\e542"}.fa-h:before{content:"\48"}.fa-tarp:before{content:"\e57b"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-heart:before{content:"\f004"}.fa-mars-and-venus:before{content:"\f224"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-dumpster-fire:before{content:"\f794"}.fa-house-crack:before{content:"\e3b1"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-bottle-water:before{content:"\e4c5"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-kitchen-set:before{content:"\e51a"}.fa-r:before{content:"\52"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-cube:before{content:"\f1b2"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-shield-dog:before{content:"\e573"}.fa-solar-panel:before{content:"\f5ba"}.fa-lock-open:before{content:"\f3c1"}.fa-elevator:before{content:"\e16d"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-circle:before{content:"\f111"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-recycle:before{content:"\f1b8"}.fa-user-astronaut:before{content:"\f4fb"}.fa-plane-slash:before{content:"\e069"}.fa-trademark:before{content:"\f25c"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-satellite-dish:before{content:"\f7c0"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-users-rays:before{content:"\e593"}.fa-wallet:before{content:"\f555"}.fa-clipboard-check:before{content:"\f46c"}.fa-file-audio:before{content:"\f1c7"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-wrench:before{content:"\f0ad"}.fa-bugs:before{content:"\e4d0"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-file-image:before{content:"\f1c5"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-plane-departure:before{content:"\f5b0"}.fa-handshake-slash:before{content:"\e060"}.fa-book-bookmark:before{content:"\e0bb"}.fa-code-branch:before{content:"\f126"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-bridge:before{content:"\e4c8"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-truck-front:before{content:"\e2b7"}.fa-cat:before{content:"\f6be"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-truck-field:before{content:"\e58d"}.fa-route:before{content:"\f4d7"}.fa-clipboard-question:before{content:"\e4e3"}.fa-panorama:before{content:"\e209"}.fa-comment-medical:before{content:"\f7f5"}.fa-teeth-open:before{content:"\f62f"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-tags:before{content:"\f02c"}.fa-wine-glass:before{content:"\f4e3"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-house-signal:before{content:"\e012"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-faucet-drip:before{content:"\e006"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-terminal:before{content:"\f120"}.fa-mobile-button:before{content:"\f10b"}.fa-house-medical-flag:before{content:"\e514"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-tape:before{content:"\f4db"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-eye:before{content:"\f06e"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-audio-description:before{content:"\f29e"}.fa-person-military-to-person:before{content:"\e54c"}.fa-file-shield:before{content:"\e4f0"}.fa-user-slash:before{content:"\f506"}.fa-pen:before{content:"\f304"}.fa-tower-observation:before{content:"\e586"}.fa-file-code:before{content:"\f1c9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-bus:before{content:"\f207"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-window-maximize:before{content:"\f2d0"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-prescription:before{content:"\f5b1"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-vihara:before{content:"\f6a7"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-plant-wilt:before{content:"\e5aa"}.fa-diamond:before{content:"\f219"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-bacterium:before{content:"\e05a"}.fa-hand-pointer:before{content:"\f25a"}.fa-drum-steelpan:before{content:"\f56a"}.fa-hand-scissors:before{content:"\f257"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-biohazard:before{content:"\f780"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-mars-double:before{content:"\f227"}.fa-child-dress:before{content:"\e59c"}.fa-users-between-lines:before{content:"\e591"}.fa-lungs-virus:before{content:"\e067"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-phone:before{content:"\f095"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-child-reaching:before{content:"\e59d"}.fa-head-side-virus:before{content:"\e064"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-door-closed:before{content:"\f52a"}.fa-shield-virus:before{content:"\e06c"}.fa-dice-six:before{content:"\f526"}.fa-mosquito-net:before{content:"\e52c"}.fa-bridge-water:before{content:"\e4ce"}.fa-person-booth:before{content:"\f756"}.fa-text-width:before{content:"\f035"}.fa-hat-wizard:before{content:"\f6e8"}.fa-pen-fancy:before{content:"\f5ac"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-trash:before{content:"\f1f8"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-book-medical:before{content:"\f7e6"}.fa-poo:before{content:"\f2fe"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-cubes:before{content:"\f1b3"}.fa-divide:before{content:"\f529"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-headphones:before{content:"\f025"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-clapping:before{content:"\e1a8"}.fa-republican:before{content:"\f75e"}.fa-arrow-left:before{content:"\f060"}.fa-person-circle-xmark:before{content:"\e543"}.fa-ruler:before{content:"\f545"}.fa-align-left:before{content:"\f036"}.fa-dice-d6:before{content:"\f6d1"}.fa-restroom:before{content:"\f7bd"}.fa-j:before{content:"\4a"}.fa-users-viewfinder:before{content:"\e595"}.fa-file-video:before{content:"\f1c8"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-file-pdf:before{content:"\f1c1"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-o:before{content:"\4f"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-user-secret:before{content:"\f21b"}.fa-otter:before{content:"\f700"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-comment-dollar:before{content:"\f651"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-clipboard-user:before{content:"\f7f3"}.fa-child:before{content:"\f1ae"}.fa-lira-sign:before{content:"\f195"}.fa-satellite:before{content:"\f7bf"}.fa-plane-lock:before{content:"\e558"}.fa-tag:before{content:"\f02b"}.fa-comment:before{content:"\f075"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-envelope:before{content:"\f0e0"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-paperclip:before{content:"\f0c6"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-ribbon:before{content:"\f4d6"}.fa-lungs:before{content:"\f604"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-border-none:before{content:"\f850"}.fa-circle-nodes:before{content:"\e4e2"}.fa-parachute-box:before{content:"\f4cd"}.fa-indent:before{content:"\f03c"}.fa-truck-field-un:before{content:"\e58e"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-mountain:before{content:"\f6fc"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-cloud-meatball:before{content:"\f73b"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-square-virus:before{content:"\e578"}.fa-meteor:before{content:"\f753"}.fa-car-on:before{content:"\e4dd"}.fa-sleigh:before{content:"\f7cc"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-water:before{content:"\f773"}.fa-calendar-check:before{content:"\f274"}.fa-braille:before{content:"\f2a1"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-landmark:before{content:"\f66f"}.fa-truck:before{content:"\f0d1"}.fa-crosshairs:before{content:"\f05b"}.fa-person-cane:before{content:"\e53c"}.fa-tent:before{content:"\e57d"}.fa-vest-patches:before{content:"\e086"}.fa-check-double:before{content:"\f560"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-cookie:before{content:"\f563"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-dumbbell:before{content:"\f44b"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-tarp-droplet:before{content:"\e57c"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-calendar-plus:before{content:"\f271"}.fa-plane-arrival:before{content:"\f5af"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-chart-gantt:before{content:"\e0e4"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-dna:before{content:"\f471"}.fa-virus-slash:before{content:"\e075"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-chess:before{content:"\f439"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-plug-circle-check:before{content:"\e55c"}.fa-street-view:before{content:"\f21d"}.fa-franc-sign:before{content:"\e18f"}.fa-volume-off:before{content:"\f026"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-star-of-david:before{content:"\f69a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-vials:before{content:"\f493"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-place-of-worship:before{content:"\f67f"}.fa-grip-vertical:before{content:"\f58e"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-u:before{content:"\55"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-pallet:before{content:"\f482"}.fa-faucet:before{content:"\e005"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-s:before{content:"\53"}.fa-timeline:before{content:"\e29c"}.fa-keyboard:before{content:"\f11c"}.fa-caret-down:before{content:"\f0d7"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-plane-up:before{content:"\e22d"}.fa-piggy-bank:before{content:"\f4d3"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-mountain-city:before{content:"\e52e"}.fa-coins:before{content:"\f51e"}.fa-khanda:before{content:"\f66d"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-folder-tree:before{content:"\f802"}.fa-network-wired:before{content:"\f6ff"}.fa-map-pin:before{content:"\f276"}.fa-hamsa:before{content:"\f665"}.fa-cent-sign:before{content:"\e3f5"}.fa-flask:before{content:"\f0c3"}.fa-person-pregnant:before{content:"\e31e"}.fa-wand-sparkles:before{content:"\f72b"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-ticket:before{content:"\f145"}.fa-power-off:before{content:"\f011"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-flag-usa:before{content:"\f74d"}.fa-laptop-file:before{content:"\e51d"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-diagram-next:before{content:"\e476"}.fa-person-rifle:before{content:"\e54e"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-closed-captioning:before{content:"\f20a"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-venus-double:before{content:"\f226"}.fa-images:before{content:"\f302"}.fa-calculator:before{content:"\f1ec"}.fa-people-pulling:before{content:"\e535"}.fa-n:before{content:"\4e"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-cloud-rain:before{content:"\f73d"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-ship:before{content:"\f21a"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-download:before{content:"\f019"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-file-circle-check:before{content:"\e5a0"}.fa-forward:before{content:"\f04e"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-align-center:before{content:"\f037"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-calendar-week:before{content:"\f784"}.fa-laptop-medical:before{content:"\f812"}.fa-b:before{content:"\42"}.fa-file-medical:before{content:"\f477"}.fa-dice-one:before{content:"\f525"}.fa-kiwi-bird:before{content:"\f535"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-mill-sign:before{content:"\e1ed"}.fa-bowl-rice:before{content:"\e2eb"}.fa-skull:before{content:"\f54c"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-truck-pickup:before{content:"\f63c"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-stop:before{content:"\f04d"}.fa-code-merge:before{content:"\f387"}.fa-upload:before{content:"\f093"}.fa-hurricane:before{content:"\f751"}.fa-mound:before{content:"\e52d"}.fa-toilet-portable:before{content:"\e583"}.fa-compact-disc:before{content:"\f51f"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-caravan:before{content:"\f8ff"}.fa-shield-cat:before{content:"\e572"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-glass-water:before{content:"\e4f4"}.fa-oil-well:before{content:"\e532"}.fa-vault:before{content:"\e2c5"}.fa-mars:before{content:"\f222"}.fa-toilet:before{content:"\f7d8"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-sun:before{content:"\f185"}.fa-guitar:before{content:"\f7a6"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-horse-head:before{content:"\f7ab"}.fa-bore-hole:before{content:"\e4c3"}.fa-industry:before{content:"\f275"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-florin-sign:before{content:"\e184"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-less-than:before{content:"\3c"}.fa-angle-down:before{content:"\f107"}.fa-car-tunnel:before{content:"\e4de"}.fa-head-side-cough:before{content:"\e061"}.fa-grip-lines:before{content:"\f7a4"}.fa-thumbs-down:before{content:"\f165"}.fa-user-lock:before{content:"\f502"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-chess-pawn:before{content:"\f443"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-person-through-window:before{content:"\e5a9"}.fa-toolbox:before{content:"\f552"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-bug:before{content:"\f188"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-mountain-sun:before{content:"\e52f"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-dice-d20:before{content:"\f6cf"}.fa-truck-droplet:before{content:"\e58c"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-medal:before{content:"\f5a2"}.fa-bed:before{content:"\f236"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-podcast:before{content:"\f2ce"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-bell:before{content:"\f0f3"}.fa-superscript:before{content:"\f12b"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-star-of-life:before{content:"\f621"}.fa-phone-slash:before{content:"\f3dd"}.fa-paint-roller:before{content:"\f5aa"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-file:before{content:"\f15b"}.fa-greater-than:before{content:"\3e"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-arrow-down:before{content:"\f063"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-eraser:before{content:"\f12d"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-person-burst:before{content:"\e53b"}.fa-dove:before{content:"\f4ba"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-socks:before{content:"\f696"}.fa-inbox:before{content:"\f01c"}.fa-section:before{content:"\e447"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-envelope-open-text:before{content:"\f658"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-wine-bottle:before{content:"\f72f"}.fa-chess-rook:before{content:"\f447"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-dharmachakra:before{content:"\f655"}.fa-hotdog:before{content:"\f80f"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-drum:before{content:"\f569"}.fa-ice-cream:before{content:"\f810"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-fax:before{content:"\f1ac"}.fa-paragraph:before{content:"\f1dd"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-star-half:before{content:"\f089"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-tree-city:before{content:"\e587"}.fa-play:before{content:"\f04b"}.fa-font:before{content:"\f031"}.fa-table-cells-row-lock:before{content:"\e67a"}.fa-rupiah-sign:before{content:"\e23d"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-naira-sign:before{content:"\e1f6"}.fa-cart-arrow-down:before{content:"\f218"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-receipt:before{content:"\f543"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-chevron-down:before{content:"\f078"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-skull-crossbones:before{content:"\f714"}.fa-code-compare:before{content:"\e13a"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-school-lock:before{content:"\e56f"}.fa-tower-cell:before{content:"\e585"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-ranking-star:before{content:"\e561"}.fa-chess-king:before{content:"\f43f"}.fa-person-harassing:before{content:"\e549"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-arrow-up:before{content:"\f062"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-shrimp:before{content:"\e448"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-jug-detergent:before{content:"\e519"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-user-shield:before{content:"\f505"}.fa-wind:before{content:"\f72e"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-y:before{content:"\59"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-fish:before{content:"\f578"}.fa-user-graduate:before{content:"\f501"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-clapperboard:before{content:"\e131"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-jet-fighter-up:before{content:"\e518"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-copy:before{content:"\f0c5"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-hand-sparkles:before{content:"\e05d"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-child-combatant:before,.fa-child-rifle:before{content:"\e4e0"}.fa-gun:before{content:"\e19b"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-expand:before{content:"\f065"}.fa-computer:before{content:"\e4e5"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-peso-sign:before{content:"\e222"}.fa-building-shield:before{content:"\e4d8"}.fa-baby:before{content:"\f77c"}.fa-users-line:before{content:"\e592"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-tractor:before{content:"\f722"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-lines-leaning:before{content:"\e51e"}.fa-ruler-combined:before{content:"\f546"}.fa-copyright:before{content:"\f1f9"}.fa-equals:before{content:"\3d"}.fa-blender:before{content:"\f517"}.fa-teeth:before{content:"\f62e"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-map:before{content:"\f279"}.fa-rocket:before{content:"\f135"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-folder-minus:before{content:"\f65d"}.fa-store:before{content:"\f54e"}.fa-arrow-trend-up:before{content:"\e098"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-bezier-curve:before{content:"\f55b"}.fa-bell-slash:before{content:"\f1f6"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-school-flag:before{content:"\e56e"}.fa-fill:before{content:"\f575"}.fa-angle-up:before{content:"\f106"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-holly-berry:before{content:"\f7aa"}.fa-chevron-left:before{content:"\f053"}.fa-bacteria:before{content:"\e059"}.fa-hand-lizard:before{content:"\f258"}.fa-notdef:before{content:"\e1fe"}.fa-disease:before{content:"\f7fa"}.fa-briefcase-medical:before{content:"\f469"}.fa-genderless:before{content:"\f22d"}.fa-chevron-right:before{content:"\f054"}.fa-retweet:before{content:"\f079"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-pump-soap:before{content:"\e06b"}.fa-video-slash:before{content:"\f4e2"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-radio:before{content:"\f8d7"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-traffic-light:before{content:"\f637"}.fa-thermometer:before{content:"\f491"}.fa-vr-cardboard:before{content:"\f729"}.fa-hand-middle-finger:before{content:"\f806"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-truck-moving:before{content:"\f4df"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-display:before{content:"\e163"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-trophy:before{content:"\f091"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-hammer:before{content:"\f6e3"}.fa-hand-peace:before{content:"\f25b"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-spinner:before{content:"\f110"}.fa-robot:before{content:"\f544"}.fa-peace:before{content:"\f67c"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-warehouse:before{content:"\f494"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-splotch:before{content:"\f5bc"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-dice-four:before{content:"\f524"}.fa-sim-card:before{content:"\f7c4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-mercury:before{content:"\f223"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-person-falling-burst:before{content:"\e547"}.fa-award:before{content:"\f559"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-building:before{content:"\f1ad"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-qrcode:before{content:"\f029"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-house-medical:before{content:"\e3b2"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-house-chimney-window:before{content:"\e00d"}.fa-pen-nib:before{content:"\f5ad"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tents:before{content:"\e582"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-dog:before{content:"\f6d3"}.fa-carrot:before{content:"\f787"}.fa-moon:before{content:"\f186"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-cheese:before{content:"\f7ef"}.fa-yin-yang:before{content:"\f6ad"}.fa-music:before{content:"\f001"}.fa-code-commit:before{content:"\f386"}.fa-temperature-low:before{content:"\f76b"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-broom:before{content:"\f51a"}.fa-shield-heart:before{content:"\e574"}.fa-gopuram:before{content:"\f664"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-hashtag:before{content:"\23"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-oil-can:before{content:"\f613"}.fa-t:before{content:"\54"}.fa-hippo:before{content:"\f6ed"}.fa-chart-column:before{content:"\e0e3"}.fa-infinity:before{content:"\f534"}.fa-vial-circle-check:before{content:"\e596"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-voicemail:before{content:"\f897"}.fa-fan:before{content:"\f863"}.fa-person-walking-luggage:before{content:"\e554"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-calendar:before{content:"\f133"}.fa-trailer:before{content:"\e041"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-sd-card:before{content:"\f7c2"}.fa-dragon:before{content:"\f6d5"}.fa-shoe-prints:before{content:"\f54b"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-hand-holding:before{content:"\f4bd"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-clone:before{content:"\f24d"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-tornado:before{content:"\f76f"}.fa-file-circle-plus:before{content:"\e494"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-anchor:before{content:"\f13d"}.fa-border-all:before{content:"\f84c"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-cookie-bite:before{content:"\f564"}.fa-arrow-trend-down:before{content:"\e097"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-draw-polygon:before{content:"\f5ee"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-shower:before{content:"\f2cc"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-m:before{content:"\4d"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-book:before{content:"\f02d"}.fa-user-plus:before{content:"\f234"}.fa-check:before{content:"\f00c"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-house-circle-check:before{content:"\e509"}.fa-angle-left:before{content:"\f104"}.fa-diagram-successor:before{content:"\e47a"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-cloud-moon:before{content:"\f6c3"}.fa-briefcase:before{content:"\f0b1"}.fa-person-falling:before{content:"\e546"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-user-tag:before{content:"\f507"}.fa-rug:before{content:"\e569"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-baht-sign:before{content:"\e0ac"}.fa-book-open:before{content:"\f518"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-handcuffs:before{content:"\e4f8"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-database:before{content:"\f1c0"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-mask-face:before{content:"\e1d7"}.fa-hill-rockslide:before{content:"\e508"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-paper-plane:before{content:"\f1d8"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-dungeon:before{content:"\f6d9"}.fa-align-right:before{content:"\f038"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-life-ring:before{content:"\f1cd"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-calendar-day:before{content:"\f783"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-dice:before{content:"\f522"}.fa-bowling-ball:before{content:"\f436"}.fa-brain:before{content:"\f5dc"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-calendar-minus:before{content:"\f272"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-gifts:before{content:"\f79c"}.fa-hotel:before{content:"\f594"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-thumbs-up:before{content:"\f164"}.fa-user-clock:before{content:"\f4fd"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-file-invoice:before{content:"\f570"}.fa-window-minimize:before{content:"\f2d1"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-brush:before{content:"\f55d"}.fa-mask:before{content:"\f6fa"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-ruler-vertical:before{content:"\f548"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-train-tram:before{content:"\e5b4"}.fa-user-nurse:before{content:"\f82f"}.fa-syringe:before{content:"\f48e"}.fa-cloud-sun:before{content:"\f6c4"}.fa-stopwatch-20:before{content:"\e06f"}.fa-square-full:before{content:"\f45c"}.fa-magnet:before{content:"\f076"}.fa-jar:before{content:"\e516"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-bug-slash:before{content:"\e490"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-bone:before{content:"\f5d7"}.fa-user-injured:before{content:"\f728"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-plane:before{content:"\f072"}.fa-tent-arrows-down:before{content:"\e581"}.fa-exclamation:before{content:"\21"}.fa-arrows-spin:before{content:"\e4bb"}.fa-print:before{content:"\f02f"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-x:before{content:"\58"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-person-military-pointing:before{content:"\e54a"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-umbrella:before{content:"\f0e9"}.fa-trowel:before{content:"\e589"}.fa-d:before{content:"\44"}.fa-stapler:before{content:"\e5af"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-kip-sign:before{content:"\e1c4"}.fa-hand-point-left:before{content:"\f0a5"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-barcode:before{content:"\f02a"}.fa-plus-minus:before{content:"\e43c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-person-circle-check:before{content:"\e53e"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"} -.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host,:root{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-pixiv:before{content:"\e640"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-jxl:before{content:"\e67b"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-brave:before{content:"\e63c"}.fa-sistrix:before{content:"\f3ee"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-threads:before{content:"\e618"}.fa-napster:before{content:"\f3d2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-opensuse:before{content:"\e62b"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-debian:before{content:"\e60b"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before,.fa-square-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-square-letterboxd:before{content:"\e62e"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-shoelace:before{content:"\e60c"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-square-threads:before{content:"\e619"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-google-scholar:before{content:"\e63b"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-signal-messenger:before{content:"\e663"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-mintbit:before{content:"\e62f"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-brave-reverse:before{content:"\e63d"}.fa-facebook-f:before{content:"\f39e"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-web-awesome:before{content:"\e682"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-letterboxd:before{content:"\e62d"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-x-twitter:before{content:"\e61b"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-square-web-awesome-stroke:before{content:"\e684"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-upwork:before{content:"\e641"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-square-upwork:before{content:"\e67c"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-square-web-awesome:before{content:"\e683"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-bluesky:before{content:"\e671"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-webflow:before{content:"\e65c"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-square-x-twitter:before{content:"\e61a"}.fa-reacteurope:before{content:"\f75d"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"}:host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a} \ No newline at end of file diff --git a/resources/fontawesome/css/brands.css b/resources/fontawesome/css/brands.css deleted file mode 100644 index 12ad3aa..0000000 --- a/resources/fontawesome/css/brands.css +++ /dev/null @@ -1,1594 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -:root, :host { - --fa-style-family-brands: 'Font Awesome 6 Brands'; - --fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; } - -@font-face { - font-family: 'Font Awesome 6 Brands'; - font-style: normal; - font-weight: 400; - font-display: block; - src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } - -.fab, -.fa-brands { - font-weight: 400; } - -.fa-monero:before { - content: "\f3d0"; } - -.fa-hooli:before { - content: "\f427"; } - -.fa-yelp:before { - content: "\f1e9"; } - -.fa-cc-visa:before { - content: "\f1f0"; } - -.fa-lastfm:before { - content: "\f202"; } - -.fa-shopware:before { - content: "\f5b5"; } - -.fa-creative-commons-nc:before { - content: "\f4e8"; } - -.fa-aws:before { - content: "\f375"; } - -.fa-redhat:before { - content: "\f7bc"; } - -.fa-yoast:before { - content: "\f2b1"; } - -.fa-cloudflare:before { - content: "\e07d"; } - -.fa-ups:before { - content: "\f7e0"; } - -.fa-pixiv:before { - content: "\e640"; } - -.fa-wpexplorer:before { - content: "\f2de"; } - -.fa-dyalog:before { - content: "\f399"; } - -.fa-bity:before { - content: "\f37a"; } - -.fa-stackpath:before { - content: "\f842"; } - -.fa-buysellads:before { - content: "\f20d"; } - -.fa-first-order:before { - content: "\f2b0"; } - -.fa-modx:before { - content: "\f285"; } - -.fa-guilded:before { - content: "\e07e"; } - -.fa-vnv:before { - content: "\f40b"; } - -.fa-square-js:before { - content: "\f3b9"; } - -.fa-js-square:before { - content: "\f3b9"; } - -.fa-microsoft:before { - content: "\f3ca"; } - -.fa-qq:before { - content: "\f1d6"; } - -.fa-orcid:before { - content: "\f8d2"; } - -.fa-java:before { - content: "\f4e4"; } - -.fa-invision:before { - content: "\f7b0"; } - -.fa-creative-commons-pd-alt:before { - content: "\f4ed"; } - -.fa-centercode:before { - content: "\f380"; } - -.fa-glide-g:before { - content: "\f2a6"; } - -.fa-drupal:before { - content: "\f1a9"; } - -.fa-jxl:before { - content: "\e67b"; } - -.fa-hire-a-helper:before { - content: "\f3b0"; } - -.fa-creative-commons-by:before { - content: "\f4e7"; } - -.fa-unity:before { - content: "\e049"; } - -.fa-whmcs:before { - content: "\f40d"; } - -.fa-rocketchat:before { - content: "\f3e8"; } - -.fa-vk:before { - content: "\f189"; } - -.fa-untappd:before { - content: "\f405"; } - -.fa-mailchimp:before { - content: "\f59e"; } - -.fa-css3-alt:before { - content: "\f38b"; } - -.fa-square-reddit:before { - content: "\f1a2"; } - -.fa-reddit-square:before { - content: "\f1a2"; } - -.fa-vimeo-v:before { - content: "\f27d"; } - -.fa-contao:before { - content: "\f26d"; } - -.fa-square-font-awesome:before { - content: "\e5ad"; } - -.fa-deskpro:before { - content: "\f38f"; } - -.fa-brave:before { - content: "\e63c"; } - -.fa-sistrix:before { - content: "\f3ee"; } - -.fa-square-instagram:before { - content: "\e055"; } - -.fa-instagram-square:before { - content: "\e055"; } - -.fa-battle-net:before { - content: "\f835"; } - -.fa-the-red-yeti:before { - content: "\f69d"; } - -.fa-square-hacker-news:before { - content: "\f3af"; } - -.fa-hacker-news-square:before { - content: "\f3af"; } - -.fa-edge:before { - content: "\f282"; } - -.fa-threads:before { - content: "\e618"; } - -.fa-napster:before { - content: "\f3d2"; } - -.fa-square-snapchat:before { - content: "\f2ad"; } - -.fa-snapchat-square:before { - content: "\f2ad"; } - -.fa-google-plus-g:before { - content: "\f0d5"; } - -.fa-artstation:before { - content: "\f77a"; } - -.fa-markdown:before { - content: "\f60f"; } - -.fa-sourcetree:before { - content: "\f7d3"; } - -.fa-google-plus:before { - content: "\f2b3"; } - -.fa-diaspora:before { - content: "\f791"; } - -.fa-foursquare:before { - content: "\f180"; } - -.fa-stack-overflow:before { - content: "\f16c"; } - -.fa-github-alt:before { - content: "\f113"; } - -.fa-phoenix-squadron:before { - content: "\f511"; } - -.fa-pagelines:before { - content: "\f18c"; } - -.fa-algolia:before { - content: "\f36c"; } - -.fa-red-river:before { - content: "\f3e3"; } - -.fa-creative-commons-sa:before { - content: "\f4ef"; } - -.fa-safari:before { - content: "\f267"; } - -.fa-google:before { - content: "\f1a0"; } - -.fa-square-font-awesome-stroke:before { - content: "\f35c"; } - -.fa-font-awesome-alt:before { - content: "\f35c"; } - -.fa-atlassian:before { - content: "\f77b"; } - -.fa-linkedin-in:before { - content: "\f0e1"; } - -.fa-digital-ocean:before { - content: "\f391"; } - -.fa-nimblr:before { - content: "\f5a8"; } - -.fa-chromecast:before { - content: "\f838"; } - -.fa-evernote:before { - content: "\f839"; } - -.fa-hacker-news:before { - content: "\f1d4"; } - -.fa-creative-commons-sampling:before { - content: "\f4f0"; } - -.fa-adversal:before { - content: "\f36a"; } - -.fa-creative-commons:before { - content: "\f25e"; } - -.fa-watchman-monitoring:before { - content: "\e087"; } - -.fa-fonticons:before { - content: "\f280"; } - -.fa-weixin:before { - content: "\f1d7"; } - -.fa-shirtsinbulk:before { - content: "\f214"; } - -.fa-codepen:before { - content: "\f1cb"; } - -.fa-git-alt:before { - content: "\f841"; } - -.fa-lyft:before { - content: "\f3c3"; } - -.fa-rev:before { - content: "\f5b2"; } - -.fa-windows:before { - content: "\f17a"; } - -.fa-wizards-of-the-coast:before { - content: "\f730"; } - -.fa-square-viadeo:before { - content: "\f2aa"; } - -.fa-viadeo-square:before { - content: "\f2aa"; } - -.fa-meetup:before { - content: "\f2e0"; } - -.fa-centos:before { - content: "\f789"; } - -.fa-adn:before { - content: "\f170"; } - -.fa-cloudsmith:before { - content: "\f384"; } - -.fa-opensuse:before { - content: "\e62b"; } - -.fa-pied-piper-alt:before { - content: "\f1a8"; } - -.fa-square-dribbble:before { - content: "\f397"; } - -.fa-dribbble-square:before { - content: "\f397"; } - -.fa-codiepie:before { - content: "\f284"; } - -.fa-node:before { - content: "\f419"; } - -.fa-mix:before { - content: "\f3cb"; } - -.fa-steam:before { - content: "\f1b6"; } - -.fa-cc-apple-pay:before { - content: "\f416"; } - -.fa-scribd:before { - content: "\f28a"; } - -.fa-debian:before { - content: "\e60b"; } - -.fa-openid:before { - content: "\f19b"; } - -.fa-instalod:before { - content: "\e081"; } - -.fa-expeditedssl:before { - content: "\f23e"; } - -.fa-sellcast:before { - content: "\f2da"; } - -.fa-square-twitter:before { - content: "\f081"; } - -.fa-twitter-square:before { - content: "\f081"; } - -.fa-r-project:before { - content: "\f4f7"; } - -.fa-delicious:before { - content: "\f1a5"; } - -.fa-freebsd:before { - content: "\f3a4"; } - -.fa-vuejs:before { - content: "\f41f"; } - -.fa-accusoft:before { - content: "\f369"; } - -.fa-ioxhost:before { - content: "\f208"; } - -.fa-fonticons-fi:before { - content: "\f3a2"; } - -.fa-app-store:before { - content: "\f36f"; } - -.fa-cc-mastercard:before { - content: "\f1f1"; } - -.fa-itunes-note:before { - content: "\f3b5"; } - -.fa-golang:before { - content: "\e40f"; } - -.fa-kickstarter:before { - content: "\f3bb"; } - -.fa-square-kickstarter:before { - content: "\f3bb"; } - -.fa-grav:before { - content: "\f2d6"; } - -.fa-weibo:before { - content: "\f18a"; } - -.fa-uncharted:before { - content: "\e084"; } - -.fa-firstdraft:before { - content: "\f3a1"; } - -.fa-square-youtube:before { - content: "\f431"; } - -.fa-youtube-square:before { - content: "\f431"; } - -.fa-wikipedia-w:before { - content: "\f266"; } - -.fa-wpressr:before { - content: "\f3e4"; } - -.fa-rendact:before { - content: "\f3e4"; } - -.fa-angellist:before { - content: "\f209"; } - -.fa-galactic-republic:before { - content: "\f50c"; } - -.fa-nfc-directional:before { - content: "\e530"; } - -.fa-skype:before { - content: "\f17e"; } - -.fa-joget:before { - content: "\f3b7"; } - -.fa-fedora:before { - content: "\f798"; } - -.fa-stripe-s:before { - content: "\f42a"; } - -.fa-meta:before { - content: "\e49b"; } - -.fa-laravel:before { - content: "\f3bd"; } - -.fa-hotjar:before { - content: "\f3b1"; } - -.fa-bluetooth-b:before { - content: "\f294"; } - -.fa-square-letterboxd:before { - content: "\e62e"; } - -.fa-sticker-mule:before { - content: "\f3f7"; } - -.fa-creative-commons-zero:before { - content: "\f4f3"; } - -.fa-hips:before { - content: "\f452"; } - -.fa-behance:before { - content: "\f1b4"; } - -.fa-reddit:before { - content: "\f1a1"; } - -.fa-discord:before { - content: "\f392"; } - -.fa-chrome:before { - content: "\f268"; } - -.fa-app-store-ios:before { - content: "\f370"; } - -.fa-cc-discover:before { - content: "\f1f2"; } - -.fa-wpbeginner:before { - content: "\f297"; } - -.fa-confluence:before { - content: "\f78d"; } - -.fa-shoelace:before { - content: "\e60c"; } - -.fa-mdb:before { - content: "\f8ca"; } - -.fa-dochub:before { - content: "\f394"; } - -.fa-accessible-icon:before { - content: "\f368"; } - -.fa-ebay:before { - content: "\f4f4"; } - -.fa-amazon:before { - content: "\f270"; } - -.fa-unsplash:before { - content: "\e07c"; } - -.fa-yarn:before { - content: "\f7e3"; } - -.fa-square-steam:before { - content: "\f1b7"; } - -.fa-steam-square:before { - content: "\f1b7"; } - -.fa-500px:before { - content: "\f26e"; } - -.fa-square-vimeo:before { - content: "\f194"; } - -.fa-vimeo-square:before { - content: "\f194"; } - -.fa-asymmetrik:before { - content: "\f372"; } - -.fa-font-awesome:before { - content: "\f2b4"; } - -.fa-font-awesome-flag:before { - content: "\f2b4"; } - -.fa-font-awesome-logo-full:before { - content: "\f2b4"; } - -.fa-gratipay:before { - content: "\f184"; } - -.fa-apple:before { - content: "\f179"; } - -.fa-hive:before { - content: "\e07f"; } - -.fa-gitkraken:before { - content: "\f3a6"; } - -.fa-keybase:before { - content: "\f4f5"; } - -.fa-apple-pay:before { - content: "\f415"; } - -.fa-padlet:before { - content: "\e4a0"; } - -.fa-amazon-pay:before { - content: "\f42c"; } - -.fa-square-github:before { - content: "\f092"; } - -.fa-github-square:before { - content: "\f092"; } - -.fa-stumbleupon:before { - content: "\f1a4"; } - -.fa-fedex:before { - content: "\f797"; } - -.fa-phoenix-framework:before { - content: "\f3dc"; } - -.fa-shopify:before { - content: "\e057"; } - -.fa-neos:before { - content: "\f612"; } - -.fa-square-threads:before { - content: "\e619"; } - -.fa-hackerrank:before { - content: "\f5f7"; } - -.fa-researchgate:before { - content: "\f4f8"; } - -.fa-swift:before { - content: "\f8e1"; } - -.fa-angular:before { - content: "\f420"; } - -.fa-speakap:before { - content: "\f3f3"; } - -.fa-angrycreative:before { - content: "\f36e"; } - -.fa-y-combinator:before { - content: "\f23b"; } - -.fa-empire:before { - content: "\f1d1"; } - -.fa-envira:before { - content: "\f299"; } - -.fa-google-scholar:before { - content: "\e63b"; } - -.fa-square-gitlab:before { - content: "\e5ae"; } - -.fa-gitlab-square:before { - content: "\e5ae"; } - -.fa-studiovinari:before { - content: "\f3f8"; } - -.fa-pied-piper:before { - content: "\f2ae"; } - -.fa-wordpress:before { - content: "\f19a"; } - -.fa-product-hunt:before { - content: "\f288"; } - -.fa-firefox:before { - content: "\f269"; } - -.fa-linode:before { - content: "\f2b8"; } - -.fa-goodreads:before { - content: "\f3a8"; } - -.fa-square-odnoklassniki:before { - content: "\f264"; } - -.fa-odnoklassniki-square:before { - content: "\f264"; } - -.fa-jsfiddle:before { - content: "\f1cc"; } - -.fa-sith:before { - content: "\f512"; } - -.fa-themeisle:before { - content: "\f2b2"; } - -.fa-page4:before { - content: "\f3d7"; } - -.fa-hashnode:before { - content: "\e499"; } - -.fa-react:before { - content: "\f41b"; } - -.fa-cc-paypal:before { - content: "\f1f4"; } - -.fa-squarespace:before { - content: "\f5be"; } - -.fa-cc-stripe:before { - content: "\f1f5"; } - -.fa-creative-commons-share:before { - content: "\f4f2"; } - -.fa-bitcoin:before { - content: "\f379"; } - -.fa-keycdn:before { - content: "\f3ba"; } - -.fa-opera:before { - content: "\f26a"; } - -.fa-itch-io:before { - content: "\f83a"; } - -.fa-umbraco:before { - content: "\f8e8"; } - -.fa-galactic-senate:before { - content: "\f50d"; } - -.fa-ubuntu:before { - content: "\f7df"; } - -.fa-draft2digital:before { - content: "\f396"; } - -.fa-stripe:before { - content: "\f429"; } - -.fa-houzz:before { - content: "\f27c"; } - -.fa-gg:before { - content: "\f260"; } - -.fa-dhl:before { - content: "\f790"; } - -.fa-square-pinterest:before { - content: "\f0d3"; } - -.fa-pinterest-square:before { - content: "\f0d3"; } - -.fa-xing:before { - content: "\f168"; } - -.fa-blackberry:before { - content: "\f37b"; } - -.fa-creative-commons-pd:before { - content: "\f4ec"; } - -.fa-playstation:before { - content: "\f3df"; } - -.fa-quinscape:before { - content: "\f459"; } - -.fa-less:before { - content: "\f41d"; } - -.fa-blogger-b:before { - content: "\f37d"; } - -.fa-opencart:before { - content: "\f23d"; } - -.fa-vine:before { - content: "\f1ca"; } - -.fa-signal-messenger:before { - content: "\e663"; } - -.fa-paypal:before { - content: "\f1ed"; } - -.fa-gitlab:before { - content: "\f296"; } - -.fa-typo3:before { - content: "\f42b"; } - -.fa-reddit-alien:before { - content: "\f281"; } - -.fa-yahoo:before { - content: "\f19e"; } - -.fa-dailymotion:before { - content: "\e052"; } - -.fa-affiliatetheme:before { - content: "\f36b"; } - -.fa-pied-piper-pp:before { - content: "\f1a7"; } - -.fa-bootstrap:before { - content: "\f836"; } - -.fa-odnoklassniki:before { - content: "\f263"; } - -.fa-nfc-symbol:before { - content: "\e531"; } - -.fa-mintbit:before { - content: "\e62f"; } - -.fa-ethereum:before { - content: "\f42e"; } - -.fa-speaker-deck:before { - content: "\f83c"; } - -.fa-creative-commons-nc-eu:before { - content: "\f4e9"; } - -.fa-patreon:before { - content: "\f3d9"; } - -.fa-avianex:before { - content: "\f374"; } - -.fa-ello:before { - content: "\f5f1"; } - -.fa-gofore:before { - content: "\f3a7"; } - -.fa-bimobject:before { - content: "\f378"; } - -.fa-brave-reverse:before { - content: "\e63d"; } - -.fa-facebook-f:before { - content: "\f39e"; } - -.fa-square-google-plus:before { - content: "\f0d4"; } - -.fa-google-plus-square:before { - content: "\f0d4"; } - -.fa-web-awesome:before { - content: "\e682"; } - -.fa-mandalorian:before { - content: "\f50f"; } - -.fa-first-order-alt:before { - content: "\f50a"; } - -.fa-osi:before { - content: "\f41a"; } - -.fa-google-wallet:before { - content: "\f1ee"; } - -.fa-d-and-d-beyond:before { - content: "\f6ca"; } - -.fa-periscope:before { - content: "\f3da"; } - -.fa-fulcrum:before { - content: "\f50b"; } - -.fa-cloudscale:before { - content: "\f383"; } - -.fa-forumbee:before { - content: "\f211"; } - -.fa-mizuni:before { - content: "\f3cc"; } - -.fa-schlix:before { - content: "\f3ea"; } - -.fa-square-xing:before { - content: "\f169"; } - -.fa-xing-square:before { - content: "\f169"; } - -.fa-bandcamp:before { - content: "\f2d5"; } - -.fa-wpforms:before { - content: "\f298"; } - -.fa-cloudversify:before { - content: "\f385"; } - -.fa-usps:before { - content: "\f7e1"; } - -.fa-megaport:before { - content: "\f5a3"; } - -.fa-magento:before { - content: "\f3c4"; } - -.fa-spotify:before { - content: "\f1bc"; } - -.fa-optin-monster:before { - content: "\f23c"; } - -.fa-fly:before { - content: "\f417"; } - -.fa-aviato:before { - content: "\f421"; } - -.fa-itunes:before { - content: "\f3b4"; } - -.fa-cuttlefish:before { - content: "\f38c"; } - -.fa-blogger:before { - content: "\f37c"; } - -.fa-flickr:before { - content: "\f16e"; } - -.fa-viber:before { - content: "\f409"; } - -.fa-soundcloud:before { - content: "\f1be"; } - -.fa-digg:before { - content: "\f1a6"; } - -.fa-tencent-weibo:before { - content: "\f1d5"; } - -.fa-letterboxd:before { - content: "\e62d"; } - -.fa-symfony:before { - content: "\f83d"; } - -.fa-maxcdn:before { - content: "\f136"; } - -.fa-etsy:before { - content: "\f2d7"; } - -.fa-facebook-messenger:before { - content: "\f39f"; } - -.fa-audible:before { - content: "\f373"; } - -.fa-think-peaks:before { - content: "\f731"; } - -.fa-bilibili:before { - content: "\e3d9"; } - -.fa-erlang:before { - content: "\f39d"; } - -.fa-x-twitter:before { - content: "\e61b"; } - -.fa-cotton-bureau:before { - content: "\f89e"; } - -.fa-dashcube:before { - content: "\f210"; } - -.fa-42-group:before { - content: "\e080"; } - -.fa-innosoft:before { - content: "\e080"; } - -.fa-stack-exchange:before { - content: "\f18d"; } - -.fa-elementor:before { - content: "\f430"; } - -.fa-square-pied-piper:before { - content: "\e01e"; } - -.fa-pied-piper-square:before { - content: "\e01e"; } - -.fa-creative-commons-nd:before { - content: "\f4eb"; } - -.fa-palfed:before { - content: "\f3d8"; } - -.fa-superpowers:before { - content: "\f2dd"; } - -.fa-resolving:before { - content: "\f3e7"; } - -.fa-xbox:before { - content: "\f412"; } - -.fa-square-web-awesome-stroke:before { - content: "\e684"; } - -.fa-searchengin:before { - content: "\f3eb"; } - -.fa-tiktok:before { - content: "\e07b"; } - -.fa-square-facebook:before { - content: "\f082"; } - -.fa-facebook-square:before { - content: "\f082"; } - -.fa-renren:before { - content: "\f18b"; } - -.fa-linux:before { - content: "\f17c"; } - -.fa-glide:before { - content: "\f2a5"; } - -.fa-linkedin:before { - content: "\f08c"; } - -.fa-hubspot:before { - content: "\f3b2"; } - -.fa-deploydog:before { - content: "\f38e"; } - -.fa-twitch:before { - content: "\f1e8"; } - -.fa-ravelry:before { - content: "\f2d9"; } - -.fa-mixer:before { - content: "\e056"; } - -.fa-square-lastfm:before { - content: "\f203"; } - -.fa-lastfm-square:before { - content: "\f203"; } - -.fa-vimeo:before { - content: "\f40a"; } - -.fa-mendeley:before { - content: "\f7b3"; } - -.fa-uniregistry:before { - content: "\f404"; } - -.fa-figma:before { - content: "\f799"; } - -.fa-creative-commons-remix:before { - content: "\f4ee"; } - -.fa-cc-amazon-pay:before { - content: "\f42d"; } - -.fa-dropbox:before { - content: "\f16b"; } - -.fa-instagram:before { - content: "\f16d"; } - -.fa-cmplid:before { - content: "\e360"; } - -.fa-upwork:before { - content: "\e641"; } - -.fa-facebook:before { - content: "\f09a"; } - -.fa-gripfire:before { - content: "\f3ac"; } - -.fa-jedi-order:before { - content: "\f50e"; } - -.fa-uikit:before { - content: "\f403"; } - -.fa-fort-awesome-alt:before { - content: "\f3a3"; } - -.fa-phabricator:before { - content: "\f3db"; } - -.fa-ussunnah:before { - content: "\f407"; } - -.fa-earlybirds:before { - content: "\f39a"; } - -.fa-trade-federation:before { - content: "\f513"; } - -.fa-autoprefixer:before { - content: "\f41c"; } - -.fa-whatsapp:before { - content: "\f232"; } - -.fa-square-upwork:before { - content: "\e67c"; } - -.fa-slideshare:before { - content: "\f1e7"; } - -.fa-google-play:before { - content: "\f3ab"; } - -.fa-viadeo:before { - content: "\f2a9"; } - -.fa-line:before { - content: "\f3c0"; } - -.fa-google-drive:before { - content: "\f3aa"; } - -.fa-servicestack:before { - content: "\f3ec"; } - -.fa-simplybuilt:before { - content: "\f215"; } - -.fa-bitbucket:before { - content: "\f171"; } - -.fa-imdb:before { - content: "\f2d8"; } - -.fa-deezer:before { - content: "\e077"; } - -.fa-raspberry-pi:before { - content: "\f7bb"; } - -.fa-jira:before { - content: "\f7b1"; } - -.fa-docker:before { - content: "\f395"; } - -.fa-screenpal:before { - content: "\e570"; } - -.fa-bluetooth:before { - content: "\f293"; } - -.fa-gitter:before { - content: "\f426"; } - -.fa-d-and-d:before { - content: "\f38d"; } - -.fa-microblog:before { - content: "\e01a"; } - -.fa-cc-diners-club:before { - content: "\f24c"; } - -.fa-gg-circle:before { - content: "\f261"; } - -.fa-pied-piper-hat:before { - content: "\f4e5"; } - -.fa-kickstarter-k:before { - content: "\f3bc"; } - -.fa-yandex:before { - content: "\f413"; } - -.fa-readme:before { - content: "\f4d5"; } - -.fa-html5:before { - content: "\f13b"; } - -.fa-sellsy:before { - content: "\f213"; } - -.fa-square-web-awesome:before { - content: "\e683"; } - -.fa-sass:before { - content: "\f41e"; } - -.fa-wirsindhandwerk:before { - content: "\e2d0"; } - -.fa-wsh:before { - content: "\e2d0"; } - -.fa-buromobelexperte:before { - content: "\f37f"; } - -.fa-salesforce:before { - content: "\f83b"; } - -.fa-octopus-deploy:before { - content: "\e082"; } - -.fa-medapps:before { - content: "\f3c6"; } - -.fa-ns8:before { - content: "\f3d5"; } - -.fa-pinterest-p:before { - content: "\f231"; } - -.fa-apper:before { - content: "\f371"; } - -.fa-fort-awesome:before { - content: "\f286"; } - -.fa-waze:before { - content: "\f83f"; } - -.fa-bluesky:before { - content: "\e671"; } - -.fa-cc-jcb:before { - content: "\f24b"; } - -.fa-snapchat:before { - content: "\f2ab"; } - -.fa-snapchat-ghost:before { - content: "\f2ab"; } - -.fa-fantasy-flight-games:before { - content: "\f6dc"; } - -.fa-rust:before { - content: "\e07a"; } - -.fa-wix:before { - content: "\f5cf"; } - -.fa-square-behance:before { - content: "\f1b5"; } - -.fa-behance-square:before { - content: "\f1b5"; } - -.fa-supple:before { - content: "\f3f9"; } - -.fa-webflow:before { - content: "\e65c"; } - -.fa-rebel:before { - content: "\f1d0"; } - -.fa-css3:before { - content: "\f13c"; } - -.fa-staylinked:before { - content: "\f3f5"; } - -.fa-kaggle:before { - content: "\f5fa"; } - -.fa-space-awesome:before { - content: "\e5ac"; } - -.fa-deviantart:before { - content: "\f1bd"; } - -.fa-cpanel:before { - content: "\f388"; } - -.fa-goodreads-g:before { - content: "\f3a9"; } - -.fa-square-git:before { - content: "\f1d2"; } - -.fa-git-square:before { - content: "\f1d2"; } - -.fa-square-tumblr:before { - content: "\f174"; } - -.fa-tumblr-square:before { - content: "\f174"; } - -.fa-trello:before { - content: "\f181"; } - -.fa-creative-commons-nc-jp:before { - content: "\f4ea"; } - -.fa-get-pocket:before { - content: "\f265"; } - -.fa-perbyte:before { - content: "\e083"; } - -.fa-grunt:before { - content: "\f3ad"; } - -.fa-weebly:before { - content: "\f5cc"; } - -.fa-connectdevelop:before { - content: "\f20e"; } - -.fa-leanpub:before { - content: "\f212"; } - -.fa-black-tie:before { - content: "\f27e"; } - -.fa-themeco:before { - content: "\f5c6"; } - -.fa-python:before { - content: "\f3e2"; } - -.fa-android:before { - content: "\f17b"; } - -.fa-bots:before { - content: "\e340"; } - -.fa-free-code-camp:before { - content: "\f2c5"; } - -.fa-hornbill:before { - content: "\f592"; } - -.fa-js:before { - content: "\f3b8"; } - -.fa-ideal:before { - content: "\e013"; } - -.fa-git:before { - content: "\f1d3"; } - -.fa-dev:before { - content: "\f6cc"; } - -.fa-sketch:before { - content: "\f7c6"; } - -.fa-yandex-international:before { - content: "\f414"; } - -.fa-cc-amex:before { - content: "\f1f3"; } - -.fa-uber:before { - content: "\f402"; } - -.fa-github:before { - content: "\f09b"; } - -.fa-php:before { - content: "\f457"; } - -.fa-alipay:before { - content: "\f642"; } - -.fa-youtube:before { - content: "\f167"; } - -.fa-skyatlas:before { - content: "\f216"; } - -.fa-firefox-browser:before { - content: "\e007"; } - -.fa-replyd:before { - content: "\f3e6"; } - -.fa-suse:before { - content: "\f7d6"; } - -.fa-jenkins:before { - content: "\f3b6"; } - -.fa-twitter:before { - content: "\f099"; } - -.fa-rockrms:before { - content: "\f3e9"; } - -.fa-pinterest:before { - content: "\f0d2"; } - -.fa-buffer:before { - content: "\f837"; } - -.fa-npm:before { - content: "\f3d4"; } - -.fa-yammer:before { - content: "\f840"; } - -.fa-btc:before { - content: "\f15a"; } - -.fa-dribbble:before { - content: "\f17d"; } - -.fa-stumbleupon-circle:before { - content: "\f1a3"; } - -.fa-internet-explorer:before { - content: "\f26b"; } - -.fa-stubber:before { - content: "\e5c7"; } - -.fa-telegram:before { - content: "\f2c6"; } - -.fa-telegram-plane:before { - content: "\f2c6"; } - -.fa-old-republic:before { - content: "\f510"; } - -.fa-odysee:before { - content: "\e5c6"; } - -.fa-square-whatsapp:before { - content: "\f40c"; } - -.fa-whatsapp-square:before { - content: "\f40c"; } - -.fa-node-js:before { - content: "\f3d3"; } - -.fa-edge-legacy:before { - content: "\e078"; } - -.fa-slack:before { - content: "\f198"; } - -.fa-slack-hash:before { - content: "\f198"; } - -.fa-medrt:before { - content: "\f3c8"; } - -.fa-usb:before { - content: "\f287"; } - -.fa-tumblr:before { - content: "\f173"; } - -.fa-vaadin:before { - content: "\f408"; } - -.fa-quora:before { - content: "\f2c4"; } - -.fa-square-x-twitter:before { - content: "\e61a"; } - -.fa-reacteurope:before { - content: "\f75d"; } - -.fa-medium:before { - content: "\f23a"; } - -.fa-medium-m:before { - content: "\f23a"; } - -.fa-amilia:before { - content: "\f36d"; } - -.fa-mixcloud:before { - content: "\f289"; } - -.fa-flipboard:before { - content: "\f44d"; } - -.fa-viacoin:before { - content: "\f237"; } - -.fa-critical-role:before { - content: "\f6c9"; } - -.fa-sitrox:before { - content: "\e44a"; } - -.fa-discourse:before { - content: "\f393"; } - -.fa-joomla:before { - content: "\f1aa"; } - -.fa-mastodon:before { - content: "\f4f6"; } - -.fa-airbnb:before { - content: "\f834"; } - -.fa-wolf-pack-battalion:before { - content: "\f514"; } - -.fa-buy-n-large:before { - content: "\f8a6"; } - -.fa-gulp:before { - content: "\f3ae"; } - -.fa-creative-commons-sampling-plus:before { - content: "\f4f1"; } - -.fa-strava:before { - content: "\f428"; } - -.fa-ember:before { - content: "\f423"; } - -.fa-canadian-maple-leaf:before { - content: "\f785"; } - -.fa-teamspeak:before { - content: "\f4f9"; } - -.fa-pushed:before { - content: "\f3e1"; } - -.fa-wordpress-simple:before { - content: "\f411"; } - -.fa-nutritionix:before { - content: "\f3d6"; } - -.fa-wodu:before { - content: "\e088"; } - -.fa-google-pay:before { - content: "\e079"; } - -.fa-intercom:before { - content: "\f7af"; } - -.fa-zhihu:before { - content: "\f63f"; } - -.fa-korvue:before { - content: "\f42f"; } - -.fa-pix:before { - content: "\e43a"; } - -.fa-steam-symbol:before { - content: "\f3f6"; } diff --git a/resources/fontawesome/css/brands.min.css b/resources/fontawesome/css/brands.min.css deleted file mode 100644 index 3e70760..0000000 --- a/resources/fontawesome/css/brands.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -:host,:root{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-pixiv:before{content:"\e640"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-jxl:before{content:"\e67b"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-brave:before{content:"\e63c"}.fa-sistrix:before{content:"\f3ee"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-threads:before{content:"\e618"}.fa-napster:before{content:"\f3d2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-opensuse:before{content:"\e62b"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-debian:before{content:"\e60b"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before,.fa-square-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-square-letterboxd:before{content:"\e62e"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-shoelace:before{content:"\e60c"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-square-threads:before{content:"\e619"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-google-scholar:before{content:"\e63b"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-signal-messenger:before{content:"\e663"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-mintbit:before{content:"\e62f"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-brave-reverse:before{content:"\e63d"}.fa-facebook-f:before{content:"\f39e"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-web-awesome:before{content:"\e682"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-letterboxd:before{content:"\e62d"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-x-twitter:before{content:"\e61b"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-square-web-awesome-stroke:before{content:"\e684"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-upwork:before{content:"\e641"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-square-upwork:before{content:"\e67c"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-square-web-awesome:before{content:"\e683"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-bluesky:before{content:"\e671"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-webflow:before{content:"\e65c"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-square-x-twitter:before{content:"\e61a"}.fa-reacteurope:before{content:"\f75d"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"} \ No newline at end of file diff --git a/resources/fontawesome/css/fontawesome.css b/resources/fontawesome/css/fontawesome.css deleted file mode 100644 index ca00c63..0000000 --- a/resources/fontawesome/css/fontawesome.css +++ /dev/null @@ -1,6375 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -.fa { - font-family: var(--fa-style-family, "Font Awesome 6 Free"); - font-weight: var(--fa-style, 900); } - -.fa, -.fa-classic, -.fa-sharp, -.fas, -.fa-solid, -.far, -.fa-regular, -.fab, -.fa-brands { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - display: var(--fa-display, inline-block); - font-style: normal; - font-variant: normal; - line-height: 1; - text-rendering: auto; } - -.fas, -.fa-classic, -.fa-solid, -.far, -.fa-regular { - font-family: 'Font Awesome 6 Free'; } - -.fab, -.fa-brands { - font-family: 'Font Awesome 6 Brands'; } - -.fa-1x { - font-size: 1em; } - -.fa-2x { - font-size: 2em; } - -.fa-3x { - font-size: 3em; } - -.fa-4x { - font-size: 4em; } - -.fa-5x { - font-size: 5em; } - -.fa-6x { - font-size: 6em; } - -.fa-7x { - font-size: 7em; } - -.fa-8x { - font-size: 8em; } - -.fa-9x { - font-size: 9em; } - -.fa-10x { - font-size: 10em; } - -.fa-2xs { - font-size: 0.625em; - line-height: 0.1em; - vertical-align: 0.225em; } - -.fa-xs { - font-size: 0.75em; - line-height: 0.08333em; - vertical-align: 0.125em; } - -.fa-sm { - font-size: 0.875em; - line-height: 0.07143em; - vertical-align: 0.05357em; } - -.fa-lg { - font-size: 1.25em; - line-height: 0.05em; - vertical-align: -0.075em; } - -.fa-xl { - font-size: 1.5em; - line-height: 0.04167em; - vertical-align: -0.125em; } - -.fa-2xl { - font-size: 2em; - line-height: 0.03125em; - vertical-align: -0.1875em; } - -.fa-fw { - text-align: center; - width: 1.25em; } - -.fa-ul { - list-style-type: none; - margin-left: var(--fa-li-margin, 2.5em); - padding-left: 0; } - .fa-ul > li { - position: relative; } - -.fa-li { - left: calc(var(--fa-li-width, 2em) * -1); - position: absolute; - text-align: center; - width: var(--fa-li-width, 2em); - line-height: inherit; } - -.fa-border { - border-color: var(--fa-border-color, #eee); - border-radius: var(--fa-border-radius, 0.1em); - border-style: var(--fa-border-style, solid); - border-width: var(--fa-border-width, 0.08em); - padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); } - -.fa-pull-left { - float: left; - margin-right: var(--fa-pull-margin, 0.3em); } - -.fa-pull-right { - float: right; - margin-left: var(--fa-pull-margin, 0.3em); } - -.fa-beat { - -webkit-animation-name: fa-beat; - animation-name: fa-beat; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); - animation-timing-function: var(--fa-animation-timing, ease-in-out); } - -.fa-bounce { - -webkit-animation-name: fa-bounce; - animation-name: fa-bounce; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); } - -.fa-fade { - -webkit-animation-name: fa-fade; - animation-name: fa-fade; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } - -.fa-beat-fade { - -webkit-animation-name: fa-beat-fade; - animation-name: fa-beat-fade; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } - -.fa-flip { - -webkit-animation-name: fa-flip; - animation-name: fa-flip; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); - animation-timing-function: var(--fa-animation-timing, ease-in-out); } - -.fa-shake { - -webkit-animation-name: fa-shake; - animation-name: fa-shake; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, linear); - animation-timing-function: var(--fa-animation-timing, linear); } - -.fa-spin { - -webkit-animation-name: fa-spin; - animation-name: fa-spin; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 2s); - animation-duration: var(--fa-animation-duration, 2s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, linear); - animation-timing-function: var(--fa-animation-timing, linear); } - -.fa-spin-reverse { - --fa-animation-direction: reverse; } - -.fa-pulse, -.fa-spin-pulse { - -webkit-animation-name: fa-spin; - animation-name: fa-spin; - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); - animation-timing-function: var(--fa-animation-timing, steps(8)); } - -@media (prefers-reduced-motion: reduce) { - .fa-beat, - .fa-bounce, - .fa-fade, - .fa-beat-fade, - .fa-flip, - .fa-pulse, - .fa-shake, - .fa-spin, - .fa-spin-pulse { - -webkit-animation-delay: -1ms; - animation-delay: -1ms; - -webkit-animation-duration: 1ms; - animation-duration: 1ms; - -webkit-animation-iteration-count: 1; - animation-iteration-count: 1; - -webkit-transition-delay: 0s; - transition-delay: 0s; - -webkit-transition-duration: 0s; - transition-duration: 0s; } } - -@-webkit-keyframes fa-beat { - 0%, 90% { - -webkit-transform: scale(1); - transform: scale(1); } - 45% { - -webkit-transform: scale(var(--fa-beat-scale, 1.25)); - transform: scale(var(--fa-beat-scale, 1.25)); } } - -@keyframes fa-beat { - 0%, 90% { - -webkit-transform: scale(1); - transform: scale(1); } - 45% { - -webkit-transform: scale(var(--fa-beat-scale, 1.25)); - transform: scale(var(--fa-beat-scale, 1.25)); } } - -@-webkit-keyframes fa-bounce { - 0% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 10% { - -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); - transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } - 30% { - -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); - transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } - 50% { - -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); - transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } - 57% { - -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); - transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } - 64% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 100% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } } - -@keyframes fa-bounce { - 0% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 10% { - -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); - transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } - 30% { - -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); - transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } - 50% { - -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); - transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } - 57% { - -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); - transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } - 64% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 100% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } } - -@-webkit-keyframes fa-fade { - 50% { - opacity: var(--fa-fade-opacity, 0.4); } } - -@keyframes fa-fade { - 50% { - opacity: var(--fa-fade-opacity, 0.4); } } - -@-webkit-keyframes fa-beat-fade { - 0%, 100% { - opacity: var(--fa-beat-fade-opacity, 0.4); - -webkit-transform: scale(1); - transform: scale(1); } - 50% { - opacity: 1; - -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); - transform: scale(var(--fa-beat-fade-scale, 1.125)); } } - -@keyframes fa-beat-fade { - 0%, 100% { - opacity: var(--fa-beat-fade-opacity, 0.4); - -webkit-transform: scale(1); - transform: scale(1); } - 50% { - opacity: 1; - -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); - transform: scale(var(--fa-beat-fade-scale, 1.125)); } } - -@-webkit-keyframes fa-flip { - 50% { - -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); - transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } - -@keyframes fa-flip { - 50% { - -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); - transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } - -@-webkit-keyframes fa-shake { - 0% { - -webkit-transform: rotate(-15deg); - transform: rotate(-15deg); } - 4% { - -webkit-transform: rotate(15deg); - transform: rotate(15deg); } - 8%, 24% { - -webkit-transform: rotate(-18deg); - transform: rotate(-18deg); } - 12%, 28% { - -webkit-transform: rotate(18deg); - transform: rotate(18deg); } - 16% { - -webkit-transform: rotate(-22deg); - transform: rotate(-22deg); } - 20% { - -webkit-transform: rotate(22deg); - transform: rotate(22deg); } - 32% { - -webkit-transform: rotate(-12deg); - transform: rotate(-12deg); } - 36% { - -webkit-transform: rotate(12deg); - transform: rotate(12deg); } - 40%, 100% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } } - -@keyframes fa-shake { - 0% { - -webkit-transform: rotate(-15deg); - transform: rotate(-15deg); } - 4% { - -webkit-transform: rotate(15deg); - transform: rotate(15deg); } - 8%, 24% { - -webkit-transform: rotate(-18deg); - transform: rotate(-18deg); } - 12%, 28% { - -webkit-transform: rotate(18deg); - transform: rotate(18deg); } - 16% { - -webkit-transform: rotate(-22deg); - transform: rotate(-22deg); } - 20% { - -webkit-transform: rotate(22deg); - transform: rotate(22deg); } - 32% { - -webkit-transform: rotate(-12deg); - transform: rotate(-12deg); } - 36% { - -webkit-transform: rotate(12deg); - transform: rotate(12deg); } - 40%, 100% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } } - -@-webkit-keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -@keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -.fa-rotate-90 { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); } - -.fa-rotate-180 { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); } - -.fa-rotate-270 { - -webkit-transform: rotate(270deg); - transform: rotate(270deg); } - -.fa-flip-horizontal { - -webkit-transform: scale(-1, 1); - transform: scale(-1, 1); } - -.fa-flip-vertical { - -webkit-transform: scale(1, -1); - transform: scale(1, -1); } - -.fa-flip-both, -.fa-flip-horizontal.fa-flip-vertical { - -webkit-transform: scale(-1, -1); - transform: scale(-1, -1); } - -.fa-rotate-by { - -webkit-transform: rotate(var(--fa-rotate-angle, 0)); - transform: rotate(var(--fa-rotate-angle, 0)); } - -.fa-stack { - display: inline-block; - height: 2em; - line-height: 2em; - position: relative; - vertical-align: middle; - width: 2.5em; } - -.fa-stack-1x, -.fa-stack-2x { - left: 0; - position: absolute; - text-align: center; - width: 100%; - z-index: var(--fa-stack-z-index, auto); } - -.fa-stack-1x { - line-height: inherit; } - -.fa-stack-2x { - font-size: 2em; } - -.fa-inverse { - color: var(--fa-inverse, #fff); } - -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen -readers do not read off random characters that represent icons */ - -.fa-0::before { - content: "\30"; } - -.fa-1::before { - content: "\31"; } - -.fa-2::before { - content: "\32"; } - -.fa-3::before { - content: "\33"; } - -.fa-4::before { - content: "\34"; } - -.fa-5::before { - content: "\35"; } - -.fa-6::before { - content: "\36"; } - -.fa-7::before { - content: "\37"; } - -.fa-8::before { - content: "\38"; } - -.fa-9::before { - content: "\39"; } - -.fa-fill-drip::before { - content: "\f576"; } - -.fa-arrows-to-circle::before { - content: "\e4bd"; } - -.fa-circle-chevron-right::before { - content: "\f138"; } - -.fa-chevron-circle-right::before { - content: "\f138"; } - -.fa-at::before { - content: "\40"; } - -.fa-trash-can::before { - content: "\f2ed"; } - -.fa-trash-alt::before { - content: "\f2ed"; } - -.fa-text-height::before { - content: "\f034"; } - -.fa-user-xmark::before { - content: "\f235"; } - -.fa-user-times::before { - content: "\f235"; } - -.fa-stethoscope::before { - content: "\f0f1"; } - -.fa-message::before { - content: "\f27a"; } - -.fa-comment-alt::before { - content: "\f27a"; } - -.fa-info::before { - content: "\f129"; } - -.fa-down-left-and-up-right-to-center::before { - content: "\f422"; } - -.fa-compress-alt::before { - content: "\f422"; } - -.fa-explosion::before { - content: "\e4e9"; } - -.fa-file-lines::before { - content: "\f15c"; } - -.fa-file-alt::before { - content: "\f15c"; } - -.fa-file-text::before { - content: "\f15c"; } - -.fa-wave-square::before { - content: "\f83e"; } - -.fa-ring::before { - content: "\f70b"; } - -.fa-building-un::before { - content: "\e4d9"; } - -.fa-dice-three::before { - content: "\f527"; } - -.fa-calendar-days::before { - content: "\f073"; } - -.fa-calendar-alt::before { - content: "\f073"; } - -.fa-anchor-circle-check::before { - content: "\e4aa"; } - -.fa-building-circle-arrow-right::before { - content: "\e4d1"; } - -.fa-volleyball::before { - content: "\f45f"; } - -.fa-volleyball-ball::before { - content: "\f45f"; } - -.fa-arrows-up-to-line::before { - content: "\e4c2"; } - -.fa-sort-down::before { - content: "\f0dd"; } - -.fa-sort-desc::before { - content: "\f0dd"; } - -.fa-circle-minus::before { - content: "\f056"; } - -.fa-minus-circle::before { - content: "\f056"; } - -.fa-door-open::before { - content: "\f52b"; } - -.fa-right-from-bracket::before { - content: "\f2f5"; } - -.fa-sign-out-alt::before { - content: "\f2f5"; } - -.fa-atom::before { - content: "\f5d2"; } - -.fa-soap::before { - content: "\e06e"; } - -.fa-icons::before { - content: "\f86d"; } - -.fa-heart-music-camera-bolt::before { - content: "\f86d"; } - -.fa-microphone-lines-slash::before { - content: "\f539"; } - -.fa-microphone-alt-slash::before { - content: "\f539"; } - -.fa-bridge-circle-check::before { - content: "\e4c9"; } - -.fa-pump-medical::before { - content: "\e06a"; } - -.fa-fingerprint::before { - content: "\f577"; } - -.fa-hand-point-right::before { - content: "\f0a4"; } - -.fa-magnifying-glass-location::before { - content: "\f689"; } - -.fa-search-location::before { - content: "\f689"; } - -.fa-forward-step::before { - content: "\f051"; } - -.fa-step-forward::before { - content: "\f051"; } - -.fa-face-smile-beam::before { - content: "\f5b8"; } - -.fa-smile-beam::before { - content: "\f5b8"; } - -.fa-flag-checkered::before { - content: "\f11e"; } - -.fa-football::before { - content: "\f44e"; } - -.fa-football-ball::before { - content: "\f44e"; } - -.fa-school-circle-exclamation::before { - content: "\e56c"; } - -.fa-crop::before { - content: "\f125"; } - -.fa-angles-down::before { - content: "\f103"; } - -.fa-angle-double-down::before { - content: "\f103"; } - -.fa-users-rectangle::before { - content: "\e594"; } - -.fa-people-roof::before { - content: "\e537"; } - -.fa-people-line::before { - content: "\e534"; } - -.fa-beer-mug-empty::before { - content: "\f0fc"; } - -.fa-beer::before { - content: "\f0fc"; } - -.fa-diagram-predecessor::before { - content: "\e477"; } - -.fa-arrow-up-long::before { - content: "\f176"; } - -.fa-long-arrow-up::before { - content: "\f176"; } - -.fa-fire-flame-simple::before { - content: "\f46a"; } - -.fa-burn::before { - content: "\f46a"; } - -.fa-person::before { - content: "\f183"; } - -.fa-male::before { - content: "\f183"; } - -.fa-laptop::before { - content: "\f109"; } - -.fa-file-csv::before { - content: "\f6dd"; } - -.fa-menorah::before { - content: "\f676"; } - -.fa-truck-plane::before { - content: "\e58f"; } - -.fa-record-vinyl::before { - content: "\f8d9"; } - -.fa-face-grin-stars::before { - content: "\f587"; } - -.fa-grin-stars::before { - content: "\f587"; } - -.fa-bong::before { - content: "\f55c"; } - -.fa-spaghetti-monster-flying::before { - content: "\f67b"; } - -.fa-pastafarianism::before { - content: "\f67b"; } - -.fa-arrow-down-up-across-line::before { - content: "\e4af"; } - -.fa-spoon::before { - content: "\f2e5"; } - -.fa-utensil-spoon::before { - content: "\f2e5"; } - -.fa-jar-wheat::before { - content: "\e517"; } - -.fa-envelopes-bulk::before { - content: "\f674"; } - -.fa-mail-bulk::before { - content: "\f674"; } - -.fa-file-circle-exclamation::before { - content: "\e4eb"; } - -.fa-circle-h::before { - content: "\f47e"; } - -.fa-hospital-symbol::before { - content: "\f47e"; } - -.fa-pager::before { - content: "\f815"; } - -.fa-address-book::before { - content: "\f2b9"; } - -.fa-contact-book::before { - content: "\f2b9"; } - -.fa-strikethrough::before { - content: "\f0cc"; } - -.fa-k::before { - content: "\4b"; } - -.fa-landmark-flag::before { - content: "\e51c"; } - -.fa-pencil::before { - content: "\f303"; } - -.fa-pencil-alt::before { - content: "\f303"; } - -.fa-backward::before { - content: "\f04a"; } - -.fa-caret-right::before { - content: "\f0da"; } - -.fa-comments::before { - content: "\f086"; } - -.fa-paste::before { - content: "\f0ea"; } - -.fa-file-clipboard::before { - content: "\f0ea"; } - -.fa-code-pull-request::before { - content: "\e13c"; } - -.fa-clipboard-list::before { - content: "\f46d"; } - -.fa-truck-ramp-box::before { - content: "\f4de"; } - -.fa-truck-loading::before { - content: "\f4de"; } - -.fa-user-check::before { - content: "\f4fc"; } - -.fa-vial-virus::before { - content: "\e597"; } - -.fa-sheet-plastic::before { - content: "\e571"; } - -.fa-blog::before { - content: "\f781"; } - -.fa-user-ninja::before { - content: "\f504"; } - -.fa-person-arrow-up-from-line::before { - content: "\e539"; } - -.fa-scroll-torah::before { - content: "\f6a0"; } - -.fa-torah::before { - content: "\f6a0"; } - -.fa-broom-ball::before { - content: "\f458"; } - -.fa-quidditch::before { - content: "\f458"; } - -.fa-quidditch-broom-ball::before { - content: "\f458"; } - -.fa-toggle-off::before { - content: "\f204"; } - -.fa-box-archive::before { - content: "\f187"; } - -.fa-archive::before { - content: "\f187"; } - -.fa-person-drowning::before { - content: "\e545"; } - -.fa-arrow-down-9-1::before { - content: "\f886"; } - -.fa-sort-numeric-desc::before { - content: "\f886"; } - -.fa-sort-numeric-down-alt::before { - content: "\f886"; } - -.fa-face-grin-tongue-squint::before { - content: "\f58a"; } - -.fa-grin-tongue-squint::before { - content: "\f58a"; } - -.fa-spray-can::before { - content: "\f5bd"; } - -.fa-truck-monster::before { - content: "\f63b"; } - -.fa-w::before { - content: "\57"; } - -.fa-earth-africa::before { - content: "\f57c"; } - -.fa-globe-africa::before { - content: "\f57c"; } - -.fa-rainbow::before { - content: "\f75b"; } - -.fa-circle-notch::before { - content: "\f1ce"; } - -.fa-tablet-screen-button::before { - content: "\f3fa"; } - -.fa-tablet-alt::before { - content: "\f3fa"; } - -.fa-paw::before { - content: "\f1b0"; } - -.fa-cloud::before { - content: "\f0c2"; } - -.fa-trowel-bricks::before { - content: "\e58a"; } - -.fa-face-flushed::before { - content: "\f579"; } - -.fa-flushed::before { - content: "\f579"; } - -.fa-hospital-user::before { - content: "\f80d"; } - -.fa-tent-arrow-left-right::before { - content: "\e57f"; } - -.fa-gavel::before { - content: "\f0e3"; } - -.fa-legal::before { - content: "\f0e3"; } - -.fa-binoculars::before { - content: "\f1e5"; } - -.fa-microphone-slash::before { - content: "\f131"; } - -.fa-box-tissue::before { - content: "\e05b"; } - -.fa-motorcycle::before { - content: "\f21c"; } - -.fa-bell-concierge::before { - content: "\f562"; } - -.fa-concierge-bell::before { - content: "\f562"; } - -.fa-pen-ruler::before { - content: "\f5ae"; } - -.fa-pencil-ruler::before { - content: "\f5ae"; } - -.fa-people-arrows::before { - content: "\e068"; } - -.fa-people-arrows-left-right::before { - content: "\e068"; } - -.fa-mars-and-venus-burst::before { - content: "\e523"; } - -.fa-square-caret-right::before { - content: "\f152"; } - -.fa-caret-square-right::before { - content: "\f152"; } - -.fa-scissors::before { - content: "\f0c4"; } - -.fa-cut::before { - content: "\f0c4"; } - -.fa-sun-plant-wilt::before { - content: "\e57a"; } - -.fa-toilets-portable::before { - content: "\e584"; } - -.fa-hockey-puck::before { - content: "\f453"; } - -.fa-table::before { - content: "\f0ce"; } - -.fa-magnifying-glass-arrow-right::before { - content: "\e521"; } - -.fa-tachograph-digital::before { - content: "\f566"; } - -.fa-digital-tachograph::before { - content: "\f566"; } - -.fa-users-slash::before { - content: "\e073"; } - -.fa-clover::before { - content: "\e139"; } - -.fa-reply::before { - content: "\f3e5"; } - -.fa-mail-reply::before { - content: "\f3e5"; } - -.fa-star-and-crescent::before { - content: "\f699"; } - -.fa-house-fire::before { - content: "\e50c"; } - -.fa-square-minus::before { - content: "\f146"; } - -.fa-minus-square::before { - content: "\f146"; } - -.fa-helicopter::before { - content: "\f533"; } - -.fa-compass::before { - content: "\f14e"; } - -.fa-square-caret-down::before { - content: "\f150"; } - -.fa-caret-square-down::before { - content: "\f150"; } - -.fa-file-circle-question::before { - content: "\e4ef"; } - -.fa-laptop-code::before { - content: "\f5fc"; } - -.fa-swatchbook::before { - content: "\f5c3"; } - -.fa-prescription-bottle::before { - content: "\f485"; } - -.fa-bars::before { - content: "\f0c9"; } - -.fa-navicon::before { - content: "\f0c9"; } - -.fa-people-group::before { - content: "\e533"; } - -.fa-hourglass-end::before { - content: "\f253"; } - -.fa-hourglass-3::before { - content: "\f253"; } - -.fa-heart-crack::before { - content: "\f7a9"; } - -.fa-heart-broken::before { - content: "\f7a9"; } - -.fa-square-up-right::before { - content: "\f360"; } - -.fa-external-link-square-alt::before { - content: "\f360"; } - -.fa-face-kiss-beam::before { - content: "\f597"; } - -.fa-kiss-beam::before { - content: "\f597"; } - -.fa-film::before { - content: "\f008"; } - -.fa-ruler-horizontal::before { - content: "\f547"; } - -.fa-people-robbery::before { - content: "\e536"; } - -.fa-lightbulb::before { - content: "\f0eb"; } - -.fa-caret-left::before { - content: "\f0d9"; } - -.fa-circle-exclamation::before { - content: "\f06a"; } - -.fa-exclamation-circle::before { - content: "\f06a"; } - -.fa-school-circle-xmark::before { - content: "\e56d"; } - -.fa-arrow-right-from-bracket::before { - content: "\f08b"; } - -.fa-sign-out::before { - content: "\f08b"; } - -.fa-circle-chevron-down::before { - content: "\f13a"; } - -.fa-chevron-circle-down::before { - content: "\f13a"; } - -.fa-unlock-keyhole::before { - content: "\f13e"; } - -.fa-unlock-alt::before { - content: "\f13e"; } - -.fa-cloud-showers-heavy::before { - content: "\f740"; } - -.fa-headphones-simple::before { - content: "\f58f"; } - -.fa-headphones-alt::before { - content: "\f58f"; } - -.fa-sitemap::before { - content: "\f0e8"; } - -.fa-circle-dollar-to-slot::before { - content: "\f4b9"; } - -.fa-donate::before { - content: "\f4b9"; } - -.fa-memory::before { - content: "\f538"; } - -.fa-road-spikes::before { - content: "\e568"; } - -.fa-fire-burner::before { - content: "\e4f1"; } - -.fa-flag::before { - content: "\f024"; } - -.fa-hanukiah::before { - content: "\f6e6"; } - -.fa-feather::before { - content: "\f52d"; } - -.fa-volume-low::before { - content: "\f027"; } - -.fa-volume-down::before { - content: "\f027"; } - -.fa-comment-slash::before { - content: "\f4b3"; } - -.fa-cloud-sun-rain::before { - content: "\f743"; } - -.fa-compress::before { - content: "\f066"; } - -.fa-wheat-awn::before { - content: "\e2cd"; } - -.fa-wheat-alt::before { - content: "\e2cd"; } - -.fa-ankh::before { - content: "\f644"; } - -.fa-hands-holding-child::before { - content: "\e4fa"; } - -.fa-asterisk::before { - content: "\2a"; } - -.fa-square-check::before { - content: "\f14a"; } - -.fa-check-square::before { - content: "\f14a"; } - -.fa-peseta-sign::before { - content: "\e221"; } - -.fa-heading::before { - content: "\f1dc"; } - -.fa-header::before { - content: "\f1dc"; } - -.fa-ghost::before { - content: "\f6e2"; } - -.fa-list::before { - content: "\f03a"; } - -.fa-list-squares::before { - content: "\f03a"; } - -.fa-square-phone-flip::before { - content: "\f87b"; } - -.fa-phone-square-alt::before { - content: "\f87b"; } - -.fa-cart-plus::before { - content: "\f217"; } - -.fa-gamepad::before { - content: "\f11b"; } - -.fa-circle-dot::before { - content: "\f192"; } - -.fa-dot-circle::before { - content: "\f192"; } - -.fa-face-dizzy::before { - content: "\f567"; } - -.fa-dizzy::before { - content: "\f567"; } - -.fa-egg::before { - content: "\f7fb"; } - -.fa-house-medical-circle-xmark::before { - content: "\e513"; } - -.fa-campground::before { - content: "\f6bb"; } - -.fa-folder-plus::before { - content: "\f65e"; } - -.fa-futbol::before { - content: "\f1e3"; } - -.fa-futbol-ball::before { - content: "\f1e3"; } - -.fa-soccer-ball::before { - content: "\f1e3"; } - -.fa-paintbrush::before { - content: "\f1fc"; } - -.fa-paint-brush::before { - content: "\f1fc"; } - -.fa-lock::before { - content: "\f023"; } - -.fa-gas-pump::before { - content: "\f52f"; } - -.fa-hot-tub-person::before { - content: "\f593"; } - -.fa-hot-tub::before { - content: "\f593"; } - -.fa-map-location::before { - content: "\f59f"; } - -.fa-map-marked::before { - content: "\f59f"; } - -.fa-house-flood-water::before { - content: "\e50e"; } - -.fa-tree::before { - content: "\f1bb"; } - -.fa-bridge-lock::before { - content: "\e4cc"; } - -.fa-sack-dollar::before { - content: "\f81d"; } - -.fa-pen-to-square::before { - content: "\f044"; } - -.fa-edit::before { - content: "\f044"; } - -.fa-car-side::before { - content: "\f5e4"; } - -.fa-share-nodes::before { - content: "\f1e0"; } - -.fa-share-alt::before { - content: "\f1e0"; } - -.fa-heart-circle-minus::before { - content: "\e4ff"; } - -.fa-hourglass-half::before { - content: "\f252"; } - -.fa-hourglass-2::before { - content: "\f252"; } - -.fa-microscope::before { - content: "\f610"; } - -.fa-sink::before { - content: "\e06d"; } - -.fa-bag-shopping::before { - content: "\f290"; } - -.fa-shopping-bag::before { - content: "\f290"; } - -.fa-arrow-down-z-a::before { - content: "\f881"; } - -.fa-sort-alpha-desc::before { - content: "\f881"; } - -.fa-sort-alpha-down-alt::before { - content: "\f881"; } - -.fa-mitten::before { - content: "\f7b5"; } - -.fa-person-rays::before { - content: "\e54d"; } - -.fa-users::before { - content: "\f0c0"; } - -.fa-eye-slash::before { - content: "\f070"; } - -.fa-flask-vial::before { - content: "\e4f3"; } - -.fa-hand::before { - content: "\f256"; } - -.fa-hand-paper::before { - content: "\f256"; } - -.fa-om::before { - content: "\f679"; } - -.fa-worm::before { - content: "\e599"; } - -.fa-house-circle-xmark::before { - content: "\e50b"; } - -.fa-plug::before { - content: "\f1e6"; } - -.fa-chevron-up::before { - content: "\f077"; } - -.fa-hand-spock::before { - content: "\f259"; } - -.fa-stopwatch::before { - content: "\f2f2"; } - -.fa-face-kiss::before { - content: "\f596"; } - -.fa-kiss::before { - content: "\f596"; } - -.fa-bridge-circle-xmark::before { - content: "\e4cb"; } - -.fa-face-grin-tongue::before { - content: "\f589"; } - -.fa-grin-tongue::before { - content: "\f589"; } - -.fa-chess-bishop::before { - content: "\f43a"; } - -.fa-face-grin-wink::before { - content: "\f58c"; } - -.fa-grin-wink::before { - content: "\f58c"; } - -.fa-ear-deaf::before { - content: "\f2a4"; } - -.fa-deaf::before { - content: "\f2a4"; } - -.fa-deafness::before { - content: "\f2a4"; } - -.fa-hard-of-hearing::before { - content: "\f2a4"; } - -.fa-road-circle-check::before { - content: "\e564"; } - -.fa-dice-five::before { - content: "\f523"; } - -.fa-square-rss::before { - content: "\f143"; } - -.fa-rss-square::before { - content: "\f143"; } - -.fa-land-mine-on::before { - content: "\e51b"; } - -.fa-i-cursor::before { - content: "\f246"; } - -.fa-stamp::before { - content: "\f5bf"; } - -.fa-stairs::before { - content: "\e289"; } - -.fa-i::before { - content: "\49"; } - -.fa-hryvnia-sign::before { - content: "\f6f2"; } - -.fa-hryvnia::before { - content: "\f6f2"; } - -.fa-pills::before { - content: "\f484"; } - -.fa-face-grin-wide::before { - content: "\f581"; } - -.fa-grin-alt::before { - content: "\f581"; } - -.fa-tooth::before { - content: "\f5c9"; } - -.fa-v::before { - content: "\56"; } - -.fa-bangladeshi-taka-sign::before { - content: "\e2e6"; } - -.fa-bicycle::before { - content: "\f206"; } - -.fa-staff-snake::before { - content: "\e579"; } - -.fa-rod-asclepius::before { - content: "\e579"; } - -.fa-rod-snake::before { - content: "\e579"; } - -.fa-staff-aesculapius::before { - content: "\e579"; } - -.fa-head-side-cough-slash::before { - content: "\e062"; } - -.fa-truck-medical::before { - content: "\f0f9"; } - -.fa-ambulance::before { - content: "\f0f9"; } - -.fa-wheat-awn-circle-exclamation::before { - content: "\e598"; } - -.fa-snowman::before { - content: "\f7d0"; } - -.fa-mortar-pestle::before { - content: "\f5a7"; } - -.fa-road-barrier::before { - content: "\e562"; } - -.fa-school::before { - content: "\f549"; } - -.fa-igloo::before { - content: "\f7ae"; } - -.fa-joint::before { - content: "\f595"; } - -.fa-angle-right::before { - content: "\f105"; } - -.fa-horse::before { - content: "\f6f0"; } - -.fa-q::before { - content: "\51"; } - -.fa-g::before { - content: "\47"; } - -.fa-notes-medical::before { - content: "\f481"; } - -.fa-temperature-half::before { - content: "\f2c9"; } - -.fa-temperature-2::before { - content: "\f2c9"; } - -.fa-thermometer-2::before { - content: "\f2c9"; } - -.fa-thermometer-half::before { - content: "\f2c9"; } - -.fa-dong-sign::before { - content: "\e169"; } - -.fa-capsules::before { - content: "\f46b"; } - -.fa-poo-storm::before { - content: "\f75a"; } - -.fa-poo-bolt::before { - content: "\f75a"; } - -.fa-face-frown-open::before { - content: "\f57a"; } - -.fa-frown-open::before { - content: "\f57a"; } - -.fa-hand-point-up::before { - content: "\f0a6"; } - -.fa-money-bill::before { - content: "\f0d6"; } - -.fa-bookmark::before { - content: "\f02e"; } - -.fa-align-justify::before { - content: "\f039"; } - -.fa-umbrella-beach::before { - content: "\f5ca"; } - -.fa-helmet-un::before { - content: "\e503"; } - -.fa-bullseye::before { - content: "\f140"; } - -.fa-bacon::before { - content: "\f7e5"; } - -.fa-hand-point-down::before { - content: "\f0a7"; } - -.fa-arrow-up-from-bracket::before { - content: "\e09a"; } - -.fa-folder::before { - content: "\f07b"; } - -.fa-folder-blank::before { - content: "\f07b"; } - -.fa-file-waveform::before { - content: "\f478"; } - -.fa-file-medical-alt::before { - content: "\f478"; } - -.fa-radiation::before { - content: "\f7b9"; } - -.fa-chart-simple::before { - content: "\e473"; } - -.fa-mars-stroke::before { - content: "\f229"; } - -.fa-vial::before { - content: "\f492"; } - -.fa-gauge::before { - content: "\f624"; } - -.fa-dashboard::before { - content: "\f624"; } - -.fa-gauge-med::before { - content: "\f624"; } - -.fa-tachometer-alt-average::before { - content: "\f624"; } - -.fa-wand-magic-sparkles::before { - content: "\e2ca"; } - -.fa-magic-wand-sparkles::before { - content: "\e2ca"; } - -.fa-e::before { - content: "\45"; } - -.fa-pen-clip::before { - content: "\f305"; } - -.fa-pen-alt::before { - content: "\f305"; } - -.fa-bridge-circle-exclamation::before { - content: "\e4ca"; } - -.fa-user::before { - content: "\f007"; } - -.fa-school-circle-check::before { - content: "\e56b"; } - -.fa-dumpster::before { - content: "\f793"; } - -.fa-van-shuttle::before { - content: "\f5b6"; } - -.fa-shuttle-van::before { - content: "\f5b6"; } - -.fa-building-user::before { - content: "\e4da"; } - -.fa-square-caret-left::before { - content: "\f191"; } - -.fa-caret-square-left::before { - content: "\f191"; } - -.fa-highlighter::before { - content: "\f591"; } - -.fa-key::before { - content: "\f084"; } - -.fa-bullhorn::before { - content: "\f0a1"; } - -.fa-globe::before { - content: "\f0ac"; } - -.fa-synagogue::before { - content: "\f69b"; } - -.fa-person-half-dress::before { - content: "\e548"; } - -.fa-road-bridge::before { - content: "\e563"; } - -.fa-location-arrow::before { - content: "\f124"; } - -.fa-c::before { - content: "\43"; } - -.fa-tablet-button::before { - content: "\f10a"; } - -.fa-building-lock::before { - content: "\e4d6"; } - -.fa-pizza-slice::before { - content: "\f818"; } - -.fa-money-bill-wave::before { - content: "\f53a"; } - -.fa-chart-area::before { - content: "\f1fe"; } - -.fa-area-chart::before { - content: "\f1fe"; } - -.fa-house-flag::before { - content: "\e50d"; } - -.fa-person-circle-minus::before { - content: "\e540"; } - -.fa-ban::before { - content: "\f05e"; } - -.fa-cancel::before { - content: "\f05e"; } - -.fa-camera-rotate::before { - content: "\e0d8"; } - -.fa-spray-can-sparkles::before { - content: "\f5d0"; } - -.fa-air-freshener::before { - content: "\f5d0"; } - -.fa-star::before { - content: "\f005"; } - -.fa-repeat::before { - content: "\f363"; } - -.fa-cross::before { - content: "\f654"; } - -.fa-box::before { - content: "\f466"; } - -.fa-venus-mars::before { - content: "\f228"; } - -.fa-arrow-pointer::before { - content: "\f245"; } - -.fa-mouse-pointer::before { - content: "\f245"; } - -.fa-maximize::before { - content: "\f31e"; } - -.fa-expand-arrows-alt::before { - content: "\f31e"; } - -.fa-charging-station::before { - content: "\f5e7"; } - -.fa-shapes::before { - content: "\f61f"; } - -.fa-triangle-circle-square::before { - content: "\f61f"; } - -.fa-shuffle::before { - content: "\f074"; } - -.fa-random::before { - content: "\f074"; } - -.fa-person-running::before { - content: "\f70c"; } - -.fa-running::before { - content: "\f70c"; } - -.fa-mobile-retro::before { - content: "\e527"; } - -.fa-grip-lines-vertical::before { - content: "\f7a5"; } - -.fa-spider::before { - content: "\f717"; } - -.fa-hands-bound::before { - content: "\e4f9"; } - -.fa-file-invoice-dollar::before { - content: "\f571"; } - -.fa-plane-circle-exclamation::before { - content: "\e556"; } - -.fa-x-ray::before { - content: "\f497"; } - -.fa-spell-check::before { - content: "\f891"; } - -.fa-slash::before { - content: "\f715"; } - -.fa-computer-mouse::before { - content: "\f8cc"; } - -.fa-mouse::before { - content: "\f8cc"; } - -.fa-arrow-right-to-bracket::before { - content: "\f090"; } - -.fa-sign-in::before { - content: "\f090"; } - -.fa-shop-slash::before { - content: "\e070"; } - -.fa-store-alt-slash::before { - content: "\e070"; } - -.fa-server::before { - content: "\f233"; } - -.fa-virus-covid-slash::before { - content: "\e4a9"; } - -.fa-shop-lock::before { - content: "\e4a5"; } - -.fa-hourglass-start::before { - content: "\f251"; } - -.fa-hourglass-1::before { - content: "\f251"; } - -.fa-blender-phone::before { - content: "\f6b6"; } - -.fa-building-wheat::before { - content: "\e4db"; } - -.fa-person-breastfeeding::before { - content: "\e53a"; } - -.fa-right-to-bracket::before { - content: "\f2f6"; } - -.fa-sign-in-alt::before { - content: "\f2f6"; } - -.fa-venus::before { - content: "\f221"; } - -.fa-passport::before { - content: "\f5ab"; } - -.fa-heart-pulse::before { - content: "\f21e"; } - -.fa-heartbeat::before { - content: "\f21e"; } - -.fa-people-carry-box::before { - content: "\f4ce"; } - -.fa-people-carry::before { - content: "\f4ce"; } - -.fa-temperature-high::before { - content: "\f769"; } - -.fa-microchip::before { - content: "\f2db"; } - -.fa-crown::before { - content: "\f521"; } - -.fa-weight-hanging::before { - content: "\f5cd"; } - -.fa-xmarks-lines::before { - content: "\e59a"; } - -.fa-file-prescription::before { - content: "\f572"; } - -.fa-weight-scale::before { - content: "\f496"; } - -.fa-weight::before { - content: "\f496"; } - -.fa-user-group::before { - content: "\f500"; } - -.fa-user-friends::before { - content: "\f500"; } - -.fa-arrow-up-a-z::before { - content: "\f15e"; } - -.fa-sort-alpha-up::before { - content: "\f15e"; } - -.fa-chess-knight::before { - content: "\f441"; } - -.fa-face-laugh-squint::before { - content: "\f59b"; } - -.fa-laugh-squint::before { - content: "\f59b"; } - -.fa-wheelchair::before { - content: "\f193"; } - -.fa-circle-arrow-up::before { - content: "\f0aa"; } - -.fa-arrow-circle-up::before { - content: "\f0aa"; } - -.fa-toggle-on::before { - content: "\f205"; } - -.fa-person-walking::before { - content: "\f554"; } - -.fa-walking::before { - content: "\f554"; } - -.fa-l::before { - content: "\4c"; } - -.fa-fire::before { - content: "\f06d"; } - -.fa-bed-pulse::before { - content: "\f487"; } - -.fa-procedures::before { - content: "\f487"; } - -.fa-shuttle-space::before { - content: "\f197"; } - -.fa-space-shuttle::before { - content: "\f197"; } - -.fa-face-laugh::before { - content: "\f599"; } - -.fa-laugh::before { - content: "\f599"; } - -.fa-folder-open::before { - content: "\f07c"; } - -.fa-heart-circle-plus::before { - content: "\e500"; } - -.fa-code-fork::before { - content: "\e13b"; } - -.fa-city::before { - content: "\f64f"; } - -.fa-microphone-lines::before { - content: "\f3c9"; } - -.fa-microphone-alt::before { - content: "\f3c9"; } - -.fa-pepper-hot::before { - content: "\f816"; } - -.fa-unlock::before { - content: "\f09c"; } - -.fa-colon-sign::before { - content: "\e140"; } - -.fa-headset::before { - content: "\f590"; } - -.fa-store-slash::before { - content: "\e071"; } - -.fa-road-circle-xmark::before { - content: "\e566"; } - -.fa-user-minus::before { - content: "\f503"; } - -.fa-mars-stroke-up::before { - content: "\f22a"; } - -.fa-mars-stroke-v::before { - content: "\f22a"; } - -.fa-champagne-glasses::before { - content: "\f79f"; } - -.fa-glass-cheers::before { - content: "\f79f"; } - -.fa-clipboard::before { - content: "\f328"; } - -.fa-house-circle-exclamation::before { - content: "\e50a"; } - -.fa-file-arrow-up::before { - content: "\f574"; } - -.fa-file-upload::before { - content: "\f574"; } - -.fa-wifi::before { - content: "\f1eb"; } - -.fa-wifi-3::before { - content: "\f1eb"; } - -.fa-wifi-strong::before { - content: "\f1eb"; } - -.fa-bath::before { - content: "\f2cd"; } - -.fa-bathtub::before { - content: "\f2cd"; } - -.fa-underline::before { - content: "\f0cd"; } - -.fa-user-pen::before { - content: "\f4ff"; } - -.fa-user-edit::before { - content: "\f4ff"; } - -.fa-signature::before { - content: "\f5b7"; } - -.fa-stroopwafel::before { - content: "\f551"; } - -.fa-bold::before { - content: "\f032"; } - -.fa-anchor-lock::before { - content: "\e4ad"; } - -.fa-building-ngo::before { - content: "\e4d7"; } - -.fa-manat-sign::before { - content: "\e1d5"; } - -.fa-not-equal::before { - content: "\f53e"; } - -.fa-border-top-left::before { - content: "\f853"; } - -.fa-border-style::before { - content: "\f853"; } - -.fa-map-location-dot::before { - content: "\f5a0"; } - -.fa-map-marked-alt::before { - content: "\f5a0"; } - -.fa-jedi::before { - content: "\f669"; } - -.fa-square-poll-vertical::before { - content: "\f681"; } - -.fa-poll::before { - content: "\f681"; } - -.fa-mug-hot::before { - content: "\f7b6"; } - -.fa-car-battery::before { - content: "\f5df"; } - -.fa-battery-car::before { - content: "\f5df"; } - -.fa-gift::before { - content: "\f06b"; } - -.fa-dice-two::before { - content: "\f528"; } - -.fa-chess-queen::before { - content: "\f445"; } - -.fa-glasses::before { - content: "\f530"; } - -.fa-chess-board::before { - content: "\f43c"; } - -.fa-building-circle-check::before { - content: "\e4d2"; } - -.fa-person-chalkboard::before { - content: "\e53d"; } - -.fa-mars-stroke-right::before { - content: "\f22b"; } - -.fa-mars-stroke-h::before { - content: "\f22b"; } - -.fa-hand-back-fist::before { - content: "\f255"; } - -.fa-hand-rock::before { - content: "\f255"; } - -.fa-square-caret-up::before { - content: "\f151"; } - -.fa-caret-square-up::before { - content: "\f151"; } - -.fa-cloud-showers-water::before { - content: "\e4e4"; } - -.fa-chart-bar::before { - content: "\f080"; } - -.fa-bar-chart::before { - content: "\f080"; } - -.fa-hands-bubbles::before { - content: "\e05e"; } - -.fa-hands-wash::before { - content: "\e05e"; } - -.fa-less-than-equal::before { - content: "\f537"; } - -.fa-train::before { - content: "\f238"; } - -.fa-eye-low-vision::before { - content: "\f2a8"; } - -.fa-low-vision::before { - content: "\f2a8"; } - -.fa-crow::before { - content: "\f520"; } - -.fa-sailboat::before { - content: "\e445"; } - -.fa-window-restore::before { - content: "\f2d2"; } - -.fa-square-plus::before { - content: "\f0fe"; } - -.fa-plus-square::before { - content: "\f0fe"; } - -.fa-torii-gate::before { - content: "\f6a1"; } - -.fa-frog::before { - content: "\f52e"; } - -.fa-bucket::before { - content: "\e4cf"; } - -.fa-image::before { - content: "\f03e"; } - -.fa-microphone::before { - content: "\f130"; } - -.fa-cow::before { - content: "\f6c8"; } - -.fa-caret-up::before { - content: "\f0d8"; } - -.fa-screwdriver::before { - content: "\f54a"; } - -.fa-folder-closed::before { - content: "\e185"; } - -.fa-house-tsunami::before { - content: "\e515"; } - -.fa-square-nfi::before { - content: "\e576"; } - -.fa-arrow-up-from-ground-water::before { - content: "\e4b5"; } - -.fa-martini-glass::before { - content: "\f57b"; } - -.fa-glass-martini-alt::before { - content: "\f57b"; } - -.fa-rotate-left::before { - content: "\f2ea"; } - -.fa-rotate-back::before { - content: "\f2ea"; } - -.fa-rotate-backward::before { - content: "\f2ea"; } - -.fa-undo-alt::before { - content: "\f2ea"; } - -.fa-table-columns::before { - content: "\f0db"; } - -.fa-columns::before { - content: "\f0db"; } - -.fa-lemon::before { - content: "\f094"; } - -.fa-head-side-mask::before { - content: "\e063"; } - -.fa-handshake::before { - content: "\f2b5"; } - -.fa-gem::before { - content: "\f3a5"; } - -.fa-dolly::before { - content: "\f472"; } - -.fa-dolly-box::before { - content: "\f472"; } - -.fa-smoking::before { - content: "\f48d"; } - -.fa-minimize::before { - content: "\f78c"; } - -.fa-compress-arrows-alt::before { - content: "\f78c"; } - -.fa-monument::before { - content: "\f5a6"; } - -.fa-snowplow::before { - content: "\f7d2"; } - -.fa-angles-right::before { - content: "\f101"; } - -.fa-angle-double-right::before { - content: "\f101"; } - -.fa-cannabis::before { - content: "\f55f"; } - -.fa-circle-play::before { - content: "\f144"; } - -.fa-play-circle::before { - content: "\f144"; } - -.fa-tablets::before { - content: "\f490"; } - -.fa-ethernet::before { - content: "\f796"; } - -.fa-euro-sign::before { - content: "\f153"; } - -.fa-eur::before { - content: "\f153"; } - -.fa-euro::before { - content: "\f153"; } - -.fa-chair::before { - content: "\f6c0"; } - -.fa-circle-check::before { - content: "\f058"; } - -.fa-check-circle::before { - content: "\f058"; } - -.fa-circle-stop::before { - content: "\f28d"; } - -.fa-stop-circle::before { - content: "\f28d"; } - -.fa-compass-drafting::before { - content: "\f568"; } - -.fa-drafting-compass::before { - content: "\f568"; } - -.fa-plate-wheat::before { - content: "\e55a"; } - -.fa-icicles::before { - content: "\f7ad"; } - -.fa-person-shelter::before { - content: "\e54f"; } - -.fa-neuter::before { - content: "\f22c"; } - -.fa-id-badge::before { - content: "\f2c1"; } - -.fa-marker::before { - content: "\f5a1"; } - -.fa-face-laugh-beam::before { - content: "\f59a"; } - -.fa-laugh-beam::before { - content: "\f59a"; } - -.fa-helicopter-symbol::before { - content: "\e502"; } - -.fa-universal-access::before { - content: "\f29a"; } - -.fa-circle-chevron-up::before { - content: "\f139"; } - -.fa-chevron-circle-up::before { - content: "\f139"; } - -.fa-lari-sign::before { - content: "\e1c8"; } - -.fa-volcano::before { - content: "\f770"; } - -.fa-person-walking-dashed-line-arrow-right::before { - content: "\e553"; } - -.fa-sterling-sign::before { - content: "\f154"; } - -.fa-gbp::before { - content: "\f154"; } - -.fa-pound-sign::before { - content: "\f154"; } - -.fa-viruses::before { - content: "\e076"; } - -.fa-square-person-confined::before { - content: "\e577"; } - -.fa-user-tie::before { - content: "\f508"; } - -.fa-arrow-down-long::before { - content: "\f175"; } - -.fa-long-arrow-down::before { - content: "\f175"; } - -.fa-tent-arrow-down-to-line::before { - content: "\e57e"; } - -.fa-certificate::before { - content: "\f0a3"; } - -.fa-reply-all::before { - content: "\f122"; } - -.fa-mail-reply-all::before { - content: "\f122"; } - -.fa-suitcase::before { - content: "\f0f2"; } - -.fa-person-skating::before { - content: "\f7c5"; } - -.fa-skating::before { - content: "\f7c5"; } - -.fa-filter-circle-dollar::before { - content: "\f662"; } - -.fa-funnel-dollar::before { - content: "\f662"; } - -.fa-camera-retro::before { - content: "\f083"; } - -.fa-circle-arrow-down::before { - content: "\f0ab"; } - -.fa-arrow-circle-down::before { - content: "\f0ab"; } - -.fa-file-import::before { - content: "\f56f"; } - -.fa-arrow-right-to-file::before { - content: "\f56f"; } - -.fa-square-arrow-up-right::before { - content: "\f14c"; } - -.fa-external-link-square::before { - content: "\f14c"; } - -.fa-box-open::before { - content: "\f49e"; } - -.fa-scroll::before { - content: "\f70e"; } - -.fa-spa::before { - content: "\f5bb"; } - -.fa-location-pin-lock::before { - content: "\e51f"; } - -.fa-pause::before { - content: "\f04c"; } - -.fa-hill-avalanche::before { - content: "\e507"; } - -.fa-temperature-empty::before { - content: "\f2cb"; } - -.fa-temperature-0::before { - content: "\f2cb"; } - -.fa-thermometer-0::before { - content: "\f2cb"; } - -.fa-thermometer-empty::before { - content: "\f2cb"; } - -.fa-bomb::before { - content: "\f1e2"; } - -.fa-registered::before { - content: "\f25d"; } - -.fa-address-card::before { - content: "\f2bb"; } - -.fa-contact-card::before { - content: "\f2bb"; } - -.fa-vcard::before { - content: "\f2bb"; } - -.fa-scale-unbalanced-flip::before { - content: "\f516"; } - -.fa-balance-scale-right::before { - content: "\f516"; } - -.fa-subscript::before { - content: "\f12c"; } - -.fa-diamond-turn-right::before { - content: "\f5eb"; } - -.fa-directions::before { - content: "\f5eb"; } - -.fa-burst::before { - content: "\e4dc"; } - -.fa-house-laptop::before { - content: "\e066"; } - -.fa-laptop-house::before { - content: "\e066"; } - -.fa-face-tired::before { - content: "\f5c8"; } - -.fa-tired::before { - content: "\f5c8"; } - -.fa-money-bills::before { - content: "\e1f3"; } - -.fa-smog::before { - content: "\f75f"; } - -.fa-crutch::before { - content: "\f7f7"; } - -.fa-cloud-arrow-up::before { - content: "\f0ee"; } - -.fa-cloud-upload::before { - content: "\f0ee"; } - -.fa-cloud-upload-alt::before { - content: "\f0ee"; } - -.fa-palette::before { - content: "\f53f"; } - -.fa-arrows-turn-right::before { - content: "\e4c0"; } - -.fa-vest::before { - content: "\e085"; } - -.fa-ferry::before { - content: "\e4ea"; } - -.fa-arrows-down-to-people::before { - content: "\e4b9"; } - -.fa-seedling::before { - content: "\f4d8"; } - -.fa-sprout::before { - content: "\f4d8"; } - -.fa-left-right::before { - content: "\f337"; } - -.fa-arrows-alt-h::before { - content: "\f337"; } - -.fa-boxes-packing::before { - content: "\e4c7"; } - -.fa-circle-arrow-left::before { - content: "\f0a8"; } - -.fa-arrow-circle-left::before { - content: "\f0a8"; } - -.fa-group-arrows-rotate::before { - content: "\e4f6"; } - -.fa-bowl-food::before { - content: "\e4c6"; } - -.fa-candy-cane::before { - content: "\f786"; } - -.fa-arrow-down-wide-short::before { - content: "\f160"; } - -.fa-sort-amount-asc::before { - content: "\f160"; } - -.fa-sort-amount-down::before { - content: "\f160"; } - -.fa-cloud-bolt::before { - content: "\f76c"; } - -.fa-thunderstorm::before { - content: "\f76c"; } - -.fa-text-slash::before { - content: "\f87d"; } - -.fa-remove-format::before { - content: "\f87d"; } - -.fa-face-smile-wink::before { - content: "\f4da"; } - -.fa-smile-wink::before { - content: "\f4da"; } - -.fa-file-word::before { - content: "\f1c2"; } - -.fa-file-powerpoint::before { - content: "\f1c4"; } - -.fa-arrows-left-right::before { - content: "\f07e"; } - -.fa-arrows-h::before { - content: "\f07e"; } - -.fa-house-lock::before { - content: "\e510"; } - -.fa-cloud-arrow-down::before { - content: "\f0ed"; } - -.fa-cloud-download::before { - content: "\f0ed"; } - -.fa-cloud-download-alt::before { - content: "\f0ed"; } - -.fa-children::before { - content: "\e4e1"; } - -.fa-chalkboard::before { - content: "\f51b"; } - -.fa-blackboard::before { - content: "\f51b"; } - -.fa-user-large-slash::before { - content: "\f4fa"; } - -.fa-user-alt-slash::before { - content: "\f4fa"; } - -.fa-envelope-open::before { - content: "\f2b6"; } - -.fa-handshake-simple-slash::before { - content: "\e05f"; } - -.fa-handshake-alt-slash::before { - content: "\e05f"; } - -.fa-mattress-pillow::before { - content: "\e525"; } - -.fa-guarani-sign::before { - content: "\e19a"; } - -.fa-arrows-rotate::before { - content: "\f021"; } - -.fa-refresh::before { - content: "\f021"; } - -.fa-sync::before { - content: "\f021"; } - -.fa-fire-extinguisher::before { - content: "\f134"; } - -.fa-cruzeiro-sign::before { - content: "\e152"; } - -.fa-greater-than-equal::before { - content: "\f532"; } - -.fa-shield-halved::before { - content: "\f3ed"; } - -.fa-shield-alt::before { - content: "\f3ed"; } - -.fa-book-atlas::before { - content: "\f558"; } - -.fa-atlas::before { - content: "\f558"; } - -.fa-virus::before { - content: "\e074"; } - -.fa-envelope-circle-check::before { - content: "\e4e8"; } - -.fa-layer-group::before { - content: "\f5fd"; } - -.fa-arrows-to-dot::before { - content: "\e4be"; } - -.fa-archway::before { - content: "\f557"; } - -.fa-heart-circle-check::before { - content: "\e4fd"; } - -.fa-house-chimney-crack::before { - content: "\f6f1"; } - -.fa-house-damage::before { - content: "\f6f1"; } - -.fa-file-zipper::before { - content: "\f1c6"; } - -.fa-file-archive::before { - content: "\f1c6"; } - -.fa-square::before { - content: "\f0c8"; } - -.fa-martini-glass-empty::before { - content: "\f000"; } - -.fa-glass-martini::before { - content: "\f000"; } - -.fa-couch::before { - content: "\f4b8"; } - -.fa-cedi-sign::before { - content: "\e0df"; } - -.fa-italic::before { - content: "\f033"; } - -.fa-table-cells-column-lock::before { - content: "\e678"; } - -.fa-church::before { - content: "\f51d"; } - -.fa-comments-dollar::before { - content: "\f653"; } - -.fa-democrat::before { - content: "\f747"; } - -.fa-z::before { - content: "\5a"; } - -.fa-person-skiing::before { - content: "\f7c9"; } - -.fa-skiing::before { - content: "\f7c9"; } - -.fa-road-lock::before { - content: "\e567"; } - -.fa-a::before { - content: "\41"; } - -.fa-temperature-arrow-down::before { - content: "\e03f"; } - -.fa-temperature-down::before { - content: "\e03f"; } - -.fa-feather-pointed::before { - content: "\f56b"; } - -.fa-feather-alt::before { - content: "\f56b"; } - -.fa-p::before { - content: "\50"; } - -.fa-snowflake::before { - content: "\f2dc"; } - -.fa-newspaper::before { - content: "\f1ea"; } - -.fa-rectangle-ad::before { - content: "\f641"; } - -.fa-ad::before { - content: "\f641"; } - -.fa-circle-arrow-right::before { - content: "\f0a9"; } - -.fa-arrow-circle-right::before { - content: "\f0a9"; } - -.fa-filter-circle-xmark::before { - content: "\e17b"; } - -.fa-locust::before { - content: "\e520"; } - -.fa-sort::before { - content: "\f0dc"; } - -.fa-unsorted::before { - content: "\f0dc"; } - -.fa-list-ol::before { - content: "\f0cb"; } - -.fa-list-1-2::before { - content: "\f0cb"; } - -.fa-list-numeric::before { - content: "\f0cb"; } - -.fa-person-dress-burst::before { - content: "\e544"; } - -.fa-money-check-dollar::before { - content: "\f53d"; } - -.fa-money-check-alt::before { - content: "\f53d"; } - -.fa-vector-square::before { - content: "\f5cb"; } - -.fa-bread-slice::before { - content: "\f7ec"; } - -.fa-language::before { - content: "\f1ab"; } - -.fa-face-kiss-wink-heart::before { - content: "\f598"; } - -.fa-kiss-wink-heart::before { - content: "\f598"; } - -.fa-filter::before { - content: "\f0b0"; } - -.fa-question::before { - content: "\3f"; } - -.fa-file-signature::before { - content: "\f573"; } - -.fa-up-down-left-right::before { - content: "\f0b2"; } - -.fa-arrows-alt::before { - content: "\f0b2"; } - -.fa-house-chimney-user::before { - content: "\e065"; } - -.fa-hand-holding-heart::before { - content: "\f4be"; } - -.fa-puzzle-piece::before { - content: "\f12e"; } - -.fa-money-check::before { - content: "\f53c"; } - -.fa-star-half-stroke::before { - content: "\f5c0"; } - -.fa-star-half-alt::before { - content: "\f5c0"; } - -.fa-code::before { - content: "\f121"; } - -.fa-whiskey-glass::before { - content: "\f7a0"; } - -.fa-glass-whiskey::before { - content: "\f7a0"; } - -.fa-building-circle-exclamation::before { - content: "\e4d3"; } - -.fa-magnifying-glass-chart::before { - content: "\e522"; } - -.fa-arrow-up-right-from-square::before { - content: "\f08e"; } - -.fa-external-link::before { - content: "\f08e"; } - -.fa-cubes-stacked::before { - content: "\e4e6"; } - -.fa-won-sign::before { - content: "\f159"; } - -.fa-krw::before { - content: "\f159"; } - -.fa-won::before { - content: "\f159"; } - -.fa-virus-covid::before { - content: "\e4a8"; } - -.fa-austral-sign::before { - content: "\e0a9"; } - -.fa-f::before { - content: "\46"; } - -.fa-leaf::before { - content: "\f06c"; } - -.fa-road::before { - content: "\f018"; } - -.fa-taxi::before { - content: "\f1ba"; } - -.fa-cab::before { - content: "\f1ba"; } - -.fa-person-circle-plus::before { - content: "\e541"; } - -.fa-chart-pie::before { - content: "\f200"; } - -.fa-pie-chart::before { - content: "\f200"; } - -.fa-bolt-lightning::before { - content: "\e0b7"; } - -.fa-sack-xmark::before { - content: "\e56a"; } - -.fa-file-excel::before { - content: "\f1c3"; } - -.fa-file-contract::before { - content: "\f56c"; } - -.fa-fish-fins::before { - content: "\e4f2"; } - -.fa-building-flag::before { - content: "\e4d5"; } - -.fa-face-grin-beam::before { - content: "\f582"; } - -.fa-grin-beam::before { - content: "\f582"; } - -.fa-object-ungroup::before { - content: "\f248"; } - -.fa-poop::before { - content: "\f619"; } - -.fa-location-pin::before { - content: "\f041"; } - -.fa-map-marker::before { - content: "\f041"; } - -.fa-kaaba::before { - content: "\f66b"; } - -.fa-toilet-paper::before { - content: "\f71e"; } - -.fa-helmet-safety::before { - content: "\f807"; } - -.fa-hard-hat::before { - content: "\f807"; } - -.fa-hat-hard::before { - content: "\f807"; } - -.fa-eject::before { - content: "\f052"; } - -.fa-circle-right::before { - content: "\f35a"; } - -.fa-arrow-alt-circle-right::before { - content: "\f35a"; } - -.fa-plane-circle-check::before { - content: "\e555"; } - -.fa-face-rolling-eyes::before { - content: "\f5a5"; } - -.fa-meh-rolling-eyes::before { - content: "\f5a5"; } - -.fa-object-group::before { - content: "\f247"; } - -.fa-chart-line::before { - content: "\f201"; } - -.fa-line-chart::before { - content: "\f201"; } - -.fa-mask-ventilator::before { - content: "\e524"; } - -.fa-arrow-right::before { - content: "\f061"; } - -.fa-signs-post::before { - content: "\f277"; } - -.fa-map-signs::before { - content: "\f277"; } - -.fa-cash-register::before { - content: "\f788"; } - -.fa-person-circle-question::before { - content: "\e542"; } - -.fa-h::before { - content: "\48"; } - -.fa-tarp::before { - content: "\e57b"; } - -.fa-screwdriver-wrench::before { - content: "\f7d9"; } - -.fa-tools::before { - content: "\f7d9"; } - -.fa-arrows-to-eye::before { - content: "\e4bf"; } - -.fa-plug-circle-bolt::before { - content: "\e55b"; } - -.fa-heart::before { - content: "\f004"; } - -.fa-mars-and-venus::before { - content: "\f224"; } - -.fa-house-user::before { - content: "\e1b0"; } - -.fa-home-user::before { - content: "\e1b0"; } - -.fa-dumpster-fire::before { - content: "\f794"; } - -.fa-house-crack::before { - content: "\e3b1"; } - -.fa-martini-glass-citrus::before { - content: "\f561"; } - -.fa-cocktail::before { - content: "\f561"; } - -.fa-face-surprise::before { - content: "\f5c2"; } - -.fa-surprise::before { - content: "\f5c2"; } - -.fa-bottle-water::before { - content: "\e4c5"; } - -.fa-circle-pause::before { - content: "\f28b"; } - -.fa-pause-circle::before { - content: "\f28b"; } - -.fa-toilet-paper-slash::before { - content: "\e072"; } - -.fa-apple-whole::before { - content: "\f5d1"; } - -.fa-apple-alt::before { - content: "\f5d1"; } - -.fa-kitchen-set::before { - content: "\e51a"; } - -.fa-r::before { - content: "\52"; } - -.fa-temperature-quarter::before { - content: "\f2ca"; } - -.fa-temperature-1::before { - content: "\f2ca"; } - -.fa-thermometer-1::before { - content: "\f2ca"; } - -.fa-thermometer-quarter::before { - content: "\f2ca"; } - -.fa-cube::before { - content: "\f1b2"; } - -.fa-bitcoin-sign::before { - content: "\e0b4"; } - -.fa-shield-dog::before { - content: "\e573"; } - -.fa-solar-panel::before { - content: "\f5ba"; } - -.fa-lock-open::before { - content: "\f3c1"; } - -.fa-elevator::before { - content: "\e16d"; } - -.fa-money-bill-transfer::before { - content: "\e528"; } - -.fa-money-bill-trend-up::before { - content: "\e529"; } - -.fa-house-flood-water-circle-arrow-right::before { - content: "\e50f"; } - -.fa-square-poll-horizontal::before { - content: "\f682"; } - -.fa-poll-h::before { - content: "\f682"; } - -.fa-circle::before { - content: "\f111"; } - -.fa-backward-fast::before { - content: "\f049"; } - -.fa-fast-backward::before { - content: "\f049"; } - -.fa-recycle::before { - content: "\f1b8"; } - -.fa-user-astronaut::before { - content: "\f4fb"; } - -.fa-plane-slash::before { - content: "\e069"; } - -.fa-trademark::before { - content: "\f25c"; } - -.fa-basketball::before { - content: "\f434"; } - -.fa-basketball-ball::before { - content: "\f434"; } - -.fa-satellite-dish::before { - content: "\f7c0"; } - -.fa-circle-up::before { - content: "\f35b"; } - -.fa-arrow-alt-circle-up::before { - content: "\f35b"; } - -.fa-mobile-screen-button::before { - content: "\f3cd"; } - -.fa-mobile-alt::before { - content: "\f3cd"; } - -.fa-volume-high::before { - content: "\f028"; } - -.fa-volume-up::before { - content: "\f028"; } - -.fa-users-rays::before { - content: "\e593"; } - -.fa-wallet::before { - content: "\f555"; } - -.fa-clipboard-check::before { - content: "\f46c"; } - -.fa-file-audio::before { - content: "\f1c7"; } - -.fa-burger::before { - content: "\f805"; } - -.fa-hamburger::before { - content: "\f805"; } - -.fa-wrench::before { - content: "\f0ad"; } - -.fa-bugs::before { - content: "\e4d0"; } - -.fa-rupee-sign::before { - content: "\f156"; } - -.fa-rupee::before { - content: "\f156"; } - -.fa-file-image::before { - content: "\f1c5"; } - -.fa-circle-question::before { - content: "\f059"; } - -.fa-question-circle::before { - content: "\f059"; } - -.fa-plane-departure::before { - content: "\f5b0"; } - -.fa-handshake-slash::before { - content: "\e060"; } - -.fa-book-bookmark::before { - content: "\e0bb"; } - -.fa-code-branch::before { - content: "\f126"; } - -.fa-hat-cowboy::before { - content: "\f8c0"; } - -.fa-bridge::before { - content: "\e4c8"; } - -.fa-phone-flip::before { - content: "\f879"; } - -.fa-phone-alt::before { - content: "\f879"; } - -.fa-truck-front::before { - content: "\e2b7"; } - -.fa-cat::before { - content: "\f6be"; } - -.fa-anchor-circle-exclamation::before { - content: "\e4ab"; } - -.fa-truck-field::before { - content: "\e58d"; } - -.fa-route::before { - content: "\f4d7"; } - -.fa-clipboard-question::before { - content: "\e4e3"; } - -.fa-panorama::before { - content: "\e209"; } - -.fa-comment-medical::before { - content: "\f7f5"; } - -.fa-teeth-open::before { - content: "\f62f"; } - -.fa-file-circle-minus::before { - content: "\e4ed"; } - -.fa-tags::before { - content: "\f02c"; } - -.fa-wine-glass::before { - content: "\f4e3"; } - -.fa-forward-fast::before { - content: "\f050"; } - -.fa-fast-forward::before { - content: "\f050"; } - -.fa-face-meh-blank::before { - content: "\f5a4"; } - -.fa-meh-blank::before { - content: "\f5a4"; } - -.fa-square-parking::before { - content: "\f540"; } - -.fa-parking::before { - content: "\f540"; } - -.fa-house-signal::before { - content: "\e012"; } - -.fa-bars-progress::before { - content: "\f828"; } - -.fa-tasks-alt::before { - content: "\f828"; } - -.fa-faucet-drip::before { - content: "\e006"; } - -.fa-cart-flatbed::before { - content: "\f474"; } - -.fa-dolly-flatbed::before { - content: "\f474"; } - -.fa-ban-smoking::before { - content: "\f54d"; } - -.fa-smoking-ban::before { - content: "\f54d"; } - -.fa-terminal::before { - content: "\f120"; } - -.fa-mobile-button::before { - content: "\f10b"; } - -.fa-house-medical-flag::before { - content: "\e514"; } - -.fa-basket-shopping::before { - content: "\f291"; } - -.fa-shopping-basket::before { - content: "\f291"; } - -.fa-tape::before { - content: "\f4db"; } - -.fa-bus-simple::before { - content: "\f55e"; } - -.fa-bus-alt::before { - content: "\f55e"; } - -.fa-eye::before { - content: "\f06e"; } - -.fa-face-sad-cry::before { - content: "\f5b3"; } - -.fa-sad-cry::before { - content: "\f5b3"; } - -.fa-audio-description::before { - content: "\f29e"; } - -.fa-person-military-to-person::before { - content: "\e54c"; } - -.fa-file-shield::before { - content: "\e4f0"; } - -.fa-user-slash::before { - content: "\f506"; } - -.fa-pen::before { - content: "\f304"; } - -.fa-tower-observation::before { - content: "\e586"; } - -.fa-file-code::before { - content: "\f1c9"; } - -.fa-signal::before { - content: "\f012"; } - -.fa-signal-5::before { - content: "\f012"; } - -.fa-signal-perfect::before { - content: "\f012"; } - -.fa-bus::before { - content: "\f207"; } - -.fa-heart-circle-xmark::before { - content: "\e501"; } - -.fa-house-chimney::before { - content: "\e3af"; } - -.fa-home-lg::before { - content: "\e3af"; } - -.fa-window-maximize::before { - content: "\f2d0"; } - -.fa-face-frown::before { - content: "\f119"; } - -.fa-frown::before { - content: "\f119"; } - -.fa-prescription::before { - content: "\f5b1"; } - -.fa-shop::before { - content: "\f54f"; } - -.fa-store-alt::before { - content: "\f54f"; } - -.fa-floppy-disk::before { - content: "\f0c7"; } - -.fa-save::before { - content: "\f0c7"; } - -.fa-vihara::before { - content: "\f6a7"; } - -.fa-scale-unbalanced::before { - content: "\f515"; } - -.fa-balance-scale-left::before { - content: "\f515"; } - -.fa-sort-up::before { - content: "\f0de"; } - -.fa-sort-asc::before { - content: "\f0de"; } - -.fa-comment-dots::before { - content: "\f4ad"; } - -.fa-commenting::before { - content: "\f4ad"; } - -.fa-plant-wilt::before { - content: "\e5aa"; } - -.fa-diamond::before { - content: "\f219"; } - -.fa-face-grin-squint::before { - content: "\f585"; } - -.fa-grin-squint::before { - content: "\f585"; } - -.fa-hand-holding-dollar::before { - content: "\f4c0"; } - -.fa-hand-holding-usd::before { - content: "\f4c0"; } - -.fa-bacterium::before { - content: "\e05a"; } - -.fa-hand-pointer::before { - content: "\f25a"; } - -.fa-drum-steelpan::before { - content: "\f56a"; } - -.fa-hand-scissors::before { - content: "\f257"; } - -.fa-hands-praying::before { - content: "\f684"; } - -.fa-praying-hands::before { - content: "\f684"; } - -.fa-arrow-rotate-right::before { - content: "\f01e"; } - -.fa-arrow-right-rotate::before { - content: "\f01e"; } - -.fa-arrow-rotate-forward::before { - content: "\f01e"; } - -.fa-redo::before { - content: "\f01e"; } - -.fa-biohazard::before { - content: "\f780"; } - -.fa-location-crosshairs::before { - content: "\f601"; } - -.fa-location::before { - content: "\f601"; } - -.fa-mars-double::before { - content: "\f227"; } - -.fa-child-dress::before { - content: "\e59c"; } - -.fa-users-between-lines::before { - content: "\e591"; } - -.fa-lungs-virus::before { - content: "\e067"; } - -.fa-face-grin-tears::before { - content: "\f588"; } - -.fa-grin-tears::before { - content: "\f588"; } - -.fa-phone::before { - content: "\f095"; } - -.fa-calendar-xmark::before { - content: "\f273"; } - -.fa-calendar-times::before { - content: "\f273"; } - -.fa-child-reaching::before { - content: "\e59d"; } - -.fa-head-side-virus::before { - content: "\e064"; } - -.fa-user-gear::before { - content: "\f4fe"; } - -.fa-user-cog::before { - content: "\f4fe"; } - -.fa-arrow-up-1-9::before { - content: "\f163"; } - -.fa-sort-numeric-up::before { - content: "\f163"; } - -.fa-door-closed::before { - content: "\f52a"; } - -.fa-shield-virus::before { - content: "\e06c"; } - -.fa-dice-six::before { - content: "\f526"; } - -.fa-mosquito-net::before { - content: "\e52c"; } - -.fa-bridge-water::before { - content: "\e4ce"; } - -.fa-person-booth::before { - content: "\f756"; } - -.fa-text-width::before { - content: "\f035"; } - -.fa-hat-wizard::before { - content: "\f6e8"; } - -.fa-pen-fancy::before { - content: "\f5ac"; } - -.fa-person-digging::before { - content: "\f85e"; } - -.fa-digging::before { - content: "\f85e"; } - -.fa-trash::before { - content: "\f1f8"; } - -.fa-gauge-simple::before { - content: "\f629"; } - -.fa-gauge-simple-med::before { - content: "\f629"; } - -.fa-tachometer-average::before { - content: "\f629"; } - -.fa-book-medical::before { - content: "\f7e6"; } - -.fa-poo::before { - content: "\f2fe"; } - -.fa-quote-right::before { - content: "\f10e"; } - -.fa-quote-right-alt::before { - content: "\f10e"; } - -.fa-shirt::before { - content: "\f553"; } - -.fa-t-shirt::before { - content: "\f553"; } - -.fa-tshirt::before { - content: "\f553"; } - -.fa-cubes::before { - content: "\f1b3"; } - -.fa-divide::before { - content: "\f529"; } - -.fa-tenge-sign::before { - content: "\f7d7"; } - -.fa-tenge::before { - content: "\f7d7"; } - -.fa-headphones::before { - content: "\f025"; } - -.fa-hands-holding::before { - content: "\f4c2"; } - -.fa-hands-clapping::before { - content: "\e1a8"; } - -.fa-republican::before { - content: "\f75e"; } - -.fa-arrow-left::before { - content: "\f060"; } - -.fa-person-circle-xmark::before { - content: "\e543"; } - -.fa-ruler::before { - content: "\f545"; } - -.fa-align-left::before { - content: "\f036"; } - -.fa-dice-d6::before { - content: "\f6d1"; } - -.fa-restroom::before { - content: "\f7bd"; } - -.fa-j::before { - content: "\4a"; } - -.fa-users-viewfinder::before { - content: "\e595"; } - -.fa-file-video::before { - content: "\f1c8"; } - -.fa-up-right-from-square::before { - content: "\f35d"; } - -.fa-external-link-alt::before { - content: "\f35d"; } - -.fa-table-cells::before { - content: "\f00a"; } - -.fa-th::before { - content: "\f00a"; } - -.fa-file-pdf::before { - content: "\f1c1"; } - -.fa-book-bible::before { - content: "\f647"; } - -.fa-bible::before { - content: "\f647"; } - -.fa-o::before { - content: "\4f"; } - -.fa-suitcase-medical::before { - content: "\f0fa"; } - -.fa-medkit::before { - content: "\f0fa"; } - -.fa-user-secret::before { - content: "\f21b"; } - -.fa-otter::before { - content: "\f700"; } - -.fa-person-dress::before { - content: "\f182"; } - -.fa-female::before { - content: "\f182"; } - -.fa-comment-dollar::before { - content: "\f651"; } - -.fa-business-time::before { - content: "\f64a"; } - -.fa-briefcase-clock::before { - content: "\f64a"; } - -.fa-table-cells-large::before { - content: "\f009"; } - -.fa-th-large::before { - content: "\f009"; } - -.fa-book-tanakh::before { - content: "\f827"; } - -.fa-tanakh::before { - content: "\f827"; } - -.fa-phone-volume::before { - content: "\f2a0"; } - -.fa-volume-control-phone::before { - content: "\f2a0"; } - -.fa-hat-cowboy-side::before { - content: "\f8c1"; } - -.fa-clipboard-user::before { - content: "\f7f3"; } - -.fa-child::before { - content: "\f1ae"; } - -.fa-lira-sign::before { - content: "\f195"; } - -.fa-satellite::before { - content: "\f7bf"; } - -.fa-plane-lock::before { - content: "\e558"; } - -.fa-tag::before { - content: "\f02b"; } - -.fa-comment::before { - content: "\f075"; } - -.fa-cake-candles::before { - content: "\f1fd"; } - -.fa-birthday-cake::before { - content: "\f1fd"; } - -.fa-cake::before { - content: "\f1fd"; } - -.fa-envelope::before { - content: "\f0e0"; } - -.fa-angles-up::before { - content: "\f102"; } - -.fa-angle-double-up::before { - content: "\f102"; } - -.fa-paperclip::before { - content: "\f0c6"; } - -.fa-arrow-right-to-city::before { - content: "\e4b3"; } - -.fa-ribbon::before { - content: "\f4d6"; } - -.fa-lungs::before { - content: "\f604"; } - -.fa-arrow-up-9-1::before { - content: "\f887"; } - -.fa-sort-numeric-up-alt::before { - content: "\f887"; } - -.fa-litecoin-sign::before { - content: "\e1d3"; } - -.fa-border-none::before { - content: "\f850"; } - -.fa-circle-nodes::before { - content: "\e4e2"; } - -.fa-parachute-box::before { - content: "\f4cd"; } - -.fa-indent::before { - content: "\f03c"; } - -.fa-truck-field-un::before { - content: "\e58e"; } - -.fa-hourglass::before { - content: "\f254"; } - -.fa-hourglass-empty::before { - content: "\f254"; } - -.fa-mountain::before { - content: "\f6fc"; } - -.fa-user-doctor::before { - content: "\f0f0"; } - -.fa-user-md::before { - content: "\f0f0"; } - -.fa-circle-info::before { - content: "\f05a"; } - -.fa-info-circle::before { - content: "\f05a"; } - -.fa-cloud-meatball::before { - content: "\f73b"; } - -.fa-camera::before { - content: "\f030"; } - -.fa-camera-alt::before { - content: "\f030"; } - -.fa-square-virus::before { - content: "\e578"; } - -.fa-meteor::before { - content: "\f753"; } - -.fa-car-on::before { - content: "\e4dd"; } - -.fa-sleigh::before { - content: "\f7cc"; } - -.fa-arrow-down-1-9::before { - content: "\f162"; } - -.fa-sort-numeric-asc::before { - content: "\f162"; } - -.fa-sort-numeric-down::before { - content: "\f162"; } - -.fa-hand-holding-droplet::before { - content: "\f4c1"; } - -.fa-hand-holding-water::before { - content: "\f4c1"; } - -.fa-water::before { - content: "\f773"; } - -.fa-calendar-check::before { - content: "\f274"; } - -.fa-braille::before { - content: "\f2a1"; } - -.fa-prescription-bottle-medical::before { - content: "\f486"; } - -.fa-prescription-bottle-alt::before { - content: "\f486"; } - -.fa-landmark::before { - content: "\f66f"; } - -.fa-truck::before { - content: "\f0d1"; } - -.fa-crosshairs::before { - content: "\f05b"; } - -.fa-person-cane::before { - content: "\e53c"; } - -.fa-tent::before { - content: "\e57d"; } - -.fa-vest-patches::before { - content: "\e086"; } - -.fa-check-double::before { - content: "\f560"; } - -.fa-arrow-down-a-z::before { - content: "\f15d"; } - -.fa-sort-alpha-asc::before { - content: "\f15d"; } - -.fa-sort-alpha-down::before { - content: "\f15d"; } - -.fa-money-bill-wheat::before { - content: "\e52a"; } - -.fa-cookie::before { - content: "\f563"; } - -.fa-arrow-rotate-left::before { - content: "\f0e2"; } - -.fa-arrow-left-rotate::before { - content: "\f0e2"; } - -.fa-arrow-rotate-back::before { - content: "\f0e2"; } - -.fa-arrow-rotate-backward::before { - content: "\f0e2"; } - -.fa-undo::before { - content: "\f0e2"; } - -.fa-hard-drive::before { - content: "\f0a0"; } - -.fa-hdd::before { - content: "\f0a0"; } - -.fa-face-grin-squint-tears::before { - content: "\f586"; } - -.fa-grin-squint-tears::before { - content: "\f586"; } - -.fa-dumbbell::before { - content: "\f44b"; } - -.fa-rectangle-list::before { - content: "\f022"; } - -.fa-list-alt::before { - content: "\f022"; } - -.fa-tarp-droplet::before { - content: "\e57c"; } - -.fa-house-medical-circle-check::before { - content: "\e511"; } - -.fa-person-skiing-nordic::before { - content: "\f7ca"; } - -.fa-skiing-nordic::before { - content: "\f7ca"; } - -.fa-calendar-plus::before { - content: "\f271"; } - -.fa-plane-arrival::before { - content: "\f5af"; } - -.fa-circle-left::before { - content: "\f359"; } - -.fa-arrow-alt-circle-left::before { - content: "\f359"; } - -.fa-train-subway::before { - content: "\f239"; } - -.fa-subway::before { - content: "\f239"; } - -.fa-chart-gantt::before { - content: "\e0e4"; } - -.fa-indian-rupee-sign::before { - content: "\e1bc"; } - -.fa-indian-rupee::before { - content: "\e1bc"; } - -.fa-inr::before { - content: "\e1bc"; } - -.fa-crop-simple::before { - content: "\f565"; } - -.fa-crop-alt::before { - content: "\f565"; } - -.fa-money-bill-1::before { - content: "\f3d1"; } - -.fa-money-bill-alt::before { - content: "\f3d1"; } - -.fa-left-long::before { - content: "\f30a"; } - -.fa-long-arrow-alt-left::before { - content: "\f30a"; } - -.fa-dna::before { - content: "\f471"; } - -.fa-virus-slash::before { - content: "\e075"; } - -.fa-minus::before { - content: "\f068"; } - -.fa-subtract::before { - content: "\f068"; } - -.fa-chess::before { - content: "\f439"; } - -.fa-arrow-left-long::before { - content: "\f177"; } - -.fa-long-arrow-left::before { - content: "\f177"; } - -.fa-plug-circle-check::before { - content: "\e55c"; } - -.fa-street-view::before { - content: "\f21d"; } - -.fa-franc-sign::before { - content: "\e18f"; } - -.fa-volume-off::before { - content: "\f026"; } - -.fa-hands-asl-interpreting::before { - content: "\f2a3"; } - -.fa-american-sign-language-interpreting::before { - content: "\f2a3"; } - -.fa-asl-interpreting::before { - content: "\f2a3"; } - -.fa-hands-american-sign-language-interpreting::before { - content: "\f2a3"; } - -.fa-gear::before { - content: "\f013"; } - -.fa-cog::before { - content: "\f013"; } - -.fa-droplet-slash::before { - content: "\f5c7"; } - -.fa-tint-slash::before { - content: "\f5c7"; } - -.fa-mosque::before { - content: "\f678"; } - -.fa-mosquito::before { - content: "\e52b"; } - -.fa-star-of-david::before { - content: "\f69a"; } - -.fa-person-military-rifle::before { - content: "\e54b"; } - -.fa-cart-shopping::before { - content: "\f07a"; } - -.fa-shopping-cart::before { - content: "\f07a"; } - -.fa-vials::before { - content: "\f493"; } - -.fa-plug-circle-plus::before { - content: "\e55f"; } - -.fa-place-of-worship::before { - content: "\f67f"; } - -.fa-grip-vertical::before { - content: "\f58e"; } - -.fa-arrow-turn-up::before { - content: "\f148"; } - -.fa-level-up::before { - content: "\f148"; } - -.fa-u::before { - content: "\55"; } - -.fa-square-root-variable::before { - content: "\f698"; } - -.fa-square-root-alt::before { - content: "\f698"; } - -.fa-clock::before { - content: "\f017"; } - -.fa-clock-four::before { - content: "\f017"; } - -.fa-backward-step::before { - content: "\f048"; } - -.fa-step-backward::before { - content: "\f048"; } - -.fa-pallet::before { - content: "\f482"; } - -.fa-faucet::before { - content: "\e005"; } - -.fa-baseball-bat-ball::before { - content: "\f432"; } - -.fa-s::before { - content: "\53"; } - -.fa-timeline::before { - content: "\e29c"; } - -.fa-keyboard::before { - content: "\f11c"; } - -.fa-caret-down::before { - content: "\f0d7"; } - -.fa-house-chimney-medical::before { - content: "\f7f2"; } - -.fa-clinic-medical::before { - content: "\f7f2"; } - -.fa-temperature-three-quarters::before { - content: "\f2c8"; } - -.fa-temperature-3::before { - content: "\f2c8"; } - -.fa-thermometer-3::before { - content: "\f2c8"; } - -.fa-thermometer-three-quarters::before { - content: "\f2c8"; } - -.fa-mobile-screen::before { - content: "\f3cf"; } - -.fa-mobile-android-alt::before { - content: "\f3cf"; } - -.fa-plane-up::before { - content: "\e22d"; } - -.fa-piggy-bank::before { - content: "\f4d3"; } - -.fa-battery-half::before { - content: "\f242"; } - -.fa-battery-3::before { - content: "\f242"; } - -.fa-mountain-city::before { - content: "\e52e"; } - -.fa-coins::before { - content: "\f51e"; } - -.fa-khanda::before { - content: "\f66d"; } - -.fa-sliders::before { - content: "\f1de"; } - -.fa-sliders-h::before { - content: "\f1de"; } - -.fa-folder-tree::before { - content: "\f802"; } - -.fa-network-wired::before { - content: "\f6ff"; } - -.fa-map-pin::before { - content: "\f276"; } - -.fa-hamsa::before { - content: "\f665"; } - -.fa-cent-sign::before { - content: "\e3f5"; } - -.fa-flask::before { - content: "\f0c3"; } - -.fa-person-pregnant::before { - content: "\e31e"; } - -.fa-wand-sparkles::before { - content: "\f72b"; } - -.fa-ellipsis-vertical::before { - content: "\f142"; } - -.fa-ellipsis-v::before { - content: "\f142"; } - -.fa-ticket::before { - content: "\f145"; } - -.fa-power-off::before { - content: "\f011"; } - -.fa-right-long::before { - content: "\f30b"; } - -.fa-long-arrow-alt-right::before { - content: "\f30b"; } - -.fa-flag-usa::before { - content: "\f74d"; } - -.fa-laptop-file::before { - content: "\e51d"; } - -.fa-tty::before { - content: "\f1e4"; } - -.fa-teletype::before { - content: "\f1e4"; } - -.fa-diagram-next::before { - content: "\e476"; } - -.fa-person-rifle::before { - content: "\e54e"; } - -.fa-house-medical-circle-exclamation::before { - content: "\e512"; } - -.fa-closed-captioning::before { - content: "\f20a"; } - -.fa-person-hiking::before { - content: "\f6ec"; } - -.fa-hiking::before { - content: "\f6ec"; } - -.fa-venus-double::before { - content: "\f226"; } - -.fa-images::before { - content: "\f302"; } - -.fa-calculator::before { - content: "\f1ec"; } - -.fa-people-pulling::before { - content: "\e535"; } - -.fa-n::before { - content: "\4e"; } - -.fa-cable-car::before { - content: "\f7da"; } - -.fa-tram::before { - content: "\f7da"; } - -.fa-cloud-rain::before { - content: "\f73d"; } - -.fa-building-circle-xmark::before { - content: "\e4d4"; } - -.fa-ship::before { - content: "\f21a"; } - -.fa-arrows-down-to-line::before { - content: "\e4b8"; } - -.fa-download::before { - content: "\f019"; } - -.fa-face-grin::before { - content: "\f580"; } - -.fa-grin::before { - content: "\f580"; } - -.fa-delete-left::before { - content: "\f55a"; } - -.fa-backspace::before { - content: "\f55a"; } - -.fa-eye-dropper::before { - content: "\f1fb"; } - -.fa-eye-dropper-empty::before { - content: "\f1fb"; } - -.fa-eyedropper::before { - content: "\f1fb"; } - -.fa-file-circle-check::before { - content: "\e5a0"; } - -.fa-forward::before { - content: "\f04e"; } - -.fa-mobile::before { - content: "\f3ce"; } - -.fa-mobile-android::before { - content: "\f3ce"; } - -.fa-mobile-phone::before { - content: "\f3ce"; } - -.fa-face-meh::before { - content: "\f11a"; } - -.fa-meh::before { - content: "\f11a"; } - -.fa-align-center::before { - content: "\f037"; } - -.fa-book-skull::before { - content: "\f6b7"; } - -.fa-book-dead::before { - content: "\f6b7"; } - -.fa-id-card::before { - content: "\f2c2"; } - -.fa-drivers-license::before { - content: "\f2c2"; } - -.fa-outdent::before { - content: "\f03b"; } - -.fa-dedent::before { - content: "\f03b"; } - -.fa-heart-circle-exclamation::before { - content: "\e4fe"; } - -.fa-house::before { - content: "\f015"; } - -.fa-home::before { - content: "\f015"; } - -.fa-home-alt::before { - content: "\f015"; } - -.fa-home-lg-alt::before { - content: "\f015"; } - -.fa-calendar-week::before { - content: "\f784"; } - -.fa-laptop-medical::before { - content: "\f812"; } - -.fa-b::before { - content: "\42"; } - -.fa-file-medical::before { - content: "\f477"; } - -.fa-dice-one::before { - content: "\f525"; } - -.fa-kiwi-bird::before { - content: "\f535"; } - -.fa-arrow-right-arrow-left::before { - content: "\f0ec"; } - -.fa-exchange::before { - content: "\f0ec"; } - -.fa-rotate-right::before { - content: "\f2f9"; } - -.fa-redo-alt::before { - content: "\f2f9"; } - -.fa-rotate-forward::before { - content: "\f2f9"; } - -.fa-utensils::before { - content: "\f2e7"; } - -.fa-cutlery::before { - content: "\f2e7"; } - -.fa-arrow-up-wide-short::before { - content: "\f161"; } - -.fa-sort-amount-up::before { - content: "\f161"; } - -.fa-mill-sign::before { - content: "\e1ed"; } - -.fa-bowl-rice::before { - content: "\e2eb"; } - -.fa-skull::before { - content: "\f54c"; } - -.fa-tower-broadcast::before { - content: "\f519"; } - -.fa-broadcast-tower::before { - content: "\f519"; } - -.fa-truck-pickup::before { - content: "\f63c"; } - -.fa-up-long::before { - content: "\f30c"; } - -.fa-long-arrow-alt-up::before { - content: "\f30c"; } - -.fa-stop::before { - content: "\f04d"; } - -.fa-code-merge::before { - content: "\f387"; } - -.fa-upload::before { - content: "\f093"; } - -.fa-hurricane::before { - content: "\f751"; } - -.fa-mound::before { - content: "\e52d"; } - -.fa-toilet-portable::before { - content: "\e583"; } - -.fa-compact-disc::before { - content: "\f51f"; } - -.fa-file-arrow-down::before { - content: "\f56d"; } - -.fa-file-download::before { - content: "\f56d"; } - -.fa-caravan::before { - content: "\f8ff"; } - -.fa-shield-cat::before { - content: "\e572"; } - -.fa-bolt::before { - content: "\f0e7"; } - -.fa-zap::before { - content: "\f0e7"; } - -.fa-glass-water::before { - content: "\e4f4"; } - -.fa-oil-well::before { - content: "\e532"; } - -.fa-vault::before { - content: "\e2c5"; } - -.fa-mars::before { - content: "\f222"; } - -.fa-toilet::before { - content: "\f7d8"; } - -.fa-plane-circle-xmark::before { - content: "\e557"; } - -.fa-yen-sign::before { - content: "\f157"; } - -.fa-cny::before { - content: "\f157"; } - -.fa-jpy::before { - content: "\f157"; } - -.fa-rmb::before { - content: "\f157"; } - -.fa-yen::before { - content: "\f157"; } - -.fa-ruble-sign::before { - content: "\f158"; } - -.fa-rouble::before { - content: "\f158"; } - -.fa-rub::before { - content: "\f158"; } - -.fa-ruble::before { - content: "\f158"; } - -.fa-sun::before { - content: "\f185"; } - -.fa-guitar::before { - content: "\f7a6"; } - -.fa-face-laugh-wink::before { - content: "\f59c"; } - -.fa-laugh-wink::before { - content: "\f59c"; } - -.fa-horse-head::before { - content: "\f7ab"; } - -.fa-bore-hole::before { - content: "\e4c3"; } - -.fa-industry::before { - content: "\f275"; } - -.fa-circle-down::before { - content: "\f358"; } - -.fa-arrow-alt-circle-down::before { - content: "\f358"; } - -.fa-arrows-turn-to-dots::before { - content: "\e4c1"; } - -.fa-florin-sign::before { - content: "\e184"; } - -.fa-arrow-down-short-wide::before { - content: "\f884"; } - -.fa-sort-amount-desc::before { - content: "\f884"; } - -.fa-sort-amount-down-alt::before { - content: "\f884"; } - -.fa-less-than::before { - content: "\3c"; } - -.fa-angle-down::before { - content: "\f107"; } - -.fa-car-tunnel::before { - content: "\e4de"; } - -.fa-head-side-cough::before { - content: "\e061"; } - -.fa-grip-lines::before { - content: "\f7a4"; } - -.fa-thumbs-down::before { - content: "\f165"; } - -.fa-user-lock::before { - content: "\f502"; } - -.fa-arrow-right-long::before { - content: "\f178"; } - -.fa-long-arrow-right::before { - content: "\f178"; } - -.fa-anchor-circle-xmark::before { - content: "\e4ac"; } - -.fa-ellipsis::before { - content: "\f141"; } - -.fa-ellipsis-h::before { - content: "\f141"; } - -.fa-chess-pawn::before { - content: "\f443"; } - -.fa-kit-medical::before { - content: "\f479"; } - -.fa-first-aid::before { - content: "\f479"; } - -.fa-person-through-window::before { - content: "\e5a9"; } - -.fa-toolbox::before { - content: "\f552"; } - -.fa-hands-holding-circle::before { - content: "\e4fb"; } - -.fa-bug::before { - content: "\f188"; } - -.fa-credit-card::before { - content: "\f09d"; } - -.fa-credit-card-alt::before { - content: "\f09d"; } - -.fa-car::before { - content: "\f1b9"; } - -.fa-automobile::before { - content: "\f1b9"; } - -.fa-hand-holding-hand::before { - content: "\e4f7"; } - -.fa-book-open-reader::before { - content: "\f5da"; } - -.fa-book-reader::before { - content: "\f5da"; } - -.fa-mountain-sun::before { - content: "\e52f"; } - -.fa-arrows-left-right-to-line::before { - content: "\e4ba"; } - -.fa-dice-d20::before { - content: "\f6cf"; } - -.fa-truck-droplet::before { - content: "\e58c"; } - -.fa-file-circle-xmark::before { - content: "\e5a1"; } - -.fa-temperature-arrow-up::before { - content: "\e040"; } - -.fa-temperature-up::before { - content: "\e040"; } - -.fa-medal::before { - content: "\f5a2"; } - -.fa-bed::before { - content: "\f236"; } - -.fa-square-h::before { - content: "\f0fd"; } - -.fa-h-square::before { - content: "\f0fd"; } - -.fa-podcast::before { - content: "\f2ce"; } - -.fa-temperature-full::before { - content: "\f2c7"; } - -.fa-temperature-4::before { - content: "\f2c7"; } - -.fa-thermometer-4::before { - content: "\f2c7"; } - -.fa-thermometer-full::before { - content: "\f2c7"; } - -.fa-bell::before { - content: "\f0f3"; } - -.fa-superscript::before { - content: "\f12b"; } - -.fa-plug-circle-xmark::before { - content: "\e560"; } - -.fa-star-of-life::before { - content: "\f621"; } - -.fa-phone-slash::before { - content: "\f3dd"; } - -.fa-paint-roller::before { - content: "\f5aa"; } - -.fa-handshake-angle::before { - content: "\f4c4"; } - -.fa-hands-helping::before { - content: "\f4c4"; } - -.fa-location-dot::before { - content: "\f3c5"; } - -.fa-map-marker-alt::before { - content: "\f3c5"; } - -.fa-file::before { - content: "\f15b"; } - -.fa-greater-than::before { - content: "\3e"; } - -.fa-person-swimming::before { - content: "\f5c4"; } - -.fa-swimmer::before { - content: "\f5c4"; } - -.fa-arrow-down::before { - content: "\f063"; } - -.fa-droplet::before { - content: "\f043"; } - -.fa-tint::before { - content: "\f043"; } - -.fa-eraser::before { - content: "\f12d"; } - -.fa-earth-americas::before { - content: "\f57d"; } - -.fa-earth::before { - content: "\f57d"; } - -.fa-earth-america::before { - content: "\f57d"; } - -.fa-globe-americas::before { - content: "\f57d"; } - -.fa-person-burst::before { - content: "\e53b"; } - -.fa-dove::before { - content: "\f4ba"; } - -.fa-battery-empty::before { - content: "\f244"; } - -.fa-battery-0::before { - content: "\f244"; } - -.fa-socks::before { - content: "\f696"; } - -.fa-inbox::before { - content: "\f01c"; } - -.fa-section::before { - content: "\e447"; } - -.fa-gauge-high::before { - content: "\f625"; } - -.fa-tachometer-alt::before { - content: "\f625"; } - -.fa-tachometer-alt-fast::before { - content: "\f625"; } - -.fa-envelope-open-text::before { - content: "\f658"; } - -.fa-hospital::before { - content: "\f0f8"; } - -.fa-hospital-alt::before { - content: "\f0f8"; } - -.fa-hospital-wide::before { - content: "\f0f8"; } - -.fa-wine-bottle::before { - content: "\f72f"; } - -.fa-chess-rook::before { - content: "\f447"; } - -.fa-bars-staggered::before { - content: "\f550"; } - -.fa-reorder::before { - content: "\f550"; } - -.fa-stream::before { - content: "\f550"; } - -.fa-dharmachakra::before { - content: "\f655"; } - -.fa-hotdog::before { - content: "\f80f"; } - -.fa-person-walking-with-cane::before { - content: "\f29d"; } - -.fa-blind::before { - content: "\f29d"; } - -.fa-drum::before { - content: "\f569"; } - -.fa-ice-cream::before { - content: "\f810"; } - -.fa-heart-circle-bolt::before { - content: "\e4fc"; } - -.fa-fax::before { - content: "\f1ac"; } - -.fa-paragraph::before { - content: "\f1dd"; } - -.fa-check-to-slot::before { - content: "\f772"; } - -.fa-vote-yea::before { - content: "\f772"; } - -.fa-star-half::before { - content: "\f089"; } - -.fa-boxes-stacked::before { - content: "\f468"; } - -.fa-boxes::before { - content: "\f468"; } - -.fa-boxes-alt::before { - content: "\f468"; } - -.fa-link::before { - content: "\f0c1"; } - -.fa-chain::before { - content: "\f0c1"; } - -.fa-ear-listen::before { - content: "\f2a2"; } - -.fa-assistive-listening-systems::before { - content: "\f2a2"; } - -.fa-tree-city::before { - content: "\e587"; } - -.fa-play::before { - content: "\f04b"; } - -.fa-font::before { - content: "\f031"; } - -.fa-table-cells-row-lock::before { - content: "\e67a"; } - -.fa-rupiah-sign::before { - content: "\e23d"; } - -.fa-magnifying-glass::before { - content: "\f002"; } - -.fa-search::before { - content: "\f002"; } - -.fa-table-tennis-paddle-ball::before { - content: "\f45d"; } - -.fa-ping-pong-paddle-ball::before { - content: "\f45d"; } - -.fa-table-tennis::before { - content: "\f45d"; } - -.fa-person-dots-from-line::before { - content: "\f470"; } - -.fa-diagnoses::before { - content: "\f470"; } - -.fa-trash-can-arrow-up::before { - content: "\f82a"; } - -.fa-trash-restore-alt::before { - content: "\f82a"; } - -.fa-naira-sign::before { - content: "\e1f6"; } - -.fa-cart-arrow-down::before { - content: "\f218"; } - -.fa-walkie-talkie::before { - content: "\f8ef"; } - -.fa-file-pen::before { - content: "\f31c"; } - -.fa-file-edit::before { - content: "\f31c"; } - -.fa-receipt::before { - content: "\f543"; } - -.fa-square-pen::before { - content: "\f14b"; } - -.fa-pen-square::before { - content: "\f14b"; } - -.fa-pencil-square::before { - content: "\f14b"; } - -.fa-suitcase-rolling::before { - content: "\f5c1"; } - -.fa-person-circle-exclamation::before { - content: "\e53f"; } - -.fa-chevron-down::before { - content: "\f078"; } - -.fa-battery-full::before { - content: "\f240"; } - -.fa-battery::before { - content: "\f240"; } - -.fa-battery-5::before { - content: "\f240"; } - -.fa-skull-crossbones::before { - content: "\f714"; } - -.fa-code-compare::before { - content: "\e13a"; } - -.fa-list-ul::before { - content: "\f0ca"; } - -.fa-list-dots::before { - content: "\f0ca"; } - -.fa-school-lock::before { - content: "\e56f"; } - -.fa-tower-cell::before { - content: "\e585"; } - -.fa-down-long::before { - content: "\f309"; } - -.fa-long-arrow-alt-down::before { - content: "\f309"; } - -.fa-ranking-star::before { - content: "\e561"; } - -.fa-chess-king::before { - content: "\f43f"; } - -.fa-person-harassing::before { - content: "\e549"; } - -.fa-brazilian-real-sign::before { - content: "\e46c"; } - -.fa-landmark-dome::before { - content: "\f752"; } - -.fa-landmark-alt::before { - content: "\f752"; } - -.fa-arrow-up::before { - content: "\f062"; } - -.fa-tv::before { - content: "\f26c"; } - -.fa-television::before { - content: "\f26c"; } - -.fa-tv-alt::before { - content: "\f26c"; } - -.fa-shrimp::before { - content: "\e448"; } - -.fa-list-check::before { - content: "\f0ae"; } - -.fa-tasks::before { - content: "\f0ae"; } - -.fa-jug-detergent::before { - content: "\e519"; } - -.fa-circle-user::before { - content: "\f2bd"; } - -.fa-user-circle::before { - content: "\f2bd"; } - -.fa-user-shield::before { - content: "\f505"; } - -.fa-wind::before { - content: "\f72e"; } - -.fa-car-burst::before { - content: "\f5e1"; } - -.fa-car-crash::before { - content: "\f5e1"; } - -.fa-y::before { - content: "\59"; } - -.fa-person-snowboarding::before { - content: "\f7ce"; } - -.fa-snowboarding::before { - content: "\f7ce"; } - -.fa-truck-fast::before { - content: "\f48b"; } - -.fa-shipping-fast::before { - content: "\f48b"; } - -.fa-fish::before { - content: "\f578"; } - -.fa-user-graduate::before { - content: "\f501"; } - -.fa-circle-half-stroke::before { - content: "\f042"; } - -.fa-adjust::before { - content: "\f042"; } - -.fa-clapperboard::before { - content: "\e131"; } - -.fa-circle-radiation::before { - content: "\f7ba"; } - -.fa-radiation-alt::before { - content: "\f7ba"; } - -.fa-baseball::before { - content: "\f433"; } - -.fa-baseball-ball::before { - content: "\f433"; } - -.fa-jet-fighter-up::before { - content: "\e518"; } - -.fa-diagram-project::before { - content: "\f542"; } - -.fa-project-diagram::before { - content: "\f542"; } - -.fa-copy::before { - content: "\f0c5"; } - -.fa-volume-xmark::before { - content: "\f6a9"; } - -.fa-volume-mute::before { - content: "\f6a9"; } - -.fa-volume-times::before { - content: "\f6a9"; } - -.fa-hand-sparkles::before { - content: "\e05d"; } - -.fa-grip::before { - content: "\f58d"; } - -.fa-grip-horizontal::before { - content: "\f58d"; } - -.fa-share-from-square::before { - content: "\f14d"; } - -.fa-share-square::before { - content: "\f14d"; } - -.fa-child-combatant::before { - content: "\e4e0"; } - -.fa-child-rifle::before { - content: "\e4e0"; } - -.fa-gun::before { - content: "\e19b"; } - -.fa-square-phone::before { - content: "\f098"; } - -.fa-phone-square::before { - content: "\f098"; } - -.fa-plus::before { - content: "\2b"; } - -.fa-add::before { - content: "\2b"; } - -.fa-expand::before { - content: "\f065"; } - -.fa-computer::before { - content: "\e4e5"; } - -.fa-xmark::before { - content: "\f00d"; } - -.fa-close::before { - content: "\f00d"; } - -.fa-multiply::before { - content: "\f00d"; } - -.fa-remove::before { - content: "\f00d"; } - -.fa-times::before { - content: "\f00d"; } - -.fa-arrows-up-down-left-right::before { - content: "\f047"; } - -.fa-arrows::before { - content: "\f047"; } - -.fa-chalkboard-user::before { - content: "\f51c"; } - -.fa-chalkboard-teacher::before { - content: "\f51c"; } - -.fa-peso-sign::before { - content: "\e222"; } - -.fa-building-shield::before { - content: "\e4d8"; } - -.fa-baby::before { - content: "\f77c"; } - -.fa-users-line::before { - content: "\e592"; } - -.fa-quote-left::before { - content: "\f10d"; } - -.fa-quote-left-alt::before { - content: "\f10d"; } - -.fa-tractor::before { - content: "\f722"; } - -.fa-trash-arrow-up::before { - content: "\f829"; } - -.fa-trash-restore::before { - content: "\f829"; } - -.fa-arrow-down-up-lock::before { - content: "\e4b0"; } - -.fa-lines-leaning::before { - content: "\e51e"; } - -.fa-ruler-combined::before { - content: "\f546"; } - -.fa-copyright::before { - content: "\f1f9"; } - -.fa-equals::before { - content: "\3d"; } - -.fa-blender::before { - content: "\f517"; } - -.fa-teeth::before { - content: "\f62e"; } - -.fa-shekel-sign::before { - content: "\f20b"; } - -.fa-ils::before { - content: "\f20b"; } - -.fa-shekel::before { - content: "\f20b"; } - -.fa-sheqel::before { - content: "\f20b"; } - -.fa-sheqel-sign::before { - content: "\f20b"; } - -.fa-map::before { - content: "\f279"; } - -.fa-rocket::before { - content: "\f135"; } - -.fa-photo-film::before { - content: "\f87c"; } - -.fa-photo-video::before { - content: "\f87c"; } - -.fa-folder-minus::before { - content: "\f65d"; } - -.fa-store::before { - content: "\f54e"; } - -.fa-arrow-trend-up::before { - content: "\e098"; } - -.fa-plug-circle-minus::before { - content: "\e55e"; } - -.fa-sign-hanging::before { - content: "\f4d9"; } - -.fa-sign::before { - content: "\f4d9"; } - -.fa-bezier-curve::before { - content: "\f55b"; } - -.fa-bell-slash::before { - content: "\f1f6"; } - -.fa-tablet::before { - content: "\f3fb"; } - -.fa-tablet-android::before { - content: "\f3fb"; } - -.fa-school-flag::before { - content: "\e56e"; } - -.fa-fill::before { - content: "\f575"; } - -.fa-angle-up::before { - content: "\f106"; } - -.fa-drumstick-bite::before { - content: "\f6d7"; } - -.fa-holly-berry::before { - content: "\f7aa"; } - -.fa-chevron-left::before { - content: "\f053"; } - -.fa-bacteria::before { - content: "\e059"; } - -.fa-hand-lizard::before { - content: "\f258"; } - -.fa-notdef::before { - content: "\e1fe"; } - -.fa-disease::before { - content: "\f7fa"; } - -.fa-briefcase-medical::before { - content: "\f469"; } - -.fa-genderless::before { - content: "\f22d"; } - -.fa-chevron-right::before { - content: "\f054"; } - -.fa-retweet::before { - content: "\f079"; } - -.fa-car-rear::before { - content: "\f5de"; } - -.fa-car-alt::before { - content: "\f5de"; } - -.fa-pump-soap::before { - content: "\e06b"; } - -.fa-video-slash::before { - content: "\f4e2"; } - -.fa-battery-quarter::before { - content: "\f243"; } - -.fa-battery-2::before { - content: "\f243"; } - -.fa-radio::before { - content: "\f8d7"; } - -.fa-baby-carriage::before { - content: "\f77d"; } - -.fa-carriage-baby::before { - content: "\f77d"; } - -.fa-traffic-light::before { - content: "\f637"; } - -.fa-thermometer::before { - content: "\f491"; } - -.fa-vr-cardboard::before { - content: "\f729"; } - -.fa-hand-middle-finger::before { - content: "\f806"; } - -.fa-percent::before { - content: "\25"; } - -.fa-percentage::before { - content: "\25"; } - -.fa-truck-moving::before { - content: "\f4df"; } - -.fa-glass-water-droplet::before { - content: "\e4f5"; } - -.fa-display::before { - content: "\e163"; } - -.fa-face-smile::before { - content: "\f118"; } - -.fa-smile::before { - content: "\f118"; } - -.fa-thumbtack::before { - content: "\f08d"; } - -.fa-thumb-tack::before { - content: "\f08d"; } - -.fa-trophy::before { - content: "\f091"; } - -.fa-person-praying::before { - content: "\f683"; } - -.fa-pray::before { - content: "\f683"; } - -.fa-hammer::before { - content: "\f6e3"; } - -.fa-hand-peace::before { - content: "\f25b"; } - -.fa-rotate::before { - content: "\f2f1"; } - -.fa-sync-alt::before { - content: "\f2f1"; } - -.fa-spinner::before { - content: "\f110"; } - -.fa-robot::before { - content: "\f544"; } - -.fa-peace::before { - content: "\f67c"; } - -.fa-gears::before { - content: "\f085"; } - -.fa-cogs::before { - content: "\f085"; } - -.fa-warehouse::before { - content: "\f494"; } - -.fa-arrow-up-right-dots::before { - content: "\e4b7"; } - -.fa-splotch::before { - content: "\f5bc"; } - -.fa-face-grin-hearts::before { - content: "\f584"; } - -.fa-grin-hearts::before { - content: "\f584"; } - -.fa-dice-four::before { - content: "\f524"; } - -.fa-sim-card::before { - content: "\f7c4"; } - -.fa-transgender::before { - content: "\f225"; } - -.fa-transgender-alt::before { - content: "\f225"; } - -.fa-mercury::before { - content: "\f223"; } - -.fa-arrow-turn-down::before { - content: "\f149"; } - -.fa-level-down::before { - content: "\f149"; } - -.fa-person-falling-burst::before { - content: "\e547"; } - -.fa-award::before { - content: "\f559"; } - -.fa-ticket-simple::before { - content: "\f3ff"; } - -.fa-ticket-alt::before { - content: "\f3ff"; } - -.fa-building::before { - content: "\f1ad"; } - -.fa-angles-left::before { - content: "\f100"; } - -.fa-angle-double-left::before { - content: "\f100"; } - -.fa-qrcode::before { - content: "\f029"; } - -.fa-clock-rotate-left::before { - content: "\f1da"; } - -.fa-history::before { - content: "\f1da"; } - -.fa-face-grin-beam-sweat::before { - content: "\f583"; } - -.fa-grin-beam-sweat::before { - content: "\f583"; } - -.fa-file-export::before { - content: "\f56e"; } - -.fa-arrow-right-from-file::before { - content: "\f56e"; } - -.fa-shield::before { - content: "\f132"; } - -.fa-shield-blank::before { - content: "\f132"; } - -.fa-arrow-up-short-wide::before { - content: "\f885"; } - -.fa-sort-amount-up-alt::before { - content: "\f885"; } - -.fa-house-medical::before { - content: "\e3b2"; } - -.fa-golf-ball-tee::before { - content: "\f450"; } - -.fa-golf-ball::before { - content: "\f450"; } - -.fa-circle-chevron-left::before { - content: "\f137"; } - -.fa-chevron-circle-left::before { - content: "\f137"; } - -.fa-house-chimney-window::before { - content: "\e00d"; } - -.fa-pen-nib::before { - content: "\f5ad"; } - -.fa-tent-arrow-turn-left::before { - content: "\e580"; } - -.fa-tents::before { - content: "\e582"; } - -.fa-wand-magic::before { - content: "\f0d0"; } - -.fa-magic::before { - content: "\f0d0"; } - -.fa-dog::before { - content: "\f6d3"; } - -.fa-carrot::before { - content: "\f787"; } - -.fa-moon::before { - content: "\f186"; } - -.fa-wine-glass-empty::before { - content: "\f5ce"; } - -.fa-wine-glass-alt::before { - content: "\f5ce"; } - -.fa-cheese::before { - content: "\f7ef"; } - -.fa-yin-yang::before { - content: "\f6ad"; } - -.fa-music::before { - content: "\f001"; } - -.fa-code-commit::before { - content: "\f386"; } - -.fa-temperature-low::before { - content: "\f76b"; } - -.fa-person-biking::before { - content: "\f84a"; } - -.fa-biking::before { - content: "\f84a"; } - -.fa-broom::before { - content: "\f51a"; } - -.fa-shield-heart::before { - content: "\e574"; } - -.fa-gopuram::before { - content: "\f664"; } - -.fa-earth-oceania::before { - content: "\e47b"; } - -.fa-globe-oceania::before { - content: "\e47b"; } - -.fa-square-xmark::before { - content: "\f2d3"; } - -.fa-times-square::before { - content: "\f2d3"; } - -.fa-xmark-square::before { - content: "\f2d3"; } - -.fa-hashtag::before { - content: "\23"; } - -.fa-up-right-and-down-left-from-center::before { - content: "\f424"; } - -.fa-expand-alt::before { - content: "\f424"; } - -.fa-oil-can::before { - content: "\f613"; } - -.fa-t::before { - content: "\54"; } - -.fa-hippo::before { - content: "\f6ed"; } - -.fa-chart-column::before { - content: "\e0e3"; } - -.fa-infinity::before { - content: "\f534"; } - -.fa-vial-circle-check::before { - content: "\e596"; } - -.fa-person-arrow-down-to-line::before { - content: "\e538"; } - -.fa-voicemail::before { - content: "\f897"; } - -.fa-fan::before { - content: "\f863"; } - -.fa-person-walking-luggage::before { - content: "\e554"; } - -.fa-up-down::before { - content: "\f338"; } - -.fa-arrows-alt-v::before { - content: "\f338"; } - -.fa-cloud-moon-rain::before { - content: "\f73c"; } - -.fa-calendar::before { - content: "\f133"; } - -.fa-trailer::before { - content: "\e041"; } - -.fa-bahai::before { - content: "\f666"; } - -.fa-haykal::before { - content: "\f666"; } - -.fa-sd-card::before { - content: "\f7c2"; } - -.fa-dragon::before { - content: "\f6d5"; } - -.fa-shoe-prints::before { - content: "\f54b"; } - -.fa-circle-plus::before { - content: "\f055"; } - -.fa-plus-circle::before { - content: "\f055"; } - -.fa-face-grin-tongue-wink::before { - content: "\f58b"; } - -.fa-grin-tongue-wink::before { - content: "\f58b"; } - -.fa-hand-holding::before { - content: "\f4bd"; } - -.fa-plug-circle-exclamation::before { - content: "\e55d"; } - -.fa-link-slash::before { - content: "\f127"; } - -.fa-chain-broken::before { - content: "\f127"; } - -.fa-chain-slash::before { - content: "\f127"; } - -.fa-unlink::before { - content: "\f127"; } - -.fa-clone::before { - content: "\f24d"; } - -.fa-person-walking-arrow-loop-left::before { - content: "\e551"; } - -.fa-arrow-up-z-a::before { - content: "\f882"; } - -.fa-sort-alpha-up-alt::before { - content: "\f882"; } - -.fa-fire-flame-curved::before { - content: "\f7e4"; } - -.fa-fire-alt::before { - content: "\f7e4"; } - -.fa-tornado::before { - content: "\f76f"; } - -.fa-file-circle-plus::before { - content: "\e494"; } - -.fa-book-quran::before { - content: "\f687"; } - -.fa-quran::before { - content: "\f687"; } - -.fa-anchor::before { - content: "\f13d"; } - -.fa-border-all::before { - content: "\f84c"; } - -.fa-face-angry::before { - content: "\f556"; } - -.fa-angry::before { - content: "\f556"; } - -.fa-cookie-bite::before { - content: "\f564"; } - -.fa-arrow-trend-down::before { - content: "\e097"; } - -.fa-rss::before { - content: "\f09e"; } - -.fa-feed::before { - content: "\f09e"; } - -.fa-draw-polygon::before { - content: "\f5ee"; } - -.fa-scale-balanced::before { - content: "\f24e"; } - -.fa-balance-scale::before { - content: "\f24e"; } - -.fa-gauge-simple-high::before { - content: "\f62a"; } - -.fa-tachometer::before { - content: "\f62a"; } - -.fa-tachometer-fast::before { - content: "\f62a"; } - -.fa-shower::before { - content: "\f2cc"; } - -.fa-desktop::before { - content: "\f390"; } - -.fa-desktop-alt::before { - content: "\f390"; } - -.fa-m::before { - content: "\4d"; } - -.fa-table-list::before { - content: "\f00b"; } - -.fa-th-list::before { - content: "\f00b"; } - -.fa-comment-sms::before { - content: "\f7cd"; } - -.fa-sms::before { - content: "\f7cd"; } - -.fa-book::before { - content: "\f02d"; } - -.fa-user-plus::before { - content: "\f234"; } - -.fa-check::before { - content: "\f00c"; } - -.fa-battery-three-quarters::before { - content: "\f241"; } - -.fa-battery-4::before { - content: "\f241"; } - -.fa-house-circle-check::before { - content: "\e509"; } - -.fa-angle-left::before { - content: "\f104"; } - -.fa-diagram-successor::before { - content: "\e47a"; } - -.fa-truck-arrow-right::before { - content: "\e58b"; } - -.fa-arrows-split-up-and-left::before { - content: "\e4bc"; } - -.fa-hand-fist::before { - content: "\f6de"; } - -.fa-fist-raised::before { - content: "\f6de"; } - -.fa-cloud-moon::before { - content: "\f6c3"; } - -.fa-briefcase::before { - content: "\f0b1"; } - -.fa-person-falling::before { - content: "\e546"; } - -.fa-image-portrait::before { - content: "\f3e0"; } - -.fa-portrait::before { - content: "\f3e0"; } - -.fa-user-tag::before { - content: "\f507"; } - -.fa-rug::before { - content: "\e569"; } - -.fa-earth-europe::before { - content: "\f7a2"; } - -.fa-globe-europe::before { - content: "\f7a2"; } - -.fa-cart-flatbed-suitcase::before { - content: "\f59d"; } - -.fa-luggage-cart::before { - content: "\f59d"; } - -.fa-rectangle-xmark::before { - content: "\f410"; } - -.fa-rectangle-times::before { - content: "\f410"; } - -.fa-times-rectangle::before { - content: "\f410"; } - -.fa-window-close::before { - content: "\f410"; } - -.fa-baht-sign::before { - content: "\e0ac"; } - -.fa-book-open::before { - content: "\f518"; } - -.fa-book-journal-whills::before { - content: "\f66a"; } - -.fa-journal-whills::before { - content: "\f66a"; } - -.fa-handcuffs::before { - content: "\e4f8"; } - -.fa-triangle-exclamation::before { - content: "\f071"; } - -.fa-exclamation-triangle::before { - content: "\f071"; } - -.fa-warning::before { - content: "\f071"; } - -.fa-database::before { - content: "\f1c0"; } - -.fa-share::before { - content: "\f064"; } - -.fa-mail-forward::before { - content: "\f064"; } - -.fa-bottle-droplet::before { - content: "\e4c4"; } - -.fa-mask-face::before { - content: "\e1d7"; } - -.fa-hill-rockslide::before { - content: "\e508"; } - -.fa-right-left::before { - content: "\f362"; } - -.fa-exchange-alt::before { - content: "\f362"; } - -.fa-paper-plane::before { - content: "\f1d8"; } - -.fa-road-circle-exclamation::before { - content: "\e565"; } - -.fa-dungeon::before { - content: "\f6d9"; } - -.fa-align-right::before { - content: "\f038"; } - -.fa-money-bill-1-wave::before { - content: "\f53b"; } - -.fa-money-bill-wave-alt::before { - content: "\f53b"; } - -.fa-life-ring::before { - content: "\f1cd"; } - -.fa-hands::before { - content: "\f2a7"; } - -.fa-sign-language::before { - content: "\f2a7"; } - -.fa-signing::before { - content: "\f2a7"; } - -.fa-calendar-day::before { - content: "\f783"; } - -.fa-water-ladder::before { - content: "\f5c5"; } - -.fa-ladder-water::before { - content: "\f5c5"; } - -.fa-swimming-pool::before { - content: "\f5c5"; } - -.fa-arrows-up-down::before { - content: "\f07d"; } - -.fa-arrows-v::before { - content: "\f07d"; } - -.fa-face-grimace::before { - content: "\f57f"; } - -.fa-grimace::before { - content: "\f57f"; } - -.fa-wheelchair-move::before { - content: "\e2ce"; } - -.fa-wheelchair-alt::before { - content: "\e2ce"; } - -.fa-turn-down::before { - content: "\f3be"; } - -.fa-level-down-alt::before { - content: "\f3be"; } - -.fa-person-walking-arrow-right::before { - content: "\e552"; } - -.fa-square-envelope::before { - content: "\f199"; } - -.fa-envelope-square::before { - content: "\f199"; } - -.fa-dice::before { - content: "\f522"; } - -.fa-bowling-ball::before { - content: "\f436"; } - -.fa-brain::before { - content: "\f5dc"; } - -.fa-bandage::before { - content: "\f462"; } - -.fa-band-aid::before { - content: "\f462"; } - -.fa-calendar-minus::before { - content: "\f272"; } - -.fa-circle-xmark::before { - content: "\f057"; } - -.fa-times-circle::before { - content: "\f057"; } - -.fa-xmark-circle::before { - content: "\f057"; } - -.fa-gifts::before { - content: "\f79c"; } - -.fa-hotel::before { - content: "\f594"; } - -.fa-earth-asia::before { - content: "\f57e"; } - -.fa-globe-asia::before { - content: "\f57e"; } - -.fa-id-card-clip::before { - content: "\f47f"; } - -.fa-id-card-alt::before { - content: "\f47f"; } - -.fa-magnifying-glass-plus::before { - content: "\f00e"; } - -.fa-search-plus::before { - content: "\f00e"; } - -.fa-thumbs-up::before { - content: "\f164"; } - -.fa-user-clock::before { - content: "\f4fd"; } - -.fa-hand-dots::before { - content: "\f461"; } - -.fa-allergies::before { - content: "\f461"; } - -.fa-file-invoice::before { - content: "\f570"; } - -.fa-window-minimize::before { - content: "\f2d1"; } - -.fa-mug-saucer::before { - content: "\f0f4"; } - -.fa-coffee::before { - content: "\f0f4"; } - -.fa-brush::before { - content: "\f55d"; } - -.fa-mask::before { - content: "\f6fa"; } - -.fa-magnifying-glass-minus::before { - content: "\f010"; } - -.fa-search-minus::before { - content: "\f010"; } - -.fa-ruler-vertical::before { - content: "\f548"; } - -.fa-user-large::before { - content: "\f406"; } - -.fa-user-alt::before { - content: "\f406"; } - -.fa-train-tram::before { - content: "\e5b4"; } - -.fa-user-nurse::before { - content: "\f82f"; } - -.fa-syringe::before { - content: "\f48e"; } - -.fa-cloud-sun::before { - content: "\f6c4"; } - -.fa-stopwatch-20::before { - content: "\e06f"; } - -.fa-square-full::before { - content: "\f45c"; } - -.fa-magnet::before { - content: "\f076"; } - -.fa-jar::before { - content: "\e516"; } - -.fa-note-sticky::before { - content: "\f249"; } - -.fa-sticky-note::before { - content: "\f249"; } - -.fa-bug-slash::before { - content: "\e490"; } - -.fa-arrow-up-from-water-pump::before { - content: "\e4b6"; } - -.fa-bone::before { - content: "\f5d7"; } - -.fa-user-injured::before { - content: "\f728"; } - -.fa-face-sad-tear::before { - content: "\f5b4"; } - -.fa-sad-tear::before { - content: "\f5b4"; } - -.fa-plane::before { - content: "\f072"; } - -.fa-tent-arrows-down::before { - content: "\e581"; } - -.fa-exclamation::before { - content: "\21"; } - -.fa-arrows-spin::before { - content: "\e4bb"; } - -.fa-print::before { - content: "\f02f"; } - -.fa-turkish-lira-sign::before { - content: "\e2bb"; } - -.fa-try::before { - content: "\e2bb"; } - -.fa-turkish-lira::before { - content: "\e2bb"; } - -.fa-dollar-sign::before { - content: "\24"; } - -.fa-dollar::before { - content: "\24"; } - -.fa-usd::before { - content: "\24"; } - -.fa-x::before { - content: "\58"; } - -.fa-magnifying-glass-dollar::before { - content: "\f688"; } - -.fa-search-dollar::before { - content: "\f688"; } - -.fa-users-gear::before { - content: "\f509"; } - -.fa-users-cog::before { - content: "\f509"; } - -.fa-person-military-pointing::before { - content: "\e54a"; } - -.fa-building-columns::before { - content: "\f19c"; } - -.fa-bank::before { - content: "\f19c"; } - -.fa-institution::before { - content: "\f19c"; } - -.fa-museum::before { - content: "\f19c"; } - -.fa-university::before { - content: "\f19c"; } - -.fa-umbrella::before { - content: "\f0e9"; } - -.fa-trowel::before { - content: "\e589"; } - -.fa-d::before { - content: "\44"; } - -.fa-stapler::before { - content: "\e5af"; } - -.fa-masks-theater::before { - content: "\f630"; } - -.fa-theater-masks::before { - content: "\f630"; } - -.fa-kip-sign::before { - content: "\e1c4"; } - -.fa-hand-point-left::before { - content: "\f0a5"; } - -.fa-handshake-simple::before { - content: "\f4c6"; } - -.fa-handshake-alt::before { - content: "\f4c6"; } - -.fa-jet-fighter::before { - content: "\f0fb"; } - -.fa-fighter-jet::before { - content: "\f0fb"; } - -.fa-square-share-nodes::before { - content: "\f1e1"; } - -.fa-share-alt-square::before { - content: "\f1e1"; } - -.fa-barcode::before { - content: "\f02a"; } - -.fa-plus-minus::before { - content: "\e43c"; } - -.fa-video::before { - content: "\f03d"; } - -.fa-video-camera::before { - content: "\f03d"; } - -.fa-graduation-cap::before { - content: "\f19d"; } - -.fa-mortar-board::before { - content: "\f19d"; } - -.fa-hand-holding-medical::before { - content: "\e05c"; } - -.fa-person-circle-check::before { - content: "\e53e"; } - -.fa-turn-up::before { - content: "\f3bf"; } - -.fa-level-up-alt::before { - content: "\f3bf"; } - -.sr-only, -.fa-sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; } - -.sr-only-focusable:not(:focus), -.fa-sr-only-focusable:not(:focus) { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; } diff --git a/resources/fontawesome/css/fontawesome.min.css b/resources/fontawesome/css/fontawesome.min.css deleted file mode 100644 index 7e1c254..0000000 --- a/resources/fontawesome/css/fontawesome.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-classic,.fa-regular,.fa-sharp,.fa-solid,.fab,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-classic,.fa-regular,.fa-solid,.far,.fas{font-family:"Font Awesome 6 Free"}.fa-brands,.fab{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,0));transform:rotate(var(--fa-rotate-angle,0))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} - -.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-fill-drip:before{content:"\f576"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-at:before{content:"\40"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-text-height:before{content:"\f034"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-stethoscope:before{content:"\f0f1"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-info:before{content:"\f129"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-explosion:before{content:"\e4e9"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-wave-square:before{content:"\f83e"}.fa-ring:before{content:"\f70b"}.fa-building-un:before{content:"\e4d9"}.fa-dice-three:before{content:"\f527"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-door-open:before{content:"\f52b"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-atom:before{content:"\f5d2"}.fa-soap:before{content:"\e06e"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-pump-medical:before{content:"\e06a"}.fa-fingerprint:before{content:"\f577"}.fa-hand-point-right:before{content:"\f0a4"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-flag-checkered:before{content:"\f11e"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-crop:before{content:"\f125"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-users-rectangle:before{content:"\e594"}.fa-people-roof:before{content:"\e537"}.fa-people-line:before{content:"\e534"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-diagram-predecessor:before{content:"\e477"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-laptop:before{content:"\f109"}.fa-file-csv:before{content:"\f6dd"}.fa-menorah:before{content:"\f676"}.fa-truck-plane:before{content:"\e58f"}.fa-record-vinyl:before{content:"\f8d9"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-bong:before{content:"\f55c"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-jar-wheat:before{content:"\e517"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-pager:before{content:"\f815"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-strikethrough:before{content:"\f0cc"}.fa-k:before{content:"\4b"}.fa-landmark-flag:before{content:"\e51c"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-backward:before{content:"\f04a"}.fa-caret-right:before{content:"\f0da"}.fa-comments:before{content:"\f086"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-code-pull-request:before{content:"\e13c"}.fa-clipboard-list:before{content:"\f46d"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-user-check:before{content:"\f4fc"}.fa-vial-virus:before{content:"\e597"}.fa-sheet-plastic:before{content:"\e571"}.fa-blog:before{content:"\f781"}.fa-user-ninja:before{content:"\f504"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-toggle-off:before{content:"\f204"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-person-drowning:before{content:"\e545"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-spray-can:before{content:"\f5bd"}.fa-truck-monster:before{content:"\f63b"}.fa-w:before{content:"\57"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-rainbow:before{content:"\f75b"}.fa-circle-notch:before{content:"\f1ce"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-paw:before{content:"\f1b0"}.fa-cloud:before{content:"\f0c2"}.fa-trowel-bricks:before{content:"\e58a"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-hospital-user:before{content:"\f80d"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-binoculars:before{content:"\f1e5"}.fa-microphone-slash:before{content:"\f131"}.fa-box-tissue:before{content:"\e05b"}.fa-motorcycle:before{content:"\f21c"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-toilets-portable:before{content:"\e584"}.fa-hockey-puck:before{content:"\f453"}.fa-table:before{content:"\f0ce"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-users-slash:before{content:"\e073"}.fa-clover:before{content:"\e139"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-star-and-crescent:before{content:"\f699"}.fa-house-fire:before{content:"\e50c"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-helicopter:before{content:"\f533"}.fa-compass:before{content:"\f14e"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-file-circle-question:before{content:"\e4ef"}.fa-laptop-code:before{content:"\f5fc"}.fa-swatchbook:before{content:"\f5c3"}.fa-prescription-bottle:before{content:"\f485"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-people-group:before{content:"\e533"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-film:before{content:"\f008"}.fa-ruler-horizontal:before{content:"\f547"}.fa-people-robbery:before{content:"\e536"}.fa-lightbulb:before{content:"\f0eb"}.fa-caret-left:before{content:"\f0d9"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-sitemap:before{content:"\f0e8"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-memory:before{content:"\f538"}.fa-road-spikes:before{content:"\e568"}.fa-fire-burner:before{content:"\e4f1"}.fa-flag:before{content:"\f024"}.fa-hanukiah:before{content:"\f6e6"}.fa-feather:before{content:"\f52d"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-comment-slash:before{content:"\f4b3"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-compress:before{content:"\f066"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-ankh:before{content:"\f644"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-asterisk:before{content:"\2a"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-peseta-sign:before{content:"\e221"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-ghost:before{content:"\f6e2"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-cart-plus:before{content:"\f217"}.fa-gamepad:before{content:"\f11b"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-egg:before{content:"\f7fb"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-campground:before{content:"\f6bb"}.fa-folder-plus:before{content:"\f65e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-lock:before{content:"\f023"}.fa-gas-pump:before{content:"\f52f"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-house-flood-water:before{content:"\e50e"}.fa-tree:before{content:"\f1bb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-sack-dollar:before{content:"\f81d"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-car-side:before{content:"\f5e4"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-microscope:before{content:"\f610"}.fa-sink:before{content:"\e06d"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-mitten:before{content:"\f7b5"}.fa-person-rays:before{content:"\e54d"}.fa-users:before{content:"\f0c0"}.fa-eye-slash:before{content:"\f070"}.fa-flask-vial:before{content:"\e4f3"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-om:before{content:"\f679"}.fa-worm:before{content:"\e599"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-plug:before{content:"\f1e6"}.fa-chevron-up:before{content:"\f077"}.fa-hand-spock:before{content:"\f259"}.fa-stopwatch:before{content:"\f2f2"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-chess-bishop:before{content:"\f43a"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-road-circle-check:before{content:"\e564"}.fa-dice-five:before{content:"\f523"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-land-mine-on:before{content:"\e51b"}.fa-i-cursor:before{content:"\f246"}.fa-stamp:before{content:"\f5bf"}.fa-stairs:before{content:"\e289"}.fa-i:before{content:"\49"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-pills:before{content:"\f484"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-tooth:before{content:"\f5c9"}.fa-v:before{content:"\56"}.fa-bangladeshi-taka-sign:before{content:"\e2e6"}.fa-bicycle:before{content:"\f206"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-snowman:before{content:"\f7d0"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-road-barrier:before{content:"\e562"}.fa-school:before{content:"\f549"}.fa-igloo:before{content:"\f7ae"}.fa-joint:before{content:"\f595"}.fa-angle-right:before{content:"\f105"}.fa-horse:before{content:"\f6f0"}.fa-q:before{content:"\51"}.fa-g:before{content:"\47"}.fa-notes-medical:before{content:"\f481"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-dong-sign:before{content:"\e169"}.fa-capsules:before{content:"\f46b"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-hand-point-up:before{content:"\f0a6"}.fa-money-bill:before{content:"\f0d6"}.fa-bookmark:before{content:"\f02e"}.fa-align-justify:before{content:"\f039"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-helmet-un:before{content:"\e503"}.fa-bullseye:before{content:"\f140"}.fa-bacon:before{content:"\f7e5"}.fa-hand-point-down:before{content:"\f0a7"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-radiation:before{content:"\f7b9"}.fa-chart-simple:before{content:"\e473"}.fa-mars-stroke:before{content:"\f229"}.fa-vial:before{content:"\f492"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-e:before{content:"\45"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-user:before{content:"\f007"}.fa-school-circle-check:before{content:"\e56b"}.fa-dumpster:before{content:"\f793"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-building-user:before{content:"\e4da"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-highlighter:before{content:"\f591"}.fa-key:before{content:"\f084"}.fa-bullhorn:before{content:"\f0a1"}.fa-globe:before{content:"\f0ac"}.fa-synagogue:before{content:"\f69b"}.fa-person-half-dress:before{content:"\e548"}.fa-road-bridge:before{content:"\e563"}.fa-location-arrow:before{content:"\f124"}.fa-c:before{content:"\43"}.fa-tablet-button:before{content:"\f10a"}.fa-building-lock:before{content:"\e4d6"}.fa-pizza-slice:before{content:"\f818"}.fa-money-bill-wave:before{content:"\f53a"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-house-flag:before{content:"\e50d"}.fa-person-circle-minus:before{content:"\e540"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-camera-rotate:before{content:"\e0d8"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-star:before{content:"\f005"}.fa-repeat:before{content:"\f363"}.fa-cross:before{content:"\f654"}.fa-box:before{content:"\f466"}.fa-venus-mars:before{content:"\f228"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-charging-station:before{content:"\f5e7"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-mobile-retro:before{content:"\e527"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-spider:before{content:"\f717"}.fa-hands-bound:before{content:"\e4f9"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-x-ray:before{content:"\f497"}.fa-spell-check:before{content:"\f891"}.fa-slash:before{content:"\f715"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-server:before{content:"\f233"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-shop-lock:before{content:"\e4a5"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-blender-phone:before{content:"\f6b6"}.fa-building-wheat:before{content:"\e4db"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-venus:before{content:"\f221"}.fa-passport:before{content:"\f5ab"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-temperature-high:before{content:"\f769"}.fa-microchip:before{content:"\f2db"}.fa-crown:before{content:"\f521"}.fa-weight-hanging:before{content:"\f5cd"}.fa-xmarks-lines:before{content:"\e59a"}.fa-file-prescription:before{content:"\f572"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-chess-knight:before{content:"\f441"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-wheelchair:before{content:"\f193"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-toggle-on:before{content:"\f205"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-l:before{content:"\4c"}.fa-fire:before{content:"\f06d"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-folder-open:before{content:"\f07c"}.fa-heart-circle-plus:before{content:"\e500"}.fa-code-fork:before{content:"\e13b"}.fa-city:before{content:"\f64f"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-pepper-hot:before{content:"\f816"}.fa-unlock:before{content:"\f09c"}.fa-colon-sign:before{content:"\e140"}.fa-headset:before{content:"\f590"}.fa-store-slash:before{content:"\e071"}.fa-road-circle-xmark:before{content:"\e566"}.fa-user-minus:before{content:"\f503"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-clipboard:before{content:"\f328"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-underline:before{content:"\f0cd"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-signature:before{content:"\f5b7"}.fa-stroopwafel:before{content:"\f551"}.fa-bold:before{content:"\f032"}.fa-anchor-lock:before{content:"\e4ad"}.fa-building-ngo:before{content:"\e4d7"}.fa-manat-sign:before{content:"\e1d5"}.fa-not-equal:before{content:"\f53e"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-jedi:before{content:"\f669"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-mug-hot:before{content:"\f7b6"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-gift:before{content:"\f06b"}.fa-dice-two:before{content:"\f528"}.fa-chess-queen:before{content:"\f445"}.fa-glasses:before{content:"\f530"}.fa-chess-board:before{content:"\f43c"}.fa-building-circle-check:before{content:"\e4d2"}.fa-person-chalkboard:before{content:"\e53d"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-less-than-equal:before{content:"\f537"}.fa-train:before{content:"\f238"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-crow:before{content:"\f520"}.fa-sailboat:before{content:"\e445"}.fa-window-restore:before{content:"\f2d2"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-torii-gate:before{content:"\f6a1"}.fa-frog:before{content:"\f52e"}.fa-bucket:before{content:"\e4cf"}.fa-image:before{content:"\f03e"}.fa-microphone:before{content:"\f130"}.fa-cow:before{content:"\f6c8"}.fa-caret-up:before{content:"\f0d8"}.fa-screwdriver:before{content:"\f54a"}.fa-folder-closed:before{content:"\e185"}.fa-house-tsunami:before{content:"\e515"}.fa-square-nfi:before{content:"\e576"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-lemon:before{content:"\f094"}.fa-head-side-mask:before{content:"\e063"}.fa-handshake:before{content:"\f2b5"}.fa-gem:before{content:"\f3a5"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-smoking:before{content:"\f48d"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-monument:before{content:"\f5a6"}.fa-snowplow:before{content:"\f7d2"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-cannabis:before{content:"\f55f"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-tablets:before{content:"\f490"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-chair:before{content:"\f6c0"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-plate-wheat:before{content:"\e55a"}.fa-icicles:before{content:"\f7ad"}.fa-person-shelter:before{content:"\e54f"}.fa-neuter:before{content:"\f22c"}.fa-id-badge:before{content:"\f2c1"}.fa-marker:before{content:"\f5a1"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-helicopter-symbol:before{content:"\e502"}.fa-universal-access:before{content:"\f29a"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-lari-sign:before{content:"\e1c8"}.fa-volcano:before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-viruses:before{content:"\e076"}.fa-square-person-confined:before{content:"\e577"}.fa-user-tie:before{content:"\f508"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-certificate:before{content:"\f0a3"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-suitcase:before{content:"\f0f2"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-camera-retro:before{content:"\f083"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-box-open:before{content:"\f49e"}.fa-scroll:before{content:"\f70e"}.fa-spa:before{content:"\f5bb"}.fa-location-pin-lock:before{content:"\e51f"}.fa-pause:before{content:"\f04c"}.fa-hill-avalanche:before{content:"\e507"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-bomb:before{content:"\f1e2"}.fa-registered:before{content:"\f25d"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-subscript:before{content:"\f12c"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-burst:before{content:"\e4dc"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-money-bills:before{content:"\e1f3"}.fa-smog:before{content:"\f75f"}.fa-crutch:before{content:"\f7f7"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-palette:before{content:"\f53f"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-vest:before{content:"\e085"}.fa-ferry:before{content:"\e4ea"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-boxes-packing:before{content:"\e4c7"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-bowl-food:before{content:"\e4c6"}.fa-candy-cane:before{content:"\f786"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-file-word:before{content:"\f1c2"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-house-lock:before{content:"\e510"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-children:before{content:"\e4e1"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-envelope-open:before{content:"\f2b6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-mattress-pillow:before{content:"\e525"}.fa-guarani-sign:before{content:"\e19a"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-fire-extinguisher:before{content:"\f134"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-greater-than-equal:before{content:"\f532"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-virus:before{content:"\e074"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-layer-group:before{content:"\f5fd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-archway:before{content:"\f557"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-square:before{content:"\f0c8"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-couch:before{content:"\f4b8"}.fa-cedi-sign:before{content:"\e0df"}.fa-italic:before{content:"\f033"}.fa-table-cells-column-lock:before{content:"\e678"}.fa-church:before{content:"\f51d"}.fa-comments-dollar:before{content:"\f653"}.fa-democrat:before{content:"\f747"}.fa-z:before{content:"\5a"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-road-lock:before{content:"\e567"}.fa-a:before{content:"\41"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-p:before{content:"\50"}.fa-snowflake:before{content:"\f2dc"}.fa-newspaper:before{content:"\f1ea"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-locust:before{content:"\e520"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-person-dress-burst:before{content:"\e544"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-vector-square:before{content:"\f5cb"}.fa-bread-slice:before{content:"\f7ec"}.fa-language:before{content:"\f1ab"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-filter:before{content:"\f0b0"}.fa-question:before{content:"\3f"}.fa-file-signature:before{content:"\f573"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-house-chimney-user:before{content:"\e065"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-puzzle-piece:before{content:"\f12e"}.fa-money-check:before{content:"\f53c"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-code:before{content:"\f121"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-virus-covid:before{content:"\e4a8"}.fa-austral-sign:before{content:"\e0a9"}.fa-f:before{content:"\46"}.fa-leaf:before{content:"\f06c"}.fa-road:before{content:"\f018"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-person-circle-plus:before{content:"\e541"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-sack-xmark:before{content:"\e56a"}.fa-file-excel:before{content:"\f1c3"}.fa-file-contract:before{content:"\f56c"}.fa-fish-fins:before{content:"\e4f2"}.fa-building-flag:before{content:"\e4d5"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-object-ungroup:before{content:"\f248"}.fa-poop:before{content:"\f619"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-kaaba:before{content:"\f66b"}.fa-toilet-paper:before{content:"\f71e"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-eject:before{content:"\f052"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-plane-circle-check:before{content:"\e555"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-object-group:before{content:"\f247"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-mask-ventilator:before{content:"\e524"}.fa-arrow-right:before{content:"\f061"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-cash-register:before{content:"\f788"}.fa-person-circle-question:before{content:"\e542"}.fa-h:before{content:"\48"}.fa-tarp:before{content:"\e57b"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-heart:before{content:"\f004"}.fa-mars-and-venus:before{content:"\f224"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-dumpster-fire:before{content:"\f794"}.fa-house-crack:before{content:"\e3b1"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-bottle-water:before{content:"\e4c5"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-kitchen-set:before{content:"\e51a"}.fa-r:before{content:"\52"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-cube:before{content:"\f1b2"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-shield-dog:before{content:"\e573"}.fa-solar-panel:before{content:"\f5ba"}.fa-lock-open:before{content:"\f3c1"}.fa-elevator:before{content:"\e16d"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-circle:before{content:"\f111"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-recycle:before{content:"\f1b8"}.fa-user-astronaut:before{content:"\f4fb"}.fa-plane-slash:before{content:"\e069"}.fa-trademark:before{content:"\f25c"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-satellite-dish:before{content:"\f7c0"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-users-rays:before{content:"\e593"}.fa-wallet:before{content:"\f555"}.fa-clipboard-check:before{content:"\f46c"}.fa-file-audio:before{content:"\f1c7"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-wrench:before{content:"\f0ad"}.fa-bugs:before{content:"\e4d0"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-file-image:before{content:"\f1c5"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-plane-departure:before{content:"\f5b0"}.fa-handshake-slash:before{content:"\e060"}.fa-book-bookmark:before{content:"\e0bb"}.fa-code-branch:before{content:"\f126"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-bridge:before{content:"\e4c8"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-truck-front:before{content:"\e2b7"}.fa-cat:before{content:"\f6be"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-truck-field:before{content:"\e58d"}.fa-route:before{content:"\f4d7"}.fa-clipboard-question:before{content:"\e4e3"}.fa-panorama:before{content:"\e209"}.fa-comment-medical:before{content:"\f7f5"}.fa-teeth-open:before{content:"\f62f"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-tags:before{content:"\f02c"}.fa-wine-glass:before{content:"\f4e3"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-house-signal:before{content:"\e012"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-faucet-drip:before{content:"\e006"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-terminal:before{content:"\f120"}.fa-mobile-button:before{content:"\f10b"}.fa-house-medical-flag:before{content:"\e514"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-tape:before{content:"\f4db"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-eye:before{content:"\f06e"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-audio-description:before{content:"\f29e"}.fa-person-military-to-person:before{content:"\e54c"}.fa-file-shield:before{content:"\e4f0"}.fa-user-slash:before{content:"\f506"}.fa-pen:before{content:"\f304"}.fa-tower-observation:before{content:"\e586"}.fa-file-code:before{content:"\f1c9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-bus:before{content:"\f207"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-window-maximize:before{content:"\f2d0"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-prescription:before{content:"\f5b1"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-vihara:before{content:"\f6a7"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-plant-wilt:before{content:"\e5aa"}.fa-diamond:before{content:"\f219"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-bacterium:before{content:"\e05a"}.fa-hand-pointer:before{content:"\f25a"}.fa-drum-steelpan:before{content:"\f56a"}.fa-hand-scissors:before{content:"\f257"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-biohazard:before{content:"\f780"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-mars-double:before{content:"\f227"}.fa-child-dress:before{content:"\e59c"}.fa-users-between-lines:before{content:"\e591"}.fa-lungs-virus:before{content:"\e067"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-phone:before{content:"\f095"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-child-reaching:before{content:"\e59d"}.fa-head-side-virus:before{content:"\e064"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-door-closed:before{content:"\f52a"}.fa-shield-virus:before{content:"\e06c"}.fa-dice-six:before{content:"\f526"}.fa-mosquito-net:before{content:"\e52c"}.fa-bridge-water:before{content:"\e4ce"}.fa-person-booth:before{content:"\f756"}.fa-text-width:before{content:"\f035"}.fa-hat-wizard:before{content:"\f6e8"}.fa-pen-fancy:before{content:"\f5ac"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-trash:before{content:"\f1f8"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-book-medical:before{content:"\f7e6"}.fa-poo:before{content:"\f2fe"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-cubes:before{content:"\f1b3"}.fa-divide:before{content:"\f529"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-headphones:before{content:"\f025"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-clapping:before{content:"\e1a8"}.fa-republican:before{content:"\f75e"}.fa-arrow-left:before{content:"\f060"}.fa-person-circle-xmark:before{content:"\e543"}.fa-ruler:before{content:"\f545"}.fa-align-left:before{content:"\f036"}.fa-dice-d6:before{content:"\f6d1"}.fa-restroom:before{content:"\f7bd"}.fa-j:before{content:"\4a"}.fa-users-viewfinder:before{content:"\e595"}.fa-file-video:before{content:"\f1c8"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-file-pdf:before{content:"\f1c1"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-o:before{content:"\4f"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-user-secret:before{content:"\f21b"}.fa-otter:before{content:"\f700"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-comment-dollar:before{content:"\f651"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-clipboard-user:before{content:"\f7f3"}.fa-child:before{content:"\f1ae"}.fa-lira-sign:before{content:"\f195"}.fa-satellite:before{content:"\f7bf"}.fa-plane-lock:before{content:"\e558"}.fa-tag:before{content:"\f02b"}.fa-comment:before{content:"\f075"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-envelope:before{content:"\f0e0"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-paperclip:before{content:"\f0c6"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-ribbon:before{content:"\f4d6"}.fa-lungs:before{content:"\f604"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-border-none:before{content:"\f850"}.fa-circle-nodes:before{content:"\e4e2"}.fa-parachute-box:before{content:"\f4cd"}.fa-indent:before{content:"\f03c"}.fa-truck-field-un:before{content:"\e58e"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-mountain:before{content:"\f6fc"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-cloud-meatball:before{content:"\f73b"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-square-virus:before{content:"\e578"}.fa-meteor:before{content:"\f753"}.fa-car-on:before{content:"\e4dd"}.fa-sleigh:before{content:"\f7cc"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-water:before{content:"\f773"}.fa-calendar-check:before{content:"\f274"}.fa-braille:before{content:"\f2a1"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-landmark:before{content:"\f66f"}.fa-truck:before{content:"\f0d1"}.fa-crosshairs:before{content:"\f05b"}.fa-person-cane:before{content:"\e53c"}.fa-tent:before{content:"\e57d"}.fa-vest-patches:before{content:"\e086"}.fa-check-double:before{content:"\f560"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-cookie:before{content:"\f563"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-dumbbell:before{content:"\f44b"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-tarp-droplet:before{content:"\e57c"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-calendar-plus:before{content:"\f271"}.fa-plane-arrival:before{content:"\f5af"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-chart-gantt:before{content:"\e0e4"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-dna:before{content:"\f471"}.fa-virus-slash:before{content:"\e075"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-chess:before{content:"\f439"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-plug-circle-check:before{content:"\e55c"}.fa-street-view:before{content:"\f21d"}.fa-franc-sign:before{content:"\e18f"}.fa-volume-off:before{content:"\f026"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-star-of-david:before{content:"\f69a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-vials:before{content:"\f493"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-place-of-worship:before{content:"\f67f"}.fa-grip-vertical:before{content:"\f58e"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-u:before{content:"\55"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-pallet:before{content:"\f482"}.fa-faucet:before{content:"\e005"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-s:before{content:"\53"}.fa-timeline:before{content:"\e29c"}.fa-keyboard:before{content:"\f11c"}.fa-caret-down:before{content:"\f0d7"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-plane-up:before{content:"\e22d"}.fa-piggy-bank:before{content:"\f4d3"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-mountain-city:before{content:"\e52e"}.fa-coins:before{content:"\f51e"}.fa-khanda:before{content:"\f66d"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-folder-tree:before{content:"\f802"}.fa-network-wired:before{content:"\f6ff"}.fa-map-pin:before{content:"\f276"}.fa-hamsa:before{content:"\f665"}.fa-cent-sign:before{content:"\e3f5"}.fa-flask:before{content:"\f0c3"}.fa-person-pregnant:before{content:"\e31e"}.fa-wand-sparkles:before{content:"\f72b"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-ticket:before{content:"\f145"}.fa-power-off:before{content:"\f011"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-flag-usa:before{content:"\f74d"}.fa-laptop-file:before{content:"\e51d"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-diagram-next:before{content:"\e476"}.fa-person-rifle:before{content:"\e54e"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-closed-captioning:before{content:"\f20a"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-venus-double:before{content:"\f226"}.fa-images:before{content:"\f302"}.fa-calculator:before{content:"\f1ec"}.fa-people-pulling:before{content:"\e535"}.fa-n:before{content:"\4e"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-cloud-rain:before{content:"\f73d"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-ship:before{content:"\f21a"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-download:before{content:"\f019"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-file-circle-check:before{content:"\e5a0"}.fa-forward:before{content:"\f04e"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-align-center:before{content:"\f037"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-calendar-week:before{content:"\f784"}.fa-laptop-medical:before{content:"\f812"}.fa-b:before{content:"\42"}.fa-file-medical:before{content:"\f477"}.fa-dice-one:before{content:"\f525"}.fa-kiwi-bird:before{content:"\f535"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-mill-sign:before{content:"\e1ed"}.fa-bowl-rice:before{content:"\e2eb"}.fa-skull:before{content:"\f54c"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-truck-pickup:before{content:"\f63c"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-stop:before{content:"\f04d"}.fa-code-merge:before{content:"\f387"}.fa-upload:before{content:"\f093"}.fa-hurricane:before{content:"\f751"}.fa-mound:before{content:"\e52d"}.fa-toilet-portable:before{content:"\e583"}.fa-compact-disc:before{content:"\f51f"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-caravan:before{content:"\f8ff"}.fa-shield-cat:before{content:"\e572"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-glass-water:before{content:"\e4f4"}.fa-oil-well:before{content:"\e532"}.fa-vault:before{content:"\e2c5"}.fa-mars:before{content:"\f222"}.fa-toilet:before{content:"\f7d8"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-sun:before{content:"\f185"}.fa-guitar:before{content:"\f7a6"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-horse-head:before{content:"\f7ab"}.fa-bore-hole:before{content:"\e4c3"}.fa-industry:before{content:"\f275"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-florin-sign:before{content:"\e184"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-less-than:before{content:"\3c"}.fa-angle-down:before{content:"\f107"}.fa-car-tunnel:before{content:"\e4de"}.fa-head-side-cough:before{content:"\e061"}.fa-grip-lines:before{content:"\f7a4"}.fa-thumbs-down:before{content:"\f165"}.fa-user-lock:before{content:"\f502"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-chess-pawn:before{content:"\f443"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-person-through-window:before{content:"\e5a9"}.fa-toolbox:before{content:"\f552"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-bug:before{content:"\f188"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-mountain-sun:before{content:"\e52f"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-dice-d20:before{content:"\f6cf"}.fa-truck-droplet:before{content:"\e58c"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-medal:before{content:"\f5a2"}.fa-bed:before{content:"\f236"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-podcast:before{content:"\f2ce"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-bell:before{content:"\f0f3"}.fa-superscript:before{content:"\f12b"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-star-of-life:before{content:"\f621"}.fa-phone-slash:before{content:"\f3dd"}.fa-paint-roller:before{content:"\f5aa"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-file:before{content:"\f15b"}.fa-greater-than:before{content:"\3e"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-arrow-down:before{content:"\f063"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-eraser:before{content:"\f12d"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-person-burst:before{content:"\e53b"}.fa-dove:before{content:"\f4ba"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-socks:before{content:"\f696"}.fa-inbox:before{content:"\f01c"}.fa-section:before{content:"\e447"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-envelope-open-text:before{content:"\f658"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-wine-bottle:before{content:"\f72f"}.fa-chess-rook:before{content:"\f447"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-dharmachakra:before{content:"\f655"}.fa-hotdog:before{content:"\f80f"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-drum:before{content:"\f569"}.fa-ice-cream:before{content:"\f810"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-fax:before{content:"\f1ac"}.fa-paragraph:before{content:"\f1dd"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-star-half:before{content:"\f089"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-tree-city:before{content:"\e587"}.fa-play:before{content:"\f04b"}.fa-font:before{content:"\f031"}.fa-table-cells-row-lock:before{content:"\e67a"}.fa-rupiah-sign:before{content:"\e23d"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-naira-sign:before{content:"\e1f6"}.fa-cart-arrow-down:before{content:"\f218"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-receipt:before{content:"\f543"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-chevron-down:before{content:"\f078"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-skull-crossbones:before{content:"\f714"}.fa-code-compare:before{content:"\e13a"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-school-lock:before{content:"\e56f"}.fa-tower-cell:before{content:"\e585"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-ranking-star:before{content:"\e561"}.fa-chess-king:before{content:"\f43f"}.fa-person-harassing:before{content:"\e549"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-arrow-up:before{content:"\f062"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-shrimp:before{content:"\e448"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-jug-detergent:before{content:"\e519"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-user-shield:before{content:"\f505"}.fa-wind:before{content:"\f72e"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-y:before{content:"\59"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-fish:before{content:"\f578"}.fa-user-graduate:before{content:"\f501"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-clapperboard:before{content:"\e131"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-jet-fighter-up:before{content:"\e518"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-copy:before{content:"\f0c5"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-hand-sparkles:before{content:"\e05d"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-child-combatant:before,.fa-child-rifle:before{content:"\e4e0"}.fa-gun:before{content:"\e19b"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-expand:before{content:"\f065"}.fa-computer:before{content:"\e4e5"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-peso-sign:before{content:"\e222"}.fa-building-shield:before{content:"\e4d8"}.fa-baby:before{content:"\f77c"}.fa-users-line:before{content:"\e592"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-tractor:before{content:"\f722"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-lines-leaning:before{content:"\e51e"}.fa-ruler-combined:before{content:"\f546"}.fa-copyright:before{content:"\f1f9"}.fa-equals:before{content:"\3d"}.fa-blender:before{content:"\f517"}.fa-teeth:before{content:"\f62e"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-map:before{content:"\f279"}.fa-rocket:before{content:"\f135"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-folder-minus:before{content:"\f65d"}.fa-store:before{content:"\f54e"}.fa-arrow-trend-up:before{content:"\e098"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-bezier-curve:before{content:"\f55b"}.fa-bell-slash:before{content:"\f1f6"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-school-flag:before{content:"\e56e"}.fa-fill:before{content:"\f575"}.fa-angle-up:before{content:"\f106"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-holly-berry:before{content:"\f7aa"}.fa-chevron-left:before{content:"\f053"}.fa-bacteria:before{content:"\e059"}.fa-hand-lizard:before{content:"\f258"}.fa-notdef:before{content:"\e1fe"}.fa-disease:before{content:"\f7fa"}.fa-briefcase-medical:before{content:"\f469"}.fa-genderless:before{content:"\f22d"}.fa-chevron-right:before{content:"\f054"}.fa-retweet:before{content:"\f079"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-pump-soap:before{content:"\e06b"}.fa-video-slash:before{content:"\f4e2"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-radio:before{content:"\f8d7"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-traffic-light:before{content:"\f637"}.fa-thermometer:before{content:"\f491"}.fa-vr-cardboard:before{content:"\f729"}.fa-hand-middle-finger:before{content:"\f806"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-truck-moving:before{content:"\f4df"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-display:before{content:"\e163"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-trophy:before{content:"\f091"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-hammer:before{content:"\f6e3"}.fa-hand-peace:before{content:"\f25b"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-spinner:before{content:"\f110"}.fa-robot:before{content:"\f544"}.fa-peace:before{content:"\f67c"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-warehouse:before{content:"\f494"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-splotch:before{content:"\f5bc"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-dice-four:before{content:"\f524"}.fa-sim-card:before{content:"\f7c4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-mercury:before{content:"\f223"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-person-falling-burst:before{content:"\e547"}.fa-award:before{content:"\f559"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-building:before{content:"\f1ad"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-qrcode:before{content:"\f029"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-house-medical:before{content:"\e3b2"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-house-chimney-window:before{content:"\e00d"}.fa-pen-nib:before{content:"\f5ad"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tents:before{content:"\e582"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-dog:before{content:"\f6d3"}.fa-carrot:before{content:"\f787"}.fa-moon:before{content:"\f186"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-cheese:before{content:"\f7ef"}.fa-yin-yang:before{content:"\f6ad"}.fa-music:before{content:"\f001"}.fa-code-commit:before{content:"\f386"}.fa-temperature-low:before{content:"\f76b"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-broom:before{content:"\f51a"}.fa-shield-heart:before{content:"\e574"}.fa-gopuram:before{content:"\f664"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-hashtag:before{content:"\23"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-oil-can:before{content:"\f613"}.fa-t:before{content:"\54"}.fa-hippo:before{content:"\f6ed"}.fa-chart-column:before{content:"\e0e3"}.fa-infinity:before{content:"\f534"}.fa-vial-circle-check:before{content:"\e596"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-voicemail:before{content:"\f897"}.fa-fan:before{content:"\f863"}.fa-person-walking-luggage:before{content:"\e554"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-calendar:before{content:"\f133"}.fa-trailer:before{content:"\e041"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-sd-card:before{content:"\f7c2"}.fa-dragon:before{content:"\f6d5"}.fa-shoe-prints:before{content:"\f54b"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-hand-holding:before{content:"\f4bd"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-clone:before{content:"\f24d"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-tornado:before{content:"\f76f"}.fa-file-circle-plus:before{content:"\e494"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-anchor:before{content:"\f13d"}.fa-border-all:before{content:"\f84c"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-cookie-bite:before{content:"\f564"}.fa-arrow-trend-down:before{content:"\e097"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-draw-polygon:before{content:"\f5ee"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-shower:before{content:"\f2cc"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-m:before{content:"\4d"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-book:before{content:"\f02d"}.fa-user-plus:before{content:"\f234"}.fa-check:before{content:"\f00c"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-house-circle-check:before{content:"\e509"}.fa-angle-left:before{content:"\f104"}.fa-diagram-successor:before{content:"\e47a"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-cloud-moon:before{content:"\f6c3"}.fa-briefcase:before{content:"\f0b1"}.fa-person-falling:before{content:"\e546"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-user-tag:before{content:"\f507"}.fa-rug:before{content:"\e569"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-baht-sign:before{content:"\e0ac"}.fa-book-open:before{content:"\f518"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-handcuffs:before{content:"\e4f8"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-database:before{content:"\f1c0"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-mask-face:before{content:"\e1d7"}.fa-hill-rockslide:before{content:"\e508"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-paper-plane:before{content:"\f1d8"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-dungeon:before{content:"\f6d9"}.fa-align-right:before{content:"\f038"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-life-ring:before{content:"\f1cd"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-calendar-day:before{content:"\f783"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-dice:before{content:"\f522"}.fa-bowling-ball:before{content:"\f436"}.fa-brain:before{content:"\f5dc"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-calendar-minus:before{content:"\f272"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-gifts:before{content:"\f79c"}.fa-hotel:before{content:"\f594"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-thumbs-up:before{content:"\f164"}.fa-user-clock:before{content:"\f4fd"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-file-invoice:before{content:"\f570"}.fa-window-minimize:before{content:"\f2d1"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-brush:before{content:"\f55d"}.fa-mask:before{content:"\f6fa"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-ruler-vertical:before{content:"\f548"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-train-tram:before{content:"\e5b4"}.fa-user-nurse:before{content:"\f82f"}.fa-syringe:before{content:"\f48e"}.fa-cloud-sun:before{content:"\f6c4"}.fa-stopwatch-20:before{content:"\e06f"}.fa-square-full:before{content:"\f45c"}.fa-magnet:before{content:"\f076"}.fa-jar:before{content:"\e516"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-bug-slash:before{content:"\e490"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-bone:before{content:"\f5d7"}.fa-user-injured:before{content:"\f728"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-plane:before{content:"\f072"}.fa-tent-arrows-down:before{content:"\e581"}.fa-exclamation:before{content:"\21"}.fa-arrows-spin:before{content:"\e4bb"}.fa-print:before{content:"\f02f"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-x:before{content:"\58"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-person-military-pointing:before{content:"\e54a"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-umbrella:before{content:"\f0e9"}.fa-trowel:before{content:"\e589"}.fa-d:before{content:"\44"}.fa-stapler:before{content:"\e5af"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-kip-sign:before{content:"\e1c4"}.fa-hand-point-left:before{content:"\f0a5"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-barcode:before{content:"\f02a"}.fa-plus-minus:before{content:"\e43c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-person-circle-check:before{content:"\e53e"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"} -.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0} \ No newline at end of file diff --git a/resources/fontawesome/css/regular.css b/resources/fontawesome/css/regular.css deleted file mode 100644 index dfb7e76..0000000 --- a/resources/fontawesome/css/regular.css +++ /dev/null @@ -1,19 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -:root, :host { - --fa-style-family-classic: 'Font Awesome 6 Free'; - --fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free'; } - -@font-face { - font-family: 'Font Awesome 6 Free'; - font-style: normal; - font-weight: 400; - font-display: block; - src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } - -.far, -.fa-regular { - font-weight: 400; } diff --git a/resources/fontawesome/css/regular.min.css b/resources/fontawesome/css/regular.min.css deleted file mode 100644 index 7f1cb00..0000000 --- a/resources/fontawesome/css/regular.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400} \ No newline at end of file diff --git a/resources/fontawesome/css/solid.css b/resources/fontawesome/css/solid.css deleted file mode 100644 index 3897c23..0000000 --- a/resources/fontawesome/css/solid.css +++ /dev/null @@ -1,19 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -:root, :host { - --fa-style-family-classic: 'Font Awesome 6 Free'; - --fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; } - -@font-face { - font-family: 'Font Awesome 6 Free'; - font-style: normal; - font-weight: 900; - font-display: block; - src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } - -.fas, -.fa-solid { - font-weight: 900; } diff --git a/resources/fontawesome/css/solid.min.css b/resources/fontawesome/css/solid.min.css deleted file mode 100644 index e7d97d2..0000000 --- a/resources/fontawesome/css/solid.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900} \ No newline at end of file diff --git a/resources/fontawesome/css/svg-with-js.css b/resources/fontawesome/css/svg-with-js.css deleted file mode 100644 index 85b8e6d..0000000 --- a/resources/fontawesome/css/svg-with-js.css +++ /dev/null @@ -1,640 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -:root, :host { - --fa-font-solid: normal 900 1em/1 'Font Awesome 6 Solid'; - --fa-font-regular: normal 400 1em/1 'Font Awesome 6 Regular'; - --fa-font-light: normal 300 1em/1 'Font Awesome 6 Light'; - --fa-font-thin: normal 100 1em/1 'Font Awesome 6 Thin'; - --fa-font-duotone: normal 900 1em/1 'Font Awesome 6 Duotone'; - --fa-font-sharp-solid: normal 900 1em/1 'Font Awesome 6 Sharp'; - --fa-font-sharp-regular: normal 400 1em/1 'Font Awesome 6 Sharp'; - --fa-font-sharp-light: normal 300 1em/1 'Font Awesome 6 Sharp'; - --fa-font-sharp-thin: normal 100 1em/1 'Font Awesome 6 Sharp'; - --fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; } - -svg:not(:root).svg-inline--fa, svg:not(:host).svg-inline--fa { - overflow: visible; - box-sizing: content-box; } - -.svg-inline--fa { - display: var(--fa-display, inline-block); - height: 1em; - overflow: visible; - vertical-align: -.125em; } - .svg-inline--fa.fa-2xs { - vertical-align: 0.1em; } - .svg-inline--fa.fa-xs { - vertical-align: 0em; } - .svg-inline--fa.fa-sm { - vertical-align: -0.07143em; } - .svg-inline--fa.fa-lg { - vertical-align: -0.2em; } - .svg-inline--fa.fa-xl { - vertical-align: -0.25em; } - .svg-inline--fa.fa-2xl { - vertical-align: -0.3125em; } - .svg-inline--fa.fa-pull-left { - margin-right: var(--fa-pull-margin, 0.3em); - width: auto; } - .svg-inline--fa.fa-pull-right { - margin-left: var(--fa-pull-margin, 0.3em); - width: auto; } - .svg-inline--fa.fa-li { - width: var(--fa-li-width, 2em); - top: 0.25em; } - .svg-inline--fa.fa-fw { - width: var(--fa-fw-width, 1.25em); } - -.fa-layers svg.svg-inline--fa { - bottom: 0; - left: 0; - margin: auto; - position: absolute; - right: 0; - top: 0; } - -.fa-layers-text, .fa-layers-counter { - display: inline-block; - position: absolute; - text-align: center; } - -.fa-layers { - display: inline-block; - height: 1em; - position: relative; - text-align: center; - vertical-align: -.125em; - width: 1em; } - .fa-layers svg.svg-inline--fa { - -webkit-transform-origin: center center; - transform-origin: center center; } - -.fa-layers-text { - left: 50%; - top: 50%; - -webkit-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); - -webkit-transform-origin: center center; - transform-origin: center center; } - -.fa-layers-counter { - background-color: var(--fa-counter-background-color, #ff253a); - border-radius: var(--fa-counter-border-radius, 1em); - box-sizing: border-box; - color: var(--fa-inverse, #fff); - line-height: var(--fa-counter-line-height, 1); - max-width: var(--fa-counter-max-width, 5em); - min-width: var(--fa-counter-min-width, 1.5em); - overflow: hidden; - padding: var(--fa-counter-padding, 0.25em 0.5em); - right: var(--fa-right, 0); - text-overflow: ellipsis; - top: var(--fa-top, 0); - -webkit-transform: scale(var(--fa-counter-scale, 0.25)); - transform: scale(var(--fa-counter-scale, 0.25)); - -webkit-transform-origin: top right; - transform-origin: top right; } - -.fa-layers-bottom-right { - bottom: var(--fa-bottom, 0); - right: var(--fa-right, 0); - top: auto; - -webkit-transform: scale(var(--fa-layers-scale, 0.25)); - transform: scale(var(--fa-layers-scale, 0.25)); - -webkit-transform-origin: bottom right; - transform-origin: bottom right; } - -.fa-layers-bottom-left { - bottom: var(--fa-bottom, 0); - left: var(--fa-left, 0); - right: auto; - top: auto; - -webkit-transform: scale(var(--fa-layers-scale, 0.25)); - transform: scale(var(--fa-layers-scale, 0.25)); - -webkit-transform-origin: bottom left; - transform-origin: bottom left; } - -.fa-layers-top-right { - top: var(--fa-top, 0); - right: var(--fa-right, 0); - -webkit-transform: scale(var(--fa-layers-scale, 0.25)); - transform: scale(var(--fa-layers-scale, 0.25)); - -webkit-transform-origin: top right; - transform-origin: top right; } - -.fa-layers-top-left { - left: var(--fa-left, 0); - right: auto; - top: var(--fa-top, 0); - -webkit-transform: scale(var(--fa-layers-scale, 0.25)); - transform: scale(var(--fa-layers-scale, 0.25)); - -webkit-transform-origin: top left; - transform-origin: top left; } - -.fa-1x { - font-size: 1em; } - -.fa-2x { - font-size: 2em; } - -.fa-3x { - font-size: 3em; } - -.fa-4x { - font-size: 4em; } - -.fa-5x { - font-size: 5em; } - -.fa-6x { - font-size: 6em; } - -.fa-7x { - font-size: 7em; } - -.fa-8x { - font-size: 8em; } - -.fa-9x { - font-size: 9em; } - -.fa-10x { - font-size: 10em; } - -.fa-2xs { - font-size: 0.625em; - line-height: 0.1em; - vertical-align: 0.225em; } - -.fa-xs { - font-size: 0.75em; - line-height: 0.08333em; - vertical-align: 0.125em; } - -.fa-sm { - font-size: 0.875em; - line-height: 0.07143em; - vertical-align: 0.05357em; } - -.fa-lg { - font-size: 1.25em; - line-height: 0.05em; - vertical-align: -0.075em; } - -.fa-xl { - font-size: 1.5em; - line-height: 0.04167em; - vertical-align: -0.125em; } - -.fa-2xl { - font-size: 2em; - line-height: 0.03125em; - vertical-align: -0.1875em; } - -.fa-fw { - text-align: center; - width: 1.25em; } - -.fa-ul { - list-style-type: none; - margin-left: var(--fa-li-margin, 2.5em); - padding-left: 0; } - .fa-ul > li { - position: relative; } - -.fa-li { - left: calc(var(--fa-li-width, 2em) * -1); - position: absolute; - text-align: center; - width: var(--fa-li-width, 2em); - line-height: inherit; } - -.fa-border { - border-color: var(--fa-border-color, #eee); - border-radius: var(--fa-border-radius, 0.1em); - border-style: var(--fa-border-style, solid); - border-width: var(--fa-border-width, 0.08em); - padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); } - -.fa-pull-left { - float: left; - margin-right: var(--fa-pull-margin, 0.3em); } - -.fa-pull-right { - float: right; - margin-left: var(--fa-pull-margin, 0.3em); } - -.fa-beat { - -webkit-animation-name: fa-beat; - animation-name: fa-beat; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); - animation-timing-function: var(--fa-animation-timing, ease-in-out); } - -.fa-bounce { - -webkit-animation-name: fa-bounce; - animation-name: fa-bounce; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); } - -.fa-fade { - -webkit-animation-name: fa-fade; - animation-name: fa-fade; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } - -.fa-beat-fade { - -webkit-animation-name: fa-beat-fade; - animation-name: fa-beat-fade; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } - -.fa-flip { - -webkit-animation-name: fa-flip; - animation-name: fa-flip; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); - animation-timing-function: var(--fa-animation-timing, ease-in-out); } - -.fa-shake { - -webkit-animation-name: fa-shake; - animation-name: fa-shake; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, linear); - animation-timing-function: var(--fa-animation-timing, linear); } - -.fa-spin { - -webkit-animation-name: fa-spin; - animation-name: fa-spin; - -webkit-animation-delay: var(--fa-animation-delay, 0s); - animation-delay: var(--fa-animation-delay, 0s); - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 2s); - animation-duration: var(--fa-animation-duration, 2s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, linear); - animation-timing-function: var(--fa-animation-timing, linear); } - -.fa-spin-reverse { - --fa-animation-direction: reverse; } - -.fa-pulse, -.fa-spin-pulse { - -webkit-animation-name: fa-spin; - animation-name: fa-spin; - -webkit-animation-direction: var(--fa-animation-direction, normal); - animation-direction: var(--fa-animation-direction, normal); - -webkit-animation-duration: var(--fa-animation-duration, 1s); - animation-duration: var(--fa-animation-duration, 1s); - -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); - animation-timing-function: var(--fa-animation-timing, steps(8)); } - -@media (prefers-reduced-motion: reduce) { - .fa-beat, - .fa-bounce, - .fa-fade, - .fa-beat-fade, - .fa-flip, - .fa-pulse, - .fa-shake, - .fa-spin, - .fa-spin-pulse { - -webkit-animation-delay: -1ms; - animation-delay: -1ms; - -webkit-animation-duration: 1ms; - animation-duration: 1ms; - -webkit-animation-iteration-count: 1; - animation-iteration-count: 1; - -webkit-transition-delay: 0s; - transition-delay: 0s; - -webkit-transition-duration: 0s; - transition-duration: 0s; } } - -@-webkit-keyframes fa-beat { - 0%, 90% { - -webkit-transform: scale(1); - transform: scale(1); } - 45% { - -webkit-transform: scale(var(--fa-beat-scale, 1.25)); - transform: scale(var(--fa-beat-scale, 1.25)); } } - -@keyframes fa-beat { - 0%, 90% { - -webkit-transform: scale(1); - transform: scale(1); } - 45% { - -webkit-transform: scale(var(--fa-beat-scale, 1.25)); - transform: scale(var(--fa-beat-scale, 1.25)); } } - -@-webkit-keyframes fa-bounce { - 0% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 10% { - -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); - transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } - 30% { - -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); - transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } - 50% { - -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); - transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } - 57% { - -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); - transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } - 64% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 100% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } } - -@keyframes fa-bounce { - 0% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 10% { - -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); - transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } - 30% { - -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); - transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } - 50% { - -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); - transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } - 57% { - -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); - transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } - 64% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } - 100% { - -webkit-transform: scale(1, 1) translateY(0); - transform: scale(1, 1) translateY(0); } } - -@-webkit-keyframes fa-fade { - 50% { - opacity: var(--fa-fade-opacity, 0.4); } } - -@keyframes fa-fade { - 50% { - opacity: var(--fa-fade-opacity, 0.4); } } - -@-webkit-keyframes fa-beat-fade { - 0%, 100% { - opacity: var(--fa-beat-fade-opacity, 0.4); - -webkit-transform: scale(1); - transform: scale(1); } - 50% { - opacity: 1; - -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); - transform: scale(var(--fa-beat-fade-scale, 1.125)); } } - -@keyframes fa-beat-fade { - 0%, 100% { - opacity: var(--fa-beat-fade-opacity, 0.4); - -webkit-transform: scale(1); - transform: scale(1); } - 50% { - opacity: 1; - -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); - transform: scale(var(--fa-beat-fade-scale, 1.125)); } } - -@-webkit-keyframes fa-flip { - 50% { - -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); - transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } - -@keyframes fa-flip { - 50% { - -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); - transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } - -@-webkit-keyframes fa-shake { - 0% { - -webkit-transform: rotate(-15deg); - transform: rotate(-15deg); } - 4% { - -webkit-transform: rotate(15deg); - transform: rotate(15deg); } - 8%, 24% { - -webkit-transform: rotate(-18deg); - transform: rotate(-18deg); } - 12%, 28% { - -webkit-transform: rotate(18deg); - transform: rotate(18deg); } - 16% { - -webkit-transform: rotate(-22deg); - transform: rotate(-22deg); } - 20% { - -webkit-transform: rotate(22deg); - transform: rotate(22deg); } - 32% { - -webkit-transform: rotate(-12deg); - transform: rotate(-12deg); } - 36% { - -webkit-transform: rotate(12deg); - transform: rotate(12deg); } - 40%, 100% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } } - -@keyframes fa-shake { - 0% { - -webkit-transform: rotate(-15deg); - transform: rotate(-15deg); } - 4% { - -webkit-transform: rotate(15deg); - transform: rotate(15deg); } - 8%, 24% { - -webkit-transform: rotate(-18deg); - transform: rotate(-18deg); } - 12%, 28% { - -webkit-transform: rotate(18deg); - transform: rotate(18deg); } - 16% { - -webkit-transform: rotate(-22deg); - transform: rotate(-22deg); } - 20% { - -webkit-transform: rotate(22deg); - transform: rotate(22deg); } - 32% { - -webkit-transform: rotate(-12deg); - transform: rotate(-12deg); } - 36% { - -webkit-transform: rotate(12deg); - transform: rotate(12deg); } - 40%, 100% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } } - -@-webkit-keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -@keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -.fa-rotate-90 { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); } - -.fa-rotate-180 { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); } - -.fa-rotate-270 { - -webkit-transform: rotate(270deg); - transform: rotate(270deg); } - -.fa-flip-horizontal { - -webkit-transform: scale(-1, 1); - transform: scale(-1, 1); } - -.fa-flip-vertical { - -webkit-transform: scale(1, -1); - transform: scale(1, -1); } - -.fa-flip-both, -.fa-flip-horizontal.fa-flip-vertical { - -webkit-transform: scale(-1, -1); - transform: scale(-1, -1); } - -.fa-rotate-by { - -webkit-transform: rotate(var(--fa-rotate-angle, 0)); - transform: rotate(var(--fa-rotate-angle, 0)); } - -.fa-stack { - display: inline-block; - vertical-align: middle; - height: 2em; - position: relative; - width: 2.5em; } - -.fa-stack-1x, -.fa-stack-2x { - bottom: 0; - left: 0; - margin: auto; - position: absolute; - right: 0; - top: 0; - z-index: var(--fa-stack-z-index, auto); } - -.svg-inline--fa.fa-stack-1x { - height: 1em; - width: 1.25em; } - -.svg-inline--fa.fa-stack-2x { - height: 2em; - width: 2.5em; } - -.fa-inverse { - color: var(--fa-inverse, #fff); } - -.sr-only, -.fa-sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; } - -.sr-only-focusable:not(:focus), -.fa-sr-only-focusable:not(:focus) { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; } - -.svg-inline--fa .fa-primary { - fill: var(--fa-primary-color, currentColor); - opacity: var(--fa-primary-opacity, 1); } - -.svg-inline--fa .fa-secondary { - fill: var(--fa-secondary-color, currentColor); - opacity: var(--fa-secondary-opacity, 0.4); } - -.svg-inline--fa.fa-swap-opacity .fa-primary { - opacity: var(--fa-secondary-opacity, 0.4); } - -.svg-inline--fa.fa-swap-opacity .fa-secondary { - opacity: var(--fa-primary-opacity, 1); } - -.svg-inline--fa mask .fa-primary, -.svg-inline--fa mask .fa-secondary { - fill: black; } - -.fad.fa-inverse, -.fa-duotone.fa-inverse { - color: var(--fa-inverse, #fff); } diff --git a/resources/fontawesome/css/svg-with-js.min.css b/resources/fontawesome/css/svg-with-js.min.css deleted file mode 100644 index a99cebb..0000000 --- a/resources/fontawesome/css/svg-with-js.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -:host,:root{--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Solid";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Regular";--fa-font-light:normal 300 1em/1 "Font Awesome 6 Light";--fa-font-thin:normal 100 1em/1 "Font Awesome 6 Thin";--fa-font-duotone:normal 900 1em/1 "Font Awesome 6 Duotone";--fa-font-sharp-solid:normal 900 1em/1 "Font Awesome 6 Sharp";--fa-font-sharp-regular:normal 400 1em/1 "Font Awesome 6 Sharp";--fa-font-sharp-light:normal 300 1em/1 "Font Awesome 6 Sharp";--fa-font-sharp-thin:normal 100 1em/1 "Font Awesome 6 Sharp";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}svg:not(:host).svg-inline--fa,svg:not(:root).svg-inline--fa{overflow:visible;box-sizing:content-box}.svg-inline--fa{display:var(--fa-display,inline-block);height:1em;overflow:visible;vertical-align:-.125em}.svg-inline--fa.fa-2xs{vertical-align:.1em}.svg-inline--fa.fa-xs{vertical-align:0}.svg-inline--fa.fa-sm{vertical-align:-.07143em}.svg-inline--fa.fa-lg{vertical-align:-.2em}.svg-inline--fa.fa-xl{vertical-align:-.25em}.svg-inline--fa.fa-2xl{vertical-align:-.3125em}.svg-inline--fa.fa-pull-left{margin-right:var(--fa-pull-margin,.3em);width:auto}.svg-inline--fa.fa-pull-right{margin-left:var(--fa-pull-margin,.3em);width:auto}.svg-inline--fa.fa-li{width:var(--fa-li-width,2em);top:.25em}.svg-inline--fa.fa-fw{width:var(--fa-fw-width,1.25em)}.fa-layers svg.svg-inline--fa{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:1em}.fa-layers svg.svg-inline--fa{-webkit-transform-origin:center center;transform-origin:center center}.fa-layers-text{left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);-webkit-transform-origin:center center;transform-origin:center center}.fa-layers-counter{background-color:var(--fa-counter-background-color,#ff253a);border-radius:var(--fa-counter-border-radius,1em);box-sizing:border-box;color:var(--fa-inverse,#fff);line-height:var(--fa-counter-line-height,1);max-width:var(--fa-counter-max-width,5em);min-width:var(--fa-counter-min-width,1.5em);overflow:hidden;padding:var(--fa-counter-padding,.25em .5em);right:var(--fa-right,0);text-overflow:ellipsis;top:var(--fa-top,0);-webkit-transform:scale(var(--fa-counter-scale,.25));transform:scale(var(--fa-counter-scale,.25));-webkit-transform-origin:top right;transform-origin:top right}.fa-layers-bottom-right{bottom:var(--fa-bottom,0);right:var(--fa-right,0);top:auto;-webkit-transform:scale(var(--fa-layers-scale,.25));transform:scale(var(--fa-layers-scale,.25));-webkit-transform-origin:bottom right;transform-origin:bottom right}.fa-layers-bottom-left{bottom:var(--fa-bottom,0);left:var(--fa-left,0);right:auto;top:auto;-webkit-transform:scale(var(--fa-layers-scale,.25));transform:scale(var(--fa-layers-scale,.25));-webkit-transform-origin:bottom left;transform-origin:bottom left}.fa-layers-top-right{top:var(--fa-top,0);right:var(--fa-right,0);-webkit-transform:scale(var(--fa-layers-scale,.25));transform:scale(var(--fa-layers-scale,.25));-webkit-transform-origin:top right;transform-origin:top right}.fa-layers-top-left{left:var(--fa-left,0);right:auto;top:var(--fa-top,0);-webkit-transform:scale(var(--fa-layers-scale,.25));transform:scale(var(--fa-layers-scale,.25));-webkit-transform-origin:top left;transform-origin:top left}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,0));transform:rotate(var(--fa-rotate-angle,0))}.fa-stack{display:inline-block;vertical-align:middle;height:2em;position:relative;width:2.5em}.fa-stack-1x,.fa-stack-2x{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0;z-index:var(--fa-stack-z-index,auto)}.svg-inline--fa.fa-stack-1x{height:1em;width:1.25em}.svg-inline--fa.fa-stack-2x{height:2em;width:2.5em}.fa-inverse{color:var(--fa-inverse,#fff)}.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.svg-inline--fa .fa-primary{fill:var(--fa-primary-color,currentColor);opacity:var(--fa-primary-opacity,1)}.svg-inline--fa .fa-secondary{fill:var(--fa-secondary-color,currentColor)}.svg-inline--fa .fa-secondary,.svg-inline--fa.fa-swap-opacity .fa-primary{opacity:var(--fa-secondary-opacity,.4)}.svg-inline--fa.fa-swap-opacity .fa-secondary{opacity:var(--fa-primary-opacity,1)}.svg-inline--fa mask .fa-primary,.svg-inline--fa mask .fa-secondary{fill:#000}.fa-duotone.fa-inverse,.fad.fa-inverse{color:var(--fa-inverse,#fff)} \ No newline at end of file diff --git a/resources/fontawesome/css/v4-font-face.css b/resources/fontawesome/css/v4-font-face.css deleted file mode 100644 index 9e02283..0000000 --- a/resources/fontawesome/css/v4-font-face.css +++ /dev/null @@ -1,26 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -@font-face { - font-family: 'FontAwesome'; - font-display: block; - src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } - -@font-face { - font-family: 'FontAwesome'; - font-display: block; - src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } - -@font-face { - font-family: 'FontAwesome'; - font-display: block; - src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); - unicode-range: U+F003,U+F006,U+F014,U+F016-F017,U+F01A-F01B,U+F01D,U+F022,U+F03E,U+F044,U+F046,U+F05C-F05D,U+F06E,U+F070,U+F087-F088,U+F08A,U+F094,U+F096-F097,U+F09D,U+F0A0,U+F0A2,U+F0A4-F0A7,U+F0C5,U+F0C7,U+F0E5-F0E6,U+F0EB,U+F0F6-F0F8,U+F10C,U+F114-F115,U+F118-F11A,U+F11C-F11D,U+F133,U+F147,U+F14E,U+F150-F152,U+F185-F186,U+F18E,U+F190-F192,U+F196,U+F1C1-F1C9,U+F1D9,U+F1DB,U+F1E3,U+F1EA,U+F1F7,U+F1F9,U+F20A,U+F247-F248,U+F24A,U+F24D,U+F255-F25B,U+F25D,U+F271-F274,U+F278,U+F27B,U+F28C,U+F28E,U+F29C,U+F2B5,U+F2B7,U+F2BA,U+F2BC,U+F2BE,U+F2C0-F2C1,U+F2C3,U+F2D0,U+F2D2,U+F2D4,U+F2DC; } - -@font-face { - font-family: 'FontAwesome'; - font-display: block; - src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype"); - unicode-range: U+F041,U+F047,U+F065-F066,U+F07D-F07E,U+F080,U+F08B,U+F08E,U+F090,U+F09A,U+F0AC,U+F0AE,U+F0B2,U+F0D0,U+F0D6,U+F0E4,U+F0EC,U+F10A-F10B,U+F123,U+F13E,U+F148-F149,U+F14C,U+F156,U+F15E,U+F160-F161,U+F163,U+F175-F178,U+F195,U+F1F8,U+F219,U+F27A; } diff --git a/resources/fontawesome/css/v4-font-face.min.css b/resources/fontawesome/css/v4-font-face.min.css deleted file mode 100644 index 140e09d..0000000 --- a/resources/fontawesome/css/v4-font-face.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a} \ No newline at end of file diff --git a/resources/fontawesome/css/v4-shims.css b/resources/fontawesome/css/v4-shims.css deleted file mode 100644 index ea60ea4..0000000 --- a/resources/fontawesome/css/v4-shims.css +++ /dev/null @@ -1,2194 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -.fa.fa-glass:before { - content: "\f000"; } - -.fa.fa-envelope-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-envelope-o:before { - content: "\f0e0"; } - -.fa.fa-star-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-star-o:before { - content: "\f005"; } - -.fa.fa-remove:before { - content: "\f00d"; } - -.fa.fa-close:before { - content: "\f00d"; } - -.fa.fa-gear:before { - content: "\f013"; } - -.fa.fa-trash-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-trash-o:before { - content: "\f2ed"; } - -.fa.fa-home:before { - content: "\f015"; } - -.fa.fa-file-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-o:before { - content: "\f15b"; } - -.fa.fa-clock-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-clock-o:before { - content: "\f017"; } - -.fa.fa-arrow-circle-o-down { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-arrow-circle-o-down:before { - content: "\f358"; } - -.fa.fa-arrow-circle-o-up { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-arrow-circle-o-up:before { - content: "\f35b"; } - -.fa.fa-play-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-play-circle-o:before { - content: "\f144"; } - -.fa.fa-repeat:before { - content: "\f01e"; } - -.fa.fa-rotate-right:before { - content: "\f01e"; } - -.fa.fa-refresh:before { - content: "\f021"; } - -.fa.fa-list-alt { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-list-alt:before { - content: "\f022"; } - -.fa.fa-dedent:before { - content: "\f03b"; } - -.fa.fa-video-camera:before { - content: "\f03d"; } - -.fa.fa-picture-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-picture-o:before { - content: "\f03e"; } - -.fa.fa-photo { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-photo:before { - content: "\f03e"; } - -.fa.fa-image { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-image:before { - content: "\f03e"; } - -.fa.fa-map-marker:before { - content: "\f3c5"; } - -.fa.fa-pencil-square-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-pencil-square-o:before { - content: "\f044"; } - -.fa.fa-edit { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-edit:before { - content: "\f044"; } - -.fa.fa-share-square-o:before { - content: "\f14d"; } - -.fa.fa-check-square-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-check-square-o:before { - content: "\f14a"; } - -.fa.fa-arrows:before { - content: "\f0b2"; } - -.fa.fa-times-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-times-circle-o:before { - content: "\f057"; } - -.fa.fa-check-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-check-circle-o:before { - content: "\f058"; } - -.fa.fa-mail-forward:before { - content: "\f064"; } - -.fa.fa-expand:before { - content: "\f424"; } - -.fa.fa-compress:before { - content: "\f422"; } - -.fa.fa-eye { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-eye-slash { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-warning:before { - content: "\f071"; } - -.fa.fa-calendar:before { - content: "\f073"; } - -.fa.fa-arrows-v:before { - content: "\f338"; } - -.fa.fa-arrows-h:before { - content: "\f337"; } - -.fa.fa-bar-chart:before { - content: "\e0e3"; } - -.fa.fa-bar-chart-o:before { - content: "\e0e3"; } - -.fa.fa-twitter-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-twitter-square:before { - content: "\f081"; } - -.fa.fa-facebook-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-facebook-square:before { - content: "\f082"; } - -.fa.fa-gears:before { - content: "\f085"; } - -.fa.fa-thumbs-o-up { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-thumbs-o-up:before { - content: "\f164"; } - -.fa.fa-thumbs-o-down { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-thumbs-o-down:before { - content: "\f165"; } - -.fa.fa-heart-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-heart-o:before { - content: "\f004"; } - -.fa.fa-sign-out:before { - content: "\f2f5"; } - -.fa.fa-linkedin-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-linkedin-square:before { - content: "\f08c"; } - -.fa.fa-thumb-tack:before { - content: "\f08d"; } - -.fa.fa-external-link:before { - content: "\f35d"; } - -.fa.fa-sign-in:before { - content: "\f2f6"; } - -.fa.fa-github-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-github-square:before { - content: "\f092"; } - -.fa.fa-lemon-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-lemon-o:before { - content: "\f094"; } - -.fa.fa-square-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-square-o:before { - content: "\f0c8"; } - -.fa.fa-bookmark-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-bookmark-o:before { - content: "\f02e"; } - -.fa.fa-twitter { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-facebook { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-facebook:before { - content: "\f39e"; } - -.fa.fa-facebook-f { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-facebook-f:before { - content: "\f39e"; } - -.fa.fa-github { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-credit-card { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-feed:before { - content: "\f09e"; } - -.fa.fa-hdd-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hdd-o:before { - content: "\f0a0"; } - -.fa.fa-hand-o-right { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-o-right:before { - content: "\f0a4"; } - -.fa.fa-hand-o-left { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-o-left:before { - content: "\f0a5"; } - -.fa.fa-hand-o-up { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-o-up:before { - content: "\f0a6"; } - -.fa.fa-hand-o-down { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-o-down:before { - content: "\f0a7"; } - -.fa.fa-globe:before { - content: "\f57d"; } - -.fa.fa-tasks:before { - content: "\f828"; } - -.fa.fa-arrows-alt:before { - content: "\f31e"; } - -.fa.fa-group:before { - content: "\f0c0"; } - -.fa.fa-chain:before { - content: "\f0c1"; } - -.fa.fa-cut:before { - content: "\f0c4"; } - -.fa.fa-files-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-files-o:before { - content: "\f0c5"; } - -.fa.fa-floppy-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-floppy-o:before { - content: "\f0c7"; } - -.fa.fa-save { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-save:before { - content: "\f0c7"; } - -.fa.fa-navicon:before { - content: "\f0c9"; } - -.fa.fa-reorder:before { - content: "\f0c9"; } - -.fa.fa-magic:before { - content: "\e2ca"; } - -.fa.fa-pinterest { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-pinterest-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-pinterest-square:before { - content: "\f0d3"; } - -.fa.fa-google-plus-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-google-plus-square:before { - content: "\f0d4"; } - -.fa.fa-google-plus { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-google-plus:before { - content: "\f0d5"; } - -.fa.fa-money:before { - content: "\f3d1"; } - -.fa.fa-unsorted:before { - content: "\f0dc"; } - -.fa.fa-sort-desc:before { - content: "\f0dd"; } - -.fa.fa-sort-asc:before { - content: "\f0de"; } - -.fa.fa-linkedin { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-linkedin:before { - content: "\f0e1"; } - -.fa.fa-rotate-left:before { - content: "\f0e2"; } - -.fa.fa-legal:before { - content: "\f0e3"; } - -.fa.fa-tachometer:before { - content: "\f625"; } - -.fa.fa-dashboard:before { - content: "\f625"; } - -.fa.fa-comment-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-comment-o:before { - content: "\f075"; } - -.fa.fa-comments-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-comments-o:before { - content: "\f086"; } - -.fa.fa-flash:before { - content: "\f0e7"; } - -.fa.fa-clipboard:before { - content: "\f0ea"; } - -.fa.fa-lightbulb-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-lightbulb-o:before { - content: "\f0eb"; } - -.fa.fa-exchange:before { - content: "\f362"; } - -.fa.fa-cloud-download:before { - content: "\f0ed"; } - -.fa.fa-cloud-upload:before { - content: "\f0ee"; } - -.fa.fa-bell-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-bell-o:before { - content: "\f0f3"; } - -.fa.fa-cutlery:before { - content: "\f2e7"; } - -.fa.fa-file-text-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-text-o:before { - content: "\f15c"; } - -.fa.fa-building-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-building-o:before { - content: "\f1ad"; } - -.fa.fa-hospital-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hospital-o:before { - content: "\f0f8"; } - -.fa.fa-tablet:before { - content: "\f3fa"; } - -.fa.fa-mobile:before { - content: "\f3cd"; } - -.fa.fa-mobile-phone:before { - content: "\f3cd"; } - -.fa.fa-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-circle-o:before { - content: "\f111"; } - -.fa.fa-mail-reply:before { - content: "\f3e5"; } - -.fa.fa-github-alt { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-folder-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-folder-o:before { - content: "\f07b"; } - -.fa.fa-folder-open-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-folder-open-o:before { - content: "\f07c"; } - -.fa.fa-smile-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-smile-o:before { - content: "\f118"; } - -.fa.fa-frown-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-frown-o:before { - content: "\f119"; } - -.fa.fa-meh-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-meh-o:before { - content: "\f11a"; } - -.fa.fa-keyboard-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-keyboard-o:before { - content: "\f11c"; } - -.fa.fa-flag-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-flag-o:before { - content: "\f024"; } - -.fa.fa-mail-reply-all:before { - content: "\f122"; } - -.fa.fa-star-half-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-star-half-o:before { - content: "\f5c0"; } - -.fa.fa-star-half-empty { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-star-half-empty:before { - content: "\f5c0"; } - -.fa.fa-star-half-full { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-star-half-full:before { - content: "\f5c0"; } - -.fa.fa-code-fork:before { - content: "\f126"; } - -.fa.fa-chain-broken:before { - content: "\f127"; } - -.fa.fa-unlink:before { - content: "\f127"; } - -.fa.fa-calendar-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-calendar-o:before { - content: "\f133"; } - -.fa.fa-maxcdn { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-html5 { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-css3 { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-unlock-alt:before { - content: "\f09c"; } - -.fa.fa-minus-square-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-minus-square-o:before { - content: "\f146"; } - -.fa.fa-level-up:before { - content: "\f3bf"; } - -.fa.fa-level-down:before { - content: "\f3be"; } - -.fa.fa-pencil-square:before { - content: "\f14b"; } - -.fa.fa-external-link-square:before { - content: "\f360"; } - -.fa.fa-compass { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-caret-square-o-down { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-caret-square-o-down:before { - content: "\f150"; } - -.fa.fa-toggle-down { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-toggle-down:before { - content: "\f150"; } - -.fa.fa-caret-square-o-up { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-caret-square-o-up:before { - content: "\f151"; } - -.fa.fa-toggle-up { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-toggle-up:before { - content: "\f151"; } - -.fa.fa-caret-square-o-right { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-caret-square-o-right:before { - content: "\f152"; } - -.fa.fa-toggle-right { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-toggle-right:before { - content: "\f152"; } - -.fa.fa-eur:before { - content: "\f153"; } - -.fa.fa-euro:before { - content: "\f153"; } - -.fa.fa-gbp:before { - content: "\f154"; } - -.fa.fa-usd:before { - content: "\24"; } - -.fa.fa-dollar:before { - content: "\24"; } - -.fa.fa-inr:before { - content: "\e1bc"; } - -.fa.fa-rupee:before { - content: "\e1bc"; } - -.fa.fa-jpy:before { - content: "\f157"; } - -.fa.fa-cny:before { - content: "\f157"; } - -.fa.fa-rmb:before { - content: "\f157"; } - -.fa.fa-yen:before { - content: "\f157"; } - -.fa.fa-rub:before { - content: "\f158"; } - -.fa.fa-ruble:before { - content: "\f158"; } - -.fa.fa-rouble:before { - content: "\f158"; } - -.fa.fa-krw:before { - content: "\f159"; } - -.fa.fa-won:before { - content: "\f159"; } - -.fa.fa-btc { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-bitcoin { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-bitcoin:before { - content: "\f15a"; } - -.fa.fa-file-text:before { - content: "\f15c"; } - -.fa.fa-sort-alpha-asc:before { - content: "\f15d"; } - -.fa.fa-sort-alpha-desc:before { - content: "\f881"; } - -.fa.fa-sort-amount-asc:before { - content: "\f884"; } - -.fa.fa-sort-amount-desc:before { - content: "\f160"; } - -.fa.fa-sort-numeric-asc:before { - content: "\f162"; } - -.fa.fa-sort-numeric-desc:before { - content: "\f886"; } - -.fa.fa-youtube-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-youtube-square:before { - content: "\f431"; } - -.fa.fa-youtube { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-xing { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-xing-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-xing-square:before { - content: "\f169"; } - -.fa.fa-youtube-play { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-youtube-play:before { - content: "\f167"; } - -.fa.fa-dropbox { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-stack-overflow { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-instagram { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-flickr { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-adn { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-bitbucket { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-bitbucket-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-bitbucket-square:before { - content: "\f171"; } - -.fa.fa-tumblr { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-tumblr-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-tumblr-square:before { - content: "\f174"; } - -.fa.fa-long-arrow-down:before { - content: "\f309"; } - -.fa.fa-long-arrow-up:before { - content: "\f30c"; } - -.fa.fa-long-arrow-left:before { - content: "\f30a"; } - -.fa.fa-long-arrow-right:before { - content: "\f30b"; } - -.fa.fa-apple { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-windows { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-android { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-linux { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-dribbble { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-skype { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-foursquare { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-trello { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-gratipay { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-gittip { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-gittip:before { - content: "\f184"; } - -.fa.fa-sun-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-sun-o:before { - content: "\f185"; } - -.fa.fa-moon-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-moon-o:before { - content: "\f186"; } - -.fa.fa-vk { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-weibo { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-renren { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-pagelines { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-stack-exchange { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-arrow-circle-o-right { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-arrow-circle-o-right:before { - content: "\f35a"; } - -.fa.fa-arrow-circle-o-left { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-arrow-circle-o-left:before { - content: "\f359"; } - -.fa.fa-caret-square-o-left { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-caret-square-o-left:before { - content: "\f191"; } - -.fa.fa-toggle-left { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-toggle-left:before { - content: "\f191"; } - -.fa.fa-dot-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-dot-circle-o:before { - content: "\f192"; } - -.fa.fa-vimeo-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-vimeo-square:before { - content: "\f194"; } - -.fa.fa-try:before { - content: "\e2bb"; } - -.fa.fa-turkish-lira:before { - content: "\e2bb"; } - -.fa.fa-plus-square-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-plus-square-o:before { - content: "\f0fe"; } - -.fa.fa-slack { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wordpress { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-openid { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-institution:before { - content: "\f19c"; } - -.fa.fa-bank:before { - content: "\f19c"; } - -.fa.fa-mortar-board:before { - content: "\f19d"; } - -.fa.fa-yahoo { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-google { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-reddit { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-reddit-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-reddit-square:before { - content: "\f1a2"; } - -.fa.fa-stumbleupon-circle { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-stumbleupon { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-delicious { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-digg { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-pied-piper-pp { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-pied-piper-alt { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-drupal { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-joomla { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-behance { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-behance-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-behance-square:before { - content: "\f1b5"; } - -.fa.fa-steam { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-steam-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-steam-square:before { - content: "\f1b7"; } - -.fa.fa-automobile:before { - content: "\f1b9"; } - -.fa.fa-cab:before { - content: "\f1ba"; } - -.fa.fa-spotify { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-deviantart { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-soundcloud { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-file-pdf-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-pdf-o:before { - content: "\f1c1"; } - -.fa.fa-file-word-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-word-o:before { - content: "\f1c2"; } - -.fa.fa-file-excel-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-excel-o:before { - content: "\f1c3"; } - -.fa.fa-file-powerpoint-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-powerpoint-o:before { - content: "\f1c4"; } - -.fa.fa-file-image-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-image-o:before { - content: "\f1c5"; } - -.fa.fa-file-photo-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-photo-o:before { - content: "\f1c5"; } - -.fa.fa-file-picture-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-picture-o:before { - content: "\f1c5"; } - -.fa.fa-file-archive-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-archive-o:before { - content: "\f1c6"; } - -.fa.fa-file-zip-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-zip-o:before { - content: "\f1c6"; } - -.fa.fa-file-audio-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-audio-o:before { - content: "\f1c7"; } - -.fa.fa-file-sound-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-sound-o:before { - content: "\f1c7"; } - -.fa.fa-file-video-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-video-o:before { - content: "\f1c8"; } - -.fa.fa-file-movie-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-movie-o:before { - content: "\f1c8"; } - -.fa.fa-file-code-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-file-code-o:before { - content: "\f1c9"; } - -.fa.fa-vine { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-codepen { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-jsfiddle { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-life-bouy:before { - content: "\f1cd"; } - -.fa.fa-life-buoy:before { - content: "\f1cd"; } - -.fa.fa-life-saver:before { - content: "\f1cd"; } - -.fa.fa-support:before { - content: "\f1cd"; } - -.fa.fa-circle-o-notch:before { - content: "\f1ce"; } - -.fa.fa-rebel { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-ra { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-ra:before { - content: "\f1d0"; } - -.fa.fa-resistance { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-resistance:before { - content: "\f1d0"; } - -.fa.fa-empire { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-ge { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-ge:before { - content: "\f1d1"; } - -.fa.fa-git-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-git-square:before { - content: "\f1d2"; } - -.fa.fa-git { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-hacker-news { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-y-combinator-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-y-combinator-square:before { - content: "\f1d4"; } - -.fa.fa-yc-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-yc-square:before { - content: "\f1d4"; } - -.fa.fa-tencent-weibo { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-qq { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-weixin { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wechat { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wechat:before { - content: "\f1d7"; } - -.fa.fa-send:before { - content: "\f1d8"; } - -.fa.fa-paper-plane-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-paper-plane-o:before { - content: "\f1d8"; } - -.fa.fa-send-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-send-o:before { - content: "\f1d8"; } - -.fa.fa-circle-thin { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-circle-thin:before { - content: "\f111"; } - -.fa.fa-header:before { - content: "\f1dc"; } - -.fa.fa-futbol-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-futbol-o:before { - content: "\f1e3"; } - -.fa.fa-soccer-ball-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-soccer-ball-o:before { - content: "\f1e3"; } - -.fa.fa-slideshare { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-twitch { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-yelp { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-newspaper-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-newspaper-o:before { - content: "\f1ea"; } - -.fa.fa-paypal { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-google-wallet { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-cc-visa { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-cc-mastercard { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-cc-discover { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-cc-amex { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-cc-paypal { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-cc-stripe { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-bell-slash-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-bell-slash-o:before { - content: "\f1f6"; } - -.fa.fa-trash:before { - content: "\f2ed"; } - -.fa.fa-copyright { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-eyedropper:before { - content: "\f1fb"; } - -.fa.fa-area-chart:before { - content: "\f1fe"; } - -.fa.fa-pie-chart:before { - content: "\f200"; } - -.fa.fa-line-chart:before { - content: "\f201"; } - -.fa.fa-lastfm { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-lastfm-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-lastfm-square:before { - content: "\f203"; } - -.fa.fa-ioxhost { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-angellist { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-cc { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-cc:before { - content: "\f20a"; } - -.fa.fa-ils:before { - content: "\f20b"; } - -.fa.fa-shekel:before { - content: "\f20b"; } - -.fa.fa-sheqel:before { - content: "\f20b"; } - -.fa.fa-buysellads { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-connectdevelop { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-dashcube { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-forumbee { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-leanpub { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-sellsy { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-shirtsinbulk { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-simplybuilt { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-skyatlas { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-diamond { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-diamond:before { - content: "\f3a5"; } - -.fa.fa-transgender:before { - content: "\f224"; } - -.fa.fa-intersex:before { - content: "\f224"; } - -.fa.fa-transgender-alt:before { - content: "\f225"; } - -.fa.fa-facebook-official { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-facebook-official:before { - content: "\f09a"; } - -.fa.fa-pinterest-p { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-whatsapp { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-hotel:before { - content: "\f236"; } - -.fa.fa-viacoin { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-medium { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-y-combinator { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-yc { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-yc:before { - content: "\f23b"; } - -.fa.fa-optin-monster { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-opencart { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-expeditedssl { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-battery-4:before { - content: "\f240"; } - -.fa.fa-battery:before { - content: "\f240"; } - -.fa.fa-battery-3:before { - content: "\f241"; } - -.fa.fa-battery-2:before { - content: "\f242"; } - -.fa.fa-battery-1:before { - content: "\f243"; } - -.fa.fa-battery-0:before { - content: "\f244"; } - -.fa.fa-object-group { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-object-ungroup { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-sticky-note-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-sticky-note-o:before { - content: "\f249"; } - -.fa.fa-cc-jcb { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-cc-diners-club { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-clone { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hourglass-o:before { - content: "\f254"; } - -.fa.fa-hourglass-1:before { - content: "\f251"; } - -.fa.fa-hourglass-2:before { - content: "\f252"; } - -.fa.fa-hourglass-3:before { - content: "\f253"; } - -.fa.fa-hand-rock-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-rock-o:before { - content: "\f255"; } - -.fa.fa-hand-grab-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-grab-o:before { - content: "\f255"; } - -.fa.fa-hand-paper-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-paper-o:before { - content: "\f256"; } - -.fa.fa-hand-stop-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-stop-o:before { - content: "\f256"; } - -.fa.fa-hand-scissors-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-scissors-o:before { - content: "\f257"; } - -.fa.fa-hand-lizard-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-lizard-o:before { - content: "\f258"; } - -.fa.fa-hand-spock-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-spock-o:before { - content: "\f259"; } - -.fa.fa-hand-pointer-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-pointer-o:before { - content: "\f25a"; } - -.fa.fa-hand-peace-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-hand-peace-o:before { - content: "\f25b"; } - -.fa.fa-registered { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-creative-commons { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-gg { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-gg-circle { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-odnoklassniki { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-odnoklassniki-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-odnoklassniki-square:before { - content: "\f264"; } - -.fa.fa-get-pocket { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wikipedia-w { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-safari { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-chrome { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-firefox { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-opera { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-internet-explorer { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-television:before { - content: "\f26c"; } - -.fa.fa-contao { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-500px { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-amazon { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-calendar-plus-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-calendar-plus-o:before { - content: "\f271"; } - -.fa.fa-calendar-minus-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-calendar-minus-o:before { - content: "\f272"; } - -.fa.fa-calendar-times-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-calendar-times-o:before { - content: "\f273"; } - -.fa.fa-calendar-check-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-calendar-check-o:before { - content: "\f274"; } - -.fa.fa-map-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-map-o:before { - content: "\f279"; } - -.fa.fa-commenting:before { - content: "\f4ad"; } - -.fa.fa-commenting-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-commenting-o:before { - content: "\f4ad"; } - -.fa.fa-houzz { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-vimeo { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-vimeo:before { - content: "\f27d"; } - -.fa.fa-black-tie { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-fonticons { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-reddit-alien { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-edge { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-credit-card-alt:before { - content: "\f09d"; } - -.fa.fa-codiepie { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-modx { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-fort-awesome { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-usb { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-product-hunt { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-mixcloud { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-scribd { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-pause-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-pause-circle-o:before { - content: "\f28b"; } - -.fa.fa-stop-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-stop-circle-o:before { - content: "\f28d"; } - -.fa.fa-bluetooth { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-bluetooth-b { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-gitlab { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wpbeginner { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wpforms { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-envira { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wheelchair-alt { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wheelchair-alt:before { - content: "\f368"; } - -.fa.fa-question-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-question-circle-o:before { - content: "\f059"; } - -.fa.fa-volume-control-phone:before { - content: "\f2a0"; } - -.fa.fa-asl-interpreting:before { - content: "\f2a3"; } - -.fa.fa-deafness:before { - content: "\f2a4"; } - -.fa.fa-hard-of-hearing:before { - content: "\f2a4"; } - -.fa.fa-glide { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-glide-g { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-signing:before { - content: "\f2a7"; } - -.fa.fa-viadeo { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-viadeo-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-viadeo-square:before { - content: "\f2aa"; } - -.fa.fa-snapchat { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-snapchat-ghost { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-snapchat-ghost:before { - content: "\f2ab"; } - -.fa.fa-snapchat-square { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-snapchat-square:before { - content: "\f2ad"; } - -.fa.fa-pied-piper { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-first-order { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-yoast { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-themeisle { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-google-plus-official { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-google-plus-official:before { - content: "\f2b3"; } - -.fa.fa-google-plus-circle { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-google-plus-circle:before { - content: "\f2b3"; } - -.fa.fa-font-awesome { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-fa { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-fa:before { - content: "\f2b4"; } - -.fa.fa-handshake-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-handshake-o:before { - content: "\f2b5"; } - -.fa.fa-envelope-open-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-envelope-open-o:before { - content: "\f2b6"; } - -.fa.fa-linode { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-address-book-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-address-book-o:before { - content: "\f2b9"; } - -.fa.fa-vcard:before { - content: "\f2bb"; } - -.fa.fa-address-card-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-address-card-o:before { - content: "\f2bb"; } - -.fa.fa-vcard-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-vcard-o:before { - content: "\f2bb"; } - -.fa.fa-user-circle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-user-circle-o:before { - content: "\f2bd"; } - -.fa.fa-user-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-user-o:before { - content: "\f007"; } - -.fa.fa-id-badge { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-drivers-license:before { - content: "\f2c2"; } - -.fa.fa-id-card-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-id-card-o:before { - content: "\f2c2"; } - -.fa.fa-drivers-license-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-drivers-license-o:before { - content: "\f2c2"; } - -.fa.fa-quora { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-free-code-camp { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-telegram { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-thermometer-4:before { - content: "\f2c7"; } - -.fa.fa-thermometer:before { - content: "\f2c7"; } - -.fa.fa-thermometer-3:before { - content: "\f2c8"; } - -.fa.fa-thermometer-2:before { - content: "\f2c9"; } - -.fa.fa-thermometer-1:before { - content: "\f2ca"; } - -.fa.fa-thermometer-0:before { - content: "\f2cb"; } - -.fa.fa-bathtub:before { - content: "\f2cd"; } - -.fa.fa-s15:before { - content: "\f2cd"; } - -.fa.fa-window-maximize { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-window-restore { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-times-rectangle:before { - content: "\f410"; } - -.fa.fa-window-close-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-window-close-o:before { - content: "\f410"; } - -.fa.fa-times-rectangle-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-times-rectangle-o:before { - content: "\f410"; } - -.fa.fa-bandcamp { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-grav { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-etsy { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-imdb { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-ravelry { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-eercast { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-eercast:before { - content: "\f2da"; } - -.fa.fa-snowflake-o { - font-family: 'Font Awesome 6 Free'; - font-weight: 400; } - -.fa.fa-snowflake-o:before { - content: "\f2dc"; } - -.fa.fa-superpowers { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-wpexplorer { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } - -.fa.fa-meetup { - font-family: 'Font Awesome 6 Brands'; - font-weight: 400; } diff --git a/resources/fontawesome/css/v4-shims.min.css b/resources/fontawesome/css/v4-shims.min.css deleted file mode 100644 index 09baf5f..0000000 --- a/resources/fontawesome/css/v4-shims.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -.fa.fa-glass:before{content:"\f000"}.fa.fa-envelope-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-envelope-o:before{content:"\f0e0"}.fa.fa-star-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-star-o:before{content:"\f005"}.fa.fa-close:before,.fa.fa-remove:before{content:"\f00d"}.fa.fa-gear:before{content:"\f013"}.fa.fa-trash-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-trash-o:before{content:"\f2ed"}.fa.fa-home:before{content:"\f015"}.fa.fa-file-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-o:before{content:"\f15b"}.fa.fa-clock-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-clock-o:before{content:"\f017"}.fa.fa-arrow-circle-o-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-arrow-circle-o-down:before{content:"\f358"}.fa.fa-arrow-circle-o-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-arrow-circle-o-up:before{content:"\f35b"}.fa.fa-play-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-play-circle-o:before{content:"\f144"}.fa.fa-repeat:before,.fa.fa-rotate-right:before{content:"\f01e"}.fa.fa-refresh:before{content:"\f021"}.fa.fa-list-alt{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-list-alt:before{content:"\f022"}.fa.fa-dedent:before{content:"\f03b"}.fa.fa-video-camera:before{content:"\f03d"}.fa.fa-picture-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-picture-o:before{content:"\f03e"}.fa.fa-photo{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-photo:before{content:"\f03e"}.fa.fa-image{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-image:before{content:"\f03e"}.fa.fa-map-marker:before{content:"\f3c5"}.fa.fa-pencil-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-pencil-square-o:before{content:"\f044"}.fa.fa-edit{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-edit:before{content:"\f044"}.fa.fa-share-square-o:before{content:"\f14d"}.fa.fa-check-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-check-square-o:before{content:"\f14a"}.fa.fa-arrows:before{content:"\f0b2"}.fa.fa-times-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-times-circle-o:before{content:"\f057"}.fa.fa-check-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-check-circle-o:before{content:"\f058"}.fa.fa-mail-forward:before{content:"\f064"}.fa.fa-expand:before{content:"\f424"}.fa.fa-compress:before{content:"\f422"}.fa.fa-eye,.fa.fa-eye-slash{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-warning:before{content:"\f071"}.fa.fa-calendar:before{content:"\f073"}.fa.fa-arrows-v:before{content:"\f338"}.fa.fa-arrows-h:before{content:"\f337"}.fa.fa-bar-chart-o:before,.fa.fa-bar-chart:before{content:"\e0e3"}.fa.fa-twitter-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-twitter-square:before{content:"\f081"}.fa.fa-facebook-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-facebook-square:before{content:"\f082"}.fa.fa-gears:before{content:"\f085"}.fa.fa-thumbs-o-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-thumbs-o-up:before{content:"\f164"}.fa.fa-thumbs-o-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-thumbs-o-down:before{content:"\f165"}.fa.fa-heart-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-heart-o:before{content:"\f004"}.fa.fa-sign-out:before{content:"\f2f5"}.fa.fa-linkedin-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-linkedin-square:before{content:"\f08c"}.fa.fa-thumb-tack:before{content:"\f08d"}.fa.fa-external-link:before{content:"\f35d"}.fa.fa-sign-in:before{content:"\f2f6"}.fa.fa-github-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-github-square:before{content:"\f092"}.fa.fa-lemon-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-lemon-o:before{content:"\f094"}.fa.fa-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-square-o:before{content:"\f0c8"}.fa.fa-bookmark-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-bookmark-o:before{content:"\f02e"}.fa.fa-facebook,.fa.fa-twitter{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-facebook:before{content:"\f39e"}.fa.fa-facebook-f{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-facebook-f:before{content:"\f39e"}.fa.fa-github{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-credit-card{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-feed:before{content:"\f09e"}.fa.fa-hdd-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hdd-o:before{content:"\f0a0"}.fa.fa-hand-o-right{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-o-right:before{content:"\f0a4"}.fa.fa-hand-o-left{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-o-left:before{content:"\f0a5"}.fa.fa-hand-o-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-o-up:before{content:"\f0a6"}.fa.fa-hand-o-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-o-down:before{content:"\f0a7"}.fa.fa-globe:before{content:"\f57d"}.fa.fa-tasks:before{content:"\f828"}.fa.fa-arrows-alt:before{content:"\f31e"}.fa.fa-group:before{content:"\f0c0"}.fa.fa-chain:before{content:"\f0c1"}.fa.fa-cut:before{content:"\f0c4"}.fa.fa-files-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-files-o:before{content:"\f0c5"}.fa.fa-floppy-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-floppy-o:before{content:"\f0c7"}.fa.fa-save{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-save:before{content:"\f0c7"}.fa.fa-navicon:before,.fa.fa-reorder:before{content:"\f0c9"}.fa.fa-magic:before{content:"\e2ca"}.fa.fa-pinterest,.fa.fa-pinterest-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-pinterest-square:before{content:"\f0d3"}.fa.fa-google-plus-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-google-plus-square:before{content:"\f0d4"}.fa.fa-google-plus{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-google-plus:before{content:"\f0d5"}.fa.fa-money:before{content:"\f3d1"}.fa.fa-unsorted:before{content:"\f0dc"}.fa.fa-sort-desc:before{content:"\f0dd"}.fa.fa-sort-asc:before{content:"\f0de"}.fa.fa-linkedin{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-linkedin:before{content:"\f0e1"}.fa.fa-rotate-left:before{content:"\f0e2"}.fa.fa-legal:before{content:"\f0e3"}.fa.fa-dashboard:before,.fa.fa-tachometer:before{content:"\f625"}.fa.fa-comment-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-comment-o:before{content:"\f075"}.fa.fa-comments-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-comments-o:before{content:"\f086"}.fa.fa-flash:before{content:"\f0e7"}.fa.fa-clipboard:before{content:"\f0ea"}.fa.fa-lightbulb-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-lightbulb-o:before{content:"\f0eb"}.fa.fa-exchange:before{content:"\f362"}.fa.fa-cloud-download:before{content:"\f0ed"}.fa.fa-cloud-upload:before{content:"\f0ee"}.fa.fa-bell-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-bell-o:before{content:"\f0f3"}.fa.fa-cutlery:before{content:"\f2e7"}.fa.fa-file-text-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-text-o:before{content:"\f15c"}.fa.fa-building-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-building-o:before{content:"\f1ad"}.fa.fa-hospital-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hospital-o:before{content:"\f0f8"}.fa.fa-tablet:before{content:"\f3fa"}.fa.fa-mobile-phone:before,.fa.fa-mobile:before{content:"\f3cd"}.fa.fa-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-circle-o:before{content:"\f111"}.fa.fa-mail-reply:before{content:"\f3e5"}.fa.fa-github-alt{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-folder-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-folder-o:before{content:"\f07b"}.fa.fa-folder-open-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-folder-open-o:before{content:"\f07c"}.fa.fa-smile-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-smile-o:before{content:"\f118"}.fa.fa-frown-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-frown-o:before{content:"\f119"}.fa.fa-meh-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-meh-o:before{content:"\f11a"}.fa.fa-keyboard-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-keyboard-o:before{content:"\f11c"}.fa.fa-flag-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-flag-o:before{content:"\f024"}.fa.fa-mail-reply-all:before{content:"\f122"}.fa.fa-star-half-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-star-half-o:before{content:"\f5c0"}.fa.fa-star-half-empty{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-star-half-empty:before{content:"\f5c0"}.fa.fa-star-half-full{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-star-half-full:before{content:"\f5c0"}.fa.fa-code-fork:before{content:"\f126"}.fa.fa-chain-broken:before,.fa.fa-unlink:before{content:"\f127"}.fa.fa-calendar-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-o:before{content:"\f133"}.fa.fa-css3,.fa.fa-html5,.fa.fa-maxcdn{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-unlock-alt:before{content:"\f09c"}.fa.fa-minus-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-minus-square-o:before{content:"\f146"}.fa.fa-level-up:before{content:"\f3bf"}.fa.fa-level-down:before{content:"\f3be"}.fa.fa-pencil-square:before{content:"\f14b"}.fa.fa-external-link-square:before{content:"\f360"}.fa.fa-compass{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-down:before{content:"\f150"}.fa.fa-toggle-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-toggle-down:before{content:"\f150"}.fa.fa-caret-square-o-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-up:before{content:"\f151"}.fa.fa-toggle-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-toggle-up:before{content:"\f151"}.fa.fa-caret-square-o-right{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-right:before{content:"\f152"}.fa.fa-toggle-right{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-toggle-right:before{content:"\f152"}.fa.fa-eur:before,.fa.fa-euro:before{content:"\f153"}.fa.fa-gbp:before{content:"\f154"}.fa.fa-dollar:before,.fa.fa-usd:before{content:"\24"}.fa.fa-inr:before,.fa.fa-rupee:before{content:"\e1bc"}.fa.fa-cny:before,.fa.fa-jpy:before,.fa.fa-rmb:before,.fa.fa-yen:before{content:"\f157"}.fa.fa-rouble:before,.fa.fa-rub:before,.fa.fa-ruble:before{content:"\f158"}.fa.fa-krw:before,.fa.fa-won:before{content:"\f159"}.fa.fa-bitcoin,.fa.fa-btc{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-bitcoin:before{content:"\f15a"}.fa.fa-file-text:before{content:"\f15c"}.fa.fa-sort-alpha-asc:before{content:"\f15d"}.fa.fa-sort-alpha-desc:before{content:"\f881"}.fa.fa-sort-amount-asc:before{content:"\f884"}.fa.fa-sort-amount-desc:before{content:"\f160"}.fa.fa-sort-numeric-asc:before{content:"\f162"}.fa.fa-sort-numeric-desc:before{content:"\f886"}.fa.fa-youtube-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-youtube-square:before{content:"\f431"}.fa.fa-xing,.fa.fa-xing-square,.fa.fa-youtube{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-xing-square:before{content:"\f169"}.fa.fa-youtube-play{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-youtube-play:before{content:"\f167"}.fa.fa-adn,.fa.fa-bitbucket,.fa.fa-bitbucket-square,.fa.fa-dropbox,.fa.fa-flickr,.fa.fa-instagram,.fa.fa-stack-overflow{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-bitbucket-square:before{content:"\f171"}.fa.fa-tumblr,.fa.fa-tumblr-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-tumblr-square:before{content:"\f174"}.fa.fa-long-arrow-down:before{content:"\f309"}.fa.fa-long-arrow-up:before{content:"\f30c"}.fa.fa-long-arrow-left:before{content:"\f30a"}.fa.fa-long-arrow-right:before{content:"\f30b"}.fa.fa-android,.fa.fa-apple,.fa.fa-dribbble,.fa.fa-foursquare,.fa.fa-gittip,.fa.fa-gratipay,.fa.fa-linux,.fa.fa-skype,.fa.fa-trello,.fa.fa-windows{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-gittip:before{content:"\f184"}.fa.fa-sun-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-sun-o:before{content:"\f185"}.fa.fa-moon-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-moon-o:before{content:"\f186"}.fa.fa-pagelines,.fa.fa-renren,.fa.fa-stack-exchange,.fa.fa-vk,.fa.fa-weibo{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-arrow-circle-o-right{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-arrow-circle-o-right:before{content:"\f35a"}.fa.fa-arrow-circle-o-left{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-arrow-circle-o-left:before{content:"\f359"}.fa.fa-caret-square-o-left{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-left:before{content:"\f191"}.fa.fa-toggle-left{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-toggle-left:before{content:"\f191"}.fa.fa-dot-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-dot-circle-o:before{content:"\f192"}.fa.fa-vimeo-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-vimeo-square:before{content:"\f194"}.fa.fa-try:before,.fa.fa-turkish-lira:before{content:"\e2bb"}.fa.fa-plus-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-plus-square-o:before{content:"\f0fe"}.fa.fa-openid,.fa.fa-slack,.fa.fa-wordpress{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-bank:before,.fa.fa-institution:before{content:"\f19c"}.fa.fa-mortar-board:before{content:"\f19d"}.fa.fa-google,.fa.fa-reddit,.fa.fa-reddit-square,.fa.fa-yahoo{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-reddit-square:before{content:"\f1a2"}.fa.fa-behance,.fa.fa-behance-square,.fa.fa-delicious,.fa.fa-digg,.fa.fa-drupal,.fa.fa-joomla,.fa.fa-pied-piper-alt,.fa.fa-pied-piper-pp,.fa.fa-stumbleupon,.fa.fa-stumbleupon-circle{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-behance-square:before{content:"\f1b5"}.fa.fa-steam,.fa.fa-steam-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-steam-square:before{content:"\f1b7"}.fa.fa-automobile:before{content:"\f1b9"}.fa.fa-cab:before{content:"\f1ba"}.fa.fa-deviantart,.fa.fa-soundcloud,.fa.fa-spotify{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-file-pdf-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-pdf-o:before{content:"\f1c1"}.fa.fa-file-word-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-word-o:before{content:"\f1c2"}.fa.fa-file-excel-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-excel-o:before{content:"\f1c3"}.fa.fa-file-powerpoint-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-powerpoint-o:before{content:"\f1c4"}.fa.fa-file-image-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-image-o:before{content:"\f1c5"}.fa.fa-file-photo-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-photo-o:before{content:"\f1c5"}.fa.fa-file-picture-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-picture-o:before{content:"\f1c5"}.fa.fa-file-archive-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-archive-o:before{content:"\f1c6"}.fa.fa-file-zip-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-zip-o:before{content:"\f1c6"}.fa.fa-file-audio-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-audio-o:before{content:"\f1c7"}.fa.fa-file-sound-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-sound-o:before{content:"\f1c7"}.fa.fa-file-video-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-video-o:before{content:"\f1c8"}.fa.fa-file-movie-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-movie-o:before{content:"\f1c8"}.fa.fa-file-code-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-code-o:before{content:"\f1c9"}.fa.fa-codepen,.fa.fa-jsfiddle,.fa.fa-vine{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-life-bouy:before,.fa.fa-life-buoy:before,.fa.fa-life-saver:before,.fa.fa-support:before{content:"\f1cd"}.fa.fa-circle-o-notch:before{content:"\f1ce"}.fa.fa-ra,.fa.fa-rebel{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-ra:before{content:"\f1d0"}.fa.fa-resistance{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-resistance:before{content:"\f1d0"}.fa.fa-empire,.fa.fa-ge{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-ge:before{content:"\f1d1"}.fa.fa-git-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-git-square:before{content:"\f1d2"}.fa.fa-git,.fa.fa-hacker-news,.fa.fa-y-combinator-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-y-combinator-square:before{content:"\f1d4"}.fa.fa-yc-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-yc-square:before{content:"\f1d4"}.fa.fa-qq,.fa.fa-tencent-weibo,.fa.fa-wechat,.fa.fa-weixin{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-wechat:before{content:"\f1d7"}.fa.fa-send:before{content:"\f1d8"}.fa.fa-paper-plane-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-paper-plane-o:before{content:"\f1d8"}.fa.fa-send-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-send-o:before{content:"\f1d8"}.fa.fa-circle-thin{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-circle-thin:before{content:"\f111"}.fa.fa-header:before{content:"\f1dc"}.fa.fa-futbol-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-futbol-o:before{content:"\f1e3"}.fa.fa-soccer-ball-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-soccer-ball-o:before{content:"\f1e3"}.fa.fa-slideshare,.fa.fa-twitch,.fa.fa-yelp{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-newspaper-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-newspaper-o:before{content:"\f1ea"}.fa.fa-cc-amex,.fa.fa-cc-discover,.fa.fa-cc-mastercard,.fa.fa-cc-paypal,.fa.fa-cc-stripe,.fa.fa-cc-visa,.fa.fa-google-wallet,.fa.fa-paypal{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-bell-slash-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-bell-slash-o:before{content:"\f1f6"}.fa.fa-trash:before{content:"\f2ed"}.fa.fa-copyright{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-eyedropper:before{content:"\f1fb"}.fa.fa-area-chart:before{content:"\f1fe"}.fa.fa-pie-chart:before{content:"\f200"}.fa.fa-line-chart:before{content:"\f201"}.fa.fa-lastfm,.fa.fa-lastfm-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-lastfm-square:before{content:"\f203"}.fa.fa-angellist,.fa.fa-ioxhost{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-cc{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-cc:before{content:"\f20a"}.fa.fa-ils:before,.fa.fa-shekel:before,.fa.fa-sheqel:before{content:"\f20b"}.fa.fa-buysellads,.fa.fa-connectdevelop,.fa.fa-dashcube,.fa.fa-forumbee,.fa.fa-leanpub,.fa.fa-sellsy,.fa.fa-shirtsinbulk,.fa.fa-simplybuilt,.fa.fa-skyatlas{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-diamond{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-diamond:before{content:"\f3a5"}.fa.fa-intersex:before,.fa.fa-transgender:before{content:"\f224"}.fa.fa-transgender-alt:before{content:"\f225"}.fa.fa-facebook-official{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-facebook-official:before{content:"\f09a"}.fa.fa-pinterest-p,.fa.fa-whatsapp{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-hotel:before{content:"\f236"}.fa.fa-medium,.fa.fa-viacoin,.fa.fa-y-combinator,.fa.fa-yc{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-yc:before{content:"\f23b"}.fa.fa-expeditedssl,.fa.fa-opencart,.fa.fa-optin-monster{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-battery-4:before,.fa.fa-battery:before{content:"\f240"}.fa.fa-battery-3:before{content:"\f241"}.fa.fa-battery-2:before{content:"\f242"}.fa.fa-battery-1:before{content:"\f243"}.fa.fa-battery-0:before{content:"\f244"}.fa.fa-object-group,.fa.fa-object-ungroup,.fa.fa-sticky-note-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-sticky-note-o:before{content:"\f249"}.fa.fa-cc-diners-club,.fa.fa-cc-jcb{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-clone{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hourglass-o:before{content:"\f254"}.fa.fa-hourglass-1:before{content:"\f251"}.fa.fa-hourglass-2:before{content:"\f252"}.fa.fa-hourglass-3:before{content:"\f253"}.fa.fa-hand-rock-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-rock-o:before{content:"\f255"}.fa.fa-hand-grab-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-grab-o:before{content:"\f255"}.fa.fa-hand-paper-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-paper-o:before{content:"\f256"}.fa.fa-hand-stop-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-stop-o:before{content:"\f256"}.fa.fa-hand-scissors-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-scissors-o:before{content:"\f257"}.fa.fa-hand-lizard-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-lizard-o:before{content:"\f258"}.fa.fa-hand-spock-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-spock-o:before{content:"\f259"}.fa.fa-hand-pointer-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-pointer-o:before{content:"\f25a"}.fa.fa-hand-peace-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-peace-o:before{content:"\f25b"}.fa.fa-registered{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-creative-commons,.fa.fa-gg,.fa.fa-gg-circle,.fa.fa-odnoklassniki,.fa.fa-odnoklassniki-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-odnoklassniki-square:before{content:"\f264"}.fa.fa-chrome,.fa.fa-firefox,.fa.fa-get-pocket,.fa.fa-internet-explorer,.fa.fa-opera,.fa.fa-safari,.fa.fa-wikipedia-w{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-television:before{content:"\f26c"}.fa.fa-500px,.fa.fa-amazon,.fa.fa-contao{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-calendar-plus-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-plus-o:before{content:"\f271"}.fa.fa-calendar-minus-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-minus-o:before{content:"\f272"}.fa.fa-calendar-times-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-times-o:before{content:"\f273"}.fa.fa-calendar-check-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-check-o:before{content:"\f274"}.fa.fa-map-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-map-o:before{content:"\f279"}.fa.fa-commenting:before{content:"\f4ad"}.fa.fa-commenting-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-commenting-o:before{content:"\f4ad"}.fa.fa-houzz,.fa.fa-vimeo{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-vimeo:before{content:"\f27d"}.fa.fa-black-tie,.fa.fa-edge,.fa.fa-fonticons,.fa.fa-reddit-alien{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-credit-card-alt:before{content:"\f09d"}.fa.fa-codiepie,.fa.fa-fort-awesome,.fa.fa-mixcloud,.fa.fa-modx,.fa.fa-product-hunt,.fa.fa-scribd,.fa.fa-usb{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-pause-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-pause-circle-o:before{content:"\f28b"}.fa.fa-stop-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-stop-circle-o:before{content:"\f28d"}.fa.fa-bluetooth,.fa.fa-bluetooth-b,.fa.fa-envira,.fa.fa-gitlab,.fa.fa-wheelchair-alt,.fa.fa-wpbeginner,.fa.fa-wpforms{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-wheelchair-alt:before{content:"\f368"}.fa.fa-question-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-question-circle-o:before{content:"\f059"}.fa.fa-volume-control-phone:before{content:"\f2a0"}.fa.fa-asl-interpreting:before{content:"\f2a3"}.fa.fa-deafness:before,.fa.fa-hard-of-hearing:before{content:"\f2a4"}.fa.fa-glide,.fa.fa-glide-g{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-signing:before{content:"\f2a7"}.fa.fa-viadeo,.fa.fa-viadeo-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-viadeo-square:before{content:"\f2aa"}.fa.fa-snapchat,.fa.fa-snapchat-ghost{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-snapchat-ghost:before{content:"\f2ab"}.fa.fa-snapchat-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-snapchat-square:before{content:"\f2ad"}.fa.fa-first-order,.fa.fa-google-plus-official,.fa.fa-pied-piper,.fa.fa-themeisle,.fa.fa-yoast{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-google-plus-official:before{content:"\f2b3"}.fa.fa-google-plus-circle{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-google-plus-circle:before{content:"\f2b3"}.fa.fa-fa,.fa.fa-font-awesome{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-fa:before{content:"\f2b4"}.fa.fa-handshake-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-handshake-o:before{content:"\f2b5"}.fa.fa-envelope-open-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-envelope-open-o:before{content:"\f2b6"}.fa.fa-linode{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-address-book-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-address-book-o:before{content:"\f2b9"}.fa.fa-vcard:before{content:"\f2bb"}.fa.fa-address-card-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-address-card-o:before{content:"\f2bb"}.fa.fa-vcard-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-vcard-o:before{content:"\f2bb"}.fa.fa-user-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-user-circle-o:before{content:"\f2bd"}.fa.fa-user-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-user-o:before{content:"\f007"}.fa.fa-id-badge{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-drivers-license:before{content:"\f2c2"}.fa.fa-id-card-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-id-card-o:before{content:"\f2c2"}.fa.fa-drivers-license-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-drivers-license-o:before{content:"\f2c2"}.fa.fa-free-code-camp,.fa.fa-quora,.fa.fa-telegram{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-thermometer-4:before,.fa.fa-thermometer:before{content:"\f2c7"}.fa.fa-thermometer-3:before{content:"\f2c8"}.fa.fa-thermometer-2:before{content:"\f2c9"}.fa.fa-thermometer-1:before{content:"\f2ca"}.fa.fa-thermometer-0:before{content:"\f2cb"}.fa.fa-bathtub:before,.fa.fa-s15:before{content:"\f2cd"}.fa.fa-window-maximize,.fa.fa-window-restore{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-times-rectangle:before{content:"\f410"}.fa.fa-window-close-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-window-close-o:before{content:"\f410"}.fa.fa-times-rectangle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-times-rectangle-o:before{content:"\f410"}.fa.fa-bandcamp,.fa.fa-eercast,.fa.fa-etsy,.fa.fa-grav,.fa.fa-imdb,.fa.fa-ravelry{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-eercast:before{content:"\f2da"}.fa.fa-snowflake-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-snowflake-o:before{content:"\f2dc"}.fa.fa-meetup,.fa.fa-superpowers,.fa.fa-wpexplorer{font-family:"Font Awesome 6 Brands";font-weight:400} \ No newline at end of file diff --git a/resources/fontawesome/css/v5-font-face.css b/resources/fontawesome/css/v5-font-face.css deleted file mode 100644 index 7b736b1..0000000 --- a/resources/fontawesome/css/v5-font-face.css +++ /dev/null @@ -1,22 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -@font-face { - font-family: 'Font Awesome 5 Brands'; - font-display: block; - font-weight: 400; - src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } - -@font-face { - font-family: 'Font Awesome 5 Free'; - font-display: block; - font-weight: 900; - src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } - -@font-face { - font-family: 'Font Awesome 5 Free'; - font-display: block; - font-weight: 400; - src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } diff --git a/resources/fontawesome/css/v5-font-face.min.css b/resources/fontawesome/css/v5-font-face.min.css deleted file mode 100644 index 0cb8f13..0000000 --- a/resources/fontawesome/css/v5-font-face.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")} \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/42-group.svg b/resources/fontawesome/svgs/brands/42-group.svg deleted file mode 100644 index 1b5e45d..0000000 --- a/resources/fontawesome/svgs/brands/42-group.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/500px.svg b/resources/fontawesome/svgs/brands/500px.svg deleted file mode 100644 index 1f17fc8..0000000 --- a/resources/fontawesome/svgs/brands/500px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/accessible-icon.svg b/resources/fontawesome/svgs/brands/accessible-icon.svg deleted file mode 100644 index 6b6aff6..0000000 --- a/resources/fontawesome/svgs/brands/accessible-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/accusoft.svg b/resources/fontawesome/svgs/brands/accusoft.svg deleted file mode 100644 index 5f6a73e..0000000 --- a/resources/fontawesome/svgs/brands/accusoft.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/adn.svg b/resources/fontawesome/svgs/brands/adn.svg deleted file mode 100644 index 54982ba..0000000 --- a/resources/fontawesome/svgs/brands/adn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/adversal.svg b/resources/fontawesome/svgs/brands/adversal.svg deleted file mode 100644 index cddc08d..0000000 --- a/resources/fontawesome/svgs/brands/adversal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/affiliatetheme.svg b/resources/fontawesome/svgs/brands/affiliatetheme.svg deleted file mode 100644 index 1f2b266..0000000 --- a/resources/fontawesome/svgs/brands/affiliatetheme.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/airbnb.svg b/resources/fontawesome/svgs/brands/airbnb.svg deleted file mode 100644 index ecd4172..0000000 --- a/resources/fontawesome/svgs/brands/airbnb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/algolia.svg b/resources/fontawesome/svgs/brands/algolia.svg deleted file mode 100644 index fa5afbf..0000000 --- a/resources/fontawesome/svgs/brands/algolia.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/alipay.svg b/resources/fontawesome/svgs/brands/alipay.svg deleted file mode 100644 index 10b10c9..0000000 --- a/resources/fontawesome/svgs/brands/alipay.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/amazon-pay.svg b/resources/fontawesome/svgs/brands/amazon-pay.svg deleted file mode 100644 index cdca9ad..0000000 --- a/resources/fontawesome/svgs/brands/amazon-pay.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/amazon.svg b/resources/fontawesome/svgs/brands/amazon.svg deleted file mode 100644 index e33ea7a..0000000 --- a/resources/fontawesome/svgs/brands/amazon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/amilia.svg b/resources/fontawesome/svgs/brands/amilia.svg deleted file mode 100644 index ca939b3..0000000 --- a/resources/fontawesome/svgs/brands/amilia.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/android.svg b/resources/fontawesome/svgs/brands/android.svg deleted file mode 100644 index c287199..0000000 --- a/resources/fontawesome/svgs/brands/android.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/angellist.svg b/resources/fontawesome/svgs/brands/angellist.svg deleted file mode 100644 index 50b5cfd..0000000 --- a/resources/fontawesome/svgs/brands/angellist.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/angrycreative.svg b/resources/fontawesome/svgs/brands/angrycreative.svg deleted file mode 100644 index b510344..0000000 --- a/resources/fontawesome/svgs/brands/angrycreative.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/angular.svg b/resources/fontawesome/svgs/brands/angular.svg deleted file mode 100644 index feb2a49..0000000 --- a/resources/fontawesome/svgs/brands/angular.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/app-store-ios.svg b/resources/fontawesome/svgs/brands/app-store-ios.svg deleted file mode 100644 index 25cf325..0000000 --- a/resources/fontawesome/svgs/brands/app-store-ios.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/app-store.svg b/resources/fontawesome/svgs/brands/app-store.svg deleted file mode 100644 index 999b116..0000000 --- a/resources/fontawesome/svgs/brands/app-store.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/apper.svg b/resources/fontawesome/svgs/brands/apper.svg deleted file mode 100644 index e579c54..0000000 --- a/resources/fontawesome/svgs/brands/apper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/apple-pay.svg b/resources/fontawesome/svgs/brands/apple-pay.svg deleted file mode 100644 index 9c90232..0000000 --- a/resources/fontawesome/svgs/brands/apple-pay.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/apple.svg b/resources/fontawesome/svgs/brands/apple.svg deleted file mode 100644 index 2540c78..0000000 --- a/resources/fontawesome/svgs/brands/apple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/artstation.svg b/resources/fontawesome/svgs/brands/artstation.svg deleted file mode 100644 index 520d755..0000000 --- a/resources/fontawesome/svgs/brands/artstation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/asymmetrik.svg b/resources/fontawesome/svgs/brands/asymmetrik.svg deleted file mode 100644 index 5a016a8..0000000 --- a/resources/fontawesome/svgs/brands/asymmetrik.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/atlassian.svg b/resources/fontawesome/svgs/brands/atlassian.svg deleted file mode 100644 index e4c2e50..0000000 --- a/resources/fontawesome/svgs/brands/atlassian.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/audible.svg b/resources/fontawesome/svgs/brands/audible.svg deleted file mode 100644 index e010329..0000000 --- a/resources/fontawesome/svgs/brands/audible.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/autoprefixer.svg b/resources/fontawesome/svgs/brands/autoprefixer.svg deleted file mode 100644 index b8c4bbc..0000000 --- a/resources/fontawesome/svgs/brands/autoprefixer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/avianex.svg b/resources/fontawesome/svgs/brands/avianex.svg deleted file mode 100644 index c52e8c8..0000000 --- a/resources/fontawesome/svgs/brands/avianex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/aviato.svg b/resources/fontawesome/svgs/brands/aviato.svg deleted file mode 100644 index 8c26685..0000000 --- a/resources/fontawesome/svgs/brands/aviato.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/aws.svg b/resources/fontawesome/svgs/brands/aws.svg deleted file mode 100644 index 1547fa0..0000000 --- a/resources/fontawesome/svgs/brands/aws.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bandcamp.svg b/resources/fontawesome/svgs/brands/bandcamp.svg deleted file mode 100644 index 24aeb08..0000000 --- a/resources/fontawesome/svgs/brands/bandcamp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/battle-net.svg b/resources/fontawesome/svgs/brands/battle-net.svg deleted file mode 100644 index 1b49d11..0000000 --- a/resources/fontawesome/svgs/brands/battle-net.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/behance.svg b/resources/fontawesome/svgs/brands/behance.svg deleted file mode 100644 index 0ec1746..0000000 --- a/resources/fontawesome/svgs/brands/behance.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bilibili.svg b/resources/fontawesome/svgs/brands/bilibili.svg deleted file mode 100644 index 8b8aff0..0000000 --- a/resources/fontawesome/svgs/brands/bilibili.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bimobject.svg b/resources/fontawesome/svgs/brands/bimobject.svg deleted file mode 100644 index 2c4f1e2..0000000 --- a/resources/fontawesome/svgs/brands/bimobject.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bitbucket.svg b/resources/fontawesome/svgs/brands/bitbucket.svg deleted file mode 100644 index e17035a..0000000 --- a/resources/fontawesome/svgs/brands/bitbucket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bitcoin.svg b/resources/fontawesome/svgs/brands/bitcoin.svg deleted file mode 100644 index 86b569d..0000000 --- a/resources/fontawesome/svgs/brands/bitcoin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bity.svg b/resources/fontawesome/svgs/brands/bity.svg deleted file mode 100644 index 13e4dee..0000000 --- a/resources/fontawesome/svgs/brands/bity.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/black-tie.svg b/resources/fontawesome/svgs/brands/black-tie.svg deleted file mode 100644 index 9879475..0000000 --- a/resources/fontawesome/svgs/brands/black-tie.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/blackberry.svg b/resources/fontawesome/svgs/brands/blackberry.svg deleted file mode 100644 index 220d866..0000000 --- a/resources/fontawesome/svgs/brands/blackberry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/blogger-b.svg b/resources/fontawesome/svgs/brands/blogger-b.svg deleted file mode 100644 index 701068f..0000000 --- a/resources/fontawesome/svgs/brands/blogger-b.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/blogger.svg b/resources/fontawesome/svgs/brands/blogger.svg deleted file mode 100644 index 8eea140..0000000 --- a/resources/fontawesome/svgs/brands/blogger.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bluesky.svg b/resources/fontawesome/svgs/brands/bluesky.svg deleted file mode 100644 index 5fe8ad3..0000000 --- a/resources/fontawesome/svgs/brands/bluesky.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bluetooth-b.svg b/resources/fontawesome/svgs/brands/bluetooth-b.svg deleted file mode 100644 index 43cd196..0000000 --- a/resources/fontawesome/svgs/brands/bluetooth-b.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bluetooth.svg b/resources/fontawesome/svgs/brands/bluetooth.svg deleted file mode 100644 index 3b9f5a5..0000000 --- a/resources/fontawesome/svgs/brands/bluetooth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bootstrap.svg b/resources/fontawesome/svgs/brands/bootstrap.svg deleted file mode 100644 index 2644ad4..0000000 --- a/resources/fontawesome/svgs/brands/bootstrap.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/bots.svg b/resources/fontawesome/svgs/brands/bots.svg deleted file mode 100644 index 4c4e68e..0000000 --- a/resources/fontawesome/svgs/brands/bots.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/brave-reverse.svg b/resources/fontawesome/svgs/brands/brave-reverse.svg deleted file mode 100644 index 61055b5..0000000 --- a/resources/fontawesome/svgs/brands/brave-reverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/brave.svg b/resources/fontawesome/svgs/brands/brave.svg deleted file mode 100644 index 5003d78..0000000 --- a/resources/fontawesome/svgs/brands/brave.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/btc.svg b/resources/fontawesome/svgs/brands/btc.svg deleted file mode 100644 index e83566e..0000000 --- a/resources/fontawesome/svgs/brands/btc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/buffer.svg b/resources/fontawesome/svgs/brands/buffer.svg deleted file mode 100644 index 1001127..0000000 --- a/resources/fontawesome/svgs/brands/buffer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/buromobelexperte.svg b/resources/fontawesome/svgs/brands/buromobelexperte.svg deleted file mode 100644 index 39bd62c..0000000 --- a/resources/fontawesome/svgs/brands/buromobelexperte.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/buy-n-large.svg b/resources/fontawesome/svgs/brands/buy-n-large.svg deleted file mode 100644 index 60ebc1d..0000000 --- a/resources/fontawesome/svgs/brands/buy-n-large.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/buysellads.svg b/resources/fontawesome/svgs/brands/buysellads.svg deleted file mode 100644 index 36f4a8f..0000000 --- a/resources/fontawesome/svgs/brands/buysellads.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/canadian-maple-leaf.svg b/resources/fontawesome/svgs/brands/canadian-maple-leaf.svg deleted file mode 100644 index 7024795..0000000 --- a/resources/fontawesome/svgs/brands/canadian-maple-leaf.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-amazon-pay.svg b/resources/fontawesome/svgs/brands/cc-amazon-pay.svg deleted file mode 100644 index c3aaee7..0000000 --- a/resources/fontawesome/svgs/brands/cc-amazon-pay.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-amex.svg b/resources/fontawesome/svgs/brands/cc-amex.svg deleted file mode 100644 index aa6c8df..0000000 --- a/resources/fontawesome/svgs/brands/cc-amex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-apple-pay.svg b/resources/fontawesome/svgs/brands/cc-apple-pay.svg deleted file mode 100644 index dab0dcb..0000000 --- a/resources/fontawesome/svgs/brands/cc-apple-pay.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-diners-club.svg b/resources/fontawesome/svgs/brands/cc-diners-club.svg deleted file mode 100644 index 85c7922..0000000 --- a/resources/fontawesome/svgs/brands/cc-diners-club.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-discover.svg b/resources/fontawesome/svgs/brands/cc-discover.svg deleted file mode 100644 index a5d13ad..0000000 --- a/resources/fontawesome/svgs/brands/cc-discover.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-jcb.svg b/resources/fontawesome/svgs/brands/cc-jcb.svg deleted file mode 100644 index 9a0c6b6..0000000 --- a/resources/fontawesome/svgs/brands/cc-jcb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-mastercard.svg b/resources/fontawesome/svgs/brands/cc-mastercard.svg deleted file mode 100644 index b7b3b85..0000000 --- a/resources/fontawesome/svgs/brands/cc-mastercard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-paypal.svg b/resources/fontawesome/svgs/brands/cc-paypal.svg deleted file mode 100644 index d5281da..0000000 --- a/resources/fontawesome/svgs/brands/cc-paypal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-stripe.svg b/resources/fontawesome/svgs/brands/cc-stripe.svg deleted file mode 100644 index 9a93918..0000000 --- a/resources/fontawesome/svgs/brands/cc-stripe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cc-visa.svg b/resources/fontawesome/svgs/brands/cc-visa.svg deleted file mode 100644 index 23526c1..0000000 --- a/resources/fontawesome/svgs/brands/cc-visa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/centercode.svg b/resources/fontawesome/svgs/brands/centercode.svg deleted file mode 100644 index d3ebe7c..0000000 --- a/resources/fontawesome/svgs/brands/centercode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/centos.svg b/resources/fontawesome/svgs/brands/centos.svg deleted file mode 100644 index 01bb0d1..0000000 --- a/resources/fontawesome/svgs/brands/centos.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/chrome.svg b/resources/fontawesome/svgs/brands/chrome.svg deleted file mode 100644 index 70f9182..0000000 --- a/resources/fontawesome/svgs/brands/chrome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/chromecast.svg b/resources/fontawesome/svgs/brands/chromecast.svg deleted file mode 100644 index 5308a4e..0000000 --- a/resources/fontawesome/svgs/brands/chromecast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cloudflare.svg b/resources/fontawesome/svgs/brands/cloudflare.svg deleted file mode 100644 index 5d68627..0000000 --- a/resources/fontawesome/svgs/brands/cloudflare.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cloudscale.svg b/resources/fontawesome/svgs/brands/cloudscale.svg deleted file mode 100644 index d107163..0000000 --- a/resources/fontawesome/svgs/brands/cloudscale.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cloudsmith.svg b/resources/fontawesome/svgs/brands/cloudsmith.svg deleted file mode 100644 index c9f0ad7..0000000 --- a/resources/fontawesome/svgs/brands/cloudsmith.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cloudversify.svg b/resources/fontawesome/svgs/brands/cloudversify.svg deleted file mode 100644 index b1fd5b2..0000000 --- a/resources/fontawesome/svgs/brands/cloudversify.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cmplid.svg b/resources/fontawesome/svgs/brands/cmplid.svg deleted file mode 100644 index 52e273e..0000000 --- a/resources/fontawesome/svgs/brands/cmplid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/codepen.svg b/resources/fontawesome/svgs/brands/codepen.svg deleted file mode 100644 index 3c2bb5d..0000000 --- a/resources/fontawesome/svgs/brands/codepen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/codiepie.svg b/resources/fontawesome/svgs/brands/codiepie.svg deleted file mode 100644 index 53188c9..0000000 --- a/resources/fontawesome/svgs/brands/codiepie.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/confluence.svg b/resources/fontawesome/svgs/brands/confluence.svg deleted file mode 100644 index 02e4f0b..0000000 --- a/resources/fontawesome/svgs/brands/confluence.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/connectdevelop.svg b/resources/fontawesome/svgs/brands/connectdevelop.svg deleted file mode 100644 index 105a86a..0000000 --- a/resources/fontawesome/svgs/brands/connectdevelop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/contao.svg b/resources/fontawesome/svgs/brands/contao.svg deleted file mode 100644 index bb56e78..0000000 --- a/resources/fontawesome/svgs/brands/contao.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cotton-bureau.svg b/resources/fontawesome/svgs/brands/cotton-bureau.svg deleted file mode 100644 index 0cb6a4d..0000000 --- a/resources/fontawesome/svgs/brands/cotton-bureau.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cpanel.svg b/resources/fontawesome/svgs/brands/cpanel.svg deleted file mode 100644 index 914a85b..0000000 --- a/resources/fontawesome/svgs/brands/cpanel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-by.svg b/resources/fontawesome/svgs/brands/creative-commons-by.svg deleted file mode 100644 index 8771d65..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-by.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-nc-eu.svg b/resources/fontawesome/svgs/brands/creative-commons-nc-eu.svg deleted file mode 100644 index 6faf966..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-nc-eu.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-nc-jp.svg b/resources/fontawesome/svgs/brands/creative-commons-nc-jp.svg deleted file mode 100644 index e72abe6..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-nc-jp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-nc.svg b/resources/fontawesome/svgs/brands/creative-commons-nc.svg deleted file mode 100644 index 1c8c294..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-nc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-nd.svg b/resources/fontawesome/svgs/brands/creative-commons-nd.svg deleted file mode 100644 index 5643f60..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-nd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-pd-alt.svg b/resources/fontawesome/svgs/brands/creative-commons-pd-alt.svg deleted file mode 100644 index 18fd93d..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-pd-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-pd.svg b/resources/fontawesome/svgs/brands/creative-commons-pd.svg deleted file mode 100644 index 96f4f14..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-pd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-remix.svg b/resources/fontawesome/svgs/brands/creative-commons-remix.svg deleted file mode 100644 index 4e64088..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-remix.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-sa.svg b/resources/fontawesome/svgs/brands/creative-commons-sa.svg deleted file mode 100644 index 183ef72..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-sa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-sampling-plus.svg b/resources/fontawesome/svgs/brands/creative-commons-sampling-plus.svg deleted file mode 100644 index 1eebde4..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-sampling-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-sampling.svg b/resources/fontawesome/svgs/brands/creative-commons-sampling.svg deleted file mode 100644 index f7ac9d2..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-sampling.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-share.svg b/resources/fontawesome/svgs/brands/creative-commons-share.svg deleted file mode 100644 index 34cda89..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-share.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons-zero.svg b/resources/fontawesome/svgs/brands/creative-commons-zero.svg deleted file mode 100644 index 1228a4a..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons-zero.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/creative-commons.svg b/resources/fontawesome/svgs/brands/creative-commons.svg deleted file mode 100644 index 6ec3aab..0000000 --- a/resources/fontawesome/svgs/brands/creative-commons.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/critical-role.svg b/resources/fontawesome/svgs/brands/critical-role.svg deleted file mode 100644 index 0821856..0000000 --- a/resources/fontawesome/svgs/brands/critical-role.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/css3-alt.svg b/resources/fontawesome/svgs/brands/css3-alt.svg deleted file mode 100644 index b74cdd7..0000000 --- a/resources/fontawesome/svgs/brands/css3-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/css3.svg b/resources/fontawesome/svgs/brands/css3.svg deleted file mode 100644 index 93fe0de..0000000 --- a/resources/fontawesome/svgs/brands/css3.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/cuttlefish.svg b/resources/fontawesome/svgs/brands/cuttlefish.svg deleted file mode 100644 index 17c93fe..0000000 --- a/resources/fontawesome/svgs/brands/cuttlefish.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/d-and-d-beyond.svg b/resources/fontawesome/svgs/brands/d-and-d-beyond.svg deleted file mode 100644 index f0f7827..0000000 --- a/resources/fontawesome/svgs/brands/d-and-d-beyond.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/d-and-d.svg b/resources/fontawesome/svgs/brands/d-and-d.svg deleted file mode 100644 index 946a86b..0000000 --- a/resources/fontawesome/svgs/brands/d-and-d.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/dailymotion.svg b/resources/fontawesome/svgs/brands/dailymotion.svg deleted file mode 100644 index d4f44c5..0000000 --- a/resources/fontawesome/svgs/brands/dailymotion.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/dashcube.svg b/resources/fontawesome/svgs/brands/dashcube.svg deleted file mode 100644 index d06d976..0000000 --- a/resources/fontawesome/svgs/brands/dashcube.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/debian.svg b/resources/fontawesome/svgs/brands/debian.svg deleted file mode 100644 index 27e2d6d..0000000 --- a/resources/fontawesome/svgs/brands/debian.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/deezer.svg b/resources/fontawesome/svgs/brands/deezer.svg deleted file mode 100644 index 9b3d891..0000000 --- a/resources/fontawesome/svgs/brands/deezer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/delicious.svg b/resources/fontawesome/svgs/brands/delicious.svg deleted file mode 100644 index 73495ba..0000000 --- a/resources/fontawesome/svgs/brands/delicious.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/deploydog.svg b/resources/fontawesome/svgs/brands/deploydog.svg deleted file mode 100644 index 2c92fc6..0000000 --- a/resources/fontawesome/svgs/brands/deploydog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/deskpro.svg b/resources/fontawesome/svgs/brands/deskpro.svg deleted file mode 100644 index 6fea4bb..0000000 --- a/resources/fontawesome/svgs/brands/deskpro.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/dev.svg b/resources/fontawesome/svgs/brands/dev.svg deleted file mode 100644 index 51be392..0000000 --- a/resources/fontawesome/svgs/brands/dev.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/deviantart.svg b/resources/fontawesome/svgs/brands/deviantart.svg deleted file mode 100644 index 19c56a4..0000000 --- a/resources/fontawesome/svgs/brands/deviantart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/dhl.svg b/resources/fontawesome/svgs/brands/dhl.svg deleted file mode 100644 index c357ea0..0000000 --- a/resources/fontawesome/svgs/brands/dhl.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/diaspora.svg b/resources/fontawesome/svgs/brands/diaspora.svg deleted file mode 100644 index 98d60e7..0000000 --- a/resources/fontawesome/svgs/brands/diaspora.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/digg.svg b/resources/fontawesome/svgs/brands/digg.svg deleted file mode 100644 index d01d751..0000000 --- a/resources/fontawesome/svgs/brands/digg.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/digital-ocean.svg b/resources/fontawesome/svgs/brands/digital-ocean.svg deleted file mode 100644 index c7b07a0..0000000 --- a/resources/fontawesome/svgs/brands/digital-ocean.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/discord.svg b/resources/fontawesome/svgs/brands/discord.svg deleted file mode 100644 index 7883ac3..0000000 --- a/resources/fontawesome/svgs/brands/discord.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/discourse.svg b/resources/fontawesome/svgs/brands/discourse.svg deleted file mode 100644 index 70cb7c5..0000000 --- a/resources/fontawesome/svgs/brands/discourse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/dochub.svg b/resources/fontawesome/svgs/brands/dochub.svg deleted file mode 100644 index e4e865f..0000000 --- a/resources/fontawesome/svgs/brands/dochub.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/docker.svg b/resources/fontawesome/svgs/brands/docker.svg deleted file mode 100644 index 1d2fd1a..0000000 --- a/resources/fontawesome/svgs/brands/docker.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/draft2digital.svg b/resources/fontawesome/svgs/brands/draft2digital.svg deleted file mode 100644 index e9194c7..0000000 --- a/resources/fontawesome/svgs/brands/draft2digital.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/dribbble.svg b/resources/fontawesome/svgs/brands/dribbble.svg deleted file mode 100644 index 2ce74c8..0000000 --- a/resources/fontawesome/svgs/brands/dribbble.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/dropbox.svg b/resources/fontawesome/svgs/brands/dropbox.svg deleted file mode 100644 index 09a94f9..0000000 --- a/resources/fontawesome/svgs/brands/dropbox.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/drupal.svg b/resources/fontawesome/svgs/brands/drupal.svg deleted file mode 100644 index d4a8cfe..0000000 --- a/resources/fontawesome/svgs/brands/drupal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/dyalog.svg b/resources/fontawesome/svgs/brands/dyalog.svg deleted file mode 100644 index 57d4fce..0000000 --- a/resources/fontawesome/svgs/brands/dyalog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/earlybirds.svg b/resources/fontawesome/svgs/brands/earlybirds.svg deleted file mode 100644 index 21b89ce..0000000 --- a/resources/fontawesome/svgs/brands/earlybirds.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ebay.svg b/resources/fontawesome/svgs/brands/ebay.svg deleted file mode 100644 index 5fe4944..0000000 --- a/resources/fontawesome/svgs/brands/ebay.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/edge-legacy.svg b/resources/fontawesome/svgs/brands/edge-legacy.svg deleted file mode 100644 index 1c1c152..0000000 --- a/resources/fontawesome/svgs/brands/edge-legacy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/edge.svg b/resources/fontawesome/svgs/brands/edge.svg deleted file mode 100644 index 58dded3..0000000 --- a/resources/fontawesome/svgs/brands/edge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/elementor.svg b/resources/fontawesome/svgs/brands/elementor.svg deleted file mode 100644 index 633c896..0000000 --- a/resources/fontawesome/svgs/brands/elementor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ello.svg b/resources/fontawesome/svgs/brands/ello.svg deleted file mode 100644 index 0fe83d5..0000000 --- a/resources/fontawesome/svgs/brands/ello.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ember.svg b/resources/fontawesome/svgs/brands/ember.svg deleted file mode 100644 index 62916cf..0000000 --- a/resources/fontawesome/svgs/brands/ember.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/empire.svg b/resources/fontawesome/svgs/brands/empire.svg deleted file mode 100644 index c8db252..0000000 --- a/resources/fontawesome/svgs/brands/empire.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/envira.svg b/resources/fontawesome/svgs/brands/envira.svg deleted file mode 100644 index 77fd55b..0000000 --- a/resources/fontawesome/svgs/brands/envira.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/erlang.svg b/resources/fontawesome/svgs/brands/erlang.svg deleted file mode 100644 index 80a7eb5..0000000 --- a/resources/fontawesome/svgs/brands/erlang.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ethereum.svg b/resources/fontawesome/svgs/brands/ethereum.svg deleted file mode 100644 index a7911bd..0000000 --- a/resources/fontawesome/svgs/brands/ethereum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/etsy.svg b/resources/fontawesome/svgs/brands/etsy.svg deleted file mode 100644 index 34b15f6..0000000 --- a/resources/fontawesome/svgs/brands/etsy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/evernote.svg b/resources/fontawesome/svgs/brands/evernote.svg deleted file mode 100644 index 4a0dd3b..0000000 --- a/resources/fontawesome/svgs/brands/evernote.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/expeditedssl.svg b/resources/fontawesome/svgs/brands/expeditedssl.svg deleted file mode 100644 index 96c39ad..0000000 --- a/resources/fontawesome/svgs/brands/expeditedssl.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/facebook-f.svg b/resources/fontawesome/svgs/brands/facebook-f.svg deleted file mode 100644 index 2c9e341..0000000 --- a/resources/fontawesome/svgs/brands/facebook-f.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/facebook-messenger.svg b/resources/fontawesome/svgs/brands/facebook-messenger.svg deleted file mode 100644 index a46911f..0000000 --- a/resources/fontawesome/svgs/brands/facebook-messenger.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/facebook.svg b/resources/fontawesome/svgs/brands/facebook.svg deleted file mode 100644 index d095174..0000000 --- a/resources/fontawesome/svgs/brands/facebook.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fantasy-flight-games.svg b/resources/fontawesome/svgs/brands/fantasy-flight-games.svg deleted file mode 100644 index 58ba450..0000000 --- a/resources/fontawesome/svgs/brands/fantasy-flight-games.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fedex.svg b/resources/fontawesome/svgs/brands/fedex.svg deleted file mode 100644 index dd00f49..0000000 --- a/resources/fontawesome/svgs/brands/fedex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fedora.svg b/resources/fontawesome/svgs/brands/fedora.svg deleted file mode 100644 index 1d1db06..0000000 --- a/resources/fontawesome/svgs/brands/fedora.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/figma.svg b/resources/fontawesome/svgs/brands/figma.svg deleted file mode 100644 index d56446a..0000000 --- a/resources/fontawesome/svgs/brands/figma.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/firefox-browser.svg b/resources/fontawesome/svgs/brands/firefox-browser.svg deleted file mode 100644 index 511b09f..0000000 --- a/resources/fontawesome/svgs/brands/firefox-browser.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/firefox.svg b/resources/fontawesome/svgs/brands/firefox.svg deleted file mode 100644 index f816fdd..0000000 --- a/resources/fontawesome/svgs/brands/firefox.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/first-order-alt.svg b/resources/fontawesome/svgs/brands/first-order-alt.svg deleted file mode 100644 index 6d6e00a..0000000 --- a/resources/fontawesome/svgs/brands/first-order-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/first-order.svg b/resources/fontawesome/svgs/brands/first-order.svg deleted file mode 100644 index 5dbf41e..0000000 --- a/resources/fontawesome/svgs/brands/first-order.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/firstdraft.svg b/resources/fontawesome/svgs/brands/firstdraft.svg deleted file mode 100644 index a3ee8a0..0000000 --- a/resources/fontawesome/svgs/brands/firstdraft.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/flickr.svg b/resources/fontawesome/svgs/brands/flickr.svg deleted file mode 100644 index f716d9e..0000000 --- a/resources/fontawesome/svgs/brands/flickr.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/flipboard.svg b/resources/fontawesome/svgs/brands/flipboard.svg deleted file mode 100644 index 6b95f3d..0000000 --- a/resources/fontawesome/svgs/brands/flipboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fly.svg b/resources/fontawesome/svgs/brands/fly.svg deleted file mode 100644 index 1064145..0000000 --- a/resources/fontawesome/svgs/brands/fly.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/font-awesome.svg b/resources/fontawesome/svgs/brands/font-awesome.svg deleted file mode 100644 index 13f10eb..0000000 --- a/resources/fontawesome/svgs/brands/font-awesome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fonticons-fi.svg b/resources/fontawesome/svgs/brands/fonticons-fi.svg deleted file mode 100644 index 2faeeef..0000000 --- a/resources/fontawesome/svgs/brands/fonticons-fi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fonticons.svg b/resources/fontawesome/svgs/brands/fonticons.svg deleted file mode 100644 index b273cfd..0000000 --- a/resources/fontawesome/svgs/brands/fonticons.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fort-awesome-alt.svg b/resources/fontawesome/svgs/brands/fort-awesome-alt.svg deleted file mode 100644 index e3f412b..0000000 --- a/resources/fontawesome/svgs/brands/fort-awesome-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fort-awesome.svg b/resources/fontawesome/svgs/brands/fort-awesome.svg deleted file mode 100644 index 58261d6..0000000 --- a/resources/fontawesome/svgs/brands/fort-awesome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/forumbee.svg b/resources/fontawesome/svgs/brands/forumbee.svg deleted file mode 100644 index 09db08b..0000000 --- a/resources/fontawesome/svgs/brands/forumbee.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/foursquare.svg b/resources/fontawesome/svgs/brands/foursquare.svg deleted file mode 100644 index 9eff37c..0000000 --- a/resources/fontawesome/svgs/brands/foursquare.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/free-code-camp.svg b/resources/fontawesome/svgs/brands/free-code-camp.svg deleted file mode 100644 index 50cccbd..0000000 --- a/resources/fontawesome/svgs/brands/free-code-camp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/freebsd.svg b/resources/fontawesome/svgs/brands/freebsd.svg deleted file mode 100644 index aff4ae6..0000000 --- a/resources/fontawesome/svgs/brands/freebsd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/fulcrum.svg b/resources/fontawesome/svgs/brands/fulcrum.svg deleted file mode 100644 index 02a07ab..0000000 --- a/resources/fontawesome/svgs/brands/fulcrum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/galactic-republic.svg b/resources/fontawesome/svgs/brands/galactic-republic.svg deleted file mode 100644 index 7616551..0000000 --- a/resources/fontawesome/svgs/brands/galactic-republic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/galactic-senate.svg b/resources/fontawesome/svgs/brands/galactic-senate.svg deleted file mode 100644 index 7395fb3..0000000 --- a/resources/fontawesome/svgs/brands/galactic-senate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/get-pocket.svg b/resources/fontawesome/svgs/brands/get-pocket.svg deleted file mode 100644 index ecf0398..0000000 --- a/resources/fontawesome/svgs/brands/get-pocket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gg-circle.svg b/resources/fontawesome/svgs/brands/gg-circle.svg deleted file mode 100644 index 2e853b5..0000000 --- a/resources/fontawesome/svgs/brands/gg-circle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gg.svg b/resources/fontawesome/svgs/brands/gg.svg deleted file mode 100644 index 3ecbea9..0000000 --- a/resources/fontawesome/svgs/brands/gg.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/git-alt.svg b/resources/fontawesome/svgs/brands/git-alt.svg deleted file mode 100644 index 1775079..0000000 --- a/resources/fontawesome/svgs/brands/git-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/git.svg b/resources/fontawesome/svgs/brands/git.svg deleted file mode 100644 index 071485e..0000000 --- a/resources/fontawesome/svgs/brands/git.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/github-alt.svg b/resources/fontawesome/svgs/brands/github-alt.svg deleted file mode 100644 index 75ab0dc..0000000 --- a/resources/fontawesome/svgs/brands/github-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/github.svg b/resources/fontawesome/svgs/brands/github.svg deleted file mode 100644 index b3da6fe..0000000 --- a/resources/fontawesome/svgs/brands/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gitkraken.svg b/resources/fontawesome/svgs/brands/gitkraken.svg deleted file mode 100644 index 4930871..0000000 --- a/resources/fontawesome/svgs/brands/gitkraken.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gitlab.svg b/resources/fontawesome/svgs/brands/gitlab.svg deleted file mode 100644 index feb6910..0000000 --- a/resources/fontawesome/svgs/brands/gitlab.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gitter.svg b/resources/fontawesome/svgs/brands/gitter.svg deleted file mode 100644 index c467578..0000000 --- a/resources/fontawesome/svgs/brands/gitter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/glide-g.svg b/resources/fontawesome/svgs/brands/glide-g.svg deleted file mode 100644 index 21733cf..0000000 --- a/resources/fontawesome/svgs/brands/glide-g.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/glide.svg b/resources/fontawesome/svgs/brands/glide.svg deleted file mode 100644 index 1500966..0000000 --- a/resources/fontawesome/svgs/brands/glide.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gofore.svg b/resources/fontawesome/svgs/brands/gofore.svg deleted file mode 100644 index 975983d..0000000 --- a/resources/fontawesome/svgs/brands/gofore.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/golang.svg b/resources/fontawesome/svgs/brands/golang.svg deleted file mode 100644 index ef17055..0000000 --- a/resources/fontawesome/svgs/brands/golang.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/goodreads-g.svg b/resources/fontawesome/svgs/brands/goodreads-g.svg deleted file mode 100644 index 2203692..0000000 --- a/resources/fontawesome/svgs/brands/goodreads-g.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/goodreads.svg b/resources/fontawesome/svgs/brands/goodreads.svg deleted file mode 100644 index e440af3..0000000 --- a/resources/fontawesome/svgs/brands/goodreads.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/google-drive.svg b/resources/fontawesome/svgs/brands/google-drive.svg deleted file mode 100644 index ee672e4..0000000 --- a/resources/fontawesome/svgs/brands/google-drive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/google-pay.svg b/resources/fontawesome/svgs/brands/google-pay.svg deleted file mode 100644 index e94e4b7..0000000 --- a/resources/fontawesome/svgs/brands/google-pay.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/google-play.svg b/resources/fontawesome/svgs/brands/google-play.svg deleted file mode 100644 index 22f0f3a..0000000 --- a/resources/fontawesome/svgs/brands/google-play.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/google-plus-g.svg b/resources/fontawesome/svgs/brands/google-plus-g.svg deleted file mode 100644 index e1298a5..0000000 --- a/resources/fontawesome/svgs/brands/google-plus-g.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/google-plus.svg b/resources/fontawesome/svgs/brands/google-plus.svg deleted file mode 100644 index 2febc73..0000000 --- a/resources/fontawesome/svgs/brands/google-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/google-scholar.svg b/resources/fontawesome/svgs/brands/google-scholar.svg deleted file mode 100644 index 2f92bce..0000000 --- a/resources/fontawesome/svgs/brands/google-scholar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/google-wallet.svg b/resources/fontawesome/svgs/brands/google-wallet.svg deleted file mode 100644 index 669d5f4..0000000 --- a/resources/fontawesome/svgs/brands/google-wallet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/google.svg b/resources/fontawesome/svgs/brands/google.svg deleted file mode 100644 index 1109139..0000000 --- a/resources/fontawesome/svgs/brands/google.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gratipay.svg b/resources/fontawesome/svgs/brands/gratipay.svg deleted file mode 100644 index 43d5320..0000000 --- a/resources/fontawesome/svgs/brands/gratipay.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/grav.svg b/resources/fontawesome/svgs/brands/grav.svg deleted file mode 100644 index bca5462..0000000 --- a/resources/fontawesome/svgs/brands/grav.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gripfire.svg b/resources/fontawesome/svgs/brands/gripfire.svg deleted file mode 100644 index a851266..0000000 --- a/resources/fontawesome/svgs/brands/gripfire.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/grunt.svg b/resources/fontawesome/svgs/brands/grunt.svg deleted file mode 100644 index cfe601b..0000000 --- a/resources/fontawesome/svgs/brands/grunt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/guilded.svg b/resources/fontawesome/svgs/brands/guilded.svg deleted file mode 100644 index 0e3bd5a..0000000 --- a/resources/fontawesome/svgs/brands/guilded.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/gulp.svg b/resources/fontawesome/svgs/brands/gulp.svg deleted file mode 100644 index ffff701..0000000 --- a/resources/fontawesome/svgs/brands/gulp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hacker-news.svg b/resources/fontawesome/svgs/brands/hacker-news.svg deleted file mode 100644 index 6f34313..0000000 --- a/resources/fontawesome/svgs/brands/hacker-news.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hackerrank.svg b/resources/fontawesome/svgs/brands/hackerrank.svg deleted file mode 100644 index e059aa0..0000000 --- a/resources/fontawesome/svgs/brands/hackerrank.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hashnode.svg b/resources/fontawesome/svgs/brands/hashnode.svg deleted file mode 100644 index 82751d9..0000000 --- a/resources/fontawesome/svgs/brands/hashnode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hips.svg b/resources/fontawesome/svgs/brands/hips.svg deleted file mode 100644 index 0e050ab..0000000 --- a/resources/fontawesome/svgs/brands/hips.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hire-a-helper.svg b/resources/fontawesome/svgs/brands/hire-a-helper.svg deleted file mode 100644 index 9e99cea..0000000 --- a/resources/fontawesome/svgs/brands/hire-a-helper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hive.svg b/resources/fontawesome/svgs/brands/hive.svg deleted file mode 100644 index 55ffa87..0000000 --- a/resources/fontawesome/svgs/brands/hive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hooli.svg b/resources/fontawesome/svgs/brands/hooli.svg deleted file mode 100644 index 1f92e73..0000000 --- a/resources/fontawesome/svgs/brands/hooli.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hornbill.svg b/resources/fontawesome/svgs/brands/hornbill.svg deleted file mode 100644 index 262c2b8..0000000 --- a/resources/fontawesome/svgs/brands/hornbill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hotjar.svg b/resources/fontawesome/svgs/brands/hotjar.svg deleted file mode 100644 index 43f2ab6..0000000 --- a/resources/fontawesome/svgs/brands/hotjar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/houzz.svg b/resources/fontawesome/svgs/brands/houzz.svg deleted file mode 100644 index db6570b..0000000 --- a/resources/fontawesome/svgs/brands/houzz.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/html5.svg b/resources/fontawesome/svgs/brands/html5.svg deleted file mode 100644 index c24f74b..0000000 --- a/resources/fontawesome/svgs/brands/html5.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/hubspot.svg b/resources/fontawesome/svgs/brands/hubspot.svg deleted file mode 100644 index ec51746..0000000 --- a/resources/fontawesome/svgs/brands/hubspot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ideal.svg b/resources/fontawesome/svgs/brands/ideal.svg deleted file mode 100644 index 36ce435..0000000 --- a/resources/fontawesome/svgs/brands/ideal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/imdb.svg b/resources/fontawesome/svgs/brands/imdb.svg deleted file mode 100644 index 92f3b78..0000000 --- a/resources/fontawesome/svgs/brands/imdb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/instagram.svg b/resources/fontawesome/svgs/brands/instagram.svg deleted file mode 100644 index b20889e..0000000 --- a/resources/fontawesome/svgs/brands/instagram.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/instalod.svg b/resources/fontawesome/svgs/brands/instalod.svg deleted file mode 100644 index cc7427d..0000000 --- a/resources/fontawesome/svgs/brands/instalod.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/intercom.svg b/resources/fontawesome/svgs/brands/intercom.svg deleted file mode 100644 index 0523bd6..0000000 --- a/resources/fontawesome/svgs/brands/intercom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/internet-explorer.svg b/resources/fontawesome/svgs/brands/internet-explorer.svg deleted file mode 100644 index d56d69a..0000000 --- a/resources/fontawesome/svgs/brands/internet-explorer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/invision.svg b/resources/fontawesome/svgs/brands/invision.svg deleted file mode 100644 index 6da0abb..0000000 --- a/resources/fontawesome/svgs/brands/invision.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ioxhost.svg b/resources/fontawesome/svgs/brands/ioxhost.svg deleted file mode 100644 index 952e62e..0000000 --- a/resources/fontawesome/svgs/brands/ioxhost.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/itch-io.svg b/resources/fontawesome/svgs/brands/itch-io.svg deleted file mode 100644 index bc49ec1..0000000 --- a/resources/fontawesome/svgs/brands/itch-io.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/itunes-note.svg b/resources/fontawesome/svgs/brands/itunes-note.svg deleted file mode 100644 index 4eecc98..0000000 --- a/resources/fontawesome/svgs/brands/itunes-note.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/itunes.svg b/resources/fontawesome/svgs/brands/itunes.svg deleted file mode 100644 index 0242728..0000000 --- a/resources/fontawesome/svgs/brands/itunes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/java.svg b/resources/fontawesome/svgs/brands/java.svg deleted file mode 100644 index 8401466..0000000 --- a/resources/fontawesome/svgs/brands/java.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/jedi-order.svg b/resources/fontawesome/svgs/brands/jedi-order.svg deleted file mode 100644 index 0dffe58..0000000 --- a/resources/fontawesome/svgs/brands/jedi-order.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/jenkins.svg b/resources/fontawesome/svgs/brands/jenkins.svg deleted file mode 100644 index e2dc8f1..0000000 --- a/resources/fontawesome/svgs/brands/jenkins.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/jira.svg b/resources/fontawesome/svgs/brands/jira.svg deleted file mode 100644 index c263775..0000000 --- a/resources/fontawesome/svgs/brands/jira.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/joget.svg b/resources/fontawesome/svgs/brands/joget.svg deleted file mode 100644 index 17f95b3..0000000 --- a/resources/fontawesome/svgs/brands/joget.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/joomla.svg b/resources/fontawesome/svgs/brands/joomla.svg deleted file mode 100644 index f47e8f9..0000000 --- a/resources/fontawesome/svgs/brands/joomla.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/js.svg b/resources/fontawesome/svgs/brands/js.svg deleted file mode 100644 index ffdc5fa..0000000 --- a/resources/fontawesome/svgs/brands/js.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/jsfiddle.svg b/resources/fontawesome/svgs/brands/jsfiddle.svg deleted file mode 100644 index b5d1623..0000000 --- a/resources/fontawesome/svgs/brands/jsfiddle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/jxl.svg b/resources/fontawesome/svgs/brands/jxl.svg deleted file mode 100644 index 93457cb..0000000 --- a/resources/fontawesome/svgs/brands/jxl.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/kaggle.svg b/resources/fontawesome/svgs/brands/kaggle.svg deleted file mode 100644 index 42be3e1..0000000 --- a/resources/fontawesome/svgs/brands/kaggle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/keybase.svg b/resources/fontawesome/svgs/brands/keybase.svg deleted file mode 100644 index 2862751..0000000 --- a/resources/fontawesome/svgs/brands/keybase.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/keycdn.svg b/resources/fontawesome/svgs/brands/keycdn.svg deleted file mode 100644 index 14e8ca8..0000000 --- a/resources/fontawesome/svgs/brands/keycdn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/kickstarter-k.svg b/resources/fontawesome/svgs/brands/kickstarter-k.svg deleted file mode 100644 index 3f124cd..0000000 --- a/resources/fontawesome/svgs/brands/kickstarter-k.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/kickstarter.svg b/resources/fontawesome/svgs/brands/kickstarter.svg deleted file mode 100644 index 6c85fe8..0000000 --- a/resources/fontawesome/svgs/brands/kickstarter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/korvue.svg b/resources/fontawesome/svgs/brands/korvue.svg deleted file mode 100644 index c5f64b3..0000000 --- a/resources/fontawesome/svgs/brands/korvue.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/laravel.svg b/resources/fontawesome/svgs/brands/laravel.svg deleted file mode 100644 index 4813f2f..0000000 --- a/resources/fontawesome/svgs/brands/laravel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/lastfm.svg b/resources/fontawesome/svgs/brands/lastfm.svg deleted file mode 100644 index a28d83c..0000000 --- a/resources/fontawesome/svgs/brands/lastfm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/leanpub.svg b/resources/fontawesome/svgs/brands/leanpub.svg deleted file mode 100644 index 6cc0b27..0000000 --- a/resources/fontawesome/svgs/brands/leanpub.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/less.svg b/resources/fontawesome/svgs/brands/less.svg deleted file mode 100644 index c20dab7..0000000 --- a/resources/fontawesome/svgs/brands/less.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/letterboxd.svg b/resources/fontawesome/svgs/brands/letterboxd.svg deleted file mode 100644 index f9a5f60..0000000 --- a/resources/fontawesome/svgs/brands/letterboxd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/line.svg b/resources/fontawesome/svgs/brands/line.svg deleted file mode 100644 index 5444888..0000000 --- a/resources/fontawesome/svgs/brands/line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/linkedin-in.svg b/resources/fontawesome/svgs/brands/linkedin-in.svg deleted file mode 100644 index e6455f6..0000000 --- a/resources/fontawesome/svgs/brands/linkedin-in.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/linkedin.svg b/resources/fontawesome/svgs/brands/linkedin.svg deleted file mode 100644 index 2c9f2ce..0000000 --- a/resources/fontawesome/svgs/brands/linkedin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/linode.svg b/resources/fontawesome/svgs/brands/linode.svg deleted file mode 100644 index a1103f1..0000000 --- a/resources/fontawesome/svgs/brands/linode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/linux.svg b/resources/fontawesome/svgs/brands/linux.svg deleted file mode 100644 index eae7ce1..0000000 --- a/resources/fontawesome/svgs/brands/linux.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/lyft.svg b/resources/fontawesome/svgs/brands/lyft.svg deleted file mode 100644 index 9529ff8..0000000 --- a/resources/fontawesome/svgs/brands/lyft.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/magento.svg b/resources/fontawesome/svgs/brands/magento.svg deleted file mode 100644 index 2066bd0..0000000 --- a/resources/fontawesome/svgs/brands/magento.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mailchimp.svg b/resources/fontawesome/svgs/brands/mailchimp.svg deleted file mode 100644 index 1aa48e9..0000000 --- a/resources/fontawesome/svgs/brands/mailchimp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mandalorian.svg b/resources/fontawesome/svgs/brands/mandalorian.svg deleted file mode 100644 index ba4473b..0000000 --- a/resources/fontawesome/svgs/brands/mandalorian.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/markdown.svg b/resources/fontawesome/svgs/brands/markdown.svg deleted file mode 100644 index 0a081fb..0000000 --- a/resources/fontawesome/svgs/brands/markdown.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mastodon.svg b/resources/fontawesome/svgs/brands/mastodon.svg deleted file mode 100644 index 03567fc..0000000 --- a/resources/fontawesome/svgs/brands/mastodon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/maxcdn.svg b/resources/fontawesome/svgs/brands/maxcdn.svg deleted file mode 100644 index c8c58ad..0000000 --- a/resources/fontawesome/svgs/brands/maxcdn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mdb.svg b/resources/fontawesome/svgs/brands/mdb.svg deleted file mode 100644 index ce02379..0000000 --- a/resources/fontawesome/svgs/brands/mdb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/medapps.svg b/resources/fontawesome/svgs/brands/medapps.svg deleted file mode 100644 index 3b5a499..0000000 --- a/resources/fontawesome/svgs/brands/medapps.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/medium.svg b/resources/fontawesome/svgs/brands/medium.svg deleted file mode 100644 index e4de78d..0000000 --- a/resources/fontawesome/svgs/brands/medium.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/medrt.svg b/resources/fontawesome/svgs/brands/medrt.svg deleted file mode 100644 index 93fcb29..0000000 --- a/resources/fontawesome/svgs/brands/medrt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/meetup.svg b/resources/fontawesome/svgs/brands/meetup.svg deleted file mode 100644 index 8ce7173..0000000 --- a/resources/fontawesome/svgs/brands/meetup.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/megaport.svg b/resources/fontawesome/svgs/brands/megaport.svg deleted file mode 100644 index e2a914c..0000000 --- a/resources/fontawesome/svgs/brands/megaport.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mendeley.svg b/resources/fontawesome/svgs/brands/mendeley.svg deleted file mode 100644 index 27b6da4..0000000 --- a/resources/fontawesome/svgs/brands/mendeley.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/meta.svg b/resources/fontawesome/svgs/brands/meta.svg deleted file mode 100644 index fbd05f7..0000000 --- a/resources/fontawesome/svgs/brands/meta.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/microblog.svg b/resources/fontawesome/svgs/brands/microblog.svg deleted file mode 100644 index e3e8819..0000000 --- a/resources/fontawesome/svgs/brands/microblog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/microsoft.svg b/resources/fontawesome/svgs/brands/microsoft.svg deleted file mode 100644 index 22edc45..0000000 --- a/resources/fontawesome/svgs/brands/microsoft.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mintbit.svg b/resources/fontawesome/svgs/brands/mintbit.svg deleted file mode 100644 index 987c052..0000000 --- a/resources/fontawesome/svgs/brands/mintbit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mix.svg b/resources/fontawesome/svgs/brands/mix.svg deleted file mode 100644 index 1b831e2..0000000 --- a/resources/fontawesome/svgs/brands/mix.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mixcloud.svg b/resources/fontawesome/svgs/brands/mixcloud.svg deleted file mode 100644 index a006056..0000000 --- a/resources/fontawesome/svgs/brands/mixcloud.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mixer.svg b/resources/fontawesome/svgs/brands/mixer.svg deleted file mode 100644 index eea171b..0000000 --- a/resources/fontawesome/svgs/brands/mixer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/mizuni.svg b/resources/fontawesome/svgs/brands/mizuni.svg deleted file mode 100644 index 089f60a..0000000 --- a/resources/fontawesome/svgs/brands/mizuni.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/modx.svg b/resources/fontawesome/svgs/brands/modx.svg deleted file mode 100644 index 1022453..0000000 --- a/resources/fontawesome/svgs/brands/modx.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/monero.svg b/resources/fontawesome/svgs/brands/monero.svg deleted file mode 100644 index 1872c2f..0000000 --- a/resources/fontawesome/svgs/brands/monero.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/napster.svg b/resources/fontawesome/svgs/brands/napster.svg deleted file mode 100644 index 003d703..0000000 --- a/resources/fontawesome/svgs/brands/napster.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/neos.svg b/resources/fontawesome/svgs/brands/neos.svg deleted file mode 100644 index c216c11..0000000 --- a/resources/fontawesome/svgs/brands/neos.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/nfc-directional.svg b/resources/fontawesome/svgs/brands/nfc-directional.svg deleted file mode 100644 index f848dfe..0000000 --- a/resources/fontawesome/svgs/brands/nfc-directional.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/nfc-symbol.svg b/resources/fontawesome/svgs/brands/nfc-symbol.svg deleted file mode 100644 index be3f1ff..0000000 --- a/resources/fontawesome/svgs/brands/nfc-symbol.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/nimblr.svg b/resources/fontawesome/svgs/brands/nimblr.svg deleted file mode 100644 index d4646ba..0000000 --- a/resources/fontawesome/svgs/brands/nimblr.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/node-js.svg b/resources/fontawesome/svgs/brands/node-js.svg deleted file mode 100644 index f8e1be5..0000000 --- a/resources/fontawesome/svgs/brands/node-js.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/node.svg b/resources/fontawesome/svgs/brands/node.svg deleted file mode 100644 index 526f004..0000000 --- a/resources/fontawesome/svgs/brands/node.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/npm.svg b/resources/fontawesome/svgs/brands/npm.svg deleted file mode 100644 index b176580..0000000 --- a/resources/fontawesome/svgs/brands/npm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ns8.svg b/resources/fontawesome/svgs/brands/ns8.svg deleted file mode 100644 index 328c950..0000000 --- a/resources/fontawesome/svgs/brands/ns8.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/nutritionix.svg b/resources/fontawesome/svgs/brands/nutritionix.svg deleted file mode 100644 index 5e94b5d..0000000 --- a/resources/fontawesome/svgs/brands/nutritionix.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/octopus-deploy.svg b/resources/fontawesome/svgs/brands/octopus-deploy.svg deleted file mode 100644 index 71273b9..0000000 --- a/resources/fontawesome/svgs/brands/octopus-deploy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/odnoklassniki.svg b/resources/fontawesome/svgs/brands/odnoklassniki.svg deleted file mode 100644 index 307f692..0000000 --- a/resources/fontawesome/svgs/brands/odnoklassniki.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/odysee.svg b/resources/fontawesome/svgs/brands/odysee.svg deleted file mode 100644 index e3933c9..0000000 --- a/resources/fontawesome/svgs/brands/odysee.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/old-republic.svg b/resources/fontawesome/svgs/brands/old-republic.svg deleted file mode 100644 index 9ee8939..0000000 --- a/resources/fontawesome/svgs/brands/old-republic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/opencart.svg b/resources/fontawesome/svgs/brands/opencart.svg deleted file mode 100644 index 5313a33..0000000 --- a/resources/fontawesome/svgs/brands/opencart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/openid.svg b/resources/fontawesome/svgs/brands/openid.svg deleted file mode 100644 index 3757199..0000000 --- a/resources/fontawesome/svgs/brands/openid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/opensuse.svg b/resources/fontawesome/svgs/brands/opensuse.svg deleted file mode 100644 index 0a4025b..0000000 --- a/resources/fontawesome/svgs/brands/opensuse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/opera.svg b/resources/fontawesome/svgs/brands/opera.svg deleted file mode 100644 index a36f90c..0000000 --- a/resources/fontawesome/svgs/brands/opera.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/optin-monster.svg b/resources/fontawesome/svgs/brands/optin-monster.svg deleted file mode 100644 index c210acf..0000000 --- a/resources/fontawesome/svgs/brands/optin-monster.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/orcid.svg b/resources/fontawesome/svgs/brands/orcid.svg deleted file mode 100644 index ee32bdd..0000000 --- a/resources/fontawesome/svgs/brands/orcid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/osi.svg b/resources/fontawesome/svgs/brands/osi.svg deleted file mode 100644 index 6767a69..0000000 --- a/resources/fontawesome/svgs/brands/osi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/padlet.svg b/resources/fontawesome/svgs/brands/padlet.svg deleted file mode 100644 index 918f3c1..0000000 --- a/resources/fontawesome/svgs/brands/padlet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/page4.svg b/resources/fontawesome/svgs/brands/page4.svg deleted file mode 100644 index 82a2219..0000000 --- a/resources/fontawesome/svgs/brands/page4.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pagelines.svg b/resources/fontawesome/svgs/brands/pagelines.svg deleted file mode 100644 index 8d6e3ba..0000000 --- a/resources/fontawesome/svgs/brands/pagelines.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/palfed.svg b/resources/fontawesome/svgs/brands/palfed.svg deleted file mode 100644 index 769757b..0000000 --- a/resources/fontawesome/svgs/brands/palfed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/patreon.svg b/resources/fontawesome/svgs/brands/patreon.svg deleted file mode 100644 index e57ff4c..0000000 --- a/resources/fontawesome/svgs/brands/patreon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/paypal.svg b/resources/fontawesome/svgs/brands/paypal.svg deleted file mode 100644 index 045d933..0000000 --- a/resources/fontawesome/svgs/brands/paypal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/perbyte.svg b/resources/fontawesome/svgs/brands/perbyte.svg deleted file mode 100644 index 5f47f7e..0000000 --- a/resources/fontawesome/svgs/brands/perbyte.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/periscope.svg b/resources/fontawesome/svgs/brands/periscope.svg deleted file mode 100644 index 10d32e0..0000000 --- a/resources/fontawesome/svgs/brands/periscope.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/phabricator.svg b/resources/fontawesome/svgs/brands/phabricator.svg deleted file mode 100644 index c3ca3b6..0000000 --- a/resources/fontawesome/svgs/brands/phabricator.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/phoenix-framework.svg b/resources/fontawesome/svgs/brands/phoenix-framework.svg deleted file mode 100644 index 7fab2f9..0000000 --- a/resources/fontawesome/svgs/brands/phoenix-framework.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/phoenix-squadron.svg b/resources/fontawesome/svgs/brands/phoenix-squadron.svg deleted file mode 100644 index 2a11a0a..0000000 --- a/resources/fontawesome/svgs/brands/phoenix-squadron.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/php.svg b/resources/fontawesome/svgs/brands/php.svg deleted file mode 100644 index c8f0548..0000000 --- a/resources/fontawesome/svgs/brands/php.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pied-piper-alt.svg b/resources/fontawesome/svgs/brands/pied-piper-alt.svg deleted file mode 100644 index 1ef84da..0000000 --- a/resources/fontawesome/svgs/brands/pied-piper-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pied-piper-hat.svg b/resources/fontawesome/svgs/brands/pied-piper-hat.svg deleted file mode 100644 index d62c9ec..0000000 --- a/resources/fontawesome/svgs/brands/pied-piper-hat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pied-piper-pp.svg b/resources/fontawesome/svgs/brands/pied-piper-pp.svg deleted file mode 100644 index 4c8c4a4..0000000 --- a/resources/fontawesome/svgs/brands/pied-piper-pp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pied-piper.svg b/resources/fontawesome/svgs/brands/pied-piper.svg deleted file mode 100644 index 930961f..0000000 --- a/resources/fontawesome/svgs/brands/pied-piper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pinterest-p.svg b/resources/fontawesome/svgs/brands/pinterest-p.svg deleted file mode 100644 index beaf436..0000000 --- a/resources/fontawesome/svgs/brands/pinterest-p.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pinterest.svg b/resources/fontawesome/svgs/brands/pinterest.svg deleted file mode 100644 index 98818f8..0000000 --- a/resources/fontawesome/svgs/brands/pinterest.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pix.svg b/resources/fontawesome/svgs/brands/pix.svg deleted file mode 100644 index d5112ce..0000000 --- a/resources/fontawesome/svgs/brands/pix.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pixiv.svg b/resources/fontawesome/svgs/brands/pixiv.svg deleted file mode 100644 index 0fa1479..0000000 --- a/resources/fontawesome/svgs/brands/pixiv.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/playstation.svg b/resources/fontawesome/svgs/brands/playstation.svg deleted file mode 100644 index 30f0c5c..0000000 --- a/resources/fontawesome/svgs/brands/playstation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/product-hunt.svg b/resources/fontawesome/svgs/brands/product-hunt.svg deleted file mode 100644 index 28d61b4..0000000 --- a/resources/fontawesome/svgs/brands/product-hunt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/pushed.svg b/resources/fontawesome/svgs/brands/pushed.svg deleted file mode 100644 index d500f79..0000000 --- a/resources/fontawesome/svgs/brands/pushed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/python.svg b/resources/fontawesome/svgs/brands/python.svg deleted file mode 100644 index 76c24b2..0000000 --- a/resources/fontawesome/svgs/brands/python.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/qq.svg b/resources/fontawesome/svgs/brands/qq.svg deleted file mode 100644 index 5f280c0..0000000 --- a/resources/fontawesome/svgs/brands/qq.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/quinscape.svg b/resources/fontawesome/svgs/brands/quinscape.svg deleted file mode 100644 index 1fc7ef3..0000000 --- a/resources/fontawesome/svgs/brands/quinscape.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/quora.svg b/resources/fontawesome/svgs/brands/quora.svg deleted file mode 100644 index 39a119f..0000000 --- a/resources/fontawesome/svgs/brands/quora.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/r-project.svg b/resources/fontawesome/svgs/brands/r-project.svg deleted file mode 100644 index cf03cc7..0000000 --- a/resources/fontawesome/svgs/brands/r-project.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/raspberry-pi.svg b/resources/fontawesome/svgs/brands/raspberry-pi.svg deleted file mode 100644 index f089f2f..0000000 --- a/resources/fontawesome/svgs/brands/raspberry-pi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ravelry.svg b/resources/fontawesome/svgs/brands/ravelry.svg deleted file mode 100644 index eafafa0..0000000 --- a/resources/fontawesome/svgs/brands/ravelry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/react.svg b/resources/fontawesome/svgs/brands/react.svg deleted file mode 100644 index 531c4c8..0000000 --- a/resources/fontawesome/svgs/brands/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/reacteurope.svg b/resources/fontawesome/svgs/brands/reacteurope.svg deleted file mode 100644 index dd64014..0000000 --- a/resources/fontawesome/svgs/brands/reacteurope.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/readme.svg b/resources/fontawesome/svgs/brands/readme.svg deleted file mode 100644 index 82c0b95..0000000 --- a/resources/fontawesome/svgs/brands/readme.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/rebel.svg b/resources/fontawesome/svgs/brands/rebel.svg deleted file mode 100644 index 9445c41..0000000 --- a/resources/fontawesome/svgs/brands/rebel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/red-river.svg b/resources/fontawesome/svgs/brands/red-river.svg deleted file mode 100644 index f52dc84..0000000 --- a/resources/fontawesome/svgs/brands/red-river.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/reddit-alien.svg b/resources/fontawesome/svgs/brands/reddit-alien.svg deleted file mode 100644 index 7c7fbfa..0000000 --- a/resources/fontawesome/svgs/brands/reddit-alien.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/reddit.svg b/resources/fontawesome/svgs/brands/reddit.svg deleted file mode 100644 index c71ba9b..0000000 --- a/resources/fontawesome/svgs/brands/reddit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/redhat.svg b/resources/fontawesome/svgs/brands/redhat.svg deleted file mode 100644 index f51c38c..0000000 --- a/resources/fontawesome/svgs/brands/redhat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/renren.svg b/resources/fontawesome/svgs/brands/renren.svg deleted file mode 100644 index a815cf4..0000000 --- a/resources/fontawesome/svgs/brands/renren.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/replyd.svg b/resources/fontawesome/svgs/brands/replyd.svg deleted file mode 100644 index 4217096..0000000 --- a/resources/fontawesome/svgs/brands/replyd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/researchgate.svg b/resources/fontawesome/svgs/brands/researchgate.svg deleted file mode 100644 index d08d1ef..0000000 --- a/resources/fontawesome/svgs/brands/researchgate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/resolving.svg b/resources/fontawesome/svgs/brands/resolving.svg deleted file mode 100644 index 6effb08..0000000 --- a/resources/fontawesome/svgs/brands/resolving.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/rev.svg b/resources/fontawesome/svgs/brands/rev.svg deleted file mode 100644 index 7267079..0000000 --- a/resources/fontawesome/svgs/brands/rev.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/rocketchat.svg b/resources/fontawesome/svgs/brands/rocketchat.svg deleted file mode 100644 index 72956a2..0000000 --- a/resources/fontawesome/svgs/brands/rocketchat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/rockrms.svg b/resources/fontawesome/svgs/brands/rockrms.svg deleted file mode 100644 index 4625717..0000000 --- a/resources/fontawesome/svgs/brands/rockrms.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/rust.svg b/resources/fontawesome/svgs/brands/rust.svg deleted file mode 100644 index c77fb29..0000000 --- a/resources/fontawesome/svgs/brands/rust.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/safari.svg b/resources/fontawesome/svgs/brands/safari.svg deleted file mode 100644 index c79e564..0000000 --- a/resources/fontawesome/svgs/brands/safari.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/salesforce.svg b/resources/fontawesome/svgs/brands/salesforce.svg deleted file mode 100644 index 3874302..0000000 --- a/resources/fontawesome/svgs/brands/salesforce.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sass.svg b/resources/fontawesome/svgs/brands/sass.svg deleted file mode 100644 index ac50b96..0000000 --- a/resources/fontawesome/svgs/brands/sass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/schlix.svg b/resources/fontawesome/svgs/brands/schlix.svg deleted file mode 100644 index cb71209..0000000 --- a/resources/fontawesome/svgs/brands/schlix.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/screenpal.svg b/resources/fontawesome/svgs/brands/screenpal.svg deleted file mode 100644 index 133d368..0000000 --- a/resources/fontawesome/svgs/brands/screenpal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/scribd.svg b/resources/fontawesome/svgs/brands/scribd.svg deleted file mode 100644 index 825d8f9..0000000 --- a/resources/fontawesome/svgs/brands/scribd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/searchengin.svg b/resources/fontawesome/svgs/brands/searchengin.svg deleted file mode 100644 index 1c23d5e..0000000 --- a/resources/fontawesome/svgs/brands/searchengin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sellcast.svg b/resources/fontawesome/svgs/brands/sellcast.svg deleted file mode 100644 index afa9224..0000000 --- a/resources/fontawesome/svgs/brands/sellcast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sellsy.svg b/resources/fontawesome/svgs/brands/sellsy.svg deleted file mode 100644 index 7d2c6a4..0000000 --- a/resources/fontawesome/svgs/brands/sellsy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/servicestack.svg b/resources/fontawesome/svgs/brands/servicestack.svg deleted file mode 100644 index d052f93..0000000 --- a/resources/fontawesome/svgs/brands/servicestack.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/shirtsinbulk.svg b/resources/fontawesome/svgs/brands/shirtsinbulk.svg deleted file mode 100644 index 30b9bf2..0000000 --- a/resources/fontawesome/svgs/brands/shirtsinbulk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/shoelace.svg b/resources/fontawesome/svgs/brands/shoelace.svg deleted file mode 100644 index 814da86..0000000 --- a/resources/fontawesome/svgs/brands/shoelace.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/shopify.svg b/resources/fontawesome/svgs/brands/shopify.svg deleted file mode 100644 index b31e4e3..0000000 --- a/resources/fontawesome/svgs/brands/shopify.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/shopware.svg b/resources/fontawesome/svgs/brands/shopware.svg deleted file mode 100644 index 79757c9..0000000 --- a/resources/fontawesome/svgs/brands/shopware.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/signal-messenger.svg b/resources/fontawesome/svgs/brands/signal-messenger.svg deleted file mode 100644 index 84b7958..0000000 --- a/resources/fontawesome/svgs/brands/signal-messenger.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/simplybuilt.svg b/resources/fontawesome/svgs/brands/simplybuilt.svg deleted file mode 100644 index f58b262..0000000 --- a/resources/fontawesome/svgs/brands/simplybuilt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sistrix.svg b/resources/fontawesome/svgs/brands/sistrix.svg deleted file mode 100644 index f468e25..0000000 --- a/resources/fontawesome/svgs/brands/sistrix.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sith.svg b/resources/fontawesome/svgs/brands/sith.svg deleted file mode 100644 index ee966c9..0000000 --- a/resources/fontawesome/svgs/brands/sith.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sitrox.svg b/resources/fontawesome/svgs/brands/sitrox.svg deleted file mode 100644 index bf2e391..0000000 --- a/resources/fontawesome/svgs/brands/sitrox.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sketch.svg b/resources/fontawesome/svgs/brands/sketch.svg deleted file mode 100644 index 1e8d210..0000000 --- a/resources/fontawesome/svgs/brands/sketch.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/skyatlas.svg b/resources/fontawesome/svgs/brands/skyatlas.svg deleted file mode 100644 index 1fe3170..0000000 --- a/resources/fontawesome/svgs/brands/skyatlas.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/skype.svg b/resources/fontawesome/svgs/brands/skype.svg deleted file mode 100644 index 8d7231e..0000000 --- a/resources/fontawesome/svgs/brands/skype.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/slack.svg b/resources/fontawesome/svgs/brands/slack.svg deleted file mode 100644 index 0bacd72..0000000 --- a/resources/fontawesome/svgs/brands/slack.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/slideshare.svg b/resources/fontawesome/svgs/brands/slideshare.svg deleted file mode 100644 index 464c4e9..0000000 --- a/resources/fontawesome/svgs/brands/slideshare.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/snapchat.svg b/resources/fontawesome/svgs/brands/snapchat.svg deleted file mode 100644 index f0be06f..0000000 --- a/resources/fontawesome/svgs/brands/snapchat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/soundcloud.svg b/resources/fontawesome/svgs/brands/soundcloud.svg deleted file mode 100644 index 0cd2d44..0000000 --- a/resources/fontawesome/svgs/brands/soundcloud.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sourcetree.svg b/resources/fontawesome/svgs/brands/sourcetree.svg deleted file mode 100644 index 1f63c65..0000000 --- a/resources/fontawesome/svgs/brands/sourcetree.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/space-awesome.svg b/resources/fontawesome/svgs/brands/space-awesome.svg deleted file mode 100644 index ab8f322..0000000 --- a/resources/fontawesome/svgs/brands/space-awesome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/speakap.svg b/resources/fontawesome/svgs/brands/speakap.svg deleted file mode 100644 index bea4cb6..0000000 --- a/resources/fontawesome/svgs/brands/speakap.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/speaker-deck.svg b/resources/fontawesome/svgs/brands/speaker-deck.svg deleted file mode 100644 index 873f3da..0000000 --- a/resources/fontawesome/svgs/brands/speaker-deck.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/spotify.svg b/resources/fontawesome/svgs/brands/spotify.svg deleted file mode 100644 index ae34c9e..0000000 --- a/resources/fontawesome/svgs/brands/spotify.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-behance.svg b/resources/fontawesome/svgs/brands/square-behance.svg deleted file mode 100644 index edfcf76..0000000 --- a/resources/fontawesome/svgs/brands/square-behance.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-dribbble.svg b/resources/fontawesome/svgs/brands/square-dribbble.svg deleted file mode 100644 index 4cdc2fd..0000000 --- a/resources/fontawesome/svgs/brands/square-dribbble.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-facebook.svg b/resources/fontawesome/svgs/brands/square-facebook.svg deleted file mode 100644 index 8f0ede4..0000000 --- a/resources/fontawesome/svgs/brands/square-facebook.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-font-awesome-stroke.svg b/resources/fontawesome/svgs/brands/square-font-awesome-stroke.svg deleted file mode 100644 index 88c8159..0000000 --- a/resources/fontawesome/svgs/brands/square-font-awesome-stroke.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-font-awesome.svg b/resources/fontawesome/svgs/brands/square-font-awesome.svg deleted file mode 100644 index adb7e81..0000000 --- a/resources/fontawesome/svgs/brands/square-font-awesome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-git.svg b/resources/fontawesome/svgs/brands/square-git.svg deleted file mode 100644 index af73f48..0000000 --- a/resources/fontawesome/svgs/brands/square-git.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-github.svg b/resources/fontawesome/svgs/brands/square-github.svg deleted file mode 100644 index 600e924..0000000 --- a/resources/fontawesome/svgs/brands/square-github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-gitlab.svg b/resources/fontawesome/svgs/brands/square-gitlab.svg deleted file mode 100644 index 2cce4f5..0000000 --- a/resources/fontawesome/svgs/brands/square-gitlab.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-google-plus.svg b/resources/fontawesome/svgs/brands/square-google-plus.svg deleted file mode 100644 index dd7eb81..0000000 --- a/resources/fontawesome/svgs/brands/square-google-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-hacker-news.svg b/resources/fontawesome/svgs/brands/square-hacker-news.svg deleted file mode 100644 index c29f122..0000000 --- a/resources/fontawesome/svgs/brands/square-hacker-news.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-instagram.svg b/resources/fontawesome/svgs/brands/square-instagram.svg deleted file mode 100644 index 443ede6..0000000 --- a/resources/fontawesome/svgs/brands/square-instagram.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-js.svg b/resources/fontawesome/svgs/brands/square-js.svg deleted file mode 100644 index 780a1c2..0000000 --- a/resources/fontawesome/svgs/brands/square-js.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-lastfm.svg b/resources/fontawesome/svgs/brands/square-lastfm.svg deleted file mode 100644 index f96d1ac..0000000 --- a/resources/fontawesome/svgs/brands/square-lastfm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-letterboxd.svg b/resources/fontawesome/svgs/brands/square-letterboxd.svg deleted file mode 100644 index 6b2c35f..0000000 --- a/resources/fontawesome/svgs/brands/square-letterboxd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-odnoklassniki.svg b/resources/fontawesome/svgs/brands/square-odnoklassniki.svg deleted file mode 100644 index 3b96935..0000000 --- a/resources/fontawesome/svgs/brands/square-odnoklassniki.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-pied-piper.svg b/resources/fontawesome/svgs/brands/square-pied-piper.svg deleted file mode 100644 index dddfd00..0000000 --- a/resources/fontawesome/svgs/brands/square-pied-piper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-pinterest.svg b/resources/fontawesome/svgs/brands/square-pinterest.svg deleted file mode 100644 index ed81eb8..0000000 --- a/resources/fontawesome/svgs/brands/square-pinterest.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-reddit.svg b/resources/fontawesome/svgs/brands/square-reddit.svg deleted file mode 100644 index c2c7363..0000000 --- a/resources/fontawesome/svgs/brands/square-reddit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-snapchat.svg b/resources/fontawesome/svgs/brands/square-snapchat.svg deleted file mode 100644 index 40ce49c..0000000 --- a/resources/fontawesome/svgs/brands/square-snapchat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-steam.svg b/resources/fontawesome/svgs/brands/square-steam.svg deleted file mode 100644 index bbbbc82..0000000 --- a/resources/fontawesome/svgs/brands/square-steam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-threads.svg b/resources/fontawesome/svgs/brands/square-threads.svg deleted file mode 100644 index 75c431a..0000000 --- a/resources/fontawesome/svgs/brands/square-threads.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-tumblr.svg b/resources/fontawesome/svgs/brands/square-tumblr.svg deleted file mode 100644 index 532bb57..0000000 --- a/resources/fontawesome/svgs/brands/square-tumblr.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-twitter.svg b/resources/fontawesome/svgs/brands/square-twitter.svg deleted file mode 100644 index d1abe7c..0000000 --- a/resources/fontawesome/svgs/brands/square-twitter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-upwork.svg b/resources/fontawesome/svgs/brands/square-upwork.svg deleted file mode 100644 index 3bc808d..0000000 --- a/resources/fontawesome/svgs/brands/square-upwork.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-viadeo.svg b/resources/fontawesome/svgs/brands/square-viadeo.svg deleted file mode 100644 index e39879f..0000000 --- a/resources/fontawesome/svgs/brands/square-viadeo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-vimeo.svg b/resources/fontawesome/svgs/brands/square-vimeo.svg deleted file mode 100644 index f0ae7db..0000000 --- a/resources/fontawesome/svgs/brands/square-vimeo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-web-awesome-stroke.svg b/resources/fontawesome/svgs/brands/square-web-awesome-stroke.svg deleted file mode 100644 index b9239b6..0000000 --- a/resources/fontawesome/svgs/brands/square-web-awesome-stroke.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-web-awesome.svg b/resources/fontawesome/svgs/brands/square-web-awesome.svg deleted file mode 100644 index a13a837..0000000 --- a/resources/fontawesome/svgs/brands/square-web-awesome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-whatsapp.svg b/resources/fontawesome/svgs/brands/square-whatsapp.svg deleted file mode 100644 index 6832a5b..0000000 --- a/resources/fontawesome/svgs/brands/square-whatsapp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-x-twitter.svg b/resources/fontawesome/svgs/brands/square-x-twitter.svg deleted file mode 100644 index 46f8d9a..0000000 --- a/resources/fontawesome/svgs/brands/square-x-twitter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-xing.svg b/resources/fontawesome/svgs/brands/square-xing.svg deleted file mode 100644 index cd36d7d..0000000 --- a/resources/fontawesome/svgs/brands/square-xing.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/square-youtube.svg b/resources/fontawesome/svgs/brands/square-youtube.svg deleted file mode 100644 index 5f657a3..0000000 --- a/resources/fontawesome/svgs/brands/square-youtube.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/squarespace.svg b/resources/fontawesome/svgs/brands/squarespace.svg deleted file mode 100644 index d6e4811..0000000 --- a/resources/fontawesome/svgs/brands/squarespace.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/stack-exchange.svg b/resources/fontawesome/svgs/brands/stack-exchange.svg deleted file mode 100644 index efc8f9a..0000000 --- a/resources/fontawesome/svgs/brands/stack-exchange.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/stack-overflow.svg b/resources/fontawesome/svgs/brands/stack-overflow.svg deleted file mode 100644 index b664c33..0000000 --- a/resources/fontawesome/svgs/brands/stack-overflow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/stackpath.svg b/resources/fontawesome/svgs/brands/stackpath.svg deleted file mode 100644 index 7992a42..0000000 --- a/resources/fontawesome/svgs/brands/stackpath.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/staylinked.svg b/resources/fontawesome/svgs/brands/staylinked.svg deleted file mode 100644 index 60949c7..0000000 --- a/resources/fontawesome/svgs/brands/staylinked.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/steam-symbol.svg b/resources/fontawesome/svgs/brands/steam-symbol.svg deleted file mode 100644 index 476b4fb..0000000 --- a/resources/fontawesome/svgs/brands/steam-symbol.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/steam.svg b/resources/fontawesome/svgs/brands/steam.svg deleted file mode 100644 index a46b942..0000000 --- a/resources/fontawesome/svgs/brands/steam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/sticker-mule.svg b/resources/fontawesome/svgs/brands/sticker-mule.svg deleted file mode 100644 index e8eb5cb..0000000 --- a/resources/fontawesome/svgs/brands/sticker-mule.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/strava.svg b/resources/fontawesome/svgs/brands/strava.svg deleted file mode 100644 index 0e21910..0000000 --- a/resources/fontawesome/svgs/brands/strava.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/stripe-s.svg b/resources/fontawesome/svgs/brands/stripe-s.svg deleted file mode 100644 index 76aa1a4..0000000 --- a/resources/fontawesome/svgs/brands/stripe-s.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/stripe.svg b/resources/fontawesome/svgs/brands/stripe.svg deleted file mode 100644 index aaf0077..0000000 --- a/resources/fontawesome/svgs/brands/stripe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/stubber.svg b/resources/fontawesome/svgs/brands/stubber.svg deleted file mode 100644 index 2813ebd..0000000 --- a/resources/fontawesome/svgs/brands/stubber.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/studiovinari.svg b/resources/fontawesome/svgs/brands/studiovinari.svg deleted file mode 100644 index b8a3f93..0000000 --- a/resources/fontawesome/svgs/brands/studiovinari.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/stumbleupon-circle.svg b/resources/fontawesome/svgs/brands/stumbleupon-circle.svg deleted file mode 100644 index 0a31b68..0000000 --- a/resources/fontawesome/svgs/brands/stumbleupon-circle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/stumbleupon.svg b/resources/fontawesome/svgs/brands/stumbleupon.svg deleted file mode 100644 index f0010c8..0000000 --- a/resources/fontawesome/svgs/brands/stumbleupon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/superpowers.svg b/resources/fontawesome/svgs/brands/superpowers.svg deleted file mode 100644 index f3c3981..0000000 --- a/resources/fontawesome/svgs/brands/superpowers.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/supple.svg b/resources/fontawesome/svgs/brands/supple.svg deleted file mode 100644 index e0353d6..0000000 --- a/resources/fontawesome/svgs/brands/supple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/suse.svg b/resources/fontawesome/svgs/brands/suse.svg deleted file mode 100644 index 98f8c5e..0000000 --- a/resources/fontawesome/svgs/brands/suse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/swift.svg b/resources/fontawesome/svgs/brands/swift.svg deleted file mode 100644 index c9de7af..0000000 --- a/resources/fontawesome/svgs/brands/swift.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/symfony.svg b/resources/fontawesome/svgs/brands/symfony.svg deleted file mode 100644 index 1885e9b..0000000 --- a/resources/fontawesome/svgs/brands/symfony.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/teamspeak.svg b/resources/fontawesome/svgs/brands/teamspeak.svg deleted file mode 100644 index c73064a..0000000 --- a/resources/fontawesome/svgs/brands/teamspeak.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/telegram.svg b/resources/fontawesome/svgs/brands/telegram.svg deleted file mode 100644 index 3f0065e..0000000 --- a/resources/fontawesome/svgs/brands/telegram.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/tencent-weibo.svg b/resources/fontawesome/svgs/brands/tencent-weibo.svg deleted file mode 100644 index fa0c3fe..0000000 --- a/resources/fontawesome/svgs/brands/tencent-weibo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/the-red-yeti.svg b/resources/fontawesome/svgs/brands/the-red-yeti.svg deleted file mode 100644 index b2ff5ee..0000000 --- a/resources/fontawesome/svgs/brands/the-red-yeti.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/themeco.svg b/resources/fontawesome/svgs/brands/themeco.svg deleted file mode 100644 index 4976c59..0000000 --- a/resources/fontawesome/svgs/brands/themeco.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/themeisle.svg b/resources/fontawesome/svgs/brands/themeisle.svg deleted file mode 100644 index 4b5313b..0000000 --- a/resources/fontawesome/svgs/brands/themeisle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/think-peaks.svg b/resources/fontawesome/svgs/brands/think-peaks.svg deleted file mode 100644 index be1e6ba..0000000 --- a/resources/fontawesome/svgs/brands/think-peaks.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/threads.svg b/resources/fontawesome/svgs/brands/threads.svg deleted file mode 100644 index f248154..0000000 --- a/resources/fontawesome/svgs/brands/threads.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/tiktok.svg b/resources/fontawesome/svgs/brands/tiktok.svg deleted file mode 100644 index a604074..0000000 --- a/resources/fontawesome/svgs/brands/tiktok.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/trade-federation.svg b/resources/fontawesome/svgs/brands/trade-federation.svg deleted file mode 100644 index 8b86f06..0000000 --- a/resources/fontawesome/svgs/brands/trade-federation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/trello.svg b/resources/fontawesome/svgs/brands/trello.svg deleted file mode 100644 index 3f3b4be..0000000 --- a/resources/fontawesome/svgs/brands/trello.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/tumblr.svg b/resources/fontawesome/svgs/brands/tumblr.svg deleted file mode 100644 index e9ac8ac..0000000 --- a/resources/fontawesome/svgs/brands/tumblr.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/twitch.svg b/resources/fontawesome/svgs/brands/twitch.svg deleted file mode 100644 index c5663b3..0000000 --- a/resources/fontawesome/svgs/brands/twitch.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/twitter.svg b/resources/fontawesome/svgs/brands/twitter.svg deleted file mode 100644 index 9a2b4b8..0000000 --- a/resources/fontawesome/svgs/brands/twitter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/typo3.svg b/resources/fontawesome/svgs/brands/typo3.svg deleted file mode 100644 index 4e50336..0000000 --- a/resources/fontawesome/svgs/brands/typo3.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/uber.svg b/resources/fontawesome/svgs/brands/uber.svg deleted file mode 100644 index f9b25c2..0000000 --- a/resources/fontawesome/svgs/brands/uber.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ubuntu.svg b/resources/fontawesome/svgs/brands/ubuntu.svg deleted file mode 100644 index a3491d0..0000000 --- a/resources/fontawesome/svgs/brands/ubuntu.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/uikit.svg b/resources/fontawesome/svgs/brands/uikit.svg deleted file mode 100644 index 983b91a..0000000 --- a/resources/fontawesome/svgs/brands/uikit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/umbraco.svg b/resources/fontawesome/svgs/brands/umbraco.svg deleted file mode 100644 index 1e29b20..0000000 --- a/resources/fontawesome/svgs/brands/umbraco.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/uncharted.svg b/resources/fontawesome/svgs/brands/uncharted.svg deleted file mode 100644 index 70c723b..0000000 --- a/resources/fontawesome/svgs/brands/uncharted.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/uniregistry.svg b/resources/fontawesome/svgs/brands/uniregistry.svg deleted file mode 100644 index 946d708..0000000 --- a/resources/fontawesome/svgs/brands/uniregistry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/unity.svg b/resources/fontawesome/svgs/brands/unity.svg deleted file mode 100644 index c850d57..0000000 --- a/resources/fontawesome/svgs/brands/unity.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/unsplash.svg b/resources/fontawesome/svgs/brands/unsplash.svg deleted file mode 100644 index dedf8ba..0000000 --- a/resources/fontawesome/svgs/brands/unsplash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/untappd.svg b/resources/fontawesome/svgs/brands/untappd.svg deleted file mode 100644 index e91d6e5..0000000 --- a/resources/fontawesome/svgs/brands/untappd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ups.svg b/resources/fontawesome/svgs/brands/ups.svg deleted file mode 100644 index 2d5f286..0000000 --- a/resources/fontawesome/svgs/brands/ups.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/upwork.svg b/resources/fontawesome/svgs/brands/upwork.svg deleted file mode 100644 index a1dae2f..0000000 --- a/resources/fontawesome/svgs/brands/upwork.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/usb.svg b/resources/fontawesome/svgs/brands/usb.svg deleted file mode 100644 index 8d848cb..0000000 --- a/resources/fontawesome/svgs/brands/usb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/usps.svg b/resources/fontawesome/svgs/brands/usps.svg deleted file mode 100644 index a2eb919..0000000 --- a/resources/fontawesome/svgs/brands/usps.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/ussunnah.svg b/resources/fontawesome/svgs/brands/ussunnah.svg deleted file mode 100644 index 7ca11fc..0000000 --- a/resources/fontawesome/svgs/brands/ussunnah.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/vaadin.svg b/resources/fontawesome/svgs/brands/vaadin.svg deleted file mode 100644 index 1efe027..0000000 --- a/resources/fontawesome/svgs/brands/vaadin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/viacoin.svg b/resources/fontawesome/svgs/brands/viacoin.svg deleted file mode 100644 index 589a801..0000000 --- a/resources/fontawesome/svgs/brands/viacoin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/viadeo.svg b/resources/fontawesome/svgs/brands/viadeo.svg deleted file mode 100644 index b7cd8d6..0000000 --- a/resources/fontawesome/svgs/brands/viadeo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/viber.svg b/resources/fontawesome/svgs/brands/viber.svg deleted file mode 100644 index d3279cf..0000000 --- a/resources/fontawesome/svgs/brands/viber.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/vimeo-v.svg b/resources/fontawesome/svgs/brands/vimeo-v.svg deleted file mode 100644 index 2c9faa3..0000000 --- a/resources/fontawesome/svgs/brands/vimeo-v.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/vimeo.svg b/resources/fontawesome/svgs/brands/vimeo.svg deleted file mode 100644 index a24d96b..0000000 --- a/resources/fontawesome/svgs/brands/vimeo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/vine.svg b/resources/fontawesome/svgs/brands/vine.svg deleted file mode 100644 index 01861de..0000000 --- a/resources/fontawesome/svgs/brands/vine.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/vk.svg b/resources/fontawesome/svgs/brands/vk.svg deleted file mode 100644 index 4e9a6d6..0000000 --- a/resources/fontawesome/svgs/brands/vk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/vnv.svg b/resources/fontawesome/svgs/brands/vnv.svg deleted file mode 100644 index 32c3be0..0000000 --- a/resources/fontawesome/svgs/brands/vnv.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/vuejs.svg b/resources/fontawesome/svgs/brands/vuejs.svg deleted file mode 100644 index 17ca09a..0000000 --- a/resources/fontawesome/svgs/brands/vuejs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/watchman-monitoring.svg b/resources/fontawesome/svgs/brands/watchman-monitoring.svg deleted file mode 100644 index d511c43..0000000 --- a/resources/fontawesome/svgs/brands/watchman-monitoring.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/waze.svg b/resources/fontawesome/svgs/brands/waze.svg deleted file mode 100644 index fc547f2..0000000 --- a/resources/fontawesome/svgs/brands/waze.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/web-awesome.svg b/resources/fontawesome/svgs/brands/web-awesome.svg deleted file mode 100644 index 1eea054..0000000 --- a/resources/fontawesome/svgs/brands/web-awesome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/webflow.svg b/resources/fontawesome/svgs/brands/webflow.svg deleted file mode 100644 index ddede50..0000000 --- a/resources/fontawesome/svgs/brands/webflow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/weebly.svg b/resources/fontawesome/svgs/brands/weebly.svg deleted file mode 100644 index e621215..0000000 --- a/resources/fontawesome/svgs/brands/weebly.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/weibo.svg b/resources/fontawesome/svgs/brands/weibo.svg deleted file mode 100644 index fb4cb58..0000000 --- a/resources/fontawesome/svgs/brands/weibo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/weixin.svg b/resources/fontawesome/svgs/brands/weixin.svg deleted file mode 100644 index c89fbe7..0000000 --- a/resources/fontawesome/svgs/brands/weixin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/whatsapp.svg b/resources/fontawesome/svgs/brands/whatsapp.svg deleted file mode 100644 index be86a03..0000000 --- a/resources/fontawesome/svgs/brands/whatsapp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/whmcs.svg b/resources/fontawesome/svgs/brands/whmcs.svg deleted file mode 100644 index 5341e9d..0000000 --- a/resources/fontawesome/svgs/brands/whmcs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wikipedia-w.svg b/resources/fontawesome/svgs/brands/wikipedia-w.svg deleted file mode 100644 index a696142..0000000 --- a/resources/fontawesome/svgs/brands/wikipedia-w.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/windows.svg b/resources/fontawesome/svgs/brands/windows.svg deleted file mode 100644 index e1b4886..0000000 --- a/resources/fontawesome/svgs/brands/windows.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wirsindhandwerk.svg b/resources/fontawesome/svgs/brands/wirsindhandwerk.svg deleted file mode 100644 index 5ffb7fd..0000000 --- a/resources/fontawesome/svgs/brands/wirsindhandwerk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wix.svg b/resources/fontawesome/svgs/brands/wix.svg deleted file mode 100644 index 300fb6b..0000000 --- a/resources/fontawesome/svgs/brands/wix.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wizards-of-the-coast.svg b/resources/fontawesome/svgs/brands/wizards-of-the-coast.svg deleted file mode 100644 index f04ecd9..0000000 --- a/resources/fontawesome/svgs/brands/wizards-of-the-coast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wodu.svg b/resources/fontawesome/svgs/brands/wodu.svg deleted file mode 100644 index 5a79955..0000000 --- a/resources/fontawesome/svgs/brands/wodu.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wolf-pack-battalion.svg b/resources/fontawesome/svgs/brands/wolf-pack-battalion.svg deleted file mode 100644 index 301a1f6..0000000 --- a/resources/fontawesome/svgs/brands/wolf-pack-battalion.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wordpress-simple.svg b/resources/fontawesome/svgs/brands/wordpress-simple.svg deleted file mode 100644 index d861fda..0000000 --- a/resources/fontawesome/svgs/brands/wordpress-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wordpress.svg b/resources/fontawesome/svgs/brands/wordpress.svg deleted file mode 100644 index 097727c..0000000 --- a/resources/fontawesome/svgs/brands/wordpress.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wpbeginner.svg b/resources/fontawesome/svgs/brands/wpbeginner.svg deleted file mode 100644 index 5f5d921..0000000 --- a/resources/fontawesome/svgs/brands/wpbeginner.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wpexplorer.svg b/resources/fontawesome/svgs/brands/wpexplorer.svg deleted file mode 100644 index cc5d177..0000000 --- a/resources/fontawesome/svgs/brands/wpexplorer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wpforms.svg b/resources/fontawesome/svgs/brands/wpforms.svg deleted file mode 100644 index 34b3f7b..0000000 --- a/resources/fontawesome/svgs/brands/wpforms.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/wpressr.svg b/resources/fontawesome/svgs/brands/wpressr.svg deleted file mode 100644 index a6c864d..0000000 --- a/resources/fontawesome/svgs/brands/wpressr.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/x-twitter.svg b/resources/fontawesome/svgs/brands/x-twitter.svg deleted file mode 100644 index e3bfafb..0000000 --- a/resources/fontawesome/svgs/brands/x-twitter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/xbox.svg b/resources/fontawesome/svgs/brands/xbox.svg deleted file mode 100644 index 4c867aa..0000000 --- a/resources/fontawesome/svgs/brands/xbox.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/xing.svg b/resources/fontawesome/svgs/brands/xing.svg deleted file mode 100644 index d71ef8a..0000000 --- a/resources/fontawesome/svgs/brands/xing.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/y-combinator.svg b/resources/fontawesome/svgs/brands/y-combinator.svg deleted file mode 100644 index d74dea0..0000000 --- a/resources/fontawesome/svgs/brands/y-combinator.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/yahoo.svg b/resources/fontawesome/svgs/brands/yahoo.svg deleted file mode 100644 index 15a909c..0000000 --- a/resources/fontawesome/svgs/brands/yahoo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/yammer.svg b/resources/fontawesome/svgs/brands/yammer.svg deleted file mode 100644 index 97e2640..0000000 --- a/resources/fontawesome/svgs/brands/yammer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/yandex-international.svg b/resources/fontawesome/svgs/brands/yandex-international.svg deleted file mode 100644 index b175881..0000000 --- a/resources/fontawesome/svgs/brands/yandex-international.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/yandex.svg b/resources/fontawesome/svgs/brands/yandex.svg deleted file mode 100644 index fd3c01f..0000000 --- a/resources/fontawesome/svgs/brands/yandex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/yarn.svg b/resources/fontawesome/svgs/brands/yarn.svg deleted file mode 100644 index f553989..0000000 --- a/resources/fontawesome/svgs/brands/yarn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/yelp.svg b/resources/fontawesome/svgs/brands/yelp.svg deleted file mode 100644 index 22c9086..0000000 --- a/resources/fontawesome/svgs/brands/yelp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/yoast.svg b/resources/fontawesome/svgs/brands/yoast.svg deleted file mode 100644 index a3c1c05..0000000 --- a/resources/fontawesome/svgs/brands/yoast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/youtube.svg b/resources/fontawesome/svgs/brands/youtube.svg deleted file mode 100644 index 753779e..0000000 --- a/resources/fontawesome/svgs/brands/youtube.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/brands/zhihu.svg b/resources/fontawesome/svgs/brands/zhihu.svg deleted file mode 100644 index 2839d70..0000000 --- a/resources/fontawesome/svgs/brands/zhihu.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/address-book.svg b/resources/fontawesome/svgs/regular/address-book.svg deleted file mode 100644 index 72c05dd..0000000 --- a/resources/fontawesome/svgs/regular/address-book.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/address-card.svg b/resources/fontawesome/svgs/regular/address-card.svg deleted file mode 100644 index c8179ac..0000000 --- a/resources/fontawesome/svgs/regular/address-card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/bell-slash.svg b/resources/fontawesome/svgs/regular/bell-slash.svg deleted file mode 100644 index 478d921..0000000 --- a/resources/fontawesome/svgs/regular/bell-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/bell.svg b/resources/fontawesome/svgs/regular/bell.svg deleted file mode 100644 index 88a515d..0000000 --- a/resources/fontawesome/svgs/regular/bell.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/bookmark.svg b/resources/fontawesome/svgs/regular/bookmark.svg deleted file mode 100644 index 9da77fd..0000000 --- a/resources/fontawesome/svgs/regular/bookmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/building.svg b/resources/fontawesome/svgs/regular/building.svg deleted file mode 100644 index 10e7888..0000000 --- a/resources/fontawesome/svgs/regular/building.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/calendar-check.svg b/resources/fontawesome/svgs/regular/calendar-check.svg deleted file mode 100644 index 1d90456..0000000 --- a/resources/fontawesome/svgs/regular/calendar-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/calendar-days.svg b/resources/fontawesome/svgs/regular/calendar-days.svg deleted file mode 100644 index d6f2e24..0000000 --- a/resources/fontawesome/svgs/regular/calendar-days.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/calendar-minus.svg b/resources/fontawesome/svgs/regular/calendar-minus.svg deleted file mode 100644 index 4f4d530..0000000 --- a/resources/fontawesome/svgs/regular/calendar-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/calendar-plus.svg b/resources/fontawesome/svgs/regular/calendar-plus.svg deleted file mode 100644 index e74c7f4..0000000 --- a/resources/fontawesome/svgs/regular/calendar-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/calendar-xmark.svg b/resources/fontawesome/svgs/regular/calendar-xmark.svg deleted file mode 100644 index 743dab4..0000000 --- a/resources/fontawesome/svgs/regular/calendar-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/calendar.svg b/resources/fontawesome/svgs/regular/calendar.svg deleted file mode 100644 index cec0b6d..0000000 --- a/resources/fontawesome/svgs/regular/calendar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/chart-bar.svg b/resources/fontawesome/svgs/regular/chart-bar.svg deleted file mode 100644 index ab7ba81..0000000 --- a/resources/fontawesome/svgs/regular/chart-bar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/chess-bishop.svg b/resources/fontawesome/svgs/regular/chess-bishop.svg deleted file mode 100644 index 6a746f4..0000000 --- a/resources/fontawesome/svgs/regular/chess-bishop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/chess-king.svg b/resources/fontawesome/svgs/regular/chess-king.svg deleted file mode 100644 index 572372a..0000000 --- a/resources/fontawesome/svgs/regular/chess-king.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/chess-knight.svg b/resources/fontawesome/svgs/regular/chess-knight.svg deleted file mode 100644 index 9860c2a..0000000 --- a/resources/fontawesome/svgs/regular/chess-knight.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/chess-pawn.svg b/resources/fontawesome/svgs/regular/chess-pawn.svg deleted file mode 100644 index 730c9b8..0000000 --- a/resources/fontawesome/svgs/regular/chess-pawn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/chess-queen.svg b/resources/fontawesome/svgs/regular/chess-queen.svg deleted file mode 100644 index b29aad1..0000000 --- a/resources/fontawesome/svgs/regular/chess-queen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/chess-rook.svg b/resources/fontawesome/svgs/regular/chess-rook.svg deleted file mode 100644 index 591f38b..0000000 --- a/resources/fontawesome/svgs/regular/chess-rook.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-check.svg b/resources/fontawesome/svgs/regular/circle-check.svg deleted file mode 100644 index 97e0b9c..0000000 --- a/resources/fontawesome/svgs/regular/circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-dot.svg b/resources/fontawesome/svgs/regular/circle-dot.svg deleted file mode 100644 index 1522792..0000000 --- a/resources/fontawesome/svgs/regular/circle-dot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-down.svg b/resources/fontawesome/svgs/regular/circle-down.svg deleted file mode 100644 index e6ef058..0000000 --- a/resources/fontawesome/svgs/regular/circle-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-left.svg b/resources/fontawesome/svgs/regular/circle-left.svg deleted file mode 100644 index d11b6a1..0000000 --- a/resources/fontawesome/svgs/regular/circle-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-pause.svg b/resources/fontawesome/svgs/regular/circle-pause.svg deleted file mode 100644 index e04c72c..0000000 --- a/resources/fontawesome/svgs/regular/circle-pause.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-play.svg b/resources/fontawesome/svgs/regular/circle-play.svg deleted file mode 100644 index eb2f649..0000000 --- a/resources/fontawesome/svgs/regular/circle-play.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-question.svg b/resources/fontawesome/svgs/regular/circle-question.svg deleted file mode 100644 index 1df2b5d..0000000 --- a/resources/fontawesome/svgs/regular/circle-question.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-right.svg b/resources/fontawesome/svgs/regular/circle-right.svg deleted file mode 100644 index f2f19d9..0000000 --- a/resources/fontawesome/svgs/regular/circle-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-stop.svg b/resources/fontawesome/svgs/regular/circle-stop.svg deleted file mode 100644 index 1ad6184..0000000 --- a/resources/fontawesome/svgs/regular/circle-stop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-up.svg b/resources/fontawesome/svgs/regular/circle-up.svg deleted file mode 100644 index 77b6989..0000000 --- a/resources/fontawesome/svgs/regular/circle-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-user.svg b/resources/fontawesome/svgs/regular/circle-user.svg deleted file mode 100644 index 65bda67..0000000 --- a/resources/fontawesome/svgs/regular/circle-user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle-xmark.svg b/resources/fontawesome/svgs/regular/circle-xmark.svg deleted file mode 100644 index e7a8ae2..0000000 --- a/resources/fontawesome/svgs/regular/circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/circle.svg b/resources/fontawesome/svgs/regular/circle.svg deleted file mode 100644 index f264990..0000000 --- a/resources/fontawesome/svgs/regular/circle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/clipboard.svg b/resources/fontawesome/svgs/regular/clipboard.svg deleted file mode 100644 index 1f9fc5d..0000000 --- a/resources/fontawesome/svgs/regular/clipboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/clock.svg b/resources/fontawesome/svgs/regular/clock.svg deleted file mode 100644 index 830baab..0000000 --- a/resources/fontawesome/svgs/regular/clock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/clone.svg b/resources/fontawesome/svgs/regular/clone.svg deleted file mode 100644 index e61db2f..0000000 --- a/resources/fontawesome/svgs/regular/clone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/closed-captioning.svg b/resources/fontawesome/svgs/regular/closed-captioning.svg deleted file mode 100644 index c6bfc9f..0000000 --- a/resources/fontawesome/svgs/regular/closed-captioning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/comment-dots.svg b/resources/fontawesome/svgs/regular/comment-dots.svg deleted file mode 100644 index 8840185..0000000 --- a/resources/fontawesome/svgs/regular/comment-dots.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/comment.svg b/resources/fontawesome/svgs/regular/comment.svg deleted file mode 100644 index 99e43ac..0000000 --- a/resources/fontawesome/svgs/regular/comment.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/comments.svg b/resources/fontawesome/svgs/regular/comments.svg deleted file mode 100644 index 4987ab9..0000000 --- a/resources/fontawesome/svgs/regular/comments.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/compass.svg b/resources/fontawesome/svgs/regular/compass.svg deleted file mode 100644 index bf8919a..0000000 --- a/resources/fontawesome/svgs/regular/compass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/copy.svg b/resources/fontawesome/svgs/regular/copy.svg deleted file mode 100644 index 88bfb2b..0000000 --- a/resources/fontawesome/svgs/regular/copy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/copyright.svg b/resources/fontawesome/svgs/regular/copyright.svg deleted file mode 100644 index ab373dc..0000000 --- a/resources/fontawesome/svgs/regular/copyright.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/credit-card.svg b/resources/fontawesome/svgs/regular/credit-card.svg deleted file mode 100644 index 0999321..0000000 --- a/resources/fontawesome/svgs/regular/credit-card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/envelope-open.svg b/resources/fontawesome/svgs/regular/envelope-open.svg deleted file mode 100644 index 7f8ea10..0000000 --- a/resources/fontawesome/svgs/regular/envelope-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/envelope.svg b/resources/fontawesome/svgs/regular/envelope.svg deleted file mode 100644 index d9f578a..0000000 --- a/resources/fontawesome/svgs/regular/envelope.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/eye-slash.svg b/resources/fontawesome/svgs/regular/eye-slash.svg deleted file mode 100644 index 9c39ae9..0000000 --- a/resources/fontawesome/svgs/regular/eye-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/eye.svg b/resources/fontawesome/svgs/regular/eye.svg deleted file mode 100644 index c0ad1d5..0000000 --- a/resources/fontawesome/svgs/regular/eye.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-angry.svg b/resources/fontawesome/svgs/regular/face-angry.svg deleted file mode 100644 index 92be0f5..0000000 --- a/resources/fontawesome/svgs/regular/face-angry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-dizzy.svg b/resources/fontawesome/svgs/regular/face-dizzy.svg deleted file mode 100644 index c0795b2..0000000 --- a/resources/fontawesome/svgs/regular/face-dizzy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-flushed.svg b/resources/fontawesome/svgs/regular/face-flushed.svg deleted file mode 100644 index f72aa13..0000000 --- a/resources/fontawesome/svgs/regular/face-flushed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-frown-open.svg b/resources/fontawesome/svgs/regular/face-frown-open.svg deleted file mode 100644 index d7ac33a..0000000 --- a/resources/fontawesome/svgs/regular/face-frown-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-frown.svg b/resources/fontawesome/svgs/regular/face-frown.svg deleted file mode 100644 index 23d0005..0000000 --- a/resources/fontawesome/svgs/regular/face-frown.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grimace.svg b/resources/fontawesome/svgs/regular/face-grimace.svg deleted file mode 100644 index 2a1ea59..0000000 --- a/resources/fontawesome/svgs/regular/face-grimace.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-beam-sweat.svg b/resources/fontawesome/svgs/regular/face-grin-beam-sweat.svg deleted file mode 100644 index 95976bb..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-beam-sweat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-beam.svg b/resources/fontawesome/svgs/regular/face-grin-beam.svg deleted file mode 100644 index 0ca50d6..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-beam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-hearts.svg b/resources/fontawesome/svgs/regular/face-grin-hearts.svg deleted file mode 100644 index 63371cb..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-hearts.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-squint-tears.svg b/resources/fontawesome/svgs/regular/face-grin-squint-tears.svg deleted file mode 100644 index afbea7e..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-squint-tears.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-squint.svg b/resources/fontawesome/svgs/regular/face-grin-squint.svg deleted file mode 100644 index f09cc7c..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-squint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-stars.svg b/resources/fontawesome/svgs/regular/face-grin-stars.svg deleted file mode 100644 index e29696c..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-stars.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-tears.svg b/resources/fontawesome/svgs/regular/face-grin-tears.svg deleted file mode 100644 index cab6c1a..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-tears.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-tongue-squint.svg b/resources/fontawesome/svgs/regular/face-grin-tongue-squint.svg deleted file mode 100644 index dc25ee1..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-tongue-squint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-tongue-wink.svg b/resources/fontawesome/svgs/regular/face-grin-tongue-wink.svg deleted file mode 100644 index 84b7220..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-tongue-wink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-tongue.svg b/resources/fontawesome/svgs/regular/face-grin-tongue.svg deleted file mode 100644 index c0da277..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-tongue.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-wide.svg b/resources/fontawesome/svgs/regular/face-grin-wide.svg deleted file mode 100644 index ca58374..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-wide.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin-wink.svg b/resources/fontawesome/svgs/regular/face-grin-wink.svg deleted file mode 100644 index 352df72..0000000 --- a/resources/fontawesome/svgs/regular/face-grin-wink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-grin.svg b/resources/fontawesome/svgs/regular/face-grin.svg deleted file mode 100644 index c30ae4a..0000000 --- a/resources/fontawesome/svgs/regular/face-grin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-kiss-beam.svg b/resources/fontawesome/svgs/regular/face-kiss-beam.svg deleted file mode 100644 index c0ec708..0000000 --- a/resources/fontawesome/svgs/regular/face-kiss-beam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-kiss-wink-heart.svg b/resources/fontawesome/svgs/regular/face-kiss-wink-heart.svg deleted file mode 100644 index 4242755..0000000 --- a/resources/fontawesome/svgs/regular/face-kiss-wink-heart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-kiss.svg b/resources/fontawesome/svgs/regular/face-kiss.svg deleted file mode 100644 index 6cd5051..0000000 --- a/resources/fontawesome/svgs/regular/face-kiss.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-laugh-beam.svg b/resources/fontawesome/svgs/regular/face-laugh-beam.svg deleted file mode 100644 index 0de4a5c..0000000 --- a/resources/fontawesome/svgs/regular/face-laugh-beam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-laugh-squint.svg b/resources/fontawesome/svgs/regular/face-laugh-squint.svg deleted file mode 100644 index 275e1c7..0000000 --- a/resources/fontawesome/svgs/regular/face-laugh-squint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-laugh-wink.svg b/resources/fontawesome/svgs/regular/face-laugh-wink.svg deleted file mode 100644 index a73525b..0000000 --- a/resources/fontawesome/svgs/regular/face-laugh-wink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-laugh.svg b/resources/fontawesome/svgs/regular/face-laugh.svg deleted file mode 100644 index 73b1241..0000000 --- a/resources/fontawesome/svgs/regular/face-laugh.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-meh-blank.svg b/resources/fontawesome/svgs/regular/face-meh-blank.svg deleted file mode 100644 index fbddc09..0000000 --- a/resources/fontawesome/svgs/regular/face-meh-blank.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-meh.svg b/resources/fontawesome/svgs/regular/face-meh.svg deleted file mode 100644 index 2243f7d..0000000 --- a/resources/fontawesome/svgs/regular/face-meh.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-rolling-eyes.svg b/resources/fontawesome/svgs/regular/face-rolling-eyes.svg deleted file mode 100644 index d049fc1..0000000 --- a/resources/fontawesome/svgs/regular/face-rolling-eyes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-sad-cry.svg b/resources/fontawesome/svgs/regular/face-sad-cry.svg deleted file mode 100644 index 1ceff35..0000000 --- a/resources/fontawesome/svgs/regular/face-sad-cry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-sad-tear.svg b/resources/fontawesome/svgs/regular/face-sad-tear.svg deleted file mode 100644 index 86c5f91..0000000 --- a/resources/fontawesome/svgs/regular/face-sad-tear.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-smile-beam.svg b/resources/fontawesome/svgs/regular/face-smile-beam.svg deleted file mode 100644 index 3881a7d..0000000 --- a/resources/fontawesome/svgs/regular/face-smile-beam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-smile-wink.svg b/resources/fontawesome/svgs/regular/face-smile-wink.svg deleted file mode 100644 index e3fcf43..0000000 --- a/resources/fontawesome/svgs/regular/face-smile-wink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-smile.svg b/resources/fontawesome/svgs/regular/face-smile.svg deleted file mode 100644 index eb6ec96..0000000 --- a/resources/fontawesome/svgs/regular/face-smile.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-surprise.svg b/resources/fontawesome/svgs/regular/face-surprise.svg deleted file mode 100644 index 06e26cf..0000000 --- a/resources/fontawesome/svgs/regular/face-surprise.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/face-tired.svg b/resources/fontawesome/svgs/regular/face-tired.svg deleted file mode 100644 index ac82416..0000000 --- a/resources/fontawesome/svgs/regular/face-tired.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-audio.svg b/resources/fontawesome/svgs/regular/file-audio.svg deleted file mode 100644 index fc4329f..0000000 --- a/resources/fontawesome/svgs/regular/file-audio.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-code.svg b/resources/fontawesome/svgs/regular/file-code.svg deleted file mode 100644 index 419e344..0000000 --- a/resources/fontawesome/svgs/regular/file-code.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-excel.svg b/resources/fontawesome/svgs/regular/file-excel.svg deleted file mode 100644 index a233b1f..0000000 --- a/resources/fontawesome/svgs/regular/file-excel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-image.svg b/resources/fontawesome/svgs/regular/file-image.svg deleted file mode 100644 index b59c95e..0000000 --- a/resources/fontawesome/svgs/regular/file-image.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-lines.svg b/resources/fontawesome/svgs/regular/file-lines.svg deleted file mode 100644 index 0b2fa7f..0000000 --- a/resources/fontawesome/svgs/regular/file-lines.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-pdf.svg b/resources/fontawesome/svgs/regular/file-pdf.svg deleted file mode 100644 index ccd8199..0000000 --- a/resources/fontawesome/svgs/regular/file-pdf.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-powerpoint.svg b/resources/fontawesome/svgs/regular/file-powerpoint.svg deleted file mode 100644 index ef13475..0000000 --- a/resources/fontawesome/svgs/regular/file-powerpoint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-video.svg b/resources/fontawesome/svgs/regular/file-video.svg deleted file mode 100644 index 7a4941f..0000000 --- a/resources/fontawesome/svgs/regular/file-video.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-word.svg b/resources/fontawesome/svgs/regular/file-word.svg deleted file mode 100644 index 9b72e93..0000000 --- a/resources/fontawesome/svgs/regular/file-word.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file-zipper.svg b/resources/fontawesome/svgs/regular/file-zipper.svg deleted file mode 100644 index 97670f1..0000000 --- a/resources/fontawesome/svgs/regular/file-zipper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/file.svg b/resources/fontawesome/svgs/regular/file.svg deleted file mode 100644 index acd882f..0000000 --- a/resources/fontawesome/svgs/regular/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/flag.svg b/resources/fontawesome/svgs/regular/flag.svg deleted file mode 100644 index 983b80f..0000000 --- a/resources/fontawesome/svgs/regular/flag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/floppy-disk.svg b/resources/fontawesome/svgs/regular/floppy-disk.svg deleted file mode 100644 index 94b4e48..0000000 --- a/resources/fontawesome/svgs/regular/floppy-disk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/folder-closed.svg b/resources/fontawesome/svgs/regular/folder-closed.svg deleted file mode 100644 index 35a0fc5..0000000 --- a/resources/fontawesome/svgs/regular/folder-closed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/folder-open.svg b/resources/fontawesome/svgs/regular/folder-open.svg deleted file mode 100644 index c8aba2b..0000000 --- a/resources/fontawesome/svgs/regular/folder-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/folder.svg b/resources/fontawesome/svgs/regular/folder.svg deleted file mode 100644 index 68033ec..0000000 --- a/resources/fontawesome/svgs/regular/folder.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/font-awesome.svg b/resources/fontawesome/svgs/regular/font-awesome.svg deleted file mode 100644 index d573bde..0000000 --- a/resources/fontawesome/svgs/regular/font-awesome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/futbol.svg b/resources/fontawesome/svgs/regular/futbol.svg deleted file mode 100644 index e0a9f3a..0000000 --- a/resources/fontawesome/svgs/regular/futbol.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/gem.svg b/resources/fontawesome/svgs/regular/gem.svg deleted file mode 100644 index 05096b0..0000000 --- a/resources/fontawesome/svgs/regular/gem.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-back-fist.svg b/resources/fontawesome/svgs/regular/hand-back-fist.svg deleted file mode 100644 index 36b6acf..0000000 --- a/resources/fontawesome/svgs/regular/hand-back-fist.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-lizard.svg b/resources/fontawesome/svgs/regular/hand-lizard.svg deleted file mode 100644 index 57c7008..0000000 --- a/resources/fontawesome/svgs/regular/hand-lizard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-peace.svg b/resources/fontawesome/svgs/regular/hand-peace.svg deleted file mode 100644 index f9df3ac..0000000 --- a/resources/fontawesome/svgs/regular/hand-peace.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-point-down.svg b/resources/fontawesome/svgs/regular/hand-point-down.svg deleted file mode 100644 index 02c0cb4..0000000 --- a/resources/fontawesome/svgs/regular/hand-point-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-point-left.svg b/resources/fontawesome/svgs/regular/hand-point-left.svg deleted file mode 100644 index 5ac6edf..0000000 --- a/resources/fontawesome/svgs/regular/hand-point-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-point-right.svg b/resources/fontawesome/svgs/regular/hand-point-right.svg deleted file mode 100644 index 70d863d..0000000 --- a/resources/fontawesome/svgs/regular/hand-point-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-point-up.svg b/resources/fontawesome/svgs/regular/hand-point-up.svg deleted file mode 100644 index cc70002..0000000 --- a/resources/fontawesome/svgs/regular/hand-point-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-pointer.svg b/resources/fontawesome/svgs/regular/hand-pointer.svg deleted file mode 100644 index 0b1a720..0000000 --- a/resources/fontawesome/svgs/regular/hand-pointer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-scissors.svg b/resources/fontawesome/svgs/regular/hand-scissors.svg deleted file mode 100644 index d1f9202..0000000 --- a/resources/fontawesome/svgs/regular/hand-scissors.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand-spock.svg b/resources/fontawesome/svgs/regular/hand-spock.svg deleted file mode 100644 index 1519220..0000000 --- a/resources/fontawesome/svgs/regular/hand-spock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hand.svg b/resources/fontawesome/svgs/regular/hand.svg deleted file mode 100644 index 5913870..0000000 --- a/resources/fontawesome/svgs/regular/hand.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/handshake.svg b/resources/fontawesome/svgs/regular/handshake.svg deleted file mode 100644 index fc00fe6..0000000 --- a/resources/fontawesome/svgs/regular/handshake.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hard-drive.svg b/resources/fontawesome/svgs/regular/hard-drive.svg deleted file mode 100644 index 80ab018..0000000 --- a/resources/fontawesome/svgs/regular/hard-drive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/heart.svg b/resources/fontawesome/svgs/regular/heart.svg deleted file mode 100644 index a4b8d8a..0000000 --- a/resources/fontawesome/svgs/regular/heart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hospital.svg b/resources/fontawesome/svgs/regular/hospital.svg deleted file mode 100644 index b3a6a9b..0000000 --- a/resources/fontawesome/svgs/regular/hospital.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hourglass-half.svg b/resources/fontawesome/svgs/regular/hourglass-half.svg deleted file mode 100644 index ebf7e45..0000000 --- a/resources/fontawesome/svgs/regular/hourglass-half.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/hourglass.svg b/resources/fontawesome/svgs/regular/hourglass.svg deleted file mode 100644 index 6b7c1ac..0000000 --- a/resources/fontawesome/svgs/regular/hourglass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/id-badge.svg b/resources/fontawesome/svgs/regular/id-badge.svg deleted file mode 100644 index df839cb..0000000 --- a/resources/fontawesome/svgs/regular/id-badge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/id-card.svg b/resources/fontawesome/svgs/regular/id-card.svg deleted file mode 100644 index 356db1f..0000000 --- a/resources/fontawesome/svgs/regular/id-card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/image.svg b/resources/fontawesome/svgs/regular/image.svg deleted file mode 100644 index f793d0d..0000000 --- a/resources/fontawesome/svgs/regular/image.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/images.svg b/resources/fontawesome/svgs/regular/images.svg deleted file mode 100644 index 2a48721..0000000 --- a/resources/fontawesome/svgs/regular/images.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/keyboard.svg b/resources/fontawesome/svgs/regular/keyboard.svg deleted file mode 100644 index 7509fe0..0000000 --- a/resources/fontawesome/svgs/regular/keyboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/lemon.svg b/resources/fontawesome/svgs/regular/lemon.svg deleted file mode 100644 index 0c10985..0000000 --- a/resources/fontawesome/svgs/regular/lemon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/life-ring.svg b/resources/fontawesome/svgs/regular/life-ring.svg deleted file mode 100644 index 6880eba..0000000 --- a/resources/fontawesome/svgs/regular/life-ring.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/lightbulb.svg b/resources/fontawesome/svgs/regular/lightbulb.svg deleted file mode 100644 index d1fc711..0000000 --- a/resources/fontawesome/svgs/regular/lightbulb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/map.svg b/resources/fontawesome/svgs/regular/map.svg deleted file mode 100644 index 3efa406..0000000 --- a/resources/fontawesome/svgs/regular/map.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/message.svg b/resources/fontawesome/svgs/regular/message.svg deleted file mode 100644 index 64f26fe..0000000 --- a/resources/fontawesome/svgs/regular/message.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/money-bill-1.svg b/resources/fontawesome/svgs/regular/money-bill-1.svg deleted file mode 100644 index 84c400d..0000000 --- a/resources/fontawesome/svgs/regular/money-bill-1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/moon.svg b/resources/fontawesome/svgs/regular/moon.svg deleted file mode 100644 index 8cd2968..0000000 --- a/resources/fontawesome/svgs/regular/moon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/newspaper.svg b/resources/fontawesome/svgs/regular/newspaper.svg deleted file mode 100644 index d30b61e..0000000 --- a/resources/fontawesome/svgs/regular/newspaper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/note-sticky.svg b/resources/fontawesome/svgs/regular/note-sticky.svg deleted file mode 100644 index 651cda0..0000000 --- a/resources/fontawesome/svgs/regular/note-sticky.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/object-group.svg b/resources/fontawesome/svgs/regular/object-group.svg deleted file mode 100644 index 91f89b6..0000000 --- a/resources/fontawesome/svgs/regular/object-group.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/object-ungroup.svg b/resources/fontawesome/svgs/regular/object-ungroup.svg deleted file mode 100644 index a820eec..0000000 --- a/resources/fontawesome/svgs/regular/object-ungroup.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/paper-plane.svg b/resources/fontawesome/svgs/regular/paper-plane.svg deleted file mode 100644 index c153a06..0000000 --- a/resources/fontawesome/svgs/regular/paper-plane.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/paste.svg b/resources/fontawesome/svgs/regular/paste.svg deleted file mode 100644 index 02b2069..0000000 --- a/resources/fontawesome/svgs/regular/paste.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/pen-to-square.svg b/resources/fontawesome/svgs/regular/pen-to-square.svg deleted file mode 100644 index 2e22757..0000000 --- a/resources/fontawesome/svgs/regular/pen-to-square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/rectangle-list.svg b/resources/fontawesome/svgs/regular/rectangle-list.svg deleted file mode 100644 index 2ab7809..0000000 --- a/resources/fontawesome/svgs/regular/rectangle-list.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/rectangle-xmark.svg b/resources/fontawesome/svgs/regular/rectangle-xmark.svg deleted file mode 100644 index f1562a5..0000000 --- a/resources/fontawesome/svgs/regular/rectangle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/registered.svg b/resources/fontawesome/svgs/regular/registered.svg deleted file mode 100644 index 7279068..0000000 --- a/resources/fontawesome/svgs/regular/registered.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/share-from-square.svg b/resources/fontawesome/svgs/regular/share-from-square.svg deleted file mode 100644 index ec316ac..0000000 --- a/resources/fontawesome/svgs/regular/share-from-square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/snowflake.svg b/resources/fontawesome/svgs/regular/snowflake.svg deleted file mode 100644 index 8e2e92d..0000000 --- a/resources/fontawesome/svgs/regular/snowflake.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square-caret-down.svg b/resources/fontawesome/svgs/regular/square-caret-down.svg deleted file mode 100644 index 825887b..0000000 --- a/resources/fontawesome/svgs/regular/square-caret-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square-caret-left.svg b/resources/fontawesome/svgs/regular/square-caret-left.svg deleted file mode 100644 index 47c6359..0000000 --- a/resources/fontawesome/svgs/regular/square-caret-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square-caret-right.svg b/resources/fontawesome/svgs/regular/square-caret-right.svg deleted file mode 100644 index d4d484f..0000000 --- a/resources/fontawesome/svgs/regular/square-caret-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square-caret-up.svg b/resources/fontawesome/svgs/regular/square-caret-up.svg deleted file mode 100644 index cfbea32..0000000 --- a/resources/fontawesome/svgs/regular/square-caret-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square-check.svg b/resources/fontawesome/svgs/regular/square-check.svg deleted file mode 100644 index bdf645d..0000000 --- a/resources/fontawesome/svgs/regular/square-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square-full.svg b/resources/fontawesome/svgs/regular/square-full.svg deleted file mode 100644 index 171621b..0000000 --- a/resources/fontawesome/svgs/regular/square-full.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square-minus.svg b/resources/fontawesome/svgs/regular/square-minus.svg deleted file mode 100644 index aa1e81a..0000000 --- a/resources/fontawesome/svgs/regular/square-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square-plus.svg b/resources/fontawesome/svgs/regular/square-plus.svg deleted file mode 100644 index b1a17fd..0000000 --- a/resources/fontawesome/svgs/regular/square-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/square.svg b/resources/fontawesome/svgs/regular/square.svg deleted file mode 100644 index 9492e1a..0000000 --- a/resources/fontawesome/svgs/regular/square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/star-half-stroke.svg b/resources/fontawesome/svgs/regular/star-half-stroke.svg deleted file mode 100644 index 36216a7..0000000 --- a/resources/fontawesome/svgs/regular/star-half-stroke.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/star-half.svg b/resources/fontawesome/svgs/regular/star-half.svg deleted file mode 100644 index fa1220b..0000000 --- a/resources/fontawesome/svgs/regular/star-half.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/star.svg b/resources/fontawesome/svgs/regular/star.svg deleted file mode 100644 index 6998d32..0000000 --- a/resources/fontawesome/svgs/regular/star.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/sun.svg b/resources/fontawesome/svgs/regular/sun.svg deleted file mode 100644 index f59c33f..0000000 --- a/resources/fontawesome/svgs/regular/sun.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/thumbs-down.svg b/resources/fontawesome/svgs/regular/thumbs-down.svg deleted file mode 100644 index eada076..0000000 --- a/resources/fontawesome/svgs/regular/thumbs-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/thumbs-up.svg b/resources/fontawesome/svgs/regular/thumbs-up.svg deleted file mode 100644 index 15130d9..0000000 --- a/resources/fontawesome/svgs/regular/thumbs-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/trash-can.svg b/resources/fontawesome/svgs/regular/trash-can.svg deleted file mode 100644 index dc6fcb3..0000000 --- a/resources/fontawesome/svgs/regular/trash-can.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/user.svg b/resources/fontawesome/svgs/regular/user.svg deleted file mode 100644 index 03f9215..0000000 --- a/resources/fontawesome/svgs/regular/user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/window-maximize.svg b/resources/fontawesome/svgs/regular/window-maximize.svg deleted file mode 100644 index ed0d143..0000000 --- a/resources/fontawesome/svgs/regular/window-maximize.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/window-minimize.svg b/resources/fontawesome/svgs/regular/window-minimize.svg deleted file mode 100644 index 00d4dba..0000000 --- a/resources/fontawesome/svgs/regular/window-minimize.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/regular/window-restore.svg b/resources/fontawesome/svgs/regular/window-restore.svg deleted file mode 100644 index 90ad737..0000000 --- a/resources/fontawesome/svgs/regular/window-restore.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/0.svg b/resources/fontawesome/svgs/solid/0.svg deleted file mode 100644 index 903d869..0000000 --- a/resources/fontawesome/svgs/solid/0.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/1.svg b/resources/fontawesome/svgs/solid/1.svg deleted file mode 100644 index 1b77e62..0000000 --- a/resources/fontawesome/svgs/solid/1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/2.svg b/resources/fontawesome/svgs/solid/2.svg deleted file mode 100644 index 192c095..0000000 --- a/resources/fontawesome/svgs/solid/2.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/3.svg b/resources/fontawesome/svgs/solid/3.svg deleted file mode 100644 index 51ca927..0000000 --- a/resources/fontawesome/svgs/solid/3.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/4.svg b/resources/fontawesome/svgs/solid/4.svg deleted file mode 100644 index fafa46d..0000000 --- a/resources/fontawesome/svgs/solid/4.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/5.svg b/resources/fontawesome/svgs/solid/5.svg deleted file mode 100644 index 35e6556..0000000 --- a/resources/fontawesome/svgs/solid/5.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/6.svg b/resources/fontawesome/svgs/solid/6.svg deleted file mode 100644 index e752747..0000000 --- a/resources/fontawesome/svgs/solid/6.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/7.svg b/resources/fontawesome/svgs/solid/7.svg deleted file mode 100644 index cfa6fe0..0000000 --- a/resources/fontawesome/svgs/solid/7.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/8.svg b/resources/fontawesome/svgs/solid/8.svg deleted file mode 100644 index 2328ce4..0000000 --- a/resources/fontawesome/svgs/solid/8.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/9.svg b/resources/fontawesome/svgs/solid/9.svg deleted file mode 100644 index 14e2407..0000000 --- a/resources/fontawesome/svgs/solid/9.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/a.svg b/resources/fontawesome/svgs/solid/a.svg deleted file mode 100644 index 92a3842..0000000 --- a/resources/fontawesome/svgs/solid/a.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/address-book.svg b/resources/fontawesome/svgs/solid/address-book.svg deleted file mode 100644 index f940b61..0000000 --- a/resources/fontawesome/svgs/solid/address-book.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/address-card.svg b/resources/fontawesome/svgs/solid/address-card.svg deleted file mode 100644 index 9b12983..0000000 --- a/resources/fontawesome/svgs/solid/address-card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/align-center.svg b/resources/fontawesome/svgs/solid/align-center.svg deleted file mode 100644 index 9a5c9be..0000000 --- a/resources/fontawesome/svgs/solid/align-center.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/align-justify.svg b/resources/fontawesome/svgs/solid/align-justify.svg deleted file mode 100644 index 57aa3e4..0000000 --- a/resources/fontawesome/svgs/solid/align-justify.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/align-left.svg b/resources/fontawesome/svgs/solid/align-left.svg deleted file mode 100644 index a61f235..0000000 --- a/resources/fontawesome/svgs/solid/align-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/align-right.svg b/resources/fontawesome/svgs/solid/align-right.svg deleted file mode 100644 index 19a91bd..0000000 --- a/resources/fontawesome/svgs/solid/align-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/anchor-circle-check.svg b/resources/fontawesome/svgs/solid/anchor-circle-check.svg deleted file mode 100644 index 4b055fa..0000000 --- a/resources/fontawesome/svgs/solid/anchor-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/anchor-circle-exclamation.svg b/resources/fontawesome/svgs/solid/anchor-circle-exclamation.svg deleted file mode 100644 index 54b5874..0000000 --- a/resources/fontawesome/svgs/solid/anchor-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/anchor-circle-xmark.svg b/resources/fontawesome/svgs/solid/anchor-circle-xmark.svg deleted file mode 100644 index 41c4745..0000000 --- a/resources/fontawesome/svgs/solid/anchor-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/anchor-lock.svg b/resources/fontawesome/svgs/solid/anchor-lock.svg deleted file mode 100644 index 12940d5..0000000 --- a/resources/fontawesome/svgs/solid/anchor-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/anchor.svg b/resources/fontawesome/svgs/solid/anchor.svg deleted file mode 100644 index 493706e..0000000 --- a/resources/fontawesome/svgs/solid/anchor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/angle-down.svg b/resources/fontawesome/svgs/solid/angle-down.svg deleted file mode 100644 index 29b24b2..0000000 --- a/resources/fontawesome/svgs/solid/angle-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/angle-left.svg b/resources/fontawesome/svgs/solid/angle-left.svg deleted file mode 100644 index a98acf0..0000000 --- a/resources/fontawesome/svgs/solid/angle-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/angle-right.svg b/resources/fontawesome/svgs/solid/angle-right.svg deleted file mode 100644 index 91f98cc..0000000 --- a/resources/fontawesome/svgs/solid/angle-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/angle-up.svg b/resources/fontawesome/svgs/solid/angle-up.svg deleted file mode 100644 index de73360..0000000 --- a/resources/fontawesome/svgs/solid/angle-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/angles-down.svg b/resources/fontawesome/svgs/solid/angles-down.svg deleted file mode 100644 index 94a016d..0000000 --- a/resources/fontawesome/svgs/solid/angles-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/angles-left.svg b/resources/fontawesome/svgs/solid/angles-left.svg deleted file mode 100644 index 8ea33ed..0000000 --- a/resources/fontawesome/svgs/solid/angles-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/angles-right.svg b/resources/fontawesome/svgs/solid/angles-right.svg deleted file mode 100644 index a9811aa..0000000 --- a/resources/fontawesome/svgs/solid/angles-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/angles-up.svg b/resources/fontawesome/svgs/solid/angles-up.svg deleted file mode 100644 index 5974c59..0000000 --- a/resources/fontawesome/svgs/solid/angles-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ankh.svg b/resources/fontawesome/svgs/solid/ankh.svg deleted file mode 100644 index 2682209..0000000 --- a/resources/fontawesome/svgs/solid/ankh.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/apple-whole.svg b/resources/fontawesome/svgs/solid/apple-whole.svg deleted file mode 100644 index 1d49aee..0000000 --- a/resources/fontawesome/svgs/solid/apple-whole.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/archway.svg b/resources/fontawesome/svgs/solid/archway.svg deleted file mode 100644 index 12357e5..0000000 --- a/resources/fontawesome/svgs/solid/archway.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-1-9.svg b/resources/fontawesome/svgs/solid/arrow-down-1-9.svg deleted file mode 100644 index 2197df5..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-1-9.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-9-1.svg b/resources/fontawesome/svgs/solid/arrow-down-9-1.svg deleted file mode 100644 index 11264fe..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-9-1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-a-z.svg b/resources/fontawesome/svgs/solid/arrow-down-a-z.svg deleted file mode 100644 index ba2ce42..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-a-z.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-long.svg b/resources/fontawesome/svgs/solid/arrow-down-long.svg deleted file mode 100644 index b73cb54..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-long.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-short-wide.svg b/resources/fontawesome/svgs/solid/arrow-down-short-wide.svg deleted file mode 100644 index debd35e..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-short-wide.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-up-across-line.svg b/resources/fontawesome/svgs/solid/arrow-down-up-across-line.svg deleted file mode 100644 index 46e4260..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-up-across-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-up-lock.svg b/resources/fontawesome/svgs/solid/arrow-down-up-lock.svg deleted file mode 100644 index 00f460d..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-up-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-wide-short.svg b/resources/fontawesome/svgs/solid/arrow-down-wide-short.svg deleted file mode 100644 index 7875c4c..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-wide-short.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down-z-a.svg b/resources/fontawesome/svgs/solid/arrow-down-z-a.svg deleted file mode 100644 index 83d7476..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down-z-a.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-down.svg b/resources/fontawesome/svgs/solid/arrow-down.svg deleted file mode 100644 index 81fd6cc..0000000 --- a/resources/fontawesome/svgs/solid/arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-left-long.svg b/resources/fontawesome/svgs/solid/arrow-left-long.svg deleted file mode 100644 index 31e1f01..0000000 --- a/resources/fontawesome/svgs/solid/arrow-left-long.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-left.svg b/resources/fontawesome/svgs/solid/arrow-left.svg deleted file mode 100644 index 9dddb48..0000000 --- a/resources/fontawesome/svgs/solid/arrow-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-pointer.svg b/resources/fontawesome/svgs/solid/arrow-pointer.svg deleted file mode 100644 index fdd1e99..0000000 --- a/resources/fontawesome/svgs/solid/arrow-pointer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-right-arrow-left.svg b/resources/fontawesome/svgs/solid/arrow-right-arrow-left.svg deleted file mode 100644 index 2ba2c44..0000000 --- a/resources/fontawesome/svgs/solid/arrow-right-arrow-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-right-from-bracket.svg b/resources/fontawesome/svgs/solid/arrow-right-from-bracket.svg deleted file mode 100644 index 23f9327..0000000 --- a/resources/fontawesome/svgs/solid/arrow-right-from-bracket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-right-long.svg b/resources/fontawesome/svgs/solid/arrow-right-long.svg deleted file mode 100644 index 8a05671..0000000 --- a/resources/fontawesome/svgs/solid/arrow-right-long.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-right-to-bracket.svg b/resources/fontawesome/svgs/solid/arrow-right-to-bracket.svg deleted file mode 100644 index c0fd565..0000000 --- a/resources/fontawesome/svgs/solid/arrow-right-to-bracket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-right-to-city.svg b/resources/fontawesome/svgs/solid/arrow-right-to-city.svg deleted file mode 100644 index f3e7b84..0000000 --- a/resources/fontawesome/svgs/solid/arrow-right-to-city.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-right.svg b/resources/fontawesome/svgs/solid/arrow-right.svg deleted file mode 100644 index d6e0975..0000000 --- a/resources/fontawesome/svgs/solid/arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-rotate-left.svg b/resources/fontawesome/svgs/solid/arrow-rotate-left.svg deleted file mode 100644 index 97b5331..0000000 --- a/resources/fontawesome/svgs/solid/arrow-rotate-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-rotate-right.svg b/resources/fontawesome/svgs/solid/arrow-rotate-right.svg deleted file mode 100644 index f5bc7f8..0000000 --- a/resources/fontawesome/svgs/solid/arrow-rotate-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-trend-down.svg b/resources/fontawesome/svgs/solid/arrow-trend-down.svg deleted file mode 100644 index 3c7e6d9..0000000 --- a/resources/fontawesome/svgs/solid/arrow-trend-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-trend-up.svg b/resources/fontawesome/svgs/solid/arrow-trend-up.svg deleted file mode 100644 index 3c643f5..0000000 --- a/resources/fontawesome/svgs/solid/arrow-trend-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-turn-down.svg b/resources/fontawesome/svgs/solid/arrow-turn-down.svg deleted file mode 100644 index c9fedbe..0000000 --- a/resources/fontawesome/svgs/solid/arrow-turn-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-turn-up.svg b/resources/fontawesome/svgs/solid/arrow-turn-up.svg deleted file mode 100644 index 544352f..0000000 --- a/resources/fontawesome/svgs/solid/arrow-turn-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-1-9.svg b/resources/fontawesome/svgs/solid/arrow-up-1-9.svg deleted file mode 100644 index 20501a6..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-1-9.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-9-1.svg b/resources/fontawesome/svgs/solid/arrow-up-9-1.svg deleted file mode 100644 index 2f02ec2..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-9-1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-a-z.svg b/resources/fontawesome/svgs/solid/arrow-up-a-z.svg deleted file mode 100644 index 47dfcbb..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-a-z.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-from-bracket.svg b/resources/fontawesome/svgs/solid/arrow-up-from-bracket.svg deleted file mode 100644 index ee5ff20..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-from-bracket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-from-ground-water.svg b/resources/fontawesome/svgs/solid/arrow-up-from-ground-water.svg deleted file mode 100644 index 7cc3ed5..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-from-ground-water.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-from-water-pump.svg b/resources/fontawesome/svgs/solid/arrow-up-from-water-pump.svg deleted file mode 100644 index ce4adfc..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-from-water-pump.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-long.svg b/resources/fontawesome/svgs/solid/arrow-up-long.svg deleted file mode 100644 index 54c6f5c..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-long.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-right-dots.svg b/resources/fontawesome/svgs/solid/arrow-up-right-dots.svg deleted file mode 100644 index 9c9f1ed..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-right-dots.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-right-from-square.svg b/resources/fontawesome/svgs/solid/arrow-up-right-from-square.svg deleted file mode 100644 index 4a49f42..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-right-from-square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-short-wide.svg b/resources/fontawesome/svgs/solid/arrow-up-short-wide.svg deleted file mode 100644 index 574159f..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-short-wide.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-wide-short.svg b/resources/fontawesome/svgs/solid/arrow-up-wide-short.svg deleted file mode 100644 index 72b40fa..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-wide-short.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up-z-a.svg b/resources/fontawesome/svgs/solid/arrow-up-z-a.svg deleted file mode 100644 index 0b3e71c..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up-z-a.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrow-up.svg b/resources/fontawesome/svgs/solid/arrow-up.svg deleted file mode 100644 index 4125fea..0000000 --- a/resources/fontawesome/svgs/solid/arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-down-to-line.svg b/resources/fontawesome/svgs/solid/arrows-down-to-line.svg deleted file mode 100644 index 6c69dba..0000000 --- a/resources/fontawesome/svgs/solid/arrows-down-to-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-down-to-people.svg b/resources/fontawesome/svgs/solid/arrows-down-to-people.svg deleted file mode 100644 index 21efa70..0000000 --- a/resources/fontawesome/svgs/solid/arrows-down-to-people.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-left-right-to-line.svg b/resources/fontawesome/svgs/solid/arrows-left-right-to-line.svg deleted file mode 100644 index a53e6d6..0000000 --- a/resources/fontawesome/svgs/solid/arrows-left-right-to-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-left-right.svg b/resources/fontawesome/svgs/solid/arrows-left-right.svg deleted file mode 100644 index dbf717b..0000000 --- a/resources/fontawesome/svgs/solid/arrows-left-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-rotate.svg b/resources/fontawesome/svgs/solid/arrows-rotate.svg deleted file mode 100644 index dcd0fc8..0000000 --- a/resources/fontawesome/svgs/solid/arrows-rotate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-spin.svg b/resources/fontawesome/svgs/solid/arrows-spin.svg deleted file mode 100644 index 8d9aadb..0000000 --- a/resources/fontawesome/svgs/solid/arrows-spin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-split-up-and-left.svg b/resources/fontawesome/svgs/solid/arrows-split-up-and-left.svg deleted file mode 100644 index 3cabc8f..0000000 --- a/resources/fontawesome/svgs/solid/arrows-split-up-and-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-to-circle.svg b/resources/fontawesome/svgs/solid/arrows-to-circle.svg deleted file mode 100644 index a554f7a..0000000 --- a/resources/fontawesome/svgs/solid/arrows-to-circle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-to-dot.svg b/resources/fontawesome/svgs/solid/arrows-to-dot.svg deleted file mode 100644 index efd4b88..0000000 --- a/resources/fontawesome/svgs/solid/arrows-to-dot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-to-eye.svg b/resources/fontawesome/svgs/solid/arrows-to-eye.svg deleted file mode 100644 index da22673..0000000 --- a/resources/fontawesome/svgs/solid/arrows-to-eye.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-turn-right.svg b/resources/fontawesome/svgs/solid/arrows-turn-right.svg deleted file mode 100644 index d9a5994..0000000 --- a/resources/fontawesome/svgs/solid/arrows-turn-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-turn-to-dots.svg b/resources/fontawesome/svgs/solid/arrows-turn-to-dots.svg deleted file mode 100644 index 7abc24c..0000000 --- a/resources/fontawesome/svgs/solid/arrows-turn-to-dots.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-up-down-left-right.svg b/resources/fontawesome/svgs/solid/arrows-up-down-left-right.svg deleted file mode 100644 index a530463..0000000 --- a/resources/fontawesome/svgs/solid/arrows-up-down-left-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-up-down.svg b/resources/fontawesome/svgs/solid/arrows-up-down.svg deleted file mode 100644 index 514ca71..0000000 --- a/resources/fontawesome/svgs/solid/arrows-up-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/arrows-up-to-line.svg b/resources/fontawesome/svgs/solid/arrows-up-to-line.svg deleted file mode 100644 index d71ec8d..0000000 --- a/resources/fontawesome/svgs/solid/arrows-up-to-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/asterisk.svg b/resources/fontawesome/svgs/solid/asterisk.svg deleted file mode 100644 index 267b302..0000000 --- a/resources/fontawesome/svgs/solid/asterisk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/at.svg b/resources/fontawesome/svgs/solid/at.svg deleted file mode 100644 index 4cf64f7..0000000 --- a/resources/fontawesome/svgs/solid/at.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/atom.svg b/resources/fontawesome/svgs/solid/atom.svg deleted file mode 100644 index e9b2c24..0000000 --- a/resources/fontawesome/svgs/solid/atom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/audio-description.svg b/resources/fontawesome/svgs/solid/audio-description.svg deleted file mode 100644 index 9eaf5ac..0000000 --- a/resources/fontawesome/svgs/solid/audio-description.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/austral-sign.svg b/resources/fontawesome/svgs/solid/austral-sign.svg deleted file mode 100644 index a9e232f..0000000 --- a/resources/fontawesome/svgs/solid/austral-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/award.svg b/resources/fontawesome/svgs/solid/award.svg deleted file mode 100644 index c1df65b..0000000 --- a/resources/fontawesome/svgs/solid/award.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/b.svg b/resources/fontawesome/svgs/solid/b.svg deleted file mode 100644 index 518aae9..0000000 --- a/resources/fontawesome/svgs/solid/b.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/baby-carriage.svg b/resources/fontawesome/svgs/solid/baby-carriage.svg deleted file mode 100644 index 4594502..0000000 --- a/resources/fontawesome/svgs/solid/baby-carriage.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/baby.svg b/resources/fontawesome/svgs/solid/baby.svg deleted file mode 100644 index a935f81..0000000 --- a/resources/fontawesome/svgs/solid/baby.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/backward-fast.svg b/resources/fontawesome/svgs/solid/backward-fast.svg deleted file mode 100644 index f3bcca6..0000000 --- a/resources/fontawesome/svgs/solid/backward-fast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/backward-step.svg b/resources/fontawesome/svgs/solid/backward-step.svg deleted file mode 100644 index 81c4a2e..0000000 --- a/resources/fontawesome/svgs/solid/backward-step.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/backward.svg b/resources/fontawesome/svgs/solid/backward.svg deleted file mode 100644 index 66456c6..0000000 --- a/resources/fontawesome/svgs/solid/backward.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bacon.svg b/resources/fontawesome/svgs/solid/bacon.svg deleted file mode 100644 index 4679af9..0000000 --- a/resources/fontawesome/svgs/solid/bacon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bacteria.svg b/resources/fontawesome/svgs/solid/bacteria.svg deleted file mode 100644 index 56dc80a..0000000 --- a/resources/fontawesome/svgs/solid/bacteria.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bacterium.svg b/resources/fontawesome/svgs/solid/bacterium.svg deleted file mode 100644 index 1b455c1..0000000 --- a/resources/fontawesome/svgs/solid/bacterium.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bag-shopping.svg b/resources/fontawesome/svgs/solid/bag-shopping.svg deleted file mode 100644 index 118405b..0000000 --- a/resources/fontawesome/svgs/solid/bag-shopping.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bahai.svg b/resources/fontawesome/svgs/solid/bahai.svg deleted file mode 100644 index 2b46e03..0000000 --- a/resources/fontawesome/svgs/solid/bahai.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/baht-sign.svg b/resources/fontawesome/svgs/solid/baht-sign.svg deleted file mode 100644 index 85acade..0000000 --- a/resources/fontawesome/svgs/solid/baht-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ban-smoking.svg b/resources/fontawesome/svgs/solid/ban-smoking.svg deleted file mode 100644 index c6547bc..0000000 --- a/resources/fontawesome/svgs/solid/ban-smoking.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ban.svg b/resources/fontawesome/svgs/solid/ban.svg deleted file mode 100644 index fd6d141..0000000 --- a/resources/fontawesome/svgs/solid/ban.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bandage.svg b/resources/fontawesome/svgs/solid/bandage.svg deleted file mode 100644 index 5d05a7c..0000000 --- a/resources/fontawesome/svgs/solid/bandage.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bangladeshi-taka-sign.svg b/resources/fontawesome/svgs/solid/bangladeshi-taka-sign.svg deleted file mode 100644 index dd2154c..0000000 --- a/resources/fontawesome/svgs/solid/bangladeshi-taka-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/barcode.svg b/resources/fontawesome/svgs/solid/barcode.svg deleted file mode 100644 index 440887d..0000000 --- a/resources/fontawesome/svgs/solid/barcode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bars-progress.svg b/resources/fontawesome/svgs/solid/bars-progress.svg deleted file mode 100644 index 85c8caf..0000000 --- a/resources/fontawesome/svgs/solid/bars-progress.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bars-staggered.svg b/resources/fontawesome/svgs/solid/bars-staggered.svg deleted file mode 100644 index 799fd02..0000000 --- a/resources/fontawesome/svgs/solid/bars-staggered.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bars.svg b/resources/fontawesome/svgs/solid/bars.svg deleted file mode 100644 index ccf9e4a..0000000 --- a/resources/fontawesome/svgs/solid/bars.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/baseball-bat-ball.svg b/resources/fontawesome/svgs/solid/baseball-bat-ball.svg deleted file mode 100644 index f5b9bf6..0000000 --- a/resources/fontawesome/svgs/solid/baseball-bat-ball.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/baseball.svg b/resources/fontawesome/svgs/solid/baseball.svg deleted file mode 100644 index a5fec87..0000000 --- a/resources/fontawesome/svgs/solid/baseball.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/basket-shopping.svg b/resources/fontawesome/svgs/solid/basket-shopping.svg deleted file mode 100644 index 454fd89..0000000 --- a/resources/fontawesome/svgs/solid/basket-shopping.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/basketball.svg b/resources/fontawesome/svgs/solid/basketball.svg deleted file mode 100644 index fa9d07f..0000000 --- a/resources/fontawesome/svgs/solid/basketball.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bath.svg b/resources/fontawesome/svgs/solid/bath.svg deleted file mode 100644 index d78a3ce..0000000 --- a/resources/fontawesome/svgs/solid/bath.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/battery-empty.svg b/resources/fontawesome/svgs/solid/battery-empty.svg deleted file mode 100644 index 7df37e7..0000000 --- a/resources/fontawesome/svgs/solid/battery-empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/battery-full.svg b/resources/fontawesome/svgs/solid/battery-full.svg deleted file mode 100644 index df66a50..0000000 --- a/resources/fontawesome/svgs/solid/battery-full.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/battery-half.svg b/resources/fontawesome/svgs/solid/battery-half.svg deleted file mode 100644 index 2f77458..0000000 --- a/resources/fontawesome/svgs/solid/battery-half.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/battery-quarter.svg b/resources/fontawesome/svgs/solid/battery-quarter.svg deleted file mode 100644 index 2bcbba1..0000000 --- a/resources/fontawesome/svgs/solid/battery-quarter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/battery-three-quarters.svg b/resources/fontawesome/svgs/solid/battery-three-quarters.svg deleted file mode 100644 index a9d9b23..0000000 --- a/resources/fontawesome/svgs/solid/battery-three-quarters.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bed-pulse.svg b/resources/fontawesome/svgs/solid/bed-pulse.svg deleted file mode 100644 index 2b64393..0000000 --- a/resources/fontawesome/svgs/solid/bed-pulse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bed.svg b/resources/fontawesome/svgs/solid/bed.svg deleted file mode 100644 index 645a2f0..0000000 --- a/resources/fontawesome/svgs/solid/bed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/beer-mug-empty.svg b/resources/fontawesome/svgs/solid/beer-mug-empty.svg deleted file mode 100644 index a24c5b1..0000000 --- a/resources/fontawesome/svgs/solid/beer-mug-empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bell-concierge.svg b/resources/fontawesome/svgs/solid/bell-concierge.svg deleted file mode 100644 index 162d177..0000000 --- a/resources/fontawesome/svgs/solid/bell-concierge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bell-slash.svg b/resources/fontawesome/svgs/solid/bell-slash.svg deleted file mode 100644 index 5476192..0000000 --- a/resources/fontawesome/svgs/solid/bell-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bell.svg b/resources/fontawesome/svgs/solid/bell.svg deleted file mode 100644 index 7828807..0000000 --- a/resources/fontawesome/svgs/solid/bell.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bezier-curve.svg b/resources/fontawesome/svgs/solid/bezier-curve.svg deleted file mode 100644 index 7da206c..0000000 --- a/resources/fontawesome/svgs/solid/bezier-curve.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bicycle.svg b/resources/fontawesome/svgs/solid/bicycle.svg deleted file mode 100644 index 51cad16..0000000 --- a/resources/fontawesome/svgs/solid/bicycle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/binoculars.svg b/resources/fontawesome/svgs/solid/binoculars.svg deleted file mode 100644 index f9c201c..0000000 --- a/resources/fontawesome/svgs/solid/binoculars.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/biohazard.svg b/resources/fontawesome/svgs/solid/biohazard.svg deleted file mode 100644 index 6d4ec68..0000000 --- a/resources/fontawesome/svgs/solid/biohazard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bitcoin-sign.svg b/resources/fontawesome/svgs/solid/bitcoin-sign.svg deleted file mode 100644 index 204fdc8..0000000 --- a/resources/fontawesome/svgs/solid/bitcoin-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/blender-phone.svg b/resources/fontawesome/svgs/solid/blender-phone.svg deleted file mode 100644 index 46cba63..0000000 --- a/resources/fontawesome/svgs/solid/blender-phone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/blender.svg b/resources/fontawesome/svgs/solid/blender.svg deleted file mode 100644 index f3e3d53..0000000 --- a/resources/fontawesome/svgs/solid/blender.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/blog.svg b/resources/fontawesome/svgs/solid/blog.svg deleted file mode 100644 index fe3911b..0000000 --- a/resources/fontawesome/svgs/solid/blog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bold.svg b/resources/fontawesome/svgs/solid/bold.svg deleted file mode 100644 index 9fd4864..0000000 --- a/resources/fontawesome/svgs/solid/bold.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bolt-lightning.svg b/resources/fontawesome/svgs/solid/bolt-lightning.svg deleted file mode 100644 index 1885f4b..0000000 --- a/resources/fontawesome/svgs/solid/bolt-lightning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bolt.svg b/resources/fontawesome/svgs/solid/bolt.svg deleted file mode 100644 index a181689..0000000 --- a/resources/fontawesome/svgs/solid/bolt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bomb.svg b/resources/fontawesome/svgs/solid/bomb.svg deleted file mode 100644 index 267d085..0000000 --- a/resources/fontawesome/svgs/solid/bomb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bone.svg b/resources/fontawesome/svgs/solid/bone.svg deleted file mode 100644 index 0255e62..0000000 --- a/resources/fontawesome/svgs/solid/bone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bong.svg b/resources/fontawesome/svgs/solid/bong.svg deleted file mode 100644 index 3eb880f..0000000 --- a/resources/fontawesome/svgs/solid/bong.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-atlas.svg b/resources/fontawesome/svgs/solid/book-atlas.svg deleted file mode 100644 index 3f1bf07..0000000 --- a/resources/fontawesome/svgs/solid/book-atlas.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-bible.svg b/resources/fontawesome/svgs/solid/book-bible.svg deleted file mode 100644 index 403b91a..0000000 --- a/resources/fontawesome/svgs/solid/book-bible.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-bookmark.svg b/resources/fontawesome/svgs/solid/book-bookmark.svg deleted file mode 100644 index fe45a98..0000000 --- a/resources/fontawesome/svgs/solid/book-bookmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-journal-whills.svg b/resources/fontawesome/svgs/solid/book-journal-whills.svg deleted file mode 100644 index 006fbfa..0000000 --- a/resources/fontawesome/svgs/solid/book-journal-whills.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-medical.svg b/resources/fontawesome/svgs/solid/book-medical.svg deleted file mode 100644 index add8303..0000000 --- a/resources/fontawesome/svgs/solid/book-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-open-reader.svg b/resources/fontawesome/svgs/solid/book-open-reader.svg deleted file mode 100644 index 2825788..0000000 --- a/resources/fontawesome/svgs/solid/book-open-reader.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-open.svg b/resources/fontawesome/svgs/solid/book-open.svg deleted file mode 100644 index 55aadbd..0000000 --- a/resources/fontawesome/svgs/solid/book-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-quran.svg b/resources/fontawesome/svgs/solid/book-quran.svg deleted file mode 100644 index 2cf43c2..0000000 --- a/resources/fontawesome/svgs/solid/book-quran.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-skull.svg b/resources/fontawesome/svgs/solid/book-skull.svg deleted file mode 100644 index 5572684..0000000 --- a/resources/fontawesome/svgs/solid/book-skull.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book-tanakh.svg b/resources/fontawesome/svgs/solid/book-tanakh.svg deleted file mode 100644 index 47d17d2..0000000 --- a/resources/fontawesome/svgs/solid/book-tanakh.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/book.svg b/resources/fontawesome/svgs/solid/book.svg deleted file mode 100644 index a5a222e..0000000 --- a/resources/fontawesome/svgs/solid/book.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bookmark.svg b/resources/fontawesome/svgs/solid/bookmark.svg deleted file mode 100644 index f106d8d..0000000 --- a/resources/fontawesome/svgs/solid/bookmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/border-all.svg b/resources/fontawesome/svgs/solid/border-all.svg deleted file mode 100644 index 7a71edb..0000000 --- a/resources/fontawesome/svgs/solid/border-all.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/border-none.svg b/resources/fontawesome/svgs/solid/border-none.svg deleted file mode 100644 index 4319049..0000000 --- a/resources/fontawesome/svgs/solid/border-none.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/border-top-left.svg b/resources/fontawesome/svgs/solid/border-top-left.svg deleted file mode 100644 index 2eab0be..0000000 --- a/resources/fontawesome/svgs/solid/border-top-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bore-hole.svg b/resources/fontawesome/svgs/solid/bore-hole.svg deleted file mode 100644 index 4ebf2d8..0000000 --- a/resources/fontawesome/svgs/solid/bore-hole.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bottle-droplet.svg b/resources/fontawesome/svgs/solid/bottle-droplet.svg deleted file mode 100644 index a467115..0000000 --- a/resources/fontawesome/svgs/solid/bottle-droplet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bottle-water.svg b/resources/fontawesome/svgs/solid/bottle-water.svg deleted file mode 100644 index 580a9cc..0000000 --- a/resources/fontawesome/svgs/solid/bottle-water.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bowl-food.svg b/resources/fontawesome/svgs/solid/bowl-food.svg deleted file mode 100644 index efcf290..0000000 --- a/resources/fontawesome/svgs/solid/bowl-food.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bowl-rice.svg b/resources/fontawesome/svgs/solid/bowl-rice.svg deleted file mode 100644 index 6095625..0000000 --- a/resources/fontawesome/svgs/solid/bowl-rice.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bowling-ball.svg b/resources/fontawesome/svgs/solid/bowling-ball.svg deleted file mode 100644 index 2015425..0000000 --- a/resources/fontawesome/svgs/solid/bowling-ball.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/box-archive.svg b/resources/fontawesome/svgs/solid/box-archive.svg deleted file mode 100644 index e2493e3..0000000 --- a/resources/fontawesome/svgs/solid/box-archive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/box-open.svg b/resources/fontawesome/svgs/solid/box-open.svg deleted file mode 100644 index bc6b187..0000000 --- a/resources/fontawesome/svgs/solid/box-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/box-tissue.svg b/resources/fontawesome/svgs/solid/box-tissue.svg deleted file mode 100644 index 0e3c11f..0000000 --- a/resources/fontawesome/svgs/solid/box-tissue.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/box.svg b/resources/fontawesome/svgs/solid/box.svg deleted file mode 100644 index ca0bdf4..0000000 --- a/resources/fontawesome/svgs/solid/box.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/boxes-packing.svg b/resources/fontawesome/svgs/solid/boxes-packing.svg deleted file mode 100644 index d4fdd1c..0000000 --- a/resources/fontawesome/svgs/solid/boxes-packing.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/boxes-stacked.svg b/resources/fontawesome/svgs/solid/boxes-stacked.svg deleted file mode 100644 index cfb9aa1..0000000 --- a/resources/fontawesome/svgs/solid/boxes-stacked.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/braille.svg b/resources/fontawesome/svgs/solid/braille.svg deleted file mode 100644 index d0f76bd..0000000 --- a/resources/fontawesome/svgs/solid/braille.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/brain.svg b/resources/fontawesome/svgs/solid/brain.svg deleted file mode 100644 index 2524c9b..0000000 --- a/resources/fontawesome/svgs/solid/brain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/brazilian-real-sign.svg b/resources/fontawesome/svgs/solid/brazilian-real-sign.svg deleted file mode 100644 index c877430..0000000 --- a/resources/fontawesome/svgs/solid/brazilian-real-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bread-slice.svg b/resources/fontawesome/svgs/solid/bread-slice.svg deleted file mode 100644 index 895139e..0000000 --- a/resources/fontawesome/svgs/solid/bread-slice.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bridge-circle-check.svg b/resources/fontawesome/svgs/solid/bridge-circle-check.svg deleted file mode 100644 index da710ae..0000000 --- a/resources/fontawesome/svgs/solid/bridge-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bridge-circle-exclamation.svg b/resources/fontawesome/svgs/solid/bridge-circle-exclamation.svg deleted file mode 100644 index 5a0cd11..0000000 --- a/resources/fontawesome/svgs/solid/bridge-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bridge-circle-xmark.svg b/resources/fontawesome/svgs/solid/bridge-circle-xmark.svg deleted file mode 100644 index 314ef19..0000000 --- a/resources/fontawesome/svgs/solid/bridge-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bridge-lock.svg b/resources/fontawesome/svgs/solid/bridge-lock.svg deleted file mode 100644 index 422f2ac..0000000 --- a/resources/fontawesome/svgs/solid/bridge-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bridge-water.svg b/resources/fontawesome/svgs/solid/bridge-water.svg deleted file mode 100644 index 8e5b628..0000000 --- a/resources/fontawesome/svgs/solid/bridge-water.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bridge.svg b/resources/fontawesome/svgs/solid/bridge.svg deleted file mode 100644 index bfff369..0000000 --- a/resources/fontawesome/svgs/solid/bridge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/briefcase-medical.svg b/resources/fontawesome/svgs/solid/briefcase-medical.svg deleted file mode 100644 index 65c0b93..0000000 --- a/resources/fontawesome/svgs/solid/briefcase-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/briefcase.svg b/resources/fontawesome/svgs/solid/briefcase.svg deleted file mode 100644 index 3b263cf..0000000 --- a/resources/fontawesome/svgs/solid/briefcase.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/broom-ball.svg b/resources/fontawesome/svgs/solid/broom-ball.svg deleted file mode 100644 index dee3238..0000000 --- a/resources/fontawesome/svgs/solid/broom-ball.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/broom.svg b/resources/fontawesome/svgs/solid/broom.svg deleted file mode 100644 index b096fad..0000000 --- a/resources/fontawesome/svgs/solid/broom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/brush.svg b/resources/fontawesome/svgs/solid/brush.svg deleted file mode 100644 index 12b8a37..0000000 --- a/resources/fontawesome/svgs/solid/brush.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bucket.svg b/resources/fontawesome/svgs/solid/bucket.svg deleted file mode 100644 index 72376a6..0000000 --- a/resources/fontawesome/svgs/solid/bucket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bug-slash.svg b/resources/fontawesome/svgs/solid/bug-slash.svg deleted file mode 100644 index 187e294..0000000 --- a/resources/fontawesome/svgs/solid/bug-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bug.svg b/resources/fontawesome/svgs/solid/bug.svg deleted file mode 100644 index b6c043a..0000000 --- a/resources/fontawesome/svgs/solid/bug.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bugs.svg b/resources/fontawesome/svgs/solid/bugs.svg deleted file mode 100644 index eca8a63..0000000 --- a/resources/fontawesome/svgs/solid/bugs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-circle-arrow-right.svg b/resources/fontawesome/svgs/solid/building-circle-arrow-right.svg deleted file mode 100644 index bc1c7c6..0000000 --- a/resources/fontawesome/svgs/solid/building-circle-arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-circle-check.svg b/resources/fontawesome/svgs/solid/building-circle-check.svg deleted file mode 100644 index 08f8a39..0000000 --- a/resources/fontawesome/svgs/solid/building-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-circle-exclamation.svg b/resources/fontawesome/svgs/solid/building-circle-exclamation.svg deleted file mode 100644 index 60c725c..0000000 --- a/resources/fontawesome/svgs/solid/building-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-circle-xmark.svg b/resources/fontawesome/svgs/solid/building-circle-xmark.svg deleted file mode 100644 index 8405a19..0000000 --- a/resources/fontawesome/svgs/solid/building-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-columns.svg b/resources/fontawesome/svgs/solid/building-columns.svg deleted file mode 100644 index 4067b79..0000000 --- a/resources/fontawesome/svgs/solid/building-columns.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-flag.svg b/resources/fontawesome/svgs/solid/building-flag.svg deleted file mode 100644 index ec49eb1..0000000 --- a/resources/fontawesome/svgs/solid/building-flag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-lock.svg b/resources/fontawesome/svgs/solid/building-lock.svg deleted file mode 100644 index ee050f7..0000000 --- a/resources/fontawesome/svgs/solid/building-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-ngo.svg b/resources/fontawesome/svgs/solid/building-ngo.svg deleted file mode 100644 index 26cd012..0000000 --- a/resources/fontawesome/svgs/solid/building-ngo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-shield.svg b/resources/fontawesome/svgs/solid/building-shield.svg deleted file mode 100644 index 64e79d5..0000000 --- a/resources/fontawesome/svgs/solid/building-shield.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-un.svg b/resources/fontawesome/svgs/solid/building-un.svg deleted file mode 100644 index c83001a..0000000 --- a/resources/fontawesome/svgs/solid/building-un.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-user.svg b/resources/fontawesome/svgs/solid/building-user.svg deleted file mode 100644 index 873a345..0000000 --- a/resources/fontawesome/svgs/solid/building-user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building-wheat.svg b/resources/fontawesome/svgs/solid/building-wheat.svg deleted file mode 100644 index e8e15d4..0000000 --- a/resources/fontawesome/svgs/solid/building-wheat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/building.svg b/resources/fontawesome/svgs/solid/building.svg deleted file mode 100644 index 10bf3b4..0000000 --- a/resources/fontawesome/svgs/solid/building.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bullhorn.svg b/resources/fontawesome/svgs/solid/bullhorn.svg deleted file mode 100644 index 6cee626..0000000 --- a/resources/fontawesome/svgs/solid/bullhorn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bullseye.svg b/resources/fontawesome/svgs/solid/bullseye.svg deleted file mode 100644 index 784b5af..0000000 --- a/resources/fontawesome/svgs/solid/bullseye.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/burger.svg b/resources/fontawesome/svgs/solid/burger.svg deleted file mode 100644 index 2f4d46e..0000000 --- a/resources/fontawesome/svgs/solid/burger.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/burst.svg b/resources/fontawesome/svgs/solid/burst.svg deleted file mode 100644 index 5b792fc..0000000 --- a/resources/fontawesome/svgs/solid/burst.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bus-simple.svg b/resources/fontawesome/svgs/solid/bus-simple.svg deleted file mode 100644 index 99de78e..0000000 --- a/resources/fontawesome/svgs/solid/bus-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/bus.svg b/resources/fontawesome/svgs/solid/bus.svg deleted file mode 100644 index f049d66..0000000 --- a/resources/fontawesome/svgs/solid/bus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/business-time.svg b/resources/fontawesome/svgs/solid/business-time.svg deleted file mode 100644 index a7e8e85..0000000 --- a/resources/fontawesome/svgs/solid/business-time.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/c.svg b/resources/fontawesome/svgs/solid/c.svg deleted file mode 100644 index cb496c4..0000000 --- a/resources/fontawesome/svgs/solid/c.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cable-car.svg b/resources/fontawesome/svgs/solid/cable-car.svg deleted file mode 100644 index 6e475a3..0000000 --- a/resources/fontawesome/svgs/solid/cable-car.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cake-candles.svg b/resources/fontawesome/svgs/solid/cake-candles.svg deleted file mode 100644 index 2767bba..0000000 --- a/resources/fontawesome/svgs/solid/cake-candles.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calculator.svg b/resources/fontawesome/svgs/solid/calculator.svg deleted file mode 100644 index 31a094c..0000000 --- a/resources/fontawesome/svgs/solid/calculator.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calendar-check.svg b/resources/fontawesome/svgs/solid/calendar-check.svg deleted file mode 100644 index 149309f..0000000 --- a/resources/fontawesome/svgs/solid/calendar-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calendar-day.svg b/resources/fontawesome/svgs/solid/calendar-day.svg deleted file mode 100644 index f97219e..0000000 --- a/resources/fontawesome/svgs/solid/calendar-day.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calendar-days.svg b/resources/fontawesome/svgs/solid/calendar-days.svg deleted file mode 100644 index f70b0d7..0000000 --- a/resources/fontawesome/svgs/solid/calendar-days.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calendar-minus.svg b/resources/fontawesome/svgs/solid/calendar-minus.svg deleted file mode 100644 index d18b470..0000000 --- a/resources/fontawesome/svgs/solid/calendar-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calendar-plus.svg b/resources/fontawesome/svgs/solid/calendar-plus.svg deleted file mode 100644 index 56dfbdf..0000000 --- a/resources/fontawesome/svgs/solid/calendar-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calendar-week.svg b/resources/fontawesome/svgs/solid/calendar-week.svg deleted file mode 100644 index eadf6fa..0000000 --- a/resources/fontawesome/svgs/solid/calendar-week.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calendar-xmark.svg b/resources/fontawesome/svgs/solid/calendar-xmark.svg deleted file mode 100644 index e455920..0000000 --- a/resources/fontawesome/svgs/solid/calendar-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/calendar.svg b/resources/fontawesome/svgs/solid/calendar.svg deleted file mode 100644 index a8ee9e6..0000000 --- a/resources/fontawesome/svgs/solid/calendar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/camera-retro.svg b/resources/fontawesome/svgs/solid/camera-retro.svg deleted file mode 100644 index 4d90dba..0000000 --- a/resources/fontawesome/svgs/solid/camera-retro.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/camera-rotate.svg b/resources/fontawesome/svgs/solid/camera-rotate.svg deleted file mode 100644 index 0e768f0..0000000 --- a/resources/fontawesome/svgs/solid/camera-rotate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/camera.svg b/resources/fontawesome/svgs/solid/camera.svg deleted file mode 100644 index 00bb6d8..0000000 --- a/resources/fontawesome/svgs/solid/camera.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/campground.svg b/resources/fontawesome/svgs/solid/campground.svg deleted file mode 100644 index 6446817..0000000 --- a/resources/fontawesome/svgs/solid/campground.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/candy-cane.svg b/resources/fontawesome/svgs/solid/candy-cane.svg deleted file mode 100644 index a188e8b..0000000 --- a/resources/fontawesome/svgs/solid/candy-cane.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cannabis.svg b/resources/fontawesome/svgs/solid/cannabis.svg deleted file mode 100644 index 782eeca..0000000 --- a/resources/fontawesome/svgs/solid/cannabis.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/capsules.svg b/resources/fontawesome/svgs/solid/capsules.svg deleted file mode 100644 index 05c539f..0000000 --- a/resources/fontawesome/svgs/solid/capsules.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/car-battery.svg b/resources/fontawesome/svgs/solid/car-battery.svg deleted file mode 100644 index ecffa9b..0000000 --- a/resources/fontawesome/svgs/solid/car-battery.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/car-burst.svg b/resources/fontawesome/svgs/solid/car-burst.svg deleted file mode 100644 index f2fae69..0000000 --- a/resources/fontawesome/svgs/solid/car-burst.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/car-on.svg b/resources/fontawesome/svgs/solid/car-on.svg deleted file mode 100644 index c9765d4..0000000 --- a/resources/fontawesome/svgs/solid/car-on.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/car-rear.svg b/resources/fontawesome/svgs/solid/car-rear.svg deleted file mode 100644 index 10dd630..0000000 --- a/resources/fontawesome/svgs/solid/car-rear.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/car-side.svg b/resources/fontawesome/svgs/solid/car-side.svg deleted file mode 100644 index 4ffee86..0000000 --- a/resources/fontawesome/svgs/solid/car-side.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/car-tunnel.svg b/resources/fontawesome/svgs/solid/car-tunnel.svg deleted file mode 100644 index 9ff4d8b..0000000 --- a/resources/fontawesome/svgs/solid/car-tunnel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/car.svg b/resources/fontawesome/svgs/solid/car.svg deleted file mode 100644 index 5483275..0000000 --- a/resources/fontawesome/svgs/solid/car.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/caravan.svg b/resources/fontawesome/svgs/solid/caravan.svg deleted file mode 100644 index 528680c..0000000 --- a/resources/fontawesome/svgs/solid/caravan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/caret-down.svg b/resources/fontawesome/svgs/solid/caret-down.svg deleted file mode 100644 index f2afcc9..0000000 --- a/resources/fontawesome/svgs/solid/caret-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/caret-left.svg b/resources/fontawesome/svgs/solid/caret-left.svg deleted file mode 100644 index b556ef8..0000000 --- a/resources/fontawesome/svgs/solid/caret-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/caret-right.svg b/resources/fontawesome/svgs/solid/caret-right.svg deleted file mode 100644 index 433eb23..0000000 --- a/resources/fontawesome/svgs/solid/caret-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/caret-up.svg b/resources/fontawesome/svgs/solid/caret-up.svg deleted file mode 100644 index 0f6f844..0000000 --- a/resources/fontawesome/svgs/solid/caret-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/carrot.svg b/resources/fontawesome/svgs/solid/carrot.svg deleted file mode 100644 index 3c8c35a..0000000 --- a/resources/fontawesome/svgs/solid/carrot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cart-arrow-down.svg b/resources/fontawesome/svgs/solid/cart-arrow-down.svg deleted file mode 100644 index ee7b5f3..0000000 --- a/resources/fontawesome/svgs/solid/cart-arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cart-flatbed-suitcase.svg b/resources/fontawesome/svgs/solid/cart-flatbed-suitcase.svg deleted file mode 100644 index 158f6f6..0000000 --- a/resources/fontawesome/svgs/solid/cart-flatbed-suitcase.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cart-flatbed.svg b/resources/fontawesome/svgs/solid/cart-flatbed.svg deleted file mode 100644 index 53e363a..0000000 --- a/resources/fontawesome/svgs/solid/cart-flatbed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cart-plus.svg b/resources/fontawesome/svgs/solid/cart-plus.svg deleted file mode 100644 index a94a446..0000000 --- a/resources/fontawesome/svgs/solid/cart-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cart-shopping.svg b/resources/fontawesome/svgs/solid/cart-shopping.svg deleted file mode 100644 index b4ba1c6..0000000 --- a/resources/fontawesome/svgs/solid/cart-shopping.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cash-register.svg b/resources/fontawesome/svgs/solid/cash-register.svg deleted file mode 100644 index 919f0d3..0000000 --- a/resources/fontawesome/svgs/solid/cash-register.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cat.svg b/resources/fontawesome/svgs/solid/cat.svg deleted file mode 100644 index 4f3fac9..0000000 --- a/resources/fontawesome/svgs/solid/cat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cedi-sign.svg b/resources/fontawesome/svgs/solid/cedi-sign.svg deleted file mode 100644 index 73edfa3..0000000 --- a/resources/fontawesome/svgs/solid/cedi-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cent-sign.svg b/resources/fontawesome/svgs/solid/cent-sign.svg deleted file mode 100644 index 23b8f6a..0000000 --- a/resources/fontawesome/svgs/solid/cent-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/certificate.svg b/resources/fontawesome/svgs/solid/certificate.svg deleted file mode 100644 index ac17bcf..0000000 --- a/resources/fontawesome/svgs/solid/certificate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chair.svg b/resources/fontawesome/svgs/solid/chair.svg deleted file mode 100644 index e75c898..0000000 --- a/resources/fontawesome/svgs/solid/chair.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chalkboard-user.svg b/resources/fontawesome/svgs/solid/chalkboard-user.svg deleted file mode 100644 index 9117fc9..0000000 --- a/resources/fontawesome/svgs/solid/chalkboard-user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chalkboard.svg b/resources/fontawesome/svgs/solid/chalkboard.svg deleted file mode 100644 index d1d3c31..0000000 --- a/resources/fontawesome/svgs/solid/chalkboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/champagne-glasses.svg b/resources/fontawesome/svgs/solid/champagne-glasses.svg deleted file mode 100644 index 9b34487..0000000 --- a/resources/fontawesome/svgs/solid/champagne-glasses.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/charging-station.svg b/resources/fontawesome/svgs/solid/charging-station.svg deleted file mode 100644 index e2ce31d..0000000 --- a/resources/fontawesome/svgs/solid/charging-station.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chart-area.svg b/resources/fontawesome/svgs/solid/chart-area.svg deleted file mode 100644 index fc47baa..0000000 --- a/resources/fontawesome/svgs/solid/chart-area.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chart-bar.svg b/resources/fontawesome/svgs/solid/chart-bar.svg deleted file mode 100644 index 3cd163b..0000000 --- a/resources/fontawesome/svgs/solid/chart-bar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chart-column.svg b/resources/fontawesome/svgs/solid/chart-column.svg deleted file mode 100644 index b181362..0000000 --- a/resources/fontawesome/svgs/solid/chart-column.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chart-gantt.svg b/resources/fontawesome/svgs/solid/chart-gantt.svg deleted file mode 100644 index d372373..0000000 --- a/resources/fontawesome/svgs/solid/chart-gantt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chart-line.svg b/resources/fontawesome/svgs/solid/chart-line.svg deleted file mode 100644 index 5b2f126..0000000 --- a/resources/fontawesome/svgs/solid/chart-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chart-pie.svg b/resources/fontawesome/svgs/solid/chart-pie.svg deleted file mode 100644 index f76d9f4..0000000 --- a/resources/fontawesome/svgs/solid/chart-pie.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chart-simple.svg b/resources/fontawesome/svgs/solid/chart-simple.svg deleted file mode 100644 index 8c12309..0000000 --- a/resources/fontawesome/svgs/solid/chart-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/check-double.svg b/resources/fontawesome/svgs/solid/check-double.svg deleted file mode 100644 index ac4a6ba..0000000 --- a/resources/fontawesome/svgs/solid/check-double.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/check-to-slot.svg b/resources/fontawesome/svgs/solid/check-to-slot.svg deleted file mode 100644 index 3cda607..0000000 --- a/resources/fontawesome/svgs/solid/check-to-slot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/check.svg b/resources/fontawesome/svgs/solid/check.svg deleted file mode 100644 index 9c7b794..0000000 --- a/resources/fontawesome/svgs/solid/check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cheese.svg b/resources/fontawesome/svgs/solid/cheese.svg deleted file mode 100644 index 8d48a49..0000000 --- a/resources/fontawesome/svgs/solid/cheese.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chess-bishop.svg b/resources/fontawesome/svgs/solid/chess-bishop.svg deleted file mode 100644 index e4d022f..0000000 --- a/resources/fontawesome/svgs/solid/chess-bishop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chess-board.svg b/resources/fontawesome/svgs/solid/chess-board.svg deleted file mode 100644 index bd628c8..0000000 --- a/resources/fontawesome/svgs/solid/chess-board.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chess-king.svg b/resources/fontawesome/svgs/solid/chess-king.svg deleted file mode 100644 index c0d7bbe..0000000 --- a/resources/fontawesome/svgs/solid/chess-king.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chess-knight.svg b/resources/fontawesome/svgs/solid/chess-knight.svg deleted file mode 100644 index c1ac5c7..0000000 --- a/resources/fontawesome/svgs/solid/chess-knight.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chess-pawn.svg b/resources/fontawesome/svgs/solid/chess-pawn.svg deleted file mode 100644 index c4e5883..0000000 --- a/resources/fontawesome/svgs/solid/chess-pawn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chess-queen.svg b/resources/fontawesome/svgs/solid/chess-queen.svg deleted file mode 100644 index fc28657..0000000 --- a/resources/fontawesome/svgs/solid/chess-queen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chess-rook.svg b/resources/fontawesome/svgs/solid/chess-rook.svg deleted file mode 100644 index 04e8b1f..0000000 --- a/resources/fontawesome/svgs/solid/chess-rook.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chess.svg b/resources/fontawesome/svgs/solid/chess.svg deleted file mode 100644 index 4136b33..0000000 --- a/resources/fontawesome/svgs/solid/chess.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chevron-down.svg b/resources/fontawesome/svgs/solid/chevron-down.svg deleted file mode 100644 index 546aeb2..0000000 --- a/resources/fontawesome/svgs/solid/chevron-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chevron-left.svg b/resources/fontawesome/svgs/solid/chevron-left.svg deleted file mode 100644 index 4ebc739..0000000 --- a/resources/fontawesome/svgs/solid/chevron-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chevron-right.svg b/resources/fontawesome/svgs/solid/chevron-right.svg deleted file mode 100644 index c4e38bc..0000000 --- a/resources/fontawesome/svgs/solid/chevron-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/chevron-up.svg b/resources/fontawesome/svgs/solid/chevron-up.svg deleted file mode 100644 index c947135..0000000 --- a/resources/fontawesome/svgs/solid/chevron-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/child-combatant.svg b/resources/fontawesome/svgs/solid/child-combatant.svg deleted file mode 100644 index 5fb86a8..0000000 --- a/resources/fontawesome/svgs/solid/child-combatant.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/child-dress.svg b/resources/fontawesome/svgs/solid/child-dress.svg deleted file mode 100644 index c3af7bc..0000000 --- a/resources/fontawesome/svgs/solid/child-dress.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/child-reaching.svg b/resources/fontawesome/svgs/solid/child-reaching.svg deleted file mode 100644 index c7e2561..0000000 --- a/resources/fontawesome/svgs/solid/child-reaching.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/child.svg b/resources/fontawesome/svgs/solid/child.svg deleted file mode 100644 index fcc266a..0000000 --- a/resources/fontawesome/svgs/solid/child.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/children.svg b/resources/fontawesome/svgs/solid/children.svg deleted file mode 100644 index 56234b1..0000000 --- a/resources/fontawesome/svgs/solid/children.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/church.svg b/resources/fontawesome/svgs/solid/church.svg deleted file mode 100644 index 11381be..0000000 --- a/resources/fontawesome/svgs/solid/church.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-arrow-down.svg b/resources/fontawesome/svgs/solid/circle-arrow-down.svg deleted file mode 100644 index e7a1d8c..0000000 --- a/resources/fontawesome/svgs/solid/circle-arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-arrow-left.svg b/resources/fontawesome/svgs/solid/circle-arrow-left.svg deleted file mode 100644 index 00e3504..0000000 --- a/resources/fontawesome/svgs/solid/circle-arrow-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-arrow-right.svg b/resources/fontawesome/svgs/solid/circle-arrow-right.svg deleted file mode 100644 index 9bc7453..0000000 --- a/resources/fontawesome/svgs/solid/circle-arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-arrow-up.svg b/resources/fontawesome/svgs/solid/circle-arrow-up.svg deleted file mode 100644 index 01d561d..0000000 --- a/resources/fontawesome/svgs/solid/circle-arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-check.svg b/resources/fontawesome/svgs/solid/circle-check.svg deleted file mode 100644 index 9bba998..0000000 --- a/resources/fontawesome/svgs/solid/circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-chevron-down.svg b/resources/fontawesome/svgs/solid/circle-chevron-down.svg deleted file mode 100644 index 0739c74..0000000 --- a/resources/fontawesome/svgs/solid/circle-chevron-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-chevron-left.svg b/resources/fontawesome/svgs/solid/circle-chevron-left.svg deleted file mode 100644 index 665cc19..0000000 --- a/resources/fontawesome/svgs/solid/circle-chevron-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-chevron-right.svg b/resources/fontawesome/svgs/solid/circle-chevron-right.svg deleted file mode 100644 index 75a5b3f..0000000 --- a/resources/fontawesome/svgs/solid/circle-chevron-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-chevron-up.svg b/resources/fontawesome/svgs/solid/circle-chevron-up.svg deleted file mode 100644 index dbbd53a..0000000 --- a/resources/fontawesome/svgs/solid/circle-chevron-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-dollar-to-slot.svg b/resources/fontawesome/svgs/solid/circle-dollar-to-slot.svg deleted file mode 100644 index 540bd01..0000000 --- a/resources/fontawesome/svgs/solid/circle-dollar-to-slot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-dot.svg b/resources/fontawesome/svgs/solid/circle-dot.svg deleted file mode 100644 index d0aab69..0000000 --- a/resources/fontawesome/svgs/solid/circle-dot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-down.svg b/resources/fontawesome/svgs/solid/circle-down.svg deleted file mode 100644 index 84e25ad..0000000 --- a/resources/fontawesome/svgs/solid/circle-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-exclamation.svg b/resources/fontawesome/svgs/solid/circle-exclamation.svg deleted file mode 100644 index 30dab4f..0000000 --- a/resources/fontawesome/svgs/solid/circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-h.svg b/resources/fontawesome/svgs/solid/circle-h.svg deleted file mode 100644 index e3900b0..0000000 --- a/resources/fontawesome/svgs/solid/circle-h.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-half-stroke.svg b/resources/fontawesome/svgs/solid/circle-half-stroke.svg deleted file mode 100644 index 42d5fc4..0000000 --- a/resources/fontawesome/svgs/solid/circle-half-stroke.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-info.svg b/resources/fontawesome/svgs/solid/circle-info.svg deleted file mode 100644 index c4803e7..0000000 --- a/resources/fontawesome/svgs/solid/circle-info.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-left.svg b/resources/fontawesome/svgs/solid/circle-left.svg deleted file mode 100644 index 1db1957..0000000 --- a/resources/fontawesome/svgs/solid/circle-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-minus.svg b/resources/fontawesome/svgs/solid/circle-minus.svg deleted file mode 100644 index dcdbc67..0000000 --- a/resources/fontawesome/svgs/solid/circle-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-nodes.svg b/resources/fontawesome/svgs/solid/circle-nodes.svg deleted file mode 100644 index ede1099..0000000 --- a/resources/fontawesome/svgs/solid/circle-nodes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-notch.svg b/resources/fontawesome/svgs/solid/circle-notch.svg deleted file mode 100644 index c47194a..0000000 --- a/resources/fontawesome/svgs/solid/circle-notch.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-pause.svg b/resources/fontawesome/svgs/solid/circle-pause.svg deleted file mode 100644 index 9e6d49c..0000000 --- a/resources/fontawesome/svgs/solid/circle-pause.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-play.svg b/resources/fontawesome/svgs/solid/circle-play.svg deleted file mode 100644 index 1db5c8f..0000000 --- a/resources/fontawesome/svgs/solid/circle-play.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-plus.svg b/resources/fontawesome/svgs/solid/circle-plus.svg deleted file mode 100644 index 50af343..0000000 --- a/resources/fontawesome/svgs/solid/circle-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-question.svg b/resources/fontawesome/svgs/solid/circle-question.svg deleted file mode 100644 index c890709..0000000 --- a/resources/fontawesome/svgs/solid/circle-question.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-radiation.svg b/resources/fontawesome/svgs/solid/circle-radiation.svg deleted file mode 100644 index e4d0dee..0000000 --- a/resources/fontawesome/svgs/solid/circle-radiation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-right.svg b/resources/fontawesome/svgs/solid/circle-right.svg deleted file mode 100644 index 794985e..0000000 --- a/resources/fontawesome/svgs/solid/circle-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-stop.svg b/resources/fontawesome/svgs/solid/circle-stop.svg deleted file mode 100644 index a7fc66c..0000000 --- a/resources/fontawesome/svgs/solid/circle-stop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-up.svg b/resources/fontawesome/svgs/solid/circle-up.svg deleted file mode 100644 index 201d815..0000000 --- a/resources/fontawesome/svgs/solid/circle-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-user.svg b/resources/fontawesome/svgs/solid/circle-user.svg deleted file mode 100644 index c0a343a..0000000 --- a/resources/fontawesome/svgs/solid/circle-user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle-xmark.svg b/resources/fontawesome/svgs/solid/circle-xmark.svg deleted file mode 100644 index 50abf8b..0000000 --- a/resources/fontawesome/svgs/solid/circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/circle.svg b/resources/fontawesome/svgs/solid/circle.svg deleted file mode 100644 index 45ea4c1..0000000 --- a/resources/fontawesome/svgs/solid/circle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/city.svg b/resources/fontawesome/svgs/solid/city.svg deleted file mode 100644 index f341ab3..0000000 --- a/resources/fontawesome/svgs/solid/city.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clapperboard.svg b/resources/fontawesome/svgs/solid/clapperboard.svg deleted file mode 100644 index 8472810..0000000 --- a/resources/fontawesome/svgs/solid/clapperboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clipboard-check.svg b/resources/fontawesome/svgs/solid/clipboard-check.svg deleted file mode 100644 index 59efa7a..0000000 --- a/resources/fontawesome/svgs/solid/clipboard-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clipboard-list.svg b/resources/fontawesome/svgs/solid/clipboard-list.svg deleted file mode 100644 index c4fcd10..0000000 --- a/resources/fontawesome/svgs/solid/clipboard-list.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clipboard-question.svg b/resources/fontawesome/svgs/solid/clipboard-question.svg deleted file mode 100644 index 1ca5670..0000000 --- a/resources/fontawesome/svgs/solid/clipboard-question.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clipboard-user.svg b/resources/fontawesome/svgs/solid/clipboard-user.svg deleted file mode 100644 index 4b36149..0000000 --- a/resources/fontawesome/svgs/solid/clipboard-user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clipboard.svg b/resources/fontawesome/svgs/solid/clipboard.svg deleted file mode 100644 index 6157ac2..0000000 --- a/resources/fontawesome/svgs/solid/clipboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clock-rotate-left.svg b/resources/fontawesome/svgs/solid/clock-rotate-left.svg deleted file mode 100644 index 48e59f3..0000000 --- a/resources/fontawesome/svgs/solid/clock-rotate-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clock.svg b/resources/fontawesome/svgs/solid/clock.svg deleted file mode 100644 index f1a39d3..0000000 --- a/resources/fontawesome/svgs/solid/clock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clone.svg b/resources/fontawesome/svgs/solid/clone.svg deleted file mode 100644 index ee03b34..0000000 --- a/resources/fontawesome/svgs/solid/clone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/closed-captioning.svg b/resources/fontawesome/svgs/solid/closed-captioning.svg deleted file mode 100644 index 3be6b3f..0000000 --- a/resources/fontawesome/svgs/solid/closed-captioning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-arrow-down.svg b/resources/fontawesome/svgs/solid/cloud-arrow-down.svg deleted file mode 100644 index a25413f..0000000 --- a/resources/fontawesome/svgs/solid/cloud-arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-arrow-up.svg b/resources/fontawesome/svgs/solid/cloud-arrow-up.svg deleted file mode 100644 index 61dfc37..0000000 --- a/resources/fontawesome/svgs/solid/cloud-arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-bolt.svg b/resources/fontawesome/svgs/solid/cloud-bolt.svg deleted file mode 100644 index 97df3fe..0000000 --- a/resources/fontawesome/svgs/solid/cloud-bolt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-meatball.svg b/resources/fontawesome/svgs/solid/cloud-meatball.svg deleted file mode 100644 index 64a6b91..0000000 --- a/resources/fontawesome/svgs/solid/cloud-meatball.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-moon-rain.svg b/resources/fontawesome/svgs/solid/cloud-moon-rain.svg deleted file mode 100644 index 71cb717..0000000 --- a/resources/fontawesome/svgs/solid/cloud-moon-rain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-moon.svg b/resources/fontawesome/svgs/solid/cloud-moon.svg deleted file mode 100644 index 09a7799..0000000 --- a/resources/fontawesome/svgs/solid/cloud-moon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-rain.svg b/resources/fontawesome/svgs/solid/cloud-rain.svg deleted file mode 100644 index 149ce05..0000000 --- a/resources/fontawesome/svgs/solid/cloud-rain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-showers-heavy.svg b/resources/fontawesome/svgs/solid/cloud-showers-heavy.svg deleted file mode 100644 index 2afc753..0000000 --- a/resources/fontawesome/svgs/solid/cloud-showers-heavy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-showers-water.svg b/resources/fontawesome/svgs/solid/cloud-showers-water.svg deleted file mode 100644 index dc53f26..0000000 --- a/resources/fontawesome/svgs/solid/cloud-showers-water.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-sun-rain.svg b/resources/fontawesome/svgs/solid/cloud-sun-rain.svg deleted file mode 100644 index 35ae3b0..0000000 --- a/resources/fontawesome/svgs/solid/cloud-sun-rain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud-sun.svg b/resources/fontawesome/svgs/solid/cloud-sun.svg deleted file mode 100644 index 673ebcd..0000000 --- a/resources/fontawesome/svgs/solid/cloud-sun.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cloud.svg b/resources/fontawesome/svgs/solid/cloud.svg deleted file mode 100644 index 4150c79..0000000 --- a/resources/fontawesome/svgs/solid/cloud.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/clover.svg b/resources/fontawesome/svgs/solid/clover.svg deleted file mode 100644 index 968452f..0000000 --- a/resources/fontawesome/svgs/solid/clover.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/code-branch.svg b/resources/fontawesome/svgs/solid/code-branch.svg deleted file mode 100644 index 076faed..0000000 --- a/resources/fontawesome/svgs/solid/code-branch.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/code-commit.svg b/resources/fontawesome/svgs/solid/code-commit.svg deleted file mode 100644 index 57c22bc..0000000 --- a/resources/fontawesome/svgs/solid/code-commit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/code-compare.svg b/resources/fontawesome/svgs/solid/code-compare.svg deleted file mode 100644 index dff132f..0000000 --- a/resources/fontawesome/svgs/solid/code-compare.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/code-fork.svg b/resources/fontawesome/svgs/solid/code-fork.svg deleted file mode 100644 index 52c6075..0000000 --- a/resources/fontawesome/svgs/solid/code-fork.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/code-merge.svg b/resources/fontawesome/svgs/solid/code-merge.svg deleted file mode 100644 index 92f09c3..0000000 --- a/resources/fontawesome/svgs/solid/code-merge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/code-pull-request.svg b/resources/fontawesome/svgs/solid/code-pull-request.svg deleted file mode 100644 index 7ec53a0..0000000 --- a/resources/fontawesome/svgs/solid/code-pull-request.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/code.svg b/resources/fontawesome/svgs/solid/code.svg deleted file mode 100644 index 3e701b6..0000000 --- a/resources/fontawesome/svgs/solid/code.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/coins.svg b/resources/fontawesome/svgs/solid/coins.svg deleted file mode 100644 index fe8911a..0000000 --- a/resources/fontawesome/svgs/solid/coins.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/colon-sign.svg b/resources/fontawesome/svgs/solid/colon-sign.svg deleted file mode 100644 index 8835582..0000000 --- a/resources/fontawesome/svgs/solid/colon-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/comment-dollar.svg b/resources/fontawesome/svgs/solid/comment-dollar.svg deleted file mode 100644 index aecd1f9..0000000 --- a/resources/fontawesome/svgs/solid/comment-dollar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/comment-dots.svg b/resources/fontawesome/svgs/solid/comment-dots.svg deleted file mode 100644 index 4cf621b..0000000 --- a/resources/fontawesome/svgs/solid/comment-dots.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/comment-medical.svg b/resources/fontawesome/svgs/solid/comment-medical.svg deleted file mode 100644 index 7f62cd4..0000000 --- a/resources/fontawesome/svgs/solid/comment-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/comment-slash.svg b/resources/fontawesome/svgs/solid/comment-slash.svg deleted file mode 100644 index 966e05b..0000000 --- a/resources/fontawesome/svgs/solid/comment-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/comment-sms.svg b/resources/fontawesome/svgs/solid/comment-sms.svg deleted file mode 100644 index 9a7e0eb..0000000 --- a/resources/fontawesome/svgs/solid/comment-sms.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/comment.svg b/resources/fontawesome/svgs/solid/comment.svg deleted file mode 100644 index 5edc983..0000000 --- a/resources/fontawesome/svgs/solid/comment.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/comments-dollar.svg b/resources/fontawesome/svgs/solid/comments-dollar.svg deleted file mode 100644 index 027fa07..0000000 --- a/resources/fontawesome/svgs/solid/comments-dollar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/comments.svg b/resources/fontawesome/svgs/solid/comments.svg deleted file mode 100644 index d4f20fc..0000000 --- a/resources/fontawesome/svgs/solid/comments.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/compact-disc.svg b/resources/fontawesome/svgs/solid/compact-disc.svg deleted file mode 100644 index 7cf4991..0000000 --- a/resources/fontawesome/svgs/solid/compact-disc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/compass-drafting.svg b/resources/fontawesome/svgs/solid/compass-drafting.svg deleted file mode 100644 index f2371e6..0000000 --- a/resources/fontawesome/svgs/solid/compass-drafting.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/compass.svg b/resources/fontawesome/svgs/solid/compass.svg deleted file mode 100644 index ca50325..0000000 --- a/resources/fontawesome/svgs/solid/compass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/compress.svg b/resources/fontawesome/svgs/solid/compress.svg deleted file mode 100644 index ab649a5..0000000 --- a/resources/fontawesome/svgs/solid/compress.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/computer-mouse.svg b/resources/fontawesome/svgs/solid/computer-mouse.svg deleted file mode 100644 index f65f928..0000000 --- a/resources/fontawesome/svgs/solid/computer-mouse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/computer.svg b/resources/fontawesome/svgs/solid/computer.svg deleted file mode 100644 index 975f8c1..0000000 --- a/resources/fontawesome/svgs/solid/computer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cookie-bite.svg b/resources/fontawesome/svgs/solid/cookie-bite.svg deleted file mode 100644 index 9dd8532..0000000 --- a/resources/fontawesome/svgs/solid/cookie-bite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cookie.svg b/resources/fontawesome/svgs/solid/cookie.svg deleted file mode 100644 index ab3e063..0000000 --- a/resources/fontawesome/svgs/solid/cookie.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/copy.svg b/resources/fontawesome/svgs/solid/copy.svg deleted file mode 100644 index b49d016..0000000 --- a/resources/fontawesome/svgs/solid/copy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/copyright.svg b/resources/fontawesome/svgs/solid/copyright.svg deleted file mode 100644 index 6b0d0f7..0000000 --- a/resources/fontawesome/svgs/solid/copyright.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/couch.svg b/resources/fontawesome/svgs/solid/couch.svg deleted file mode 100644 index 0d2a85e..0000000 --- a/resources/fontawesome/svgs/solid/couch.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cow.svg b/resources/fontawesome/svgs/solid/cow.svg deleted file mode 100644 index 0c6cc59..0000000 --- a/resources/fontawesome/svgs/solid/cow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/credit-card.svg b/resources/fontawesome/svgs/solid/credit-card.svg deleted file mode 100644 index 7285ee7..0000000 --- a/resources/fontawesome/svgs/solid/credit-card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/crop-simple.svg b/resources/fontawesome/svgs/solid/crop-simple.svg deleted file mode 100644 index 90fe0c8..0000000 --- a/resources/fontawesome/svgs/solid/crop-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/crop.svg b/resources/fontawesome/svgs/solid/crop.svg deleted file mode 100644 index 239ab74..0000000 --- a/resources/fontawesome/svgs/solid/crop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cross.svg b/resources/fontawesome/svgs/solid/cross.svg deleted file mode 100644 index fb43376..0000000 --- a/resources/fontawesome/svgs/solid/cross.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/crosshairs.svg b/resources/fontawesome/svgs/solid/crosshairs.svg deleted file mode 100644 index 9597b28..0000000 --- a/resources/fontawesome/svgs/solid/crosshairs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/crow.svg b/resources/fontawesome/svgs/solid/crow.svg deleted file mode 100644 index 24a269b..0000000 --- a/resources/fontawesome/svgs/solid/crow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/crown.svg b/resources/fontawesome/svgs/solid/crown.svg deleted file mode 100644 index c80289e..0000000 --- a/resources/fontawesome/svgs/solid/crown.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/crutch.svg b/resources/fontawesome/svgs/solid/crutch.svg deleted file mode 100644 index f9cc9b8..0000000 --- a/resources/fontawesome/svgs/solid/crutch.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cruzeiro-sign.svg b/resources/fontawesome/svgs/solid/cruzeiro-sign.svg deleted file mode 100644 index 4e0ac9c..0000000 --- a/resources/fontawesome/svgs/solid/cruzeiro-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cube.svg b/resources/fontawesome/svgs/solid/cube.svg deleted file mode 100644 index 429bfa3..0000000 --- a/resources/fontawesome/svgs/solid/cube.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cubes-stacked.svg b/resources/fontawesome/svgs/solid/cubes-stacked.svg deleted file mode 100644 index d525ec0..0000000 --- a/resources/fontawesome/svgs/solid/cubes-stacked.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/cubes.svg b/resources/fontawesome/svgs/solid/cubes.svg deleted file mode 100644 index dd709f9..0000000 --- a/resources/fontawesome/svgs/solid/cubes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/d.svg b/resources/fontawesome/svgs/solid/d.svg deleted file mode 100644 index 017aace..0000000 --- a/resources/fontawesome/svgs/solid/d.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/database.svg b/resources/fontawesome/svgs/solid/database.svg deleted file mode 100644 index 3a925fa..0000000 --- a/resources/fontawesome/svgs/solid/database.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/delete-left.svg b/resources/fontawesome/svgs/solid/delete-left.svg deleted file mode 100644 index 3558ba0..0000000 --- a/resources/fontawesome/svgs/solid/delete-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/democrat.svg b/resources/fontawesome/svgs/solid/democrat.svg deleted file mode 100644 index 6bb22c0..0000000 --- a/resources/fontawesome/svgs/solid/democrat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/desktop.svg b/resources/fontawesome/svgs/solid/desktop.svg deleted file mode 100644 index 239dea4..0000000 --- a/resources/fontawesome/svgs/solid/desktop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dharmachakra.svg b/resources/fontawesome/svgs/solid/dharmachakra.svg deleted file mode 100644 index 289cd05..0000000 --- a/resources/fontawesome/svgs/solid/dharmachakra.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/diagram-next.svg b/resources/fontawesome/svgs/solid/diagram-next.svg deleted file mode 100644 index 92701c3..0000000 --- a/resources/fontawesome/svgs/solid/diagram-next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/diagram-predecessor.svg b/resources/fontawesome/svgs/solid/diagram-predecessor.svg deleted file mode 100644 index bb78775..0000000 --- a/resources/fontawesome/svgs/solid/diagram-predecessor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/diagram-project.svg b/resources/fontawesome/svgs/solid/diagram-project.svg deleted file mode 100644 index 31b008c..0000000 --- a/resources/fontawesome/svgs/solid/diagram-project.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/diagram-successor.svg b/resources/fontawesome/svgs/solid/diagram-successor.svg deleted file mode 100644 index e5ba752..0000000 --- a/resources/fontawesome/svgs/solid/diagram-successor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/diamond-turn-right.svg b/resources/fontawesome/svgs/solid/diamond-turn-right.svg deleted file mode 100644 index 7bd8064..0000000 --- a/resources/fontawesome/svgs/solid/diamond-turn-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/diamond.svg b/resources/fontawesome/svgs/solid/diamond.svg deleted file mode 100644 index 1f7bab8..0000000 --- a/resources/fontawesome/svgs/solid/diamond.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice-d20.svg b/resources/fontawesome/svgs/solid/dice-d20.svg deleted file mode 100644 index d441921..0000000 --- a/resources/fontawesome/svgs/solid/dice-d20.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice-d6.svg b/resources/fontawesome/svgs/solid/dice-d6.svg deleted file mode 100644 index 7c9fe05..0000000 --- a/resources/fontawesome/svgs/solid/dice-d6.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice-five.svg b/resources/fontawesome/svgs/solid/dice-five.svg deleted file mode 100644 index b11a49d..0000000 --- a/resources/fontawesome/svgs/solid/dice-five.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice-four.svg b/resources/fontawesome/svgs/solid/dice-four.svg deleted file mode 100644 index 3617e08..0000000 --- a/resources/fontawesome/svgs/solid/dice-four.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice-one.svg b/resources/fontawesome/svgs/solid/dice-one.svg deleted file mode 100644 index efc9a5a..0000000 --- a/resources/fontawesome/svgs/solid/dice-one.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice-six.svg b/resources/fontawesome/svgs/solid/dice-six.svg deleted file mode 100644 index fc9d8aa..0000000 --- a/resources/fontawesome/svgs/solid/dice-six.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice-three.svg b/resources/fontawesome/svgs/solid/dice-three.svg deleted file mode 100644 index 5216d8f..0000000 --- a/resources/fontawesome/svgs/solid/dice-three.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice-two.svg b/resources/fontawesome/svgs/solid/dice-two.svg deleted file mode 100644 index e11b47d..0000000 --- a/resources/fontawesome/svgs/solid/dice-two.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dice.svg b/resources/fontawesome/svgs/solid/dice.svg deleted file mode 100644 index 4863493..0000000 --- a/resources/fontawesome/svgs/solid/dice.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/disease.svg b/resources/fontawesome/svgs/solid/disease.svg deleted file mode 100644 index b5bc7d9..0000000 --- a/resources/fontawesome/svgs/solid/disease.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/display.svg b/resources/fontawesome/svgs/solid/display.svg deleted file mode 100644 index 04912a6..0000000 --- a/resources/fontawesome/svgs/solid/display.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/divide.svg b/resources/fontawesome/svgs/solid/divide.svg deleted file mode 100644 index d8e6c63..0000000 --- a/resources/fontawesome/svgs/solid/divide.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dna.svg b/resources/fontawesome/svgs/solid/dna.svg deleted file mode 100644 index dc86a90..0000000 --- a/resources/fontawesome/svgs/solid/dna.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dog.svg b/resources/fontawesome/svgs/solid/dog.svg deleted file mode 100644 index 20bce80..0000000 --- a/resources/fontawesome/svgs/solid/dog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dollar-sign.svg b/resources/fontawesome/svgs/solid/dollar-sign.svg deleted file mode 100644 index 2efab77..0000000 --- a/resources/fontawesome/svgs/solid/dollar-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dolly.svg b/resources/fontawesome/svgs/solid/dolly.svg deleted file mode 100644 index c50e8dd..0000000 --- a/resources/fontawesome/svgs/solid/dolly.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dong-sign.svg b/resources/fontawesome/svgs/solid/dong-sign.svg deleted file mode 100644 index 4b37bbe..0000000 --- a/resources/fontawesome/svgs/solid/dong-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/door-closed.svg b/resources/fontawesome/svgs/solid/door-closed.svg deleted file mode 100644 index 4dd4541..0000000 --- a/resources/fontawesome/svgs/solid/door-closed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/door-open.svg b/resources/fontawesome/svgs/solid/door-open.svg deleted file mode 100644 index 7f3ef35..0000000 --- a/resources/fontawesome/svgs/solid/door-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dove.svg b/resources/fontawesome/svgs/solid/dove.svg deleted file mode 100644 index 03b590a..0000000 --- a/resources/fontawesome/svgs/solid/dove.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/down-left-and-up-right-to-center.svg b/resources/fontawesome/svgs/solid/down-left-and-up-right-to-center.svg deleted file mode 100644 index f81a6d3..0000000 --- a/resources/fontawesome/svgs/solid/down-left-and-up-right-to-center.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/down-long.svg b/resources/fontawesome/svgs/solid/down-long.svg deleted file mode 100644 index 58d8672..0000000 --- a/resources/fontawesome/svgs/solid/down-long.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/download.svg b/resources/fontawesome/svgs/solid/download.svg deleted file mode 100644 index 0375764..0000000 --- a/resources/fontawesome/svgs/solid/download.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dragon.svg b/resources/fontawesome/svgs/solid/dragon.svg deleted file mode 100644 index 61f665f..0000000 --- a/resources/fontawesome/svgs/solid/dragon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/draw-polygon.svg b/resources/fontawesome/svgs/solid/draw-polygon.svg deleted file mode 100644 index b413bc1..0000000 --- a/resources/fontawesome/svgs/solid/draw-polygon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/droplet-slash.svg b/resources/fontawesome/svgs/solid/droplet-slash.svg deleted file mode 100644 index 2200a45..0000000 --- a/resources/fontawesome/svgs/solid/droplet-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/droplet.svg b/resources/fontawesome/svgs/solid/droplet.svg deleted file mode 100644 index 281825e..0000000 --- a/resources/fontawesome/svgs/solid/droplet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/drum-steelpan.svg b/resources/fontawesome/svgs/solid/drum-steelpan.svg deleted file mode 100644 index a5ca6a4..0000000 --- a/resources/fontawesome/svgs/solid/drum-steelpan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/drum.svg b/resources/fontawesome/svgs/solid/drum.svg deleted file mode 100644 index aeb3bba..0000000 --- a/resources/fontawesome/svgs/solid/drum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/drumstick-bite.svg b/resources/fontawesome/svgs/solid/drumstick-bite.svg deleted file mode 100644 index 7a66c85..0000000 --- a/resources/fontawesome/svgs/solid/drumstick-bite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dumbbell.svg b/resources/fontawesome/svgs/solid/dumbbell.svg deleted file mode 100644 index b79e24d..0000000 --- a/resources/fontawesome/svgs/solid/dumbbell.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dumpster-fire.svg b/resources/fontawesome/svgs/solid/dumpster-fire.svg deleted file mode 100644 index 9161131..0000000 --- a/resources/fontawesome/svgs/solid/dumpster-fire.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dumpster.svg b/resources/fontawesome/svgs/solid/dumpster.svg deleted file mode 100644 index f5d3024..0000000 --- a/resources/fontawesome/svgs/solid/dumpster.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/dungeon.svg b/resources/fontawesome/svgs/solid/dungeon.svg deleted file mode 100644 index 0c7a41c..0000000 --- a/resources/fontawesome/svgs/solid/dungeon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/e.svg b/resources/fontawesome/svgs/solid/e.svg deleted file mode 100644 index 8ee973e..0000000 --- a/resources/fontawesome/svgs/solid/e.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ear-deaf.svg b/resources/fontawesome/svgs/solid/ear-deaf.svg deleted file mode 100644 index 1e8a18d..0000000 --- a/resources/fontawesome/svgs/solid/ear-deaf.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ear-listen.svg b/resources/fontawesome/svgs/solid/ear-listen.svg deleted file mode 100644 index c291c73..0000000 --- a/resources/fontawesome/svgs/solid/ear-listen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/earth-africa.svg b/resources/fontawesome/svgs/solid/earth-africa.svg deleted file mode 100644 index 8499bf9..0000000 --- a/resources/fontawesome/svgs/solid/earth-africa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/earth-americas.svg b/resources/fontawesome/svgs/solid/earth-americas.svg deleted file mode 100644 index cc85139..0000000 --- a/resources/fontawesome/svgs/solid/earth-americas.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/earth-asia.svg b/resources/fontawesome/svgs/solid/earth-asia.svg deleted file mode 100644 index 37a3ab9..0000000 --- a/resources/fontawesome/svgs/solid/earth-asia.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/earth-europe.svg b/resources/fontawesome/svgs/solid/earth-europe.svg deleted file mode 100644 index eb096da..0000000 --- a/resources/fontawesome/svgs/solid/earth-europe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/earth-oceania.svg b/resources/fontawesome/svgs/solid/earth-oceania.svg deleted file mode 100644 index 0720b59..0000000 --- a/resources/fontawesome/svgs/solid/earth-oceania.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/egg.svg b/resources/fontawesome/svgs/solid/egg.svg deleted file mode 100644 index 288e4d4..0000000 --- a/resources/fontawesome/svgs/solid/egg.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/eject.svg b/resources/fontawesome/svgs/solid/eject.svg deleted file mode 100644 index ad28847..0000000 --- a/resources/fontawesome/svgs/solid/eject.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/elevator.svg b/resources/fontawesome/svgs/solid/elevator.svg deleted file mode 100644 index 1ce8232..0000000 --- a/resources/fontawesome/svgs/solid/elevator.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ellipsis-vertical.svg b/resources/fontawesome/svgs/solid/ellipsis-vertical.svg deleted file mode 100644 index 5107e72..0000000 --- a/resources/fontawesome/svgs/solid/ellipsis-vertical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ellipsis.svg b/resources/fontawesome/svgs/solid/ellipsis.svg deleted file mode 100644 index d910004..0000000 --- a/resources/fontawesome/svgs/solid/ellipsis.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/envelope-circle-check.svg b/resources/fontawesome/svgs/solid/envelope-circle-check.svg deleted file mode 100644 index bad4ef1..0000000 --- a/resources/fontawesome/svgs/solid/envelope-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/envelope-open-text.svg b/resources/fontawesome/svgs/solid/envelope-open-text.svg deleted file mode 100644 index 146403f..0000000 --- a/resources/fontawesome/svgs/solid/envelope-open-text.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/envelope-open.svg b/resources/fontawesome/svgs/solid/envelope-open.svg deleted file mode 100644 index e915611..0000000 --- a/resources/fontawesome/svgs/solid/envelope-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/envelope.svg b/resources/fontawesome/svgs/solid/envelope.svg deleted file mode 100644 index 8cfe321..0000000 --- a/resources/fontawesome/svgs/solid/envelope.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/envelopes-bulk.svg b/resources/fontawesome/svgs/solid/envelopes-bulk.svg deleted file mode 100644 index a7ed864..0000000 --- a/resources/fontawesome/svgs/solid/envelopes-bulk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/equals.svg b/resources/fontawesome/svgs/solid/equals.svg deleted file mode 100644 index 40617b3..0000000 --- a/resources/fontawesome/svgs/solid/equals.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/eraser.svg b/resources/fontawesome/svgs/solid/eraser.svg deleted file mode 100644 index 4c45953..0000000 --- a/resources/fontawesome/svgs/solid/eraser.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ethernet.svg b/resources/fontawesome/svgs/solid/ethernet.svg deleted file mode 100644 index 7d17336..0000000 --- a/resources/fontawesome/svgs/solid/ethernet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/euro-sign.svg b/resources/fontawesome/svgs/solid/euro-sign.svg deleted file mode 100644 index c1792e3..0000000 --- a/resources/fontawesome/svgs/solid/euro-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/exclamation.svg b/resources/fontawesome/svgs/solid/exclamation.svg deleted file mode 100644 index 5d2b83c..0000000 --- a/resources/fontawesome/svgs/solid/exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/expand.svg b/resources/fontawesome/svgs/solid/expand.svg deleted file mode 100644 index b0d6a41..0000000 --- a/resources/fontawesome/svgs/solid/expand.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/explosion.svg b/resources/fontawesome/svgs/solid/explosion.svg deleted file mode 100644 index 1ae07c1..0000000 --- a/resources/fontawesome/svgs/solid/explosion.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/eye-dropper.svg b/resources/fontawesome/svgs/solid/eye-dropper.svg deleted file mode 100644 index 960f90c..0000000 --- a/resources/fontawesome/svgs/solid/eye-dropper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/eye-low-vision.svg b/resources/fontawesome/svgs/solid/eye-low-vision.svg deleted file mode 100644 index fd0d9e1..0000000 --- a/resources/fontawesome/svgs/solid/eye-low-vision.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/eye-slash.svg b/resources/fontawesome/svgs/solid/eye-slash.svg deleted file mode 100644 index 332b3b1..0000000 --- a/resources/fontawesome/svgs/solid/eye-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/eye.svg b/resources/fontawesome/svgs/solid/eye.svg deleted file mode 100644 index fabe442..0000000 --- a/resources/fontawesome/svgs/solid/eye.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/f.svg b/resources/fontawesome/svgs/solid/f.svg deleted file mode 100644 index a8cba1e..0000000 --- a/resources/fontawesome/svgs/solid/f.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-angry.svg b/resources/fontawesome/svgs/solid/face-angry.svg deleted file mode 100644 index 4e01d65..0000000 --- a/resources/fontawesome/svgs/solid/face-angry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-dizzy.svg b/resources/fontawesome/svgs/solid/face-dizzy.svg deleted file mode 100644 index aab1b3e..0000000 --- a/resources/fontawesome/svgs/solid/face-dizzy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-flushed.svg b/resources/fontawesome/svgs/solid/face-flushed.svg deleted file mode 100644 index f03bfcc..0000000 --- a/resources/fontawesome/svgs/solid/face-flushed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-frown-open.svg b/resources/fontawesome/svgs/solid/face-frown-open.svg deleted file mode 100644 index 569cc1c..0000000 --- a/resources/fontawesome/svgs/solid/face-frown-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-frown.svg b/resources/fontawesome/svgs/solid/face-frown.svg deleted file mode 100644 index bc9880c..0000000 --- a/resources/fontawesome/svgs/solid/face-frown.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grimace.svg b/resources/fontawesome/svgs/solid/face-grimace.svg deleted file mode 100644 index 7824657..0000000 --- a/resources/fontawesome/svgs/solid/face-grimace.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-beam-sweat.svg b/resources/fontawesome/svgs/solid/face-grin-beam-sweat.svg deleted file mode 100644 index d432b72..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-beam-sweat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-beam.svg b/resources/fontawesome/svgs/solid/face-grin-beam.svg deleted file mode 100644 index 93d07f3..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-beam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-hearts.svg b/resources/fontawesome/svgs/solid/face-grin-hearts.svg deleted file mode 100644 index 701966a..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-hearts.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-squint-tears.svg b/resources/fontawesome/svgs/solid/face-grin-squint-tears.svg deleted file mode 100644 index b50a68e..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-squint-tears.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-squint.svg b/resources/fontawesome/svgs/solid/face-grin-squint.svg deleted file mode 100644 index 4e659e3..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-squint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-stars.svg b/resources/fontawesome/svgs/solid/face-grin-stars.svg deleted file mode 100644 index 7fc8072..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-stars.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-tears.svg b/resources/fontawesome/svgs/solid/face-grin-tears.svg deleted file mode 100644 index 3257593..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-tears.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-tongue-squint.svg b/resources/fontawesome/svgs/solid/face-grin-tongue-squint.svg deleted file mode 100644 index 6b88a31..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-tongue-squint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-tongue-wink.svg b/resources/fontawesome/svgs/solid/face-grin-tongue-wink.svg deleted file mode 100644 index 1f8b1fd..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-tongue-wink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-tongue.svg b/resources/fontawesome/svgs/solid/face-grin-tongue.svg deleted file mode 100644 index 0f852d2..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-tongue.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-wide.svg b/resources/fontawesome/svgs/solid/face-grin-wide.svg deleted file mode 100644 index cfb7667..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-wide.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin-wink.svg b/resources/fontawesome/svgs/solid/face-grin-wink.svg deleted file mode 100644 index d8a3b12..0000000 --- a/resources/fontawesome/svgs/solid/face-grin-wink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-grin.svg b/resources/fontawesome/svgs/solid/face-grin.svg deleted file mode 100644 index 60325de..0000000 --- a/resources/fontawesome/svgs/solid/face-grin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-kiss-beam.svg b/resources/fontawesome/svgs/solid/face-kiss-beam.svg deleted file mode 100644 index adc6f59..0000000 --- a/resources/fontawesome/svgs/solid/face-kiss-beam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-kiss-wink-heart.svg b/resources/fontawesome/svgs/solid/face-kiss-wink-heart.svg deleted file mode 100644 index 7adbc8e..0000000 --- a/resources/fontawesome/svgs/solid/face-kiss-wink-heart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-kiss.svg b/resources/fontawesome/svgs/solid/face-kiss.svg deleted file mode 100644 index c51b9f9..0000000 --- a/resources/fontawesome/svgs/solid/face-kiss.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-laugh-beam.svg b/resources/fontawesome/svgs/solid/face-laugh-beam.svg deleted file mode 100644 index 664f39f..0000000 --- a/resources/fontawesome/svgs/solid/face-laugh-beam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-laugh-squint.svg b/resources/fontawesome/svgs/solid/face-laugh-squint.svg deleted file mode 100644 index 19d5c4d..0000000 --- a/resources/fontawesome/svgs/solid/face-laugh-squint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-laugh-wink.svg b/resources/fontawesome/svgs/solid/face-laugh-wink.svg deleted file mode 100644 index 90c38c3..0000000 --- a/resources/fontawesome/svgs/solid/face-laugh-wink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-laugh.svg b/resources/fontawesome/svgs/solid/face-laugh.svg deleted file mode 100644 index 8669ecc..0000000 --- a/resources/fontawesome/svgs/solid/face-laugh.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-meh-blank.svg b/resources/fontawesome/svgs/solid/face-meh-blank.svg deleted file mode 100644 index e60f00d..0000000 --- a/resources/fontawesome/svgs/solid/face-meh-blank.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-meh.svg b/resources/fontawesome/svgs/solid/face-meh.svg deleted file mode 100644 index 271d448..0000000 --- a/resources/fontawesome/svgs/solid/face-meh.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-rolling-eyes.svg b/resources/fontawesome/svgs/solid/face-rolling-eyes.svg deleted file mode 100644 index 0ac7908..0000000 --- a/resources/fontawesome/svgs/solid/face-rolling-eyes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-sad-cry.svg b/resources/fontawesome/svgs/solid/face-sad-cry.svg deleted file mode 100644 index 63a764c..0000000 --- a/resources/fontawesome/svgs/solid/face-sad-cry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-sad-tear.svg b/resources/fontawesome/svgs/solid/face-sad-tear.svg deleted file mode 100644 index 1bed800..0000000 --- a/resources/fontawesome/svgs/solid/face-sad-tear.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-smile-beam.svg b/resources/fontawesome/svgs/solid/face-smile-beam.svg deleted file mode 100644 index ccf2e60..0000000 --- a/resources/fontawesome/svgs/solid/face-smile-beam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-smile-wink.svg b/resources/fontawesome/svgs/solid/face-smile-wink.svg deleted file mode 100644 index 16be2f2..0000000 --- a/resources/fontawesome/svgs/solid/face-smile-wink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-smile.svg b/resources/fontawesome/svgs/solid/face-smile.svg deleted file mode 100644 index 654a95a..0000000 --- a/resources/fontawesome/svgs/solid/face-smile.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-surprise.svg b/resources/fontawesome/svgs/solid/face-surprise.svg deleted file mode 100644 index 60a5a72..0000000 --- a/resources/fontawesome/svgs/solid/face-surprise.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/face-tired.svg b/resources/fontawesome/svgs/solid/face-tired.svg deleted file mode 100644 index 25b1ab4..0000000 --- a/resources/fontawesome/svgs/solid/face-tired.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fan.svg b/resources/fontawesome/svgs/solid/fan.svg deleted file mode 100644 index ffdb958..0000000 --- a/resources/fontawesome/svgs/solid/fan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/faucet-drip.svg b/resources/fontawesome/svgs/solid/faucet-drip.svg deleted file mode 100644 index df61d42..0000000 --- a/resources/fontawesome/svgs/solid/faucet-drip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/faucet.svg b/resources/fontawesome/svgs/solid/faucet.svg deleted file mode 100644 index b902fbc..0000000 --- a/resources/fontawesome/svgs/solid/faucet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fax.svg b/resources/fontawesome/svgs/solid/fax.svg deleted file mode 100644 index b6f05e3..0000000 --- a/resources/fontawesome/svgs/solid/fax.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/feather-pointed.svg b/resources/fontawesome/svgs/solid/feather-pointed.svg deleted file mode 100644 index d8fce4f..0000000 --- a/resources/fontawesome/svgs/solid/feather-pointed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/feather.svg b/resources/fontawesome/svgs/solid/feather.svg deleted file mode 100644 index 2d5727d..0000000 --- a/resources/fontawesome/svgs/solid/feather.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ferry.svg b/resources/fontawesome/svgs/solid/ferry.svg deleted file mode 100644 index fb43669..0000000 --- a/resources/fontawesome/svgs/solid/ferry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-arrow-down.svg b/resources/fontawesome/svgs/solid/file-arrow-down.svg deleted file mode 100644 index a73227b..0000000 --- a/resources/fontawesome/svgs/solid/file-arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-arrow-up.svg b/resources/fontawesome/svgs/solid/file-arrow-up.svg deleted file mode 100644 index b3df565..0000000 --- a/resources/fontawesome/svgs/solid/file-arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-audio.svg b/resources/fontawesome/svgs/solid/file-audio.svg deleted file mode 100644 index cf7c8ea..0000000 --- a/resources/fontawesome/svgs/solid/file-audio.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-circle-check.svg b/resources/fontawesome/svgs/solid/file-circle-check.svg deleted file mode 100644 index 69e7c42..0000000 --- a/resources/fontawesome/svgs/solid/file-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-circle-exclamation.svg b/resources/fontawesome/svgs/solid/file-circle-exclamation.svg deleted file mode 100644 index 7a75c0e..0000000 --- a/resources/fontawesome/svgs/solid/file-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-circle-minus.svg b/resources/fontawesome/svgs/solid/file-circle-minus.svg deleted file mode 100644 index e84f120..0000000 --- a/resources/fontawesome/svgs/solid/file-circle-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-circle-plus.svg b/resources/fontawesome/svgs/solid/file-circle-plus.svg deleted file mode 100644 index ecda482..0000000 --- a/resources/fontawesome/svgs/solid/file-circle-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-circle-question.svg b/resources/fontawesome/svgs/solid/file-circle-question.svg deleted file mode 100644 index 1634abd..0000000 --- a/resources/fontawesome/svgs/solid/file-circle-question.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-circle-xmark.svg b/resources/fontawesome/svgs/solid/file-circle-xmark.svg deleted file mode 100644 index aad2dc8..0000000 --- a/resources/fontawesome/svgs/solid/file-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-code.svg b/resources/fontawesome/svgs/solid/file-code.svg deleted file mode 100644 index 2a0f53a..0000000 --- a/resources/fontawesome/svgs/solid/file-code.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-contract.svg b/resources/fontawesome/svgs/solid/file-contract.svg deleted file mode 100644 index b9d9eb7..0000000 --- a/resources/fontawesome/svgs/solid/file-contract.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-csv.svg b/resources/fontawesome/svgs/solid/file-csv.svg deleted file mode 100644 index 5cf435f..0000000 --- a/resources/fontawesome/svgs/solid/file-csv.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-excel.svg b/resources/fontawesome/svgs/solid/file-excel.svg deleted file mode 100644 index 30919ce..0000000 --- a/resources/fontawesome/svgs/solid/file-excel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-export.svg b/resources/fontawesome/svgs/solid/file-export.svg deleted file mode 100644 index 50cd8d7..0000000 --- a/resources/fontawesome/svgs/solid/file-export.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-image.svg b/resources/fontawesome/svgs/solid/file-image.svg deleted file mode 100644 index 7859a4f..0000000 --- a/resources/fontawesome/svgs/solid/file-image.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-import.svg b/resources/fontawesome/svgs/solid/file-import.svg deleted file mode 100644 index a04558a..0000000 --- a/resources/fontawesome/svgs/solid/file-import.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-invoice-dollar.svg b/resources/fontawesome/svgs/solid/file-invoice-dollar.svg deleted file mode 100644 index ac73d58..0000000 --- a/resources/fontawesome/svgs/solid/file-invoice-dollar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-invoice.svg b/resources/fontawesome/svgs/solid/file-invoice.svg deleted file mode 100644 index ea7c173..0000000 --- a/resources/fontawesome/svgs/solid/file-invoice.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-lines.svg b/resources/fontawesome/svgs/solid/file-lines.svg deleted file mode 100644 index e0b23ba..0000000 --- a/resources/fontawesome/svgs/solid/file-lines.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-medical.svg b/resources/fontawesome/svgs/solid/file-medical.svg deleted file mode 100644 index b50229e..0000000 --- a/resources/fontawesome/svgs/solid/file-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-pdf.svg b/resources/fontawesome/svgs/solid/file-pdf.svg deleted file mode 100644 index 32439fb..0000000 --- a/resources/fontawesome/svgs/solid/file-pdf.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-pen.svg b/resources/fontawesome/svgs/solid/file-pen.svg deleted file mode 100644 index 723aa1a..0000000 --- a/resources/fontawesome/svgs/solid/file-pen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-powerpoint.svg b/resources/fontawesome/svgs/solid/file-powerpoint.svg deleted file mode 100644 index 173bd3c..0000000 --- a/resources/fontawesome/svgs/solid/file-powerpoint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-prescription.svg b/resources/fontawesome/svgs/solid/file-prescription.svg deleted file mode 100644 index 8f23a28..0000000 --- a/resources/fontawesome/svgs/solid/file-prescription.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-shield.svg b/resources/fontawesome/svgs/solid/file-shield.svg deleted file mode 100644 index a47c508..0000000 --- a/resources/fontawesome/svgs/solid/file-shield.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-signature.svg b/resources/fontawesome/svgs/solid/file-signature.svg deleted file mode 100644 index 9ae933b..0000000 --- a/resources/fontawesome/svgs/solid/file-signature.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-video.svg b/resources/fontawesome/svgs/solid/file-video.svg deleted file mode 100644 index d23da62..0000000 --- a/resources/fontawesome/svgs/solid/file-video.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-waveform.svg b/resources/fontawesome/svgs/solid/file-waveform.svg deleted file mode 100644 index 2138d89..0000000 --- a/resources/fontawesome/svgs/solid/file-waveform.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-word.svg b/resources/fontawesome/svgs/solid/file-word.svg deleted file mode 100644 index 5af05f2..0000000 --- a/resources/fontawesome/svgs/solid/file-word.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file-zipper.svg b/resources/fontawesome/svgs/solid/file-zipper.svg deleted file mode 100644 index aed3f46..0000000 --- a/resources/fontawesome/svgs/solid/file-zipper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/file.svg b/resources/fontawesome/svgs/solid/file.svg deleted file mode 100644 index 40745de..0000000 --- a/resources/fontawesome/svgs/solid/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fill-drip.svg b/resources/fontawesome/svgs/solid/fill-drip.svg deleted file mode 100644 index 8fff64c..0000000 --- a/resources/fontawesome/svgs/solid/fill-drip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fill.svg b/resources/fontawesome/svgs/solid/fill.svg deleted file mode 100644 index f1f4212..0000000 --- a/resources/fontawesome/svgs/solid/fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/film.svg b/resources/fontawesome/svgs/solid/film.svg deleted file mode 100644 index 4fd12f9..0000000 --- a/resources/fontawesome/svgs/solid/film.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/filter-circle-dollar.svg b/resources/fontawesome/svgs/solid/filter-circle-dollar.svg deleted file mode 100644 index 752d566..0000000 --- a/resources/fontawesome/svgs/solid/filter-circle-dollar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/filter-circle-xmark.svg b/resources/fontawesome/svgs/solid/filter-circle-xmark.svg deleted file mode 100644 index 109acdb..0000000 --- a/resources/fontawesome/svgs/solid/filter-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/filter.svg b/resources/fontawesome/svgs/solid/filter.svg deleted file mode 100644 index a3ad17e..0000000 --- a/resources/fontawesome/svgs/solid/filter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fingerprint.svg b/resources/fontawesome/svgs/solid/fingerprint.svg deleted file mode 100644 index 2933997..0000000 --- a/resources/fontawesome/svgs/solid/fingerprint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fire-burner.svg b/resources/fontawesome/svgs/solid/fire-burner.svg deleted file mode 100644 index 1d7202b..0000000 --- a/resources/fontawesome/svgs/solid/fire-burner.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fire-extinguisher.svg b/resources/fontawesome/svgs/solid/fire-extinguisher.svg deleted file mode 100644 index 3926d73..0000000 --- a/resources/fontawesome/svgs/solid/fire-extinguisher.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fire-flame-curved.svg b/resources/fontawesome/svgs/solid/fire-flame-curved.svg deleted file mode 100644 index 549119c..0000000 --- a/resources/fontawesome/svgs/solid/fire-flame-curved.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fire-flame-simple.svg b/resources/fontawesome/svgs/solid/fire-flame-simple.svg deleted file mode 100644 index bcf53cf..0000000 --- a/resources/fontawesome/svgs/solid/fire-flame-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fire.svg b/resources/fontawesome/svgs/solid/fire.svg deleted file mode 100644 index 348c549..0000000 --- a/resources/fontawesome/svgs/solid/fire.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fish-fins.svg b/resources/fontawesome/svgs/solid/fish-fins.svg deleted file mode 100644 index beedce2..0000000 --- a/resources/fontawesome/svgs/solid/fish-fins.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/fish.svg b/resources/fontawesome/svgs/solid/fish.svg deleted file mode 100644 index f836864..0000000 --- a/resources/fontawesome/svgs/solid/fish.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/flag-checkered.svg b/resources/fontawesome/svgs/solid/flag-checkered.svg deleted file mode 100644 index d424a92..0000000 --- a/resources/fontawesome/svgs/solid/flag-checkered.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/flag-usa.svg b/resources/fontawesome/svgs/solid/flag-usa.svg deleted file mode 100644 index 7a9138b..0000000 --- a/resources/fontawesome/svgs/solid/flag-usa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/flag.svg b/resources/fontawesome/svgs/solid/flag.svg deleted file mode 100644 index cf64819..0000000 --- a/resources/fontawesome/svgs/solid/flag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/flask-vial.svg b/resources/fontawesome/svgs/solid/flask-vial.svg deleted file mode 100644 index 59caf12..0000000 --- a/resources/fontawesome/svgs/solid/flask-vial.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/flask.svg b/resources/fontawesome/svgs/solid/flask.svg deleted file mode 100644 index 56ee4f5..0000000 --- a/resources/fontawesome/svgs/solid/flask.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/floppy-disk.svg b/resources/fontawesome/svgs/solid/floppy-disk.svg deleted file mode 100644 index 1180269..0000000 --- a/resources/fontawesome/svgs/solid/floppy-disk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/florin-sign.svg b/resources/fontawesome/svgs/solid/florin-sign.svg deleted file mode 100644 index 0dafa6c..0000000 --- a/resources/fontawesome/svgs/solid/florin-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/folder-closed.svg b/resources/fontawesome/svgs/solid/folder-closed.svg deleted file mode 100644 index 80334e5..0000000 --- a/resources/fontawesome/svgs/solid/folder-closed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/folder-minus.svg b/resources/fontawesome/svgs/solid/folder-minus.svg deleted file mode 100644 index 473d68b..0000000 --- a/resources/fontawesome/svgs/solid/folder-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/folder-open.svg b/resources/fontawesome/svgs/solid/folder-open.svg deleted file mode 100644 index 9378edf..0000000 --- a/resources/fontawesome/svgs/solid/folder-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/folder-plus.svg b/resources/fontawesome/svgs/solid/folder-plus.svg deleted file mode 100644 index a0fd601..0000000 --- a/resources/fontawesome/svgs/solid/folder-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/folder-tree.svg b/resources/fontawesome/svgs/solid/folder-tree.svg deleted file mode 100644 index 9d0ee42..0000000 --- a/resources/fontawesome/svgs/solid/folder-tree.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/folder.svg b/resources/fontawesome/svgs/solid/folder.svg deleted file mode 100644 index 64cb6fb..0000000 --- a/resources/fontawesome/svgs/solid/folder.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/font-awesome.svg b/resources/fontawesome/svgs/solid/font-awesome.svg deleted file mode 100644 index 13f10eb..0000000 --- a/resources/fontawesome/svgs/solid/font-awesome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/font.svg b/resources/fontawesome/svgs/solid/font.svg deleted file mode 100644 index a2197d2..0000000 --- a/resources/fontawesome/svgs/solid/font.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/football.svg b/resources/fontawesome/svgs/solid/football.svg deleted file mode 100644 index 769425a..0000000 --- a/resources/fontawesome/svgs/solid/football.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/forward-fast.svg b/resources/fontawesome/svgs/solid/forward-fast.svg deleted file mode 100644 index 65289a5..0000000 --- a/resources/fontawesome/svgs/solid/forward-fast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/forward-step.svg b/resources/fontawesome/svgs/solid/forward-step.svg deleted file mode 100644 index e19c950..0000000 --- a/resources/fontawesome/svgs/solid/forward-step.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/forward.svg b/resources/fontawesome/svgs/solid/forward.svg deleted file mode 100644 index 53d1a67..0000000 --- a/resources/fontawesome/svgs/solid/forward.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/franc-sign.svg b/resources/fontawesome/svgs/solid/franc-sign.svg deleted file mode 100644 index d9f6c79..0000000 --- a/resources/fontawesome/svgs/solid/franc-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/frog.svg b/resources/fontawesome/svgs/solid/frog.svg deleted file mode 100644 index bde49c6..0000000 --- a/resources/fontawesome/svgs/solid/frog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/futbol.svg b/resources/fontawesome/svgs/solid/futbol.svg deleted file mode 100644 index 0273ec6..0000000 --- a/resources/fontawesome/svgs/solid/futbol.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/g.svg b/resources/fontawesome/svgs/solid/g.svg deleted file mode 100644 index 9b35a04..0000000 --- a/resources/fontawesome/svgs/solid/g.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gamepad.svg b/resources/fontawesome/svgs/solid/gamepad.svg deleted file mode 100644 index 5b0f53c..0000000 --- a/resources/fontawesome/svgs/solid/gamepad.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gas-pump.svg b/resources/fontawesome/svgs/solid/gas-pump.svg deleted file mode 100644 index 8845133..0000000 --- a/resources/fontawesome/svgs/solid/gas-pump.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gauge-high.svg b/resources/fontawesome/svgs/solid/gauge-high.svg deleted file mode 100644 index a571987..0000000 --- a/resources/fontawesome/svgs/solid/gauge-high.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gauge-simple-high.svg b/resources/fontawesome/svgs/solid/gauge-simple-high.svg deleted file mode 100644 index 81a1a44..0000000 --- a/resources/fontawesome/svgs/solid/gauge-simple-high.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gauge-simple.svg b/resources/fontawesome/svgs/solid/gauge-simple.svg deleted file mode 100644 index 0f916ad..0000000 --- a/resources/fontawesome/svgs/solid/gauge-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gauge.svg b/resources/fontawesome/svgs/solid/gauge.svg deleted file mode 100644 index ca51375..0000000 --- a/resources/fontawesome/svgs/solid/gauge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gavel.svg b/resources/fontawesome/svgs/solid/gavel.svg deleted file mode 100644 index 81bd5fa..0000000 --- a/resources/fontawesome/svgs/solid/gavel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gear.svg b/resources/fontawesome/svgs/solid/gear.svg deleted file mode 100644 index dbdef76..0000000 --- a/resources/fontawesome/svgs/solid/gear.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gears.svg b/resources/fontawesome/svgs/solid/gears.svg deleted file mode 100644 index fd2a070..0000000 --- a/resources/fontawesome/svgs/solid/gears.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gem.svg b/resources/fontawesome/svgs/solid/gem.svg deleted file mode 100644 index 097cdf2..0000000 --- a/resources/fontawesome/svgs/solid/gem.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/genderless.svg b/resources/fontawesome/svgs/solid/genderless.svg deleted file mode 100644 index 4a333bb..0000000 --- a/resources/fontawesome/svgs/solid/genderless.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ghost.svg b/resources/fontawesome/svgs/solid/ghost.svg deleted file mode 100644 index 3709683..0000000 --- a/resources/fontawesome/svgs/solid/ghost.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gift.svg b/resources/fontawesome/svgs/solid/gift.svg deleted file mode 100644 index bcecb88..0000000 --- a/resources/fontawesome/svgs/solid/gift.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gifts.svg b/resources/fontawesome/svgs/solid/gifts.svg deleted file mode 100644 index f6a8ba2..0000000 --- a/resources/fontawesome/svgs/solid/gifts.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/glass-water-droplet.svg b/resources/fontawesome/svgs/solid/glass-water-droplet.svg deleted file mode 100644 index f323b59..0000000 --- a/resources/fontawesome/svgs/solid/glass-water-droplet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/glass-water.svg b/resources/fontawesome/svgs/solid/glass-water.svg deleted file mode 100644 index 1e95603..0000000 --- a/resources/fontawesome/svgs/solid/glass-water.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/glasses.svg b/resources/fontawesome/svgs/solid/glasses.svg deleted file mode 100644 index 9c98fb0..0000000 --- a/resources/fontawesome/svgs/solid/glasses.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/globe.svg b/resources/fontawesome/svgs/solid/globe.svg deleted file mode 100644 index a1a6863..0000000 --- a/resources/fontawesome/svgs/solid/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/golf-ball-tee.svg b/resources/fontawesome/svgs/solid/golf-ball-tee.svg deleted file mode 100644 index f4825ab..0000000 --- a/resources/fontawesome/svgs/solid/golf-ball-tee.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gopuram.svg b/resources/fontawesome/svgs/solid/gopuram.svg deleted file mode 100644 index 5ef07ed..0000000 --- a/resources/fontawesome/svgs/solid/gopuram.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/graduation-cap.svg b/resources/fontawesome/svgs/solid/graduation-cap.svg deleted file mode 100644 index ff4b22a..0000000 --- a/resources/fontawesome/svgs/solid/graduation-cap.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/greater-than-equal.svg b/resources/fontawesome/svgs/solid/greater-than-equal.svg deleted file mode 100644 index e34ac1c..0000000 --- a/resources/fontawesome/svgs/solid/greater-than-equal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/greater-than.svg b/resources/fontawesome/svgs/solid/greater-than.svg deleted file mode 100644 index 432ca4a..0000000 --- a/resources/fontawesome/svgs/solid/greater-than.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/grip-lines-vertical.svg b/resources/fontawesome/svgs/solid/grip-lines-vertical.svg deleted file mode 100644 index 8144d00..0000000 --- a/resources/fontawesome/svgs/solid/grip-lines-vertical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/grip-lines.svg b/resources/fontawesome/svgs/solid/grip-lines.svg deleted file mode 100644 index 3c08390..0000000 --- a/resources/fontawesome/svgs/solid/grip-lines.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/grip-vertical.svg b/resources/fontawesome/svgs/solid/grip-vertical.svg deleted file mode 100644 index e3c9ebd..0000000 --- a/resources/fontawesome/svgs/solid/grip-vertical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/grip.svg b/resources/fontawesome/svgs/solid/grip.svg deleted file mode 100644 index 5abd104..0000000 --- a/resources/fontawesome/svgs/solid/grip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/group-arrows-rotate.svg b/resources/fontawesome/svgs/solid/group-arrows-rotate.svg deleted file mode 100644 index 776abfa..0000000 --- a/resources/fontawesome/svgs/solid/group-arrows-rotate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/guarani-sign.svg b/resources/fontawesome/svgs/solid/guarani-sign.svg deleted file mode 100644 index 2558820..0000000 --- a/resources/fontawesome/svgs/solid/guarani-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/guitar.svg b/resources/fontawesome/svgs/solid/guitar.svg deleted file mode 100644 index 9a278b3..0000000 --- a/resources/fontawesome/svgs/solid/guitar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/gun.svg b/resources/fontawesome/svgs/solid/gun.svg deleted file mode 100644 index 6f3bc42..0000000 --- a/resources/fontawesome/svgs/solid/gun.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/h.svg b/resources/fontawesome/svgs/solid/h.svg deleted file mode 100644 index 76bef18..0000000 --- a/resources/fontawesome/svgs/solid/h.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hammer.svg b/resources/fontawesome/svgs/solid/hammer.svg deleted file mode 100644 index 3149bb0..0000000 --- a/resources/fontawesome/svgs/solid/hammer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hamsa.svg b/resources/fontawesome/svgs/solid/hamsa.svg deleted file mode 100644 index e88193b..0000000 --- a/resources/fontawesome/svgs/solid/hamsa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-back-fist.svg b/resources/fontawesome/svgs/solid/hand-back-fist.svg deleted file mode 100644 index d1633e5..0000000 --- a/resources/fontawesome/svgs/solid/hand-back-fist.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-dots.svg b/resources/fontawesome/svgs/solid/hand-dots.svg deleted file mode 100644 index e30ed34..0000000 --- a/resources/fontawesome/svgs/solid/hand-dots.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-fist.svg b/resources/fontawesome/svgs/solid/hand-fist.svg deleted file mode 100644 index d6ae22f..0000000 --- a/resources/fontawesome/svgs/solid/hand-fist.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-holding-dollar.svg b/resources/fontawesome/svgs/solid/hand-holding-dollar.svg deleted file mode 100644 index 74c14c4..0000000 --- a/resources/fontawesome/svgs/solid/hand-holding-dollar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-holding-droplet.svg b/resources/fontawesome/svgs/solid/hand-holding-droplet.svg deleted file mode 100644 index c3a2fef..0000000 --- a/resources/fontawesome/svgs/solid/hand-holding-droplet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-holding-hand.svg b/resources/fontawesome/svgs/solid/hand-holding-hand.svg deleted file mode 100644 index da81b0d..0000000 --- a/resources/fontawesome/svgs/solid/hand-holding-hand.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-holding-heart.svg b/resources/fontawesome/svgs/solid/hand-holding-heart.svg deleted file mode 100644 index 60edcd1..0000000 --- a/resources/fontawesome/svgs/solid/hand-holding-heart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-holding-medical.svg b/resources/fontawesome/svgs/solid/hand-holding-medical.svg deleted file mode 100644 index b475837..0000000 --- a/resources/fontawesome/svgs/solid/hand-holding-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-holding.svg b/resources/fontawesome/svgs/solid/hand-holding.svg deleted file mode 100644 index 343d6bc..0000000 --- a/resources/fontawesome/svgs/solid/hand-holding.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-lizard.svg b/resources/fontawesome/svgs/solid/hand-lizard.svg deleted file mode 100644 index 1609e96..0000000 --- a/resources/fontawesome/svgs/solid/hand-lizard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-middle-finger.svg b/resources/fontawesome/svgs/solid/hand-middle-finger.svg deleted file mode 100644 index 105cd49..0000000 --- a/resources/fontawesome/svgs/solid/hand-middle-finger.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-peace.svg b/resources/fontawesome/svgs/solid/hand-peace.svg deleted file mode 100644 index 2a45da1..0000000 --- a/resources/fontawesome/svgs/solid/hand-peace.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-point-down.svg b/resources/fontawesome/svgs/solid/hand-point-down.svg deleted file mode 100644 index b5941bf..0000000 --- a/resources/fontawesome/svgs/solid/hand-point-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-point-left.svg b/resources/fontawesome/svgs/solid/hand-point-left.svg deleted file mode 100644 index 7988522..0000000 --- a/resources/fontawesome/svgs/solid/hand-point-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-point-right.svg b/resources/fontawesome/svgs/solid/hand-point-right.svg deleted file mode 100644 index cc9236f..0000000 --- a/resources/fontawesome/svgs/solid/hand-point-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-point-up.svg b/resources/fontawesome/svgs/solid/hand-point-up.svg deleted file mode 100644 index 69054b5..0000000 --- a/resources/fontawesome/svgs/solid/hand-point-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-pointer.svg b/resources/fontawesome/svgs/solid/hand-pointer.svg deleted file mode 100644 index 110f20e..0000000 --- a/resources/fontawesome/svgs/solid/hand-pointer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-scissors.svg b/resources/fontawesome/svgs/solid/hand-scissors.svg deleted file mode 100644 index cc52c31..0000000 --- a/resources/fontawesome/svgs/solid/hand-scissors.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-sparkles.svg b/resources/fontawesome/svgs/solid/hand-sparkles.svg deleted file mode 100644 index 52a4f7c..0000000 --- a/resources/fontawesome/svgs/solid/hand-sparkles.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand-spock.svg b/resources/fontawesome/svgs/solid/hand-spock.svg deleted file mode 100644 index 35678f1..0000000 --- a/resources/fontawesome/svgs/solid/hand-spock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hand.svg b/resources/fontawesome/svgs/solid/hand.svg deleted file mode 100644 index 94fd6e4..0000000 --- a/resources/fontawesome/svgs/solid/hand.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/handcuffs.svg b/resources/fontawesome/svgs/solid/handcuffs.svg deleted file mode 100644 index ad5e311..0000000 --- a/resources/fontawesome/svgs/solid/handcuffs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands-asl-interpreting.svg b/resources/fontawesome/svgs/solid/hands-asl-interpreting.svg deleted file mode 100644 index 88f6a6f..0000000 --- a/resources/fontawesome/svgs/solid/hands-asl-interpreting.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands-bound.svg b/resources/fontawesome/svgs/solid/hands-bound.svg deleted file mode 100644 index 50cab79..0000000 --- a/resources/fontawesome/svgs/solid/hands-bound.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands-bubbles.svg b/resources/fontawesome/svgs/solid/hands-bubbles.svg deleted file mode 100644 index 793049e..0000000 --- a/resources/fontawesome/svgs/solid/hands-bubbles.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands-clapping.svg b/resources/fontawesome/svgs/solid/hands-clapping.svg deleted file mode 100644 index c695c41..0000000 --- a/resources/fontawesome/svgs/solid/hands-clapping.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands-holding-child.svg b/resources/fontawesome/svgs/solid/hands-holding-child.svg deleted file mode 100644 index 276151e..0000000 --- a/resources/fontawesome/svgs/solid/hands-holding-child.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands-holding-circle.svg b/resources/fontawesome/svgs/solid/hands-holding-circle.svg deleted file mode 100644 index c33c65b..0000000 --- a/resources/fontawesome/svgs/solid/hands-holding-circle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands-holding.svg b/resources/fontawesome/svgs/solid/hands-holding.svg deleted file mode 100644 index e12be5d..0000000 --- a/resources/fontawesome/svgs/solid/hands-holding.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands-praying.svg b/resources/fontawesome/svgs/solid/hands-praying.svg deleted file mode 100644 index 3936261..0000000 --- a/resources/fontawesome/svgs/solid/hands-praying.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hands.svg b/resources/fontawesome/svgs/solid/hands.svg deleted file mode 100644 index 682b6cd..0000000 --- a/resources/fontawesome/svgs/solid/hands.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/handshake-angle.svg b/resources/fontawesome/svgs/solid/handshake-angle.svg deleted file mode 100644 index d8b202d..0000000 --- a/resources/fontawesome/svgs/solid/handshake-angle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/handshake-simple-slash.svg b/resources/fontawesome/svgs/solid/handshake-simple-slash.svg deleted file mode 100644 index 1989191..0000000 --- a/resources/fontawesome/svgs/solid/handshake-simple-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/handshake-simple.svg b/resources/fontawesome/svgs/solid/handshake-simple.svg deleted file mode 100644 index dd945a0..0000000 --- a/resources/fontawesome/svgs/solid/handshake-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/handshake-slash.svg b/resources/fontawesome/svgs/solid/handshake-slash.svg deleted file mode 100644 index e10d2af..0000000 --- a/resources/fontawesome/svgs/solid/handshake-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/handshake.svg b/resources/fontawesome/svgs/solid/handshake.svg deleted file mode 100644 index 82f5b8b..0000000 --- a/resources/fontawesome/svgs/solid/handshake.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hanukiah.svg b/resources/fontawesome/svgs/solid/hanukiah.svg deleted file mode 100644 index 798e463..0000000 --- a/resources/fontawesome/svgs/solid/hanukiah.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hard-drive.svg b/resources/fontawesome/svgs/solid/hard-drive.svg deleted file mode 100644 index 67a2200..0000000 --- a/resources/fontawesome/svgs/solid/hard-drive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hashtag.svg b/resources/fontawesome/svgs/solid/hashtag.svg deleted file mode 100644 index 9a04eda..0000000 --- a/resources/fontawesome/svgs/solid/hashtag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hat-cowboy-side.svg b/resources/fontawesome/svgs/solid/hat-cowboy-side.svg deleted file mode 100644 index 8c6cb6a..0000000 --- a/resources/fontawesome/svgs/solid/hat-cowboy-side.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hat-cowboy.svg b/resources/fontawesome/svgs/solid/hat-cowboy.svg deleted file mode 100644 index 24b10c8..0000000 --- a/resources/fontawesome/svgs/solid/hat-cowboy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hat-wizard.svg b/resources/fontawesome/svgs/solid/hat-wizard.svg deleted file mode 100644 index adb3437..0000000 --- a/resources/fontawesome/svgs/solid/hat-wizard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/head-side-cough-slash.svg b/resources/fontawesome/svgs/solid/head-side-cough-slash.svg deleted file mode 100644 index e38c0bc..0000000 --- a/resources/fontawesome/svgs/solid/head-side-cough-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/head-side-cough.svg b/resources/fontawesome/svgs/solid/head-side-cough.svg deleted file mode 100644 index 24b50d3..0000000 --- a/resources/fontawesome/svgs/solid/head-side-cough.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/head-side-mask.svg b/resources/fontawesome/svgs/solid/head-side-mask.svg deleted file mode 100644 index b049e1a..0000000 --- a/resources/fontawesome/svgs/solid/head-side-mask.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/head-side-virus.svg b/resources/fontawesome/svgs/solid/head-side-virus.svg deleted file mode 100644 index 2d0f89c..0000000 --- a/resources/fontawesome/svgs/solid/head-side-virus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heading.svg b/resources/fontawesome/svgs/solid/heading.svg deleted file mode 100644 index f684766..0000000 --- a/resources/fontawesome/svgs/solid/heading.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/headphones-simple.svg b/resources/fontawesome/svgs/solid/headphones-simple.svg deleted file mode 100644 index 0a2f9b8..0000000 --- a/resources/fontawesome/svgs/solid/headphones-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/headphones.svg b/resources/fontawesome/svgs/solid/headphones.svg deleted file mode 100644 index 34c94ed..0000000 --- a/resources/fontawesome/svgs/solid/headphones.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/headset.svg b/resources/fontawesome/svgs/solid/headset.svg deleted file mode 100644 index 4174aab..0000000 --- a/resources/fontawesome/svgs/solid/headset.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart-circle-bolt.svg b/resources/fontawesome/svgs/solid/heart-circle-bolt.svg deleted file mode 100644 index 2d484e7..0000000 --- a/resources/fontawesome/svgs/solid/heart-circle-bolt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart-circle-check.svg b/resources/fontawesome/svgs/solid/heart-circle-check.svg deleted file mode 100644 index b98cceb..0000000 --- a/resources/fontawesome/svgs/solid/heart-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart-circle-exclamation.svg b/resources/fontawesome/svgs/solid/heart-circle-exclamation.svg deleted file mode 100644 index defbd4f..0000000 --- a/resources/fontawesome/svgs/solid/heart-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart-circle-minus.svg b/resources/fontawesome/svgs/solid/heart-circle-minus.svg deleted file mode 100644 index f3855f3..0000000 --- a/resources/fontawesome/svgs/solid/heart-circle-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart-circle-plus.svg b/resources/fontawesome/svgs/solid/heart-circle-plus.svg deleted file mode 100644 index d2f0a1b..0000000 --- a/resources/fontawesome/svgs/solid/heart-circle-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart-circle-xmark.svg b/resources/fontawesome/svgs/solid/heart-circle-xmark.svg deleted file mode 100644 index 6d540f1..0000000 --- a/resources/fontawesome/svgs/solid/heart-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart-crack.svg b/resources/fontawesome/svgs/solid/heart-crack.svg deleted file mode 100644 index faa6ae6..0000000 --- a/resources/fontawesome/svgs/solid/heart-crack.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart-pulse.svg b/resources/fontawesome/svgs/solid/heart-pulse.svg deleted file mode 100644 index d26b6e9..0000000 --- a/resources/fontawesome/svgs/solid/heart-pulse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/heart.svg b/resources/fontawesome/svgs/solid/heart.svg deleted file mode 100644 index 66be76d..0000000 --- a/resources/fontawesome/svgs/solid/heart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/helicopter-symbol.svg b/resources/fontawesome/svgs/solid/helicopter-symbol.svg deleted file mode 100644 index b3f9d07..0000000 --- a/resources/fontawesome/svgs/solid/helicopter-symbol.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/helicopter.svg b/resources/fontawesome/svgs/solid/helicopter.svg deleted file mode 100644 index e2caf3e..0000000 --- a/resources/fontawesome/svgs/solid/helicopter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/helmet-safety.svg b/resources/fontawesome/svgs/solid/helmet-safety.svg deleted file mode 100644 index 741d3c1..0000000 --- a/resources/fontawesome/svgs/solid/helmet-safety.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/helmet-un.svg b/resources/fontawesome/svgs/solid/helmet-un.svg deleted file mode 100644 index 2a71998..0000000 --- a/resources/fontawesome/svgs/solid/helmet-un.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/highlighter.svg b/resources/fontawesome/svgs/solid/highlighter.svg deleted file mode 100644 index 37a7f7e..0000000 --- a/resources/fontawesome/svgs/solid/highlighter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hill-avalanche.svg b/resources/fontawesome/svgs/solid/hill-avalanche.svg deleted file mode 100644 index 583691c..0000000 --- a/resources/fontawesome/svgs/solid/hill-avalanche.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hill-rockslide.svg b/resources/fontawesome/svgs/solid/hill-rockslide.svg deleted file mode 100644 index 06862a8..0000000 --- a/resources/fontawesome/svgs/solid/hill-rockslide.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hippo.svg b/resources/fontawesome/svgs/solid/hippo.svg deleted file mode 100644 index bb0e7b7..0000000 --- a/resources/fontawesome/svgs/solid/hippo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hockey-puck.svg b/resources/fontawesome/svgs/solid/hockey-puck.svg deleted file mode 100644 index 3b5fe29..0000000 --- a/resources/fontawesome/svgs/solid/hockey-puck.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/holly-berry.svg b/resources/fontawesome/svgs/solid/holly-berry.svg deleted file mode 100644 index 3bc6ca3..0000000 --- a/resources/fontawesome/svgs/solid/holly-berry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/horse-head.svg b/resources/fontawesome/svgs/solid/horse-head.svg deleted file mode 100644 index 9d97bdf..0000000 --- a/resources/fontawesome/svgs/solid/horse-head.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/horse.svg b/resources/fontawesome/svgs/solid/horse.svg deleted file mode 100644 index 065b9e5..0000000 --- a/resources/fontawesome/svgs/solid/horse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hospital-user.svg b/resources/fontawesome/svgs/solid/hospital-user.svg deleted file mode 100644 index e9e29f9..0000000 --- a/resources/fontawesome/svgs/solid/hospital-user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hospital.svg b/resources/fontawesome/svgs/solid/hospital.svg deleted file mode 100644 index d1cb794..0000000 --- a/resources/fontawesome/svgs/solid/hospital.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hot-tub-person.svg b/resources/fontawesome/svgs/solid/hot-tub-person.svg deleted file mode 100644 index aa14308..0000000 --- a/resources/fontawesome/svgs/solid/hot-tub-person.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hotdog.svg b/resources/fontawesome/svgs/solid/hotdog.svg deleted file mode 100644 index e483ba9..0000000 --- a/resources/fontawesome/svgs/solid/hotdog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hotel.svg b/resources/fontawesome/svgs/solid/hotel.svg deleted file mode 100644 index 235c2de..0000000 --- a/resources/fontawesome/svgs/solid/hotel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hourglass-end.svg b/resources/fontawesome/svgs/solid/hourglass-end.svg deleted file mode 100644 index 1712d62..0000000 --- a/resources/fontawesome/svgs/solid/hourglass-end.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hourglass-half.svg b/resources/fontawesome/svgs/solid/hourglass-half.svg deleted file mode 100644 index a0f8fb0..0000000 --- a/resources/fontawesome/svgs/solid/hourglass-half.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hourglass-start.svg b/resources/fontawesome/svgs/solid/hourglass-start.svg deleted file mode 100644 index 28fa24f..0000000 --- a/resources/fontawesome/svgs/solid/hourglass-start.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hourglass.svg b/resources/fontawesome/svgs/solid/hourglass.svg deleted file mode 100644 index ab09f44..0000000 --- a/resources/fontawesome/svgs/solid/hourglass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-chimney-crack.svg b/resources/fontawesome/svgs/solid/house-chimney-crack.svg deleted file mode 100644 index 03ef78f..0000000 --- a/resources/fontawesome/svgs/solid/house-chimney-crack.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-chimney-medical.svg b/resources/fontawesome/svgs/solid/house-chimney-medical.svg deleted file mode 100644 index ea35869..0000000 --- a/resources/fontawesome/svgs/solid/house-chimney-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-chimney-user.svg b/resources/fontawesome/svgs/solid/house-chimney-user.svg deleted file mode 100644 index 3c7ac4a..0000000 --- a/resources/fontawesome/svgs/solid/house-chimney-user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-chimney-window.svg b/resources/fontawesome/svgs/solid/house-chimney-window.svg deleted file mode 100644 index 3d98cc5..0000000 --- a/resources/fontawesome/svgs/solid/house-chimney-window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-chimney.svg b/resources/fontawesome/svgs/solid/house-chimney.svg deleted file mode 100644 index 283705c..0000000 --- a/resources/fontawesome/svgs/solid/house-chimney.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-circle-check.svg b/resources/fontawesome/svgs/solid/house-circle-check.svg deleted file mode 100644 index d7f24e2..0000000 --- a/resources/fontawesome/svgs/solid/house-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-circle-exclamation.svg b/resources/fontawesome/svgs/solid/house-circle-exclamation.svg deleted file mode 100644 index 48d82a7..0000000 --- a/resources/fontawesome/svgs/solid/house-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-circle-xmark.svg b/resources/fontawesome/svgs/solid/house-circle-xmark.svg deleted file mode 100644 index 4064862..0000000 --- a/resources/fontawesome/svgs/solid/house-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-crack.svg b/resources/fontawesome/svgs/solid/house-crack.svg deleted file mode 100644 index 12cc3b3..0000000 --- a/resources/fontawesome/svgs/solid/house-crack.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-fire.svg b/resources/fontawesome/svgs/solid/house-fire.svg deleted file mode 100644 index c787945..0000000 --- a/resources/fontawesome/svgs/solid/house-fire.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-flag.svg b/resources/fontawesome/svgs/solid/house-flag.svg deleted file mode 100644 index 8940f11..0000000 --- a/resources/fontawesome/svgs/solid/house-flag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-flood-water-circle-arrow-right.svg b/resources/fontawesome/svgs/solid/house-flood-water-circle-arrow-right.svg deleted file mode 100644 index 00b658a..0000000 --- a/resources/fontawesome/svgs/solid/house-flood-water-circle-arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-flood-water.svg b/resources/fontawesome/svgs/solid/house-flood-water.svg deleted file mode 100644 index 5f9ee00..0000000 --- a/resources/fontawesome/svgs/solid/house-flood-water.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-laptop.svg b/resources/fontawesome/svgs/solid/house-laptop.svg deleted file mode 100644 index 1a6c859..0000000 --- a/resources/fontawesome/svgs/solid/house-laptop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-lock.svg b/resources/fontawesome/svgs/solid/house-lock.svg deleted file mode 100644 index 25f758a..0000000 --- a/resources/fontawesome/svgs/solid/house-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-medical-circle-check.svg b/resources/fontawesome/svgs/solid/house-medical-circle-check.svg deleted file mode 100644 index 1379c9e..0000000 --- a/resources/fontawesome/svgs/solid/house-medical-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-medical-circle-exclamation.svg b/resources/fontawesome/svgs/solid/house-medical-circle-exclamation.svg deleted file mode 100644 index 4b6d7eb..0000000 --- a/resources/fontawesome/svgs/solid/house-medical-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-medical-circle-xmark.svg b/resources/fontawesome/svgs/solid/house-medical-circle-xmark.svg deleted file mode 100644 index e56d1db..0000000 --- a/resources/fontawesome/svgs/solid/house-medical-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-medical-flag.svg b/resources/fontawesome/svgs/solid/house-medical-flag.svg deleted file mode 100644 index 6ab0a54..0000000 --- a/resources/fontawesome/svgs/solid/house-medical-flag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-medical.svg b/resources/fontawesome/svgs/solid/house-medical.svg deleted file mode 100644 index bb39f74..0000000 --- a/resources/fontawesome/svgs/solid/house-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-signal.svg b/resources/fontawesome/svgs/solid/house-signal.svg deleted file mode 100644 index ea20512..0000000 --- a/resources/fontawesome/svgs/solid/house-signal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-tsunami.svg b/resources/fontawesome/svgs/solid/house-tsunami.svg deleted file mode 100644 index 46d4666..0000000 --- a/resources/fontawesome/svgs/solid/house-tsunami.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house-user.svg b/resources/fontawesome/svgs/solid/house-user.svg deleted file mode 100644 index 3661257..0000000 --- a/resources/fontawesome/svgs/solid/house-user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/house.svg b/resources/fontawesome/svgs/solid/house.svg deleted file mode 100644 index a180cb4..0000000 --- a/resources/fontawesome/svgs/solid/house.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hryvnia-sign.svg b/resources/fontawesome/svgs/solid/hryvnia-sign.svg deleted file mode 100644 index 4f6175b..0000000 --- a/resources/fontawesome/svgs/solid/hryvnia-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/hurricane.svg b/resources/fontawesome/svgs/solid/hurricane.svg deleted file mode 100644 index daa0e7d..0000000 --- a/resources/fontawesome/svgs/solid/hurricane.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/i-cursor.svg b/resources/fontawesome/svgs/solid/i-cursor.svg deleted file mode 100644 index b03b8bd..0000000 --- a/resources/fontawesome/svgs/solid/i-cursor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/i.svg b/resources/fontawesome/svgs/solid/i.svg deleted file mode 100644 index 821239c..0000000 --- a/resources/fontawesome/svgs/solid/i.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ice-cream.svg b/resources/fontawesome/svgs/solid/ice-cream.svg deleted file mode 100644 index ead8fbb..0000000 --- a/resources/fontawesome/svgs/solid/ice-cream.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/icicles.svg b/resources/fontawesome/svgs/solid/icicles.svg deleted file mode 100644 index 7e4eda2..0000000 --- a/resources/fontawesome/svgs/solid/icicles.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/icons.svg b/resources/fontawesome/svgs/solid/icons.svg deleted file mode 100644 index 2a333b4..0000000 --- a/resources/fontawesome/svgs/solid/icons.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/id-badge.svg b/resources/fontawesome/svgs/solid/id-badge.svg deleted file mode 100644 index 198e06e..0000000 --- a/resources/fontawesome/svgs/solid/id-badge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/id-card-clip.svg b/resources/fontawesome/svgs/solid/id-card-clip.svg deleted file mode 100644 index b7dabb3..0000000 --- a/resources/fontawesome/svgs/solid/id-card-clip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/id-card.svg b/resources/fontawesome/svgs/solid/id-card.svg deleted file mode 100644 index 1df5174..0000000 --- a/resources/fontawesome/svgs/solid/id-card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/igloo.svg b/resources/fontawesome/svgs/solid/igloo.svg deleted file mode 100644 index d207851..0000000 --- a/resources/fontawesome/svgs/solid/igloo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/image-portrait.svg b/resources/fontawesome/svgs/solid/image-portrait.svg deleted file mode 100644 index 995a400..0000000 --- a/resources/fontawesome/svgs/solid/image-portrait.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/image.svg b/resources/fontawesome/svgs/solid/image.svg deleted file mode 100644 index 6c84f25..0000000 --- a/resources/fontawesome/svgs/solid/image.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/images.svg b/resources/fontawesome/svgs/solid/images.svg deleted file mode 100644 index 34d4117..0000000 --- a/resources/fontawesome/svgs/solid/images.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/inbox.svg b/resources/fontawesome/svgs/solid/inbox.svg deleted file mode 100644 index b713ab6..0000000 --- a/resources/fontawesome/svgs/solid/inbox.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/indent.svg b/resources/fontawesome/svgs/solid/indent.svg deleted file mode 100644 index 57a0cb3..0000000 --- a/resources/fontawesome/svgs/solid/indent.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/indian-rupee-sign.svg b/resources/fontawesome/svgs/solid/indian-rupee-sign.svg deleted file mode 100644 index d9795f3..0000000 --- a/resources/fontawesome/svgs/solid/indian-rupee-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/industry.svg b/resources/fontawesome/svgs/solid/industry.svg deleted file mode 100644 index aa3465a..0000000 --- a/resources/fontawesome/svgs/solid/industry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/infinity.svg b/resources/fontawesome/svgs/solid/infinity.svg deleted file mode 100644 index bd408e6..0000000 --- a/resources/fontawesome/svgs/solid/infinity.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/info.svg b/resources/fontawesome/svgs/solid/info.svg deleted file mode 100644 index 1c9b64b..0000000 --- a/resources/fontawesome/svgs/solid/info.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/italic.svg b/resources/fontawesome/svgs/solid/italic.svg deleted file mode 100644 index 62b6522..0000000 --- a/resources/fontawesome/svgs/solid/italic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/j.svg b/resources/fontawesome/svgs/solid/j.svg deleted file mode 100644 index 22d2a8c..0000000 --- a/resources/fontawesome/svgs/solid/j.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/jar-wheat.svg b/resources/fontawesome/svgs/solid/jar-wheat.svg deleted file mode 100644 index ddf0ca0..0000000 --- a/resources/fontawesome/svgs/solid/jar-wheat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/jar.svg b/resources/fontawesome/svgs/solid/jar.svg deleted file mode 100644 index b9c9414..0000000 --- a/resources/fontawesome/svgs/solid/jar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/jedi.svg b/resources/fontawesome/svgs/solid/jedi.svg deleted file mode 100644 index 303ea0f..0000000 --- a/resources/fontawesome/svgs/solid/jedi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/jet-fighter-up.svg b/resources/fontawesome/svgs/solid/jet-fighter-up.svg deleted file mode 100644 index d33de13..0000000 --- a/resources/fontawesome/svgs/solid/jet-fighter-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/jet-fighter.svg b/resources/fontawesome/svgs/solid/jet-fighter.svg deleted file mode 100644 index caa3ce8..0000000 --- a/resources/fontawesome/svgs/solid/jet-fighter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/joint.svg b/resources/fontawesome/svgs/solid/joint.svg deleted file mode 100644 index 191e510..0000000 --- a/resources/fontawesome/svgs/solid/joint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/jug-detergent.svg b/resources/fontawesome/svgs/solid/jug-detergent.svg deleted file mode 100644 index 06b5826..0000000 --- a/resources/fontawesome/svgs/solid/jug-detergent.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/k.svg b/resources/fontawesome/svgs/solid/k.svg deleted file mode 100644 index bacc71b..0000000 --- a/resources/fontawesome/svgs/solid/k.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/kaaba.svg b/resources/fontawesome/svgs/solid/kaaba.svg deleted file mode 100644 index aed6628..0000000 --- a/resources/fontawesome/svgs/solid/kaaba.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/key.svg b/resources/fontawesome/svgs/solid/key.svg deleted file mode 100644 index 62fd10a..0000000 --- a/resources/fontawesome/svgs/solid/key.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/keyboard.svg b/resources/fontawesome/svgs/solid/keyboard.svg deleted file mode 100644 index 0747d3c..0000000 --- a/resources/fontawesome/svgs/solid/keyboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/khanda.svg b/resources/fontawesome/svgs/solid/khanda.svg deleted file mode 100644 index f51e751..0000000 --- a/resources/fontawesome/svgs/solid/khanda.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/kip-sign.svg b/resources/fontawesome/svgs/solid/kip-sign.svg deleted file mode 100644 index a52b01d..0000000 --- a/resources/fontawesome/svgs/solid/kip-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/kit-medical.svg b/resources/fontawesome/svgs/solid/kit-medical.svg deleted file mode 100644 index db14ec5..0000000 --- a/resources/fontawesome/svgs/solid/kit-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/kitchen-set.svg b/resources/fontawesome/svgs/solid/kitchen-set.svg deleted file mode 100644 index 3de26ee..0000000 --- a/resources/fontawesome/svgs/solid/kitchen-set.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/kiwi-bird.svg b/resources/fontawesome/svgs/solid/kiwi-bird.svg deleted file mode 100644 index 1311e32..0000000 --- a/resources/fontawesome/svgs/solid/kiwi-bird.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/l.svg b/resources/fontawesome/svgs/solid/l.svg deleted file mode 100644 index f93a506..0000000 --- a/resources/fontawesome/svgs/solid/l.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/land-mine-on.svg b/resources/fontawesome/svgs/solid/land-mine-on.svg deleted file mode 100644 index 8278ed8..0000000 --- a/resources/fontawesome/svgs/solid/land-mine-on.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/landmark-dome.svg b/resources/fontawesome/svgs/solid/landmark-dome.svg deleted file mode 100644 index 6b7e407..0000000 --- a/resources/fontawesome/svgs/solid/landmark-dome.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/landmark-flag.svg b/resources/fontawesome/svgs/solid/landmark-flag.svg deleted file mode 100644 index 10497a4..0000000 --- a/resources/fontawesome/svgs/solid/landmark-flag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/landmark.svg b/resources/fontawesome/svgs/solid/landmark.svg deleted file mode 100644 index 2099be4..0000000 --- a/resources/fontawesome/svgs/solid/landmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/language.svg b/resources/fontawesome/svgs/solid/language.svg deleted file mode 100644 index cf9af4d..0000000 --- a/resources/fontawesome/svgs/solid/language.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/laptop-code.svg b/resources/fontawesome/svgs/solid/laptop-code.svg deleted file mode 100644 index 862ad7e..0000000 --- a/resources/fontawesome/svgs/solid/laptop-code.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/laptop-file.svg b/resources/fontawesome/svgs/solid/laptop-file.svg deleted file mode 100644 index 9bad1d8..0000000 --- a/resources/fontawesome/svgs/solid/laptop-file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/laptop-medical.svg b/resources/fontawesome/svgs/solid/laptop-medical.svg deleted file mode 100644 index 67d63ec..0000000 --- a/resources/fontawesome/svgs/solid/laptop-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/laptop.svg b/resources/fontawesome/svgs/solid/laptop.svg deleted file mode 100644 index 42aac45..0000000 --- a/resources/fontawesome/svgs/solid/laptop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lari-sign.svg b/resources/fontawesome/svgs/solid/lari-sign.svg deleted file mode 100644 index 3277e32..0000000 --- a/resources/fontawesome/svgs/solid/lari-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/layer-group.svg b/resources/fontawesome/svgs/solid/layer-group.svg deleted file mode 100644 index 3c7138b..0000000 --- a/resources/fontawesome/svgs/solid/layer-group.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/leaf.svg b/resources/fontawesome/svgs/solid/leaf.svg deleted file mode 100644 index 80f9dd9..0000000 --- a/resources/fontawesome/svgs/solid/leaf.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/left-long.svg b/resources/fontawesome/svgs/solid/left-long.svg deleted file mode 100644 index a6c5043..0000000 --- a/resources/fontawesome/svgs/solid/left-long.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/left-right.svg b/resources/fontawesome/svgs/solid/left-right.svg deleted file mode 100644 index 638b484..0000000 --- a/resources/fontawesome/svgs/solid/left-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lemon.svg b/resources/fontawesome/svgs/solid/lemon.svg deleted file mode 100644 index d7708ed..0000000 --- a/resources/fontawesome/svgs/solid/lemon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/less-than-equal.svg b/resources/fontawesome/svgs/solid/less-than-equal.svg deleted file mode 100644 index f4c5148..0000000 --- a/resources/fontawesome/svgs/solid/less-than-equal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/less-than.svg b/resources/fontawesome/svgs/solid/less-than.svg deleted file mode 100644 index 767bebd..0000000 --- a/resources/fontawesome/svgs/solid/less-than.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/life-ring.svg b/resources/fontawesome/svgs/solid/life-ring.svg deleted file mode 100644 index a5132b3..0000000 --- a/resources/fontawesome/svgs/solid/life-ring.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lightbulb.svg b/resources/fontawesome/svgs/solid/lightbulb.svg deleted file mode 100644 index 0aef257..0000000 --- a/resources/fontawesome/svgs/solid/lightbulb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lines-leaning.svg b/resources/fontawesome/svgs/solid/lines-leaning.svg deleted file mode 100644 index d1912d1..0000000 --- a/resources/fontawesome/svgs/solid/lines-leaning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/link-slash.svg b/resources/fontawesome/svgs/solid/link-slash.svg deleted file mode 100644 index 61c1c3e..0000000 --- a/resources/fontawesome/svgs/solid/link-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/link.svg b/resources/fontawesome/svgs/solid/link.svg deleted file mode 100644 index a25ba92..0000000 --- a/resources/fontawesome/svgs/solid/link.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lira-sign.svg b/resources/fontawesome/svgs/solid/lira-sign.svg deleted file mode 100644 index aceea7c..0000000 --- a/resources/fontawesome/svgs/solid/lira-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/list-check.svg b/resources/fontawesome/svgs/solid/list-check.svg deleted file mode 100644 index afad4b4..0000000 --- a/resources/fontawesome/svgs/solid/list-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/list-ol.svg b/resources/fontawesome/svgs/solid/list-ol.svg deleted file mode 100644 index 93616e6..0000000 --- a/resources/fontawesome/svgs/solid/list-ol.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/list-ul.svg b/resources/fontawesome/svgs/solid/list-ul.svg deleted file mode 100644 index 6119e2b..0000000 --- a/resources/fontawesome/svgs/solid/list-ul.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/list.svg b/resources/fontawesome/svgs/solid/list.svg deleted file mode 100644 index cb92026..0000000 --- a/resources/fontawesome/svgs/solid/list.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/litecoin-sign.svg b/resources/fontawesome/svgs/solid/litecoin-sign.svg deleted file mode 100644 index 220b312..0000000 --- a/resources/fontawesome/svgs/solid/litecoin-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/location-arrow.svg b/resources/fontawesome/svgs/solid/location-arrow.svg deleted file mode 100644 index 2126f4d..0000000 --- a/resources/fontawesome/svgs/solid/location-arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/location-crosshairs.svg b/resources/fontawesome/svgs/solid/location-crosshairs.svg deleted file mode 100644 index b130ea4..0000000 --- a/resources/fontawesome/svgs/solid/location-crosshairs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/location-dot.svg b/resources/fontawesome/svgs/solid/location-dot.svg deleted file mode 100644 index bf49c06..0000000 --- a/resources/fontawesome/svgs/solid/location-dot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/location-pin-lock.svg b/resources/fontawesome/svgs/solid/location-pin-lock.svg deleted file mode 100644 index 0a68114..0000000 --- a/resources/fontawesome/svgs/solid/location-pin-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/location-pin.svg b/resources/fontawesome/svgs/solid/location-pin.svg deleted file mode 100644 index 43a1cab..0000000 --- a/resources/fontawesome/svgs/solid/location-pin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lock-open.svg b/resources/fontawesome/svgs/solid/lock-open.svg deleted file mode 100644 index 82f6b1a..0000000 --- a/resources/fontawesome/svgs/solid/lock-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lock.svg b/resources/fontawesome/svgs/solid/lock.svg deleted file mode 100644 index a094fad..0000000 --- a/resources/fontawesome/svgs/solid/lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/locust.svg b/resources/fontawesome/svgs/solid/locust.svg deleted file mode 100644 index 5039cac..0000000 --- a/resources/fontawesome/svgs/solid/locust.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lungs-virus.svg b/resources/fontawesome/svgs/solid/lungs-virus.svg deleted file mode 100644 index 4c76401..0000000 --- a/resources/fontawesome/svgs/solid/lungs-virus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/lungs.svg b/resources/fontawesome/svgs/solid/lungs.svg deleted file mode 100644 index 308aa9a..0000000 --- a/resources/fontawesome/svgs/solid/lungs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/m.svg b/resources/fontawesome/svgs/solid/m.svg deleted file mode 100644 index c1e5d11..0000000 --- a/resources/fontawesome/svgs/solid/m.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/magnet.svg b/resources/fontawesome/svgs/solid/magnet.svg deleted file mode 100644 index fbc81cc..0000000 --- a/resources/fontawesome/svgs/solid/magnet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/magnifying-glass-arrow-right.svg b/resources/fontawesome/svgs/solid/magnifying-glass-arrow-right.svg deleted file mode 100644 index da7dcdd..0000000 --- a/resources/fontawesome/svgs/solid/magnifying-glass-arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/magnifying-glass-chart.svg b/resources/fontawesome/svgs/solid/magnifying-glass-chart.svg deleted file mode 100644 index 3624471..0000000 --- a/resources/fontawesome/svgs/solid/magnifying-glass-chart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/magnifying-glass-dollar.svg b/resources/fontawesome/svgs/solid/magnifying-glass-dollar.svg deleted file mode 100644 index 0d7c473..0000000 --- a/resources/fontawesome/svgs/solid/magnifying-glass-dollar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/magnifying-glass-location.svg b/resources/fontawesome/svgs/solid/magnifying-glass-location.svg deleted file mode 100644 index 9e61bb1..0000000 --- a/resources/fontawesome/svgs/solid/magnifying-glass-location.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/magnifying-glass-minus.svg b/resources/fontawesome/svgs/solid/magnifying-glass-minus.svg deleted file mode 100644 index d526287..0000000 --- a/resources/fontawesome/svgs/solid/magnifying-glass-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/magnifying-glass-plus.svg b/resources/fontawesome/svgs/solid/magnifying-glass-plus.svg deleted file mode 100644 index dc04510..0000000 --- a/resources/fontawesome/svgs/solid/magnifying-glass-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/magnifying-glass.svg b/resources/fontawesome/svgs/solid/magnifying-glass.svg deleted file mode 100644 index 36f6ad1..0000000 --- a/resources/fontawesome/svgs/solid/magnifying-glass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/manat-sign.svg b/resources/fontawesome/svgs/solid/manat-sign.svg deleted file mode 100644 index 821ba63..0000000 --- a/resources/fontawesome/svgs/solid/manat-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/map-location-dot.svg b/resources/fontawesome/svgs/solid/map-location-dot.svg deleted file mode 100644 index 85982da..0000000 --- a/resources/fontawesome/svgs/solid/map-location-dot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/map-location.svg b/resources/fontawesome/svgs/solid/map-location.svg deleted file mode 100644 index 856a1ea..0000000 --- a/resources/fontawesome/svgs/solid/map-location.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/map-pin.svg b/resources/fontawesome/svgs/solid/map-pin.svg deleted file mode 100644 index 4308db0..0000000 --- a/resources/fontawesome/svgs/solid/map-pin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/map.svg b/resources/fontawesome/svgs/solid/map.svg deleted file mode 100644 index b49f5cd..0000000 --- a/resources/fontawesome/svgs/solid/map.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/marker.svg b/resources/fontawesome/svgs/solid/marker.svg deleted file mode 100644 index 0e8293e..0000000 --- a/resources/fontawesome/svgs/solid/marker.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mars-and-venus-burst.svg b/resources/fontawesome/svgs/solid/mars-and-venus-burst.svg deleted file mode 100644 index b5b386d..0000000 --- a/resources/fontawesome/svgs/solid/mars-and-venus-burst.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mars-and-venus.svg b/resources/fontawesome/svgs/solid/mars-and-venus.svg deleted file mode 100644 index c8ecc22..0000000 --- a/resources/fontawesome/svgs/solid/mars-and-venus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mars-double.svg b/resources/fontawesome/svgs/solid/mars-double.svg deleted file mode 100644 index a1b3875..0000000 --- a/resources/fontawesome/svgs/solid/mars-double.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mars-stroke-right.svg b/resources/fontawesome/svgs/solid/mars-stroke-right.svg deleted file mode 100644 index 2274fe5..0000000 --- a/resources/fontawesome/svgs/solid/mars-stroke-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mars-stroke-up.svg b/resources/fontawesome/svgs/solid/mars-stroke-up.svg deleted file mode 100644 index 676dfbe..0000000 --- a/resources/fontawesome/svgs/solid/mars-stroke-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mars-stroke.svg b/resources/fontawesome/svgs/solid/mars-stroke.svg deleted file mode 100644 index 8913783..0000000 --- a/resources/fontawesome/svgs/solid/mars-stroke.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mars.svg b/resources/fontawesome/svgs/solid/mars.svg deleted file mode 100644 index 22f05a3..0000000 --- a/resources/fontawesome/svgs/solid/mars.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/martini-glass-citrus.svg b/resources/fontawesome/svgs/solid/martini-glass-citrus.svg deleted file mode 100644 index 80a4a2b..0000000 --- a/resources/fontawesome/svgs/solid/martini-glass-citrus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/martini-glass-empty.svg b/resources/fontawesome/svgs/solid/martini-glass-empty.svg deleted file mode 100644 index 1bc5c21..0000000 --- a/resources/fontawesome/svgs/solid/martini-glass-empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/martini-glass.svg b/resources/fontawesome/svgs/solid/martini-glass.svg deleted file mode 100644 index 1cd3f17..0000000 --- a/resources/fontawesome/svgs/solid/martini-glass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mask-face.svg b/resources/fontawesome/svgs/solid/mask-face.svg deleted file mode 100644 index 214294a..0000000 --- a/resources/fontawesome/svgs/solid/mask-face.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mask-ventilator.svg b/resources/fontawesome/svgs/solid/mask-ventilator.svg deleted file mode 100644 index 5060429..0000000 --- a/resources/fontawesome/svgs/solid/mask-ventilator.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mask.svg b/resources/fontawesome/svgs/solid/mask.svg deleted file mode 100644 index 98f0bdd..0000000 --- a/resources/fontawesome/svgs/solid/mask.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/masks-theater.svg b/resources/fontawesome/svgs/solid/masks-theater.svg deleted file mode 100644 index 69ce99d..0000000 --- a/resources/fontawesome/svgs/solid/masks-theater.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mattress-pillow.svg b/resources/fontawesome/svgs/solid/mattress-pillow.svg deleted file mode 100644 index 1eb549d..0000000 --- a/resources/fontawesome/svgs/solid/mattress-pillow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/maximize.svg b/resources/fontawesome/svgs/solid/maximize.svg deleted file mode 100644 index 584d56b..0000000 --- a/resources/fontawesome/svgs/solid/maximize.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/medal.svg b/resources/fontawesome/svgs/solid/medal.svg deleted file mode 100644 index eda5421..0000000 --- a/resources/fontawesome/svgs/solid/medal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/memory.svg b/resources/fontawesome/svgs/solid/memory.svg deleted file mode 100644 index 1ae4c01..0000000 --- a/resources/fontawesome/svgs/solid/memory.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/menorah.svg b/resources/fontawesome/svgs/solid/menorah.svg deleted file mode 100644 index f2a1a65..0000000 --- a/resources/fontawesome/svgs/solid/menorah.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mercury.svg b/resources/fontawesome/svgs/solid/mercury.svg deleted file mode 100644 index 4221438..0000000 --- a/resources/fontawesome/svgs/solid/mercury.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/message.svg b/resources/fontawesome/svgs/solid/message.svg deleted file mode 100644 index 3d8aee6..0000000 --- a/resources/fontawesome/svgs/solid/message.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/meteor.svg b/resources/fontawesome/svgs/solid/meteor.svg deleted file mode 100644 index 0f08a24..0000000 --- a/resources/fontawesome/svgs/solid/meteor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/microchip.svg b/resources/fontawesome/svgs/solid/microchip.svg deleted file mode 100644 index 11c4d62..0000000 --- a/resources/fontawesome/svgs/solid/microchip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/microphone-lines-slash.svg b/resources/fontawesome/svgs/solid/microphone-lines-slash.svg deleted file mode 100644 index ba101b9..0000000 --- a/resources/fontawesome/svgs/solid/microphone-lines-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/microphone-lines.svg b/resources/fontawesome/svgs/solid/microphone-lines.svg deleted file mode 100644 index 56a3f83..0000000 --- a/resources/fontawesome/svgs/solid/microphone-lines.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/microphone-slash.svg b/resources/fontawesome/svgs/solid/microphone-slash.svg deleted file mode 100644 index 5d91a15..0000000 --- a/resources/fontawesome/svgs/solid/microphone-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/microphone.svg b/resources/fontawesome/svgs/solid/microphone.svg deleted file mode 100644 index c99dbad..0000000 --- a/resources/fontawesome/svgs/solid/microphone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/microscope.svg b/resources/fontawesome/svgs/solid/microscope.svg deleted file mode 100644 index 690bed5..0000000 --- a/resources/fontawesome/svgs/solid/microscope.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mill-sign.svg b/resources/fontawesome/svgs/solid/mill-sign.svg deleted file mode 100644 index 2c52842..0000000 --- a/resources/fontawesome/svgs/solid/mill-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/minimize.svg b/resources/fontawesome/svgs/solid/minimize.svg deleted file mode 100644 index 7dea413..0000000 --- a/resources/fontawesome/svgs/solid/minimize.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/minus.svg b/resources/fontawesome/svgs/solid/minus.svg deleted file mode 100644 index c46b820..0000000 --- a/resources/fontawesome/svgs/solid/minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mitten.svg b/resources/fontawesome/svgs/solid/mitten.svg deleted file mode 100644 index 831d86e..0000000 --- a/resources/fontawesome/svgs/solid/mitten.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mobile-button.svg b/resources/fontawesome/svgs/solid/mobile-button.svg deleted file mode 100644 index 43bd566..0000000 --- a/resources/fontawesome/svgs/solid/mobile-button.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mobile-retro.svg b/resources/fontawesome/svgs/solid/mobile-retro.svg deleted file mode 100644 index 2219937..0000000 --- a/resources/fontawesome/svgs/solid/mobile-retro.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mobile-screen-button.svg b/resources/fontawesome/svgs/solid/mobile-screen-button.svg deleted file mode 100644 index 638f121..0000000 --- a/resources/fontawesome/svgs/solid/mobile-screen-button.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mobile-screen.svg b/resources/fontawesome/svgs/solid/mobile-screen.svg deleted file mode 100644 index 527dd33..0000000 --- a/resources/fontawesome/svgs/solid/mobile-screen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mobile.svg b/resources/fontawesome/svgs/solid/mobile.svg deleted file mode 100644 index 0422763..0000000 --- a/resources/fontawesome/svgs/solid/mobile.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-bill-1-wave.svg b/resources/fontawesome/svgs/solid/money-bill-1-wave.svg deleted file mode 100644 index 009e8ba..0000000 --- a/resources/fontawesome/svgs/solid/money-bill-1-wave.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-bill-1.svg b/resources/fontawesome/svgs/solid/money-bill-1.svg deleted file mode 100644 index ddda082..0000000 --- a/resources/fontawesome/svgs/solid/money-bill-1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-bill-transfer.svg b/resources/fontawesome/svgs/solid/money-bill-transfer.svg deleted file mode 100644 index ba9cd8d..0000000 --- a/resources/fontawesome/svgs/solid/money-bill-transfer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-bill-trend-up.svg b/resources/fontawesome/svgs/solid/money-bill-trend-up.svg deleted file mode 100644 index 15691af..0000000 --- a/resources/fontawesome/svgs/solid/money-bill-trend-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-bill-wave.svg b/resources/fontawesome/svgs/solid/money-bill-wave.svg deleted file mode 100644 index 573a387..0000000 --- a/resources/fontawesome/svgs/solid/money-bill-wave.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-bill-wheat.svg b/resources/fontawesome/svgs/solid/money-bill-wheat.svg deleted file mode 100644 index d1837d1..0000000 --- a/resources/fontawesome/svgs/solid/money-bill-wheat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-bill.svg b/resources/fontawesome/svgs/solid/money-bill.svg deleted file mode 100644 index 6ac0412..0000000 --- a/resources/fontawesome/svgs/solid/money-bill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-bills.svg b/resources/fontawesome/svgs/solid/money-bills.svg deleted file mode 100644 index fdeab04..0000000 --- a/resources/fontawesome/svgs/solid/money-bills.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-check-dollar.svg b/resources/fontawesome/svgs/solid/money-check-dollar.svg deleted file mode 100644 index 1c52929..0000000 --- a/resources/fontawesome/svgs/solid/money-check-dollar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/money-check.svg b/resources/fontawesome/svgs/solid/money-check.svg deleted file mode 100644 index 62edb6a..0000000 --- a/resources/fontawesome/svgs/solid/money-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/monument.svg b/resources/fontawesome/svgs/solid/monument.svg deleted file mode 100644 index bb67a46..0000000 --- a/resources/fontawesome/svgs/solid/monument.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/moon.svg b/resources/fontawesome/svgs/solid/moon.svg deleted file mode 100644 index 0de7b24..0000000 --- a/resources/fontawesome/svgs/solid/moon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mortar-pestle.svg b/resources/fontawesome/svgs/solid/mortar-pestle.svg deleted file mode 100644 index 7763e73..0000000 --- a/resources/fontawesome/svgs/solid/mortar-pestle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mosque.svg b/resources/fontawesome/svgs/solid/mosque.svg deleted file mode 100644 index e114275..0000000 --- a/resources/fontawesome/svgs/solid/mosque.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mosquito-net.svg b/resources/fontawesome/svgs/solid/mosquito-net.svg deleted file mode 100644 index e2b2d76..0000000 --- a/resources/fontawesome/svgs/solid/mosquito-net.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mosquito.svg b/resources/fontawesome/svgs/solid/mosquito.svg deleted file mode 100644 index f5e5e55..0000000 --- a/resources/fontawesome/svgs/solid/mosquito.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/motorcycle.svg b/resources/fontawesome/svgs/solid/motorcycle.svg deleted file mode 100644 index f653537..0000000 --- a/resources/fontawesome/svgs/solid/motorcycle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mound.svg b/resources/fontawesome/svgs/solid/mound.svg deleted file mode 100644 index 3362a93..0000000 --- a/resources/fontawesome/svgs/solid/mound.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mountain-city.svg b/resources/fontawesome/svgs/solid/mountain-city.svg deleted file mode 100644 index e3f160e..0000000 --- a/resources/fontawesome/svgs/solid/mountain-city.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mountain-sun.svg b/resources/fontawesome/svgs/solid/mountain-sun.svg deleted file mode 100644 index d6be8e3..0000000 --- a/resources/fontawesome/svgs/solid/mountain-sun.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mountain.svg b/resources/fontawesome/svgs/solid/mountain.svg deleted file mode 100644 index 41d24b7..0000000 --- a/resources/fontawesome/svgs/solid/mountain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mug-hot.svg b/resources/fontawesome/svgs/solid/mug-hot.svg deleted file mode 100644 index 572e58e..0000000 --- a/resources/fontawesome/svgs/solid/mug-hot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/mug-saucer.svg b/resources/fontawesome/svgs/solid/mug-saucer.svg deleted file mode 100644 index 287ae58..0000000 --- a/resources/fontawesome/svgs/solid/mug-saucer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/music.svg b/resources/fontawesome/svgs/solid/music.svg deleted file mode 100644 index a195b69..0000000 --- a/resources/fontawesome/svgs/solid/music.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/n.svg b/resources/fontawesome/svgs/solid/n.svg deleted file mode 100644 index fb48a82..0000000 --- a/resources/fontawesome/svgs/solid/n.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/naira-sign.svg b/resources/fontawesome/svgs/solid/naira-sign.svg deleted file mode 100644 index 2c0b035..0000000 --- a/resources/fontawesome/svgs/solid/naira-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/network-wired.svg b/resources/fontawesome/svgs/solid/network-wired.svg deleted file mode 100644 index b92bcf9..0000000 --- a/resources/fontawesome/svgs/solid/network-wired.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/neuter.svg b/resources/fontawesome/svgs/solid/neuter.svg deleted file mode 100644 index 086ca7b..0000000 --- a/resources/fontawesome/svgs/solid/neuter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/newspaper.svg b/resources/fontawesome/svgs/solid/newspaper.svg deleted file mode 100644 index c748291..0000000 --- a/resources/fontawesome/svgs/solid/newspaper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/not-equal.svg b/resources/fontawesome/svgs/solid/not-equal.svg deleted file mode 100644 index f37849a..0000000 --- a/resources/fontawesome/svgs/solid/not-equal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/notdef.svg b/resources/fontawesome/svgs/solid/notdef.svg deleted file mode 100644 index 5010639..0000000 --- a/resources/fontawesome/svgs/solid/notdef.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/note-sticky.svg b/resources/fontawesome/svgs/solid/note-sticky.svg deleted file mode 100644 index de3cd35..0000000 --- a/resources/fontawesome/svgs/solid/note-sticky.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/notes-medical.svg b/resources/fontawesome/svgs/solid/notes-medical.svg deleted file mode 100644 index ab51477..0000000 --- a/resources/fontawesome/svgs/solid/notes-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/o.svg b/resources/fontawesome/svgs/solid/o.svg deleted file mode 100644 index 7d84d4c..0000000 --- a/resources/fontawesome/svgs/solid/o.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/object-group.svg b/resources/fontawesome/svgs/solid/object-group.svg deleted file mode 100644 index 9a61dbd..0000000 --- a/resources/fontawesome/svgs/solid/object-group.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/object-ungroup.svg b/resources/fontawesome/svgs/solid/object-ungroup.svg deleted file mode 100644 index fe90753..0000000 --- a/resources/fontawesome/svgs/solid/object-ungroup.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/oil-can.svg b/resources/fontawesome/svgs/solid/oil-can.svg deleted file mode 100644 index 6289e38..0000000 --- a/resources/fontawesome/svgs/solid/oil-can.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/oil-well.svg b/resources/fontawesome/svgs/solid/oil-well.svg deleted file mode 100644 index d3ccb72..0000000 --- a/resources/fontawesome/svgs/solid/oil-well.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/om.svg b/resources/fontawesome/svgs/solid/om.svg deleted file mode 100644 index ee28726..0000000 --- a/resources/fontawesome/svgs/solid/om.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/otter.svg b/resources/fontawesome/svgs/solid/otter.svg deleted file mode 100644 index 1621697..0000000 --- a/resources/fontawesome/svgs/solid/otter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/outdent.svg b/resources/fontawesome/svgs/solid/outdent.svg deleted file mode 100644 index 5121dc6..0000000 --- a/resources/fontawesome/svgs/solid/outdent.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/p.svg b/resources/fontawesome/svgs/solid/p.svg deleted file mode 100644 index 1ff291a..0000000 --- a/resources/fontawesome/svgs/solid/p.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pager.svg b/resources/fontawesome/svgs/solid/pager.svg deleted file mode 100644 index 071ef79..0000000 --- a/resources/fontawesome/svgs/solid/pager.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/paint-roller.svg b/resources/fontawesome/svgs/solid/paint-roller.svg deleted file mode 100644 index f4e451e..0000000 --- a/resources/fontawesome/svgs/solid/paint-roller.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/paintbrush.svg b/resources/fontawesome/svgs/solid/paintbrush.svg deleted file mode 100644 index 8c9a00d..0000000 --- a/resources/fontawesome/svgs/solid/paintbrush.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/palette.svg b/resources/fontawesome/svgs/solid/palette.svg deleted file mode 100644 index 122e162..0000000 --- a/resources/fontawesome/svgs/solid/palette.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pallet.svg b/resources/fontawesome/svgs/solid/pallet.svg deleted file mode 100644 index 9e3b639..0000000 --- a/resources/fontawesome/svgs/solid/pallet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/panorama.svg b/resources/fontawesome/svgs/solid/panorama.svg deleted file mode 100644 index 6a5cb8b..0000000 --- a/resources/fontawesome/svgs/solid/panorama.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/paper-plane.svg b/resources/fontawesome/svgs/solid/paper-plane.svg deleted file mode 100644 index de86f47..0000000 --- a/resources/fontawesome/svgs/solid/paper-plane.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/paperclip.svg b/resources/fontawesome/svgs/solid/paperclip.svg deleted file mode 100644 index 0bfef5a..0000000 --- a/resources/fontawesome/svgs/solid/paperclip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/parachute-box.svg b/resources/fontawesome/svgs/solid/parachute-box.svg deleted file mode 100644 index 70f21f8..0000000 --- a/resources/fontawesome/svgs/solid/parachute-box.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/paragraph.svg b/resources/fontawesome/svgs/solid/paragraph.svg deleted file mode 100644 index e811afc..0000000 --- a/resources/fontawesome/svgs/solid/paragraph.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/passport.svg b/resources/fontawesome/svgs/solid/passport.svg deleted file mode 100644 index ce2811f..0000000 --- a/resources/fontawesome/svgs/solid/passport.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/paste.svg b/resources/fontawesome/svgs/solid/paste.svg deleted file mode 100644 index 707af72..0000000 --- a/resources/fontawesome/svgs/solid/paste.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pause.svg b/resources/fontawesome/svgs/solid/pause.svg deleted file mode 100644 index cc9a15c..0000000 --- a/resources/fontawesome/svgs/solid/pause.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/paw.svg b/resources/fontawesome/svgs/solid/paw.svg deleted file mode 100644 index 5a91302..0000000 --- a/resources/fontawesome/svgs/solid/paw.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/peace.svg b/resources/fontawesome/svgs/solid/peace.svg deleted file mode 100644 index 91044a5..0000000 --- a/resources/fontawesome/svgs/solid/peace.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pen-clip.svg b/resources/fontawesome/svgs/solid/pen-clip.svg deleted file mode 100644 index a4726ee..0000000 --- a/resources/fontawesome/svgs/solid/pen-clip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pen-fancy.svg b/resources/fontawesome/svgs/solid/pen-fancy.svg deleted file mode 100644 index b9e0127..0000000 --- a/resources/fontawesome/svgs/solid/pen-fancy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pen-nib.svg b/resources/fontawesome/svgs/solid/pen-nib.svg deleted file mode 100644 index 578037d..0000000 --- a/resources/fontawesome/svgs/solid/pen-nib.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pen-ruler.svg b/resources/fontawesome/svgs/solid/pen-ruler.svg deleted file mode 100644 index 29bfd0b..0000000 --- a/resources/fontawesome/svgs/solid/pen-ruler.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pen-to-square.svg b/resources/fontawesome/svgs/solid/pen-to-square.svg deleted file mode 100644 index 2b8eab8..0000000 --- a/resources/fontawesome/svgs/solid/pen-to-square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pen.svg b/resources/fontawesome/svgs/solid/pen.svg deleted file mode 100644 index 973c511..0000000 --- a/resources/fontawesome/svgs/solid/pen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pencil.svg b/resources/fontawesome/svgs/solid/pencil.svg deleted file mode 100644 index 0cbedc3..0000000 --- a/resources/fontawesome/svgs/solid/pencil.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/people-arrows.svg b/resources/fontawesome/svgs/solid/people-arrows.svg deleted file mode 100644 index 70dbcb0..0000000 --- a/resources/fontawesome/svgs/solid/people-arrows.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/people-carry-box.svg b/resources/fontawesome/svgs/solid/people-carry-box.svg deleted file mode 100644 index c027edf..0000000 --- a/resources/fontawesome/svgs/solid/people-carry-box.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/people-group.svg b/resources/fontawesome/svgs/solid/people-group.svg deleted file mode 100644 index b901288..0000000 --- a/resources/fontawesome/svgs/solid/people-group.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/people-line.svg b/resources/fontawesome/svgs/solid/people-line.svg deleted file mode 100644 index 8a651c0..0000000 --- a/resources/fontawesome/svgs/solid/people-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/people-pulling.svg b/resources/fontawesome/svgs/solid/people-pulling.svg deleted file mode 100644 index 959f7cd..0000000 --- a/resources/fontawesome/svgs/solid/people-pulling.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/people-robbery.svg b/resources/fontawesome/svgs/solid/people-robbery.svg deleted file mode 100644 index fbb3fcf..0000000 --- a/resources/fontawesome/svgs/solid/people-robbery.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/people-roof.svg b/resources/fontawesome/svgs/solid/people-roof.svg deleted file mode 100644 index 9cde503..0000000 --- a/resources/fontawesome/svgs/solid/people-roof.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pepper-hot.svg b/resources/fontawesome/svgs/solid/pepper-hot.svg deleted file mode 100644 index b122488..0000000 --- a/resources/fontawesome/svgs/solid/pepper-hot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/percent.svg b/resources/fontawesome/svgs/solid/percent.svg deleted file mode 100644 index 242e060..0000000 --- a/resources/fontawesome/svgs/solid/percent.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-arrow-down-to-line.svg b/resources/fontawesome/svgs/solid/person-arrow-down-to-line.svg deleted file mode 100644 index 54b8f0b..0000000 --- a/resources/fontawesome/svgs/solid/person-arrow-down-to-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-arrow-up-from-line.svg b/resources/fontawesome/svgs/solid/person-arrow-up-from-line.svg deleted file mode 100644 index d9e6451..0000000 --- a/resources/fontawesome/svgs/solid/person-arrow-up-from-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-biking.svg b/resources/fontawesome/svgs/solid/person-biking.svg deleted file mode 100644 index c4ceb9f..0000000 --- a/resources/fontawesome/svgs/solid/person-biking.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-booth.svg b/resources/fontawesome/svgs/solid/person-booth.svg deleted file mode 100644 index 9955f09..0000000 --- a/resources/fontawesome/svgs/solid/person-booth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-breastfeeding.svg b/resources/fontawesome/svgs/solid/person-breastfeeding.svg deleted file mode 100644 index 6bd1b36..0000000 --- a/resources/fontawesome/svgs/solid/person-breastfeeding.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-burst.svg b/resources/fontawesome/svgs/solid/person-burst.svg deleted file mode 100644 index 63eb342..0000000 --- a/resources/fontawesome/svgs/solid/person-burst.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-cane.svg b/resources/fontawesome/svgs/solid/person-cane.svg deleted file mode 100644 index 09344c4..0000000 --- a/resources/fontawesome/svgs/solid/person-cane.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-chalkboard.svg b/resources/fontawesome/svgs/solid/person-chalkboard.svg deleted file mode 100644 index 793e6fc..0000000 --- a/resources/fontawesome/svgs/solid/person-chalkboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-circle-check.svg b/resources/fontawesome/svgs/solid/person-circle-check.svg deleted file mode 100644 index 29f54fe..0000000 --- a/resources/fontawesome/svgs/solid/person-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-circle-exclamation.svg b/resources/fontawesome/svgs/solid/person-circle-exclamation.svg deleted file mode 100644 index a4dc41d..0000000 --- a/resources/fontawesome/svgs/solid/person-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-circle-minus.svg b/resources/fontawesome/svgs/solid/person-circle-minus.svg deleted file mode 100644 index bc99611..0000000 --- a/resources/fontawesome/svgs/solid/person-circle-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-circle-plus.svg b/resources/fontawesome/svgs/solid/person-circle-plus.svg deleted file mode 100644 index c4462be..0000000 --- a/resources/fontawesome/svgs/solid/person-circle-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-circle-question.svg b/resources/fontawesome/svgs/solid/person-circle-question.svg deleted file mode 100644 index 6d48945..0000000 --- a/resources/fontawesome/svgs/solid/person-circle-question.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-circle-xmark.svg b/resources/fontawesome/svgs/solid/person-circle-xmark.svg deleted file mode 100644 index 8581dad..0000000 --- a/resources/fontawesome/svgs/solid/person-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-digging.svg b/resources/fontawesome/svgs/solid/person-digging.svg deleted file mode 100644 index 26cf4b3..0000000 --- a/resources/fontawesome/svgs/solid/person-digging.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-dots-from-line.svg b/resources/fontawesome/svgs/solid/person-dots-from-line.svg deleted file mode 100644 index b08b0ec..0000000 --- a/resources/fontawesome/svgs/solid/person-dots-from-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-dress-burst.svg b/resources/fontawesome/svgs/solid/person-dress-burst.svg deleted file mode 100644 index 8d50c8a..0000000 --- a/resources/fontawesome/svgs/solid/person-dress-burst.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-dress.svg b/resources/fontawesome/svgs/solid/person-dress.svg deleted file mode 100644 index 2db5695..0000000 --- a/resources/fontawesome/svgs/solid/person-dress.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-drowning.svg b/resources/fontawesome/svgs/solid/person-drowning.svg deleted file mode 100644 index 2bc7448..0000000 --- a/resources/fontawesome/svgs/solid/person-drowning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-falling-burst.svg b/resources/fontawesome/svgs/solid/person-falling-burst.svg deleted file mode 100644 index efb289a..0000000 --- a/resources/fontawesome/svgs/solid/person-falling-burst.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-falling.svg b/resources/fontawesome/svgs/solid/person-falling.svg deleted file mode 100644 index fb971ab..0000000 --- a/resources/fontawesome/svgs/solid/person-falling.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-half-dress.svg b/resources/fontawesome/svgs/solid/person-half-dress.svg deleted file mode 100644 index c98591b..0000000 --- a/resources/fontawesome/svgs/solid/person-half-dress.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-harassing.svg b/resources/fontawesome/svgs/solid/person-harassing.svg deleted file mode 100644 index eee20fe..0000000 --- a/resources/fontawesome/svgs/solid/person-harassing.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-hiking.svg b/resources/fontawesome/svgs/solid/person-hiking.svg deleted file mode 100644 index 0883cdf..0000000 --- a/resources/fontawesome/svgs/solid/person-hiking.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-military-pointing.svg b/resources/fontawesome/svgs/solid/person-military-pointing.svg deleted file mode 100644 index 6f71aec..0000000 --- a/resources/fontawesome/svgs/solid/person-military-pointing.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-military-rifle.svg b/resources/fontawesome/svgs/solid/person-military-rifle.svg deleted file mode 100644 index ce4d0f7..0000000 --- a/resources/fontawesome/svgs/solid/person-military-rifle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-military-to-person.svg b/resources/fontawesome/svgs/solid/person-military-to-person.svg deleted file mode 100644 index 288b28d..0000000 --- a/resources/fontawesome/svgs/solid/person-military-to-person.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-praying.svg b/resources/fontawesome/svgs/solid/person-praying.svg deleted file mode 100644 index ea6734b..0000000 --- a/resources/fontawesome/svgs/solid/person-praying.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-pregnant.svg b/resources/fontawesome/svgs/solid/person-pregnant.svg deleted file mode 100644 index 5a4a498..0000000 --- a/resources/fontawesome/svgs/solid/person-pregnant.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-rays.svg b/resources/fontawesome/svgs/solid/person-rays.svg deleted file mode 100644 index e55a5d9..0000000 --- a/resources/fontawesome/svgs/solid/person-rays.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-rifle.svg b/resources/fontawesome/svgs/solid/person-rifle.svg deleted file mode 100644 index 8ffd67b..0000000 --- a/resources/fontawesome/svgs/solid/person-rifle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-running.svg b/resources/fontawesome/svgs/solid/person-running.svg deleted file mode 100644 index 9a14174..0000000 --- a/resources/fontawesome/svgs/solid/person-running.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-shelter.svg b/resources/fontawesome/svgs/solid/person-shelter.svg deleted file mode 100644 index 96ec9a4..0000000 --- a/resources/fontawesome/svgs/solid/person-shelter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-skating.svg b/resources/fontawesome/svgs/solid/person-skating.svg deleted file mode 100644 index e7f303c..0000000 --- a/resources/fontawesome/svgs/solid/person-skating.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-skiing-nordic.svg b/resources/fontawesome/svgs/solid/person-skiing-nordic.svg deleted file mode 100644 index e38a01e..0000000 --- a/resources/fontawesome/svgs/solid/person-skiing-nordic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-skiing.svg b/resources/fontawesome/svgs/solid/person-skiing.svg deleted file mode 100644 index 8a6801d..0000000 --- a/resources/fontawesome/svgs/solid/person-skiing.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-snowboarding.svg b/resources/fontawesome/svgs/solid/person-snowboarding.svg deleted file mode 100644 index 1cee06b..0000000 --- a/resources/fontawesome/svgs/solid/person-snowboarding.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-swimming.svg b/resources/fontawesome/svgs/solid/person-swimming.svg deleted file mode 100644 index 151d20c..0000000 --- a/resources/fontawesome/svgs/solid/person-swimming.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-through-window.svg b/resources/fontawesome/svgs/solid/person-through-window.svg deleted file mode 100644 index 7c17cb1..0000000 --- a/resources/fontawesome/svgs/solid/person-through-window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-walking-arrow-loop-left.svg b/resources/fontawesome/svgs/solid/person-walking-arrow-loop-left.svg deleted file mode 100644 index 4d34f73..0000000 --- a/resources/fontawesome/svgs/solid/person-walking-arrow-loop-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-walking-arrow-right.svg b/resources/fontawesome/svgs/solid/person-walking-arrow-right.svg deleted file mode 100644 index 62eac72..0000000 --- a/resources/fontawesome/svgs/solid/person-walking-arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-walking-dashed-line-arrow-right.svg b/resources/fontawesome/svgs/solid/person-walking-dashed-line-arrow-right.svg deleted file mode 100644 index 4ff5af1..0000000 --- a/resources/fontawesome/svgs/solid/person-walking-dashed-line-arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-walking-luggage.svg b/resources/fontawesome/svgs/solid/person-walking-luggage.svg deleted file mode 100644 index f729a8f..0000000 --- a/resources/fontawesome/svgs/solid/person-walking-luggage.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-walking-with-cane.svg b/resources/fontawesome/svgs/solid/person-walking-with-cane.svg deleted file mode 100644 index f480212..0000000 --- a/resources/fontawesome/svgs/solid/person-walking-with-cane.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person-walking.svg b/resources/fontawesome/svgs/solid/person-walking.svg deleted file mode 100644 index 0c854c9..0000000 --- a/resources/fontawesome/svgs/solid/person-walking.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/person.svg b/resources/fontawesome/svgs/solid/person.svg deleted file mode 100644 index 1355260..0000000 --- a/resources/fontawesome/svgs/solid/person.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/peseta-sign.svg b/resources/fontawesome/svgs/solid/peseta-sign.svg deleted file mode 100644 index ec3e9a1..0000000 --- a/resources/fontawesome/svgs/solid/peseta-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/peso-sign.svg b/resources/fontawesome/svgs/solid/peso-sign.svg deleted file mode 100644 index 0b29a45..0000000 --- a/resources/fontawesome/svgs/solid/peso-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/phone-flip.svg b/resources/fontawesome/svgs/solid/phone-flip.svg deleted file mode 100644 index 0f108fe..0000000 --- a/resources/fontawesome/svgs/solid/phone-flip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/phone-slash.svg b/resources/fontawesome/svgs/solid/phone-slash.svg deleted file mode 100644 index 1f283ee..0000000 --- a/resources/fontawesome/svgs/solid/phone-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/phone-volume.svg b/resources/fontawesome/svgs/solid/phone-volume.svg deleted file mode 100644 index eb3d314..0000000 --- a/resources/fontawesome/svgs/solid/phone-volume.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/phone.svg b/resources/fontawesome/svgs/solid/phone.svg deleted file mode 100644 index f248e0b..0000000 --- a/resources/fontawesome/svgs/solid/phone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/photo-film.svg b/resources/fontawesome/svgs/solid/photo-film.svg deleted file mode 100644 index a3a6a05..0000000 --- a/resources/fontawesome/svgs/solid/photo-film.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/piggy-bank.svg b/resources/fontawesome/svgs/solid/piggy-bank.svg deleted file mode 100644 index 590f68a..0000000 --- a/resources/fontawesome/svgs/solid/piggy-bank.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pills.svg b/resources/fontawesome/svgs/solid/pills.svg deleted file mode 100644 index c1d48b5..0000000 --- a/resources/fontawesome/svgs/solid/pills.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pizza-slice.svg b/resources/fontawesome/svgs/solid/pizza-slice.svg deleted file mode 100644 index 7b4efb0..0000000 --- a/resources/fontawesome/svgs/solid/pizza-slice.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/place-of-worship.svg b/resources/fontawesome/svgs/solid/place-of-worship.svg deleted file mode 100644 index 157cbf7..0000000 --- a/resources/fontawesome/svgs/solid/place-of-worship.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane-arrival.svg b/resources/fontawesome/svgs/solid/plane-arrival.svg deleted file mode 100644 index 3db7b96..0000000 --- a/resources/fontawesome/svgs/solid/plane-arrival.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane-circle-check.svg b/resources/fontawesome/svgs/solid/plane-circle-check.svg deleted file mode 100644 index c2ed901..0000000 --- a/resources/fontawesome/svgs/solid/plane-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane-circle-exclamation.svg b/resources/fontawesome/svgs/solid/plane-circle-exclamation.svg deleted file mode 100644 index a7bdcb2..0000000 --- a/resources/fontawesome/svgs/solid/plane-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane-circle-xmark.svg b/resources/fontawesome/svgs/solid/plane-circle-xmark.svg deleted file mode 100644 index f20d73c..0000000 --- a/resources/fontawesome/svgs/solid/plane-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane-departure.svg b/resources/fontawesome/svgs/solid/plane-departure.svg deleted file mode 100644 index db57814..0000000 --- a/resources/fontawesome/svgs/solid/plane-departure.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane-lock.svg b/resources/fontawesome/svgs/solid/plane-lock.svg deleted file mode 100644 index f032f34..0000000 --- a/resources/fontawesome/svgs/solid/plane-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane-slash.svg b/resources/fontawesome/svgs/solid/plane-slash.svg deleted file mode 100644 index f29d864..0000000 --- a/resources/fontawesome/svgs/solid/plane-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane-up.svg b/resources/fontawesome/svgs/solid/plane-up.svg deleted file mode 100644 index 9650205..0000000 --- a/resources/fontawesome/svgs/solid/plane-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plane.svg b/resources/fontawesome/svgs/solid/plane.svg deleted file mode 100644 index 7161588..0000000 --- a/resources/fontawesome/svgs/solid/plane.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plant-wilt.svg b/resources/fontawesome/svgs/solid/plant-wilt.svg deleted file mode 100644 index b1509e8..0000000 --- a/resources/fontawesome/svgs/solid/plant-wilt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plate-wheat.svg b/resources/fontawesome/svgs/solid/plate-wheat.svg deleted file mode 100644 index 60f6d8a..0000000 --- a/resources/fontawesome/svgs/solid/plate-wheat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/play.svg b/resources/fontawesome/svgs/solid/play.svg deleted file mode 100644 index c3376a0..0000000 --- a/resources/fontawesome/svgs/solid/play.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plug-circle-bolt.svg b/resources/fontawesome/svgs/solid/plug-circle-bolt.svg deleted file mode 100644 index 856eba4..0000000 --- a/resources/fontawesome/svgs/solid/plug-circle-bolt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plug-circle-check.svg b/resources/fontawesome/svgs/solid/plug-circle-check.svg deleted file mode 100644 index c1db6be..0000000 --- a/resources/fontawesome/svgs/solid/plug-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plug-circle-exclamation.svg b/resources/fontawesome/svgs/solid/plug-circle-exclamation.svg deleted file mode 100644 index 15843d9..0000000 --- a/resources/fontawesome/svgs/solid/plug-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plug-circle-minus.svg b/resources/fontawesome/svgs/solid/plug-circle-minus.svg deleted file mode 100644 index e37c335..0000000 --- a/resources/fontawesome/svgs/solid/plug-circle-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plug-circle-plus.svg b/resources/fontawesome/svgs/solid/plug-circle-plus.svg deleted file mode 100644 index aeb7350..0000000 --- a/resources/fontawesome/svgs/solid/plug-circle-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plug-circle-xmark.svg b/resources/fontawesome/svgs/solid/plug-circle-xmark.svg deleted file mode 100644 index a5de870..0000000 --- a/resources/fontawesome/svgs/solid/plug-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plug.svg b/resources/fontawesome/svgs/solid/plug.svg deleted file mode 100644 index 6afcd76..0000000 --- a/resources/fontawesome/svgs/solid/plug.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plus-minus.svg b/resources/fontawesome/svgs/solid/plus-minus.svg deleted file mode 100644 index 0d2a5c5..0000000 --- a/resources/fontawesome/svgs/solid/plus-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/plus.svg b/resources/fontawesome/svgs/solid/plus.svg deleted file mode 100644 index 95b98d3..0000000 --- a/resources/fontawesome/svgs/solid/plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/podcast.svg b/resources/fontawesome/svgs/solid/podcast.svg deleted file mode 100644 index 1ff6e7d..0000000 --- a/resources/fontawesome/svgs/solid/podcast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/poo-storm.svg b/resources/fontawesome/svgs/solid/poo-storm.svg deleted file mode 100644 index 7c51164..0000000 --- a/resources/fontawesome/svgs/solid/poo-storm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/poo.svg b/resources/fontawesome/svgs/solid/poo.svg deleted file mode 100644 index 14ceb5c..0000000 --- a/resources/fontawesome/svgs/solid/poo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/poop.svg b/resources/fontawesome/svgs/solid/poop.svg deleted file mode 100644 index 3d9ee96..0000000 --- a/resources/fontawesome/svgs/solid/poop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/power-off.svg b/resources/fontawesome/svgs/solid/power-off.svg deleted file mode 100644 index e7947ad..0000000 --- a/resources/fontawesome/svgs/solid/power-off.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/prescription-bottle-medical.svg b/resources/fontawesome/svgs/solid/prescription-bottle-medical.svg deleted file mode 100644 index f34495b..0000000 --- a/resources/fontawesome/svgs/solid/prescription-bottle-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/prescription-bottle.svg b/resources/fontawesome/svgs/solid/prescription-bottle.svg deleted file mode 100644 index eb156e5..0000000 --- a/resources/fontawesome/svgs/solid/prescription-bottle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/prescription.svg b/resources/fontawesome/svgs/solid/prescription.svg deleted file mode 100644 index 6939c21..0000000 --- a/resources/fontawesome/svgs/solid/prescription.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/print.svg b/resources/fontawesome/svgs/solid/print.svg deleted file mode 100644 index cc6a757..0000000 --- a/resources/fontawesome/svgs/solid/print.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pump-medical.svg b/resources/fontawesome/svgs/solid/pump-medical.svg deleted file mode 100644 index 6a01b88..0000000 --- a/resources/fontawesome/svgs/solid/pump-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/pump-soap.svg b/resources/fontawesome/svgs/solid/pump-soap.svg deleted file mode 100644 index eb3c11b..0000000 --- a/resources/fontawesome/svgs/solid/pump-soap.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/puzzle-piece.svg b/resources/fontawesome/svgs/solid/puzzle-piece.svg deleted file mode 100644 index 3b3d09b..0000000 --- a/resources/fontawesome/svgs/solid/puzzle-piece.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/q.svg b/resources/fontawesome/svgs/solid/q.svg deleted file mode 100644 index 72b7fa4..0000000 --- a/resources/fontawesome/svgs/solid/q.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/qrcode.svg b/resources/fontawesome/svgs/solid/qrcode.svg deleted file mode 100644 index b18c9de..0000000 --- a/resources/fontawesome/svgs/solid/qrcode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/question.svg b/resources/fontawesome/svgs/solid/question.svg deleted file mode 100644 index e4f9889..0000000 --- a/resources/fontawesome/svgs/solid/question.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/quote-left.svg b/resources/fontawesome/svgs/solid/quote-left.svg deleted file mode 100644 index a07ef22..0000000 --- a/resources/fontawesome/svgs/solid/quote-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/quote-right.svg b/resources/fontawesome/svgs/solid/quote-right.svg deleted file mode 100644 index 7308a13..0000000 --- a/resources/fontawesome/svgs/solid/quote-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/r.svg b/resources/fontawesome/svgs/solid/r.svg deleted file mode 100644 index 56b279e..0000000 --- a/resources/fontawesome/svgs/solid/r.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/radiation.svg b/resources/fontawesome/svgs/solid/radiation.svg deleted file mode 100644 index e77a68b..0000000 --- a/resources/fontawesome/svgs/solid/radiation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/radio.svg b/resources/fontawesome/svgs/solid/radio.svg deleted file mode 100644 index 7eb1038..0000000 --- a/resources/fontawesome/svgs/solid/radio.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rainbow.svg b/resources/fontawesome/svgs/solid/rainbow.svg deleted file mode 100644 index 9e5ac19..0000000 --- a/resources/fontawesome/svgs/solid/rainbow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ranking-star.svg b/resources/fontawesome/svgs/solid/ranking-star.svg deleted file mode 100644 index a87eb89..0000000 --- a/resources/fontawesome/svgs/solid/ranking-star.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/receipt.svg b/resources/fontawesome/svgs/solid/receipt.svg deleted file mode 100644 index 74ec154..0000000 --- a/resources/fontawesome/svgs/solid/receipt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/record-vinyl.svg b/resources/fontawesome/svgs/solid/record-vinyl.svg deleted file mode 100644 index b56ac01..0000000 --- a/resources/fontawesome/svgs/solid/record-vinyl.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rectangle-ad.svg b/resources/fontawesome/svgs/solid/rectangle-ad.svg deleted file mode 100644 index f827dcb..0000000 --- a/resources/fontawesome/svgs/solid/rectangle-ad.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rectangle-list.svg b/resources/fontawesome/svgs/solid/rectangle-list.svg deleted file mode 100644 index 92ae185..0000000 --- a/resources/fontawesome/svgs/solid/rectangle-list.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rectangle-xmark.svg b/resources/fontawesome/svgs/solid/rectangle-xmark.svg deleted file mode 100644 index 791225c..0000000 --- a/resources/fontawesome/svgs/solid/rectangle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/recycle.svg b/resources/fontawesome/svgs/solid/recycle.svg deleted file mode 100644 index 5e2837e..0000000 --- a/resources/fontawesome/svgs/solid/recycle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/registered.svg b/resources/fontawesome/svgs/solid/registered.svg deleted file mode 100644 index a68c8d1..0000000 --- a/resources/fontawesome/svgs/solid/registered.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/repeat.svg b/resources/fontawesome/svgs/solid/repeat.svg deleted file mode 100644 index 74b83c5..0000000 --- a/resources/fontawesome/svgs/solid/repeat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/reply-all.svg b/resources/fontawesome/svgs/solid/reply-all.svg deleted file mode 100644 index 795f93e..0000000 --- a/resources/fontawesome/svgs/solid/reply-all.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/reply.svg b/resources/fontawesome/svgs/solid/reply.svg deleted file mode 100644 index 19cccdf..0000000 --- a/resources/fontawesome/svgs/solid/reply.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/republican.svg b/resources/fontawesome/svgs/solid/republican.svg deleted file mode 100644 index 5077e9c..0000000 --- a/resources/fontawesome/svgs/solid/republican.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/restroom.svg b/resources/fontawesome/svgs/solid/restroom.svg deleted file mode 100644 index 5634ed4..0000000 --- a/resources/fontawesome/svgs/solid/restroom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/retweet.svg b/resources/fontawesome/svgs/solid/retweet.svg deleted file mode 100644 index e57fdb4..0000000 --- a/resources/fontawesome/svgs/solid/retweet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ribbon.svg b/resources/fontawesome/svgs/solid/ribbon.svg deleted file mode 100644 index 0932765..0000000 --- a/resources/fontawesome/svgs/solid/ribbon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/right-from-bracket.svg b/resources/fontawesome/svgs/solid/right-from-bracket.svg deleted file mode 100644 index 1c4d485..0000000 --- a/resources/fontawesome/svgs/solid/right-from-bracket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/right-left.svg b/resources/fontawesome/svgs/solid/right-left.svg deleted file mode 100644 index a39811b..0000000 --- a/resources/fontawesome/svgs/solid/right-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/right-long.svg b/resources/fontawesome/svgs/solid/right-long.svg deleted file mode 100644 index 11853ce..0000000 --- a/resources/fontawesome/svgs/solid/right-long.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/right-to-bracket.svg b/resources/fontawesome/svgs/solid/right-to-bracket.svg deleted file mode 100644 index 33b7e18..0000000 --- a/resources/fontawesome/svgs/solid/right-to-bracket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ring.svg b/resources/fontawesome/svgs/solid/ring.svg deleted file mode 100644 index c029064..0000000 --- a/resources/fontawesome/svgs/solid/ring.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/road-barrier.svg b/resources/fontawesome/svgs/solid/road-barrier.svg deleted file mode 100644 index 6c00b46..0000000 --- a/resources/fontawesome/svgs/solid/road-barrier.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/road-bridge.svg b/resources/fontawesome/svgs/solid/road-bridge.svg deleted file mode 100644 index 15a7972..0000000 --- a/resources/fontawesome/svgs/solid/road-bridge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/road-circle-check.svg b/resources/fontawesome/svgs/solid/road-circle-check.svg deleted file mode 100644 index 1c9a7aa..0000000 --- a/resources/fontawesome/svgs/solid/road-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/road-circle-exclamation.svg b/resources/fontawesome/svgs/solid/road-circle-exclamation.svg deleted file mode 100644 index 18561e7..0000000 --- a/resources/fontawesome/svgs/solid/road-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/road-circle-xmark.svg b/resources/fontawesome/svgs/solid/road-circle-xmark.svg deleted file mode 100644 index 78542d8..0000000 --- a/resources/fontawesome/svgs/solid/road-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/road-lock.svg b/resources/fontawesome/svgs/solid/road-lock.svg deleted file mode 100644 index 1ddd46b..0000000 --- a/resources/fontawesome/svgs/solid/road-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/road-spikes.svg b/resources/fontawesome/svgs/solid/road-spikes.svg deleted file mode 100644 index 065a42a..0000000 --- a/resources/fontawesome/svgs/solid/road-spikes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/road.svg b/resources/fontawesome/svgs/solid/road.svg deleted file mode 100644 index 4e43bd4..0000000 --- a/resources/fontawesome/svgs/solid/road.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/robot.svg b/resources/fontawesome/svgs/solid/robot.svg deleted file mode 100644 index 3312089..0000000 --- a/resources/fontawesome/svgs/solid/robot.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rocket.svg b/resources/fontawesome/svgs/solid/rocket.svg deleted file mode 100644 index 44e0746..0000000 --- a/resources/fontawesome/svgs/solid/rocket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rotate-left.svg b/resources/fontawesome/svgs/solid/rotate-left.svg deleted file mode 100644 index 280287b..0000000 --- a/resources/fontawesome/svgs/solid/rotate-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rotate-right.svg b/resources/fontawesome/svgs/solid/rotate-right.svg deleted file mode 100644 index 39ad71e..0000000 --- a/resources/fontawesome/svgs/solid/rotate-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rotate.svg b/resources/fontawesome/svgs/solid/rotate.svg deleted file mode 100644 index 449fc6b..0000000 --- a/resources/fontawesome/svgs/solid/rotate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/route.svg b/resources/fontawesome/svgs/solid/route.svg deleted file mode 100644 index 0088584..0000000 --- a/resources/fontawesome/svgs/solid/route.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rss.svg b/resources/fontawesome/svgs/solid/rss.svg deleted file mode 100644 index 7473df3..0000000 --- a/resources/fontawesome/svgs/solid/rss.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ruble-sign.svg b/resources/fontawesome/svgs/solid/ruble-sign.svg deleted file mode 100644 index 2605229..0000000 --- a/resources/fontawesome/svgs/solid/ruble-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rug.svg b/resources/fontawesome/svgs/solid/rug.svg deleted file mode 100644 index 8c25634..0000000 --- a/resources/fontawesome/svgs/solid/rug.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ruler-combined.svg b/resources/fontawesome/svgs/solid/ruler-combined.svg deleted file mode 100644 index e86ccb2..0000000 --- a/resources/fontawesome/svgs/solid/ruler-combined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ruler-horizontal.svg b/resources/fontawesome/svgs/solid/ruler-horizontal.svg deleted file mode 100644 index 96c71bf..0000000 --- a/resources/fontawesome/svgs/solid/ruler-horizontal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ruler-vertical.svg b/resources/fontawesome/svgs/solid/ruler-vertical.svg deleted file mode 100644 index f34cd57..0000000 --- a/resources/fontawesome/svgs/solid/ruler-vertical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ruler.svg b/resources/fontawesome/svgs/solid/ruler.svg deleted file mode 100644 index 2402f03..0000000 --- a/resources/fontawesome/svgs/solid/ruler.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rupee-sign.svg b/resources/fontawesome/svgs/solid/rupee-sign.svg deleted file mode 100644 index b2187b9..0000000 --- a/resources/fontawesome/svgs/solid/rupee-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/rupiah-sign.svg b/resources/fontawesome/svgs/solid/rupiah-sign.svg deleted file mode 100644 index 8290184..0000000 --- a/resources/fontawesome/svgs/solid/rupiah-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/s.svg b/resources/fontawesome/svgs/solid/s.svg deleted file mode 100644 index 08ac4aa..0000000 --- a/resources/fontawesome/svgs/solid/s.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sack-dollar.svg b/resources/fontawesome/svgs/solid/sack-dollar.svg deleted file mode 100644 index ae32d68..0000000 --- a/resources/fontawesome/svgs/solid/sack-dollar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sack-xmark.svg b/resources/fontawesome/svgs/solid/sack-xmark.svg deleted file mode 100644 index c8a8449..0000000 --- a/resources/fontawesome/svgs/solid/sack-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sailboat.svg b/resources/fontawesome/svgs/solid/sailboat.svg deleted file mode 100644 index 9861da5..0000000 --- a/resources/fontawesome/svgs/solid/sailboat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/satellite-dish.svg b/resources/fontawesome/svgs/solid/satellite-dish.svg deleted file mode 100644 index 70f28dd..0000000 --- a/resources/fontawesome/svgs/solid/satellite-dish.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/satellite.svg b/resources/fontawesome/svgs/solid/satellite.svg deleted file mode 100644 index 49c6de5..0000000 --- a/resources/fontawesome/svgs/solid/satellite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/scale-balanced.svg b/resources/fontawesome/svgs/solid/scale-balanced.svg deleted file mode 100644 index 1b17106..0000000 --- a/resources/fontawesome/svgs/solid/scale-balanced.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/scale-unbalanced-flip.svg b/resources/fontawesome/svgs/solid/scale-unbalanced-flip.svg deleted file mode 100644 index 40b85f8..0000000 --- a/resources/fontawesome/svgs/solid/scale-unbalanced-flip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/scale-unbalanced.svg b/resources/fontawesome/svgs/solid/scale-unbalanced.svg deleted file mode 100644 index 67dba9b..0000000 --- a/resources/fontawesome/svgs/solid/scale-unbalanced.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/school-circle-check.svg b/resources/fontawesome/svgs/solid/school-circle-check.svg deleted file mode 100644 index 4356220..0000000 --- a/resources/fontawesome/svgs/solid/school-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/school-circle-exclamation.svg b/resources/fontawesome/svgs/solid/school-circle-exclamation.svg deleted file mode 100644 index 567a327..0000000 --- a/resources/fontawesome/svgs/solid/school-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/school-circle-xmark.svg b/resources/fontawesome/svgs/solid/school-circle-xmark.svg deleted file mode 100644 index c25303b..0000000 --- a/resources/fontawesome/svgs/solid/school-circle-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/school-flag.svg b/resources/fontawesome/svgs/solid/school-flag.svg deleted file mode 100644 index 62b4375..0000000 --- a/resources/fontawesome/svgs/solid/school-flag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/school-lock.svg b/resources/fontawesome/svgs/solid/school-lock.svg deleted file mode 100644 index 71d4790..0000000 --- a/resources/fontawesome/svgs/solid/school-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/school.svg b/resources/fontawesome/svgs/solid/school.svg deleted file mode 100644 index 9a5eda8..0000000 --- a/resources/fontawesome/svgs/solid/school.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/scissors.svg b/resources/fontawesome/svgs/solid/scissors.svg deleted file mode 100644 index 727b2a8..0000000 --- a/resources/fontawesome/svgs/solid/scissors.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/screwdriver-wrench.svg b/resources/fontawesome/svgs/solid/screwdriver-wrench.svg deleted file mode 100644 index e5a848c..0000000 --- a/resources/fontawesome/svgs/solid/screwdriver-wrench.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/screwdriver.svg b/resources/fontawesome/svgs/solid/screwdriver.svg deleted file mode 100644 index 4674aac..0000000 --- a/resources/fontawesome/svgs/solid/screwdriver.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/scroll-torah.svg b/resources/fontawesome/svgs/solid/scroll-torah.svg deleted file mode 100644 index 0c3fe2f..0000000 --- a/resources/fontawesome/svgs/solid/scroll-torah.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/scroll.svg b/resources/fontawesome/svgs/solid/scroll.svg deleted file mode 100644 index 615bad9..0000000 --- a/resources/fontawesome/svgs/solid/scroll.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sd-card.svg b/resources/fontawesome/svgs/solid/sd-card.svg deleted file mode 100644 index 1d7e79b..0000000 --- a/resources/fontawesome/svgs/solid/sd-card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/section.svg b/resources/fontawesome/svgs/solid/section.svg deleted file mode 100644 index 047cd5e..0000000 --- a/resources/fontawesome/svgs/solid/section.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/seedling.svg b/resources/fontawesome/svgs/solid/seedling.svg deleted file mode 100644 index 4da904d..0000000 --- a/resources/fontawesome/svgs/solid/seedling.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/server.svg b/resources/fontawesome/svgs/solid/server.svg deleted file mode 100644 index cf226ce..0000000 --- a/resources/fontawesome/svgs/solid/server.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shapes.svg b/resources/fontawesome/svgs/solid/shapes.svg deleted file mode 100644 index 0cd9abb..0000000 --- a/resources/fontawesome/svgs/solid/shapes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/share-from-square.svg b/resources/fontawesome/svgs/solid/share-from-square.svg deleted file mode 100644 index 6c5886d..0000000 --- a/resources/fontawesome/svgs/solid/share-from-square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/share-nodes.svg b/resources/fontawesome/svgs/solid/share-nodes.svg deleted file mode 100644 index d0429bb..0000000 --- a/resources/fontawesome/svgs/solid/share-nodes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/share.svg b/resources/fontawesome/svgs/solid/share.svg deleted file mode 100644 index c996ffa..0000000 --- a/resources/fontawesome/svgs/solid/share.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sheet-plastic.svg b/resources/fontawesome/svgs/solid/sheet-plastic.svg deleted file mode 100644 index 374b57c..0000000 --- a/resources/fontawesome/svgs/solid/sheet-plastic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shekel-sign.svg b/resources/fontawesome/svgs/solid/shekel-sign.svg deleted file mode 100644 index 92c9cb2..0000000 --- a/resources/fontawesome/svgs/solid/shekel-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shield-cat.svg b/resources/fontawesome/svgs/solid/shield-cat.svg deleted file mode 100644 index 6cf8c2c..0000000 --- a/resources/fontawesome/svgs/solid/shield-cat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shield-dog.svg b/resources/fontawesome/svgs/solid/shield-dog.svg deleted file mode 100644 index e28a5ad..0000000 --- a/resources/fontawesome/svgs/solid/shield-dog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shield-halved.svg b/resources/fontawesome/svgs/solid/shield-halved.svg deleted file mode 100644 index fbdb321..0000000 --- a/resources/fontawesome/svgs/solid/shield-halved.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shield-heart.svg b/resources/fontawesome/svgs/solid/shield-heart.svg deleted file mode 100644 index 495900a..0000000 --- a/resources/fontawesome/svgs/solid/shield-heart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shield-virus.svg b/resources/fontawesome/svgs/solid/shield-virus.svg deleted file mode 100644 index 57a21fd..0000000 --- a/resources/fontawesome/svgs/solid/shield-virus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shield.svg b/resources/fontawesome/svgs/solid/shield.svg deleted file mode 100644 index b44e882..0000000 --- a/resources/fontawesome/svgs/solid/shield.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ship.svg b/resources/fontawesome/svgs/solid/ship.svg deleted file mode 100644 index 44c85df..0000000 --- a/resources/fontawesome/svgs/solid/ship.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shirt.svg b/resources/fontawesome/svgs/solid/shirt.svg deleted file mode 100644 index 2c2302f..0000000 --- a/resources/fontawesome/svgs/solid/shirt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shoe-prints.svg b/resources/fontawesome/svgs/solid/shoe-prints.svg deleted file mode 100644 index 65105fe..0000000 --- a/resources/fontawesome/svgs/solid/shoe-prints.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shop-lock.svg b/resources/fontawesome/svgs/solid/shop-lock.svg deleted file mode 100644 index 970d8ee..0000000 --- a/resources/fontawesome/svgs/solid/shop-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shop-slash.svg b/resources/fontawesome/svgs/solid/shop-slash.svg deleted file mode 100644 index 660d7c0..0000000 --- a/resources/fontawesome/svgs/solid/shop-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shop.svg b/resources/fontawesome/svgs/solid/shop.svg deleted file mode 100644 index 8b13f11..0000000 --- a/resources/fontawesome/svgs/solid/shop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shower.svg b/resources/fontawesome/svgs/solid/shower.svg deleted file mode 100644 index db5ac36..0000000 --- a/resources/fontawesome/svgs/solid/shower.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shrimp.svg b/resources/fontawesome/svgs/solid/shrimp.svg deleted file mode 100644 index 389a634..0000000 --- a/resources/fontawesome/svgs/solid/shrimp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shuffle.svg b/resources/fontawesome/svgs/solid/shuffle.svg deleted file mode 100644 index dd0df32..0000000 --- a/resources/fontawesome/svgs/solid/shuffle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/shuttle-space.svg b/resources/fontawesome/svgs/solid/shuttle-space.svg deleted file mode 100644 index c1067b1..0000000 --- a/resources/fontawesome/svgs/solid/shuttle-space.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sign-hanging.svg b/resources/fontawesome/svgs/solid/sign-hanging.svg deleted file mode 100644 index 4775643..0000000 --- a/resources/fontawesome/svgs/solid/sign-hanging.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/signal.svg b/resources/fontawesome/svgs/solid/signal.svg deleted file mode 100644 index 771becb..0000000 --- a/resources/fontawesome/svgs/solid/signal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/signature.svg b/resources/fontawesome/svgs/solid/signature.svg deleted file mode 100644 index 43f0660..0000000 --- a/resources/fontawesome/svgs/solid/signature.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/signs-post.svg b/resources/fontawesome/svgs/solid/signs-post.svg deleted file mode 100644 index c384307..0000000 --- a/resources/fontawesome/svgs/solid/signs-post.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sim-card.svg b/resources/fontawesome/svgs/solid/sim-card.svg deleted file mode 100644 index 33dad9d..0000000 --- a/resources/fontawesome/svgs/solid/sim-card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sink.svg b/resources/fontawesome/svgs/solid/sink.svg deleted file mode 100644 index 8ea1d91..0000000 --- a/resources/fontawesome/svgs/solid/sink.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sitemap.svg b/resources/fontawesome/svgs/solid/sitemap.svg deleted file mode 100644 index 13582bd..0000000 --- a/resources/fontawesome/svgs/solid/sitemap.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/skull-crossbones.svg b/resources/fontawesome/svgs/solid/skull-crossbones.svg deleted file mode 100644 index 1a4c953..0000000 --- a/resources/fontawesome/svgs/solid/skull-crossbones.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/skull.svg b/resources/fontawesome/svgs/solid/skull.svg deleted file mode 100644 index 0a224a6..0000000 --- a/resources/fontawesome/svgs/solid/skull.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/slash.svg b/resources/fontawesome/svgs/solid/slash.svg deleted file mode 100644 index 7262c83..0000000 --- a/resources/fontawesome/svgs/solid/slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sleigh.svg b/resources/fontawesome/svgs/solid/sleigh.svg deleted file mode 100644 index ee1ea83..0000000 --- a/resources/fontawesome/svgs/solid/sleigh.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sliders.svg b/resources/fontawesome/svgs/solid/sliders.svg deleted file mode 100644 index 25b6839..0000000 --- a/resources/fontawesome/svgs/solid/sliders.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/smog.svg b/resources/fontawesome/svgs/solid/smog.svg deleted file mode 100644 index 21f4cdd..0000000 --- a/resources/fontawesome/svgs/solid/smog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/smoking.svg b/resources/fontawesome/svgs/solid/smoking.svg deleted file mode 100644 index a1ad4f0..0000000 --- a/resources/fontawesome/svgs/solid/smoking.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/snowflake.svg b/resources/fontawesome/svgs/solid/snowflake.svg deleted file mode 100644 index b4b8a44..0000000 --- a/resources/fontawesome/svgs/solid/snowflake.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/snowman.svg b/resources/fontawesome/svgs/solid/snowman.svg deleted file mode 100644 index 44113d1..0000000 --- a/resources/fontawesome/svgs/solid/snowman.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/snowplow.svg b/resources/fontawesome/svgs/solid/snowplow.svg deleted file mode 100644 index 6871b1f..0000000 --- a/resources/fontawesome/svgs/solid/snowplow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/soap.svg b/resources/fontawesome/svgs/solid/soap.svg deleted file mode 100644 index 9c228ba..0000000 --- a/resources/fontawesome/svgs/solid/soap.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/socks.svg b/resources/fontawesome/svgs/solid/socks.svg deleted file mode 100644 index 8aafe9a..0000000 --- a/resources/fontawesome/svgs/solid/socks.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/solar-panel.svg b/resources/fontawesome/svgs/solid/solar-panel.svg deleted file mode 100644 index 80ae3cd..0000000 --- a/resources/fontawesome/svgs/solid/solar-panel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sort-down.svg b/resources/fontawesome/svgs/solid/sort-down.svg deleted file mode 100644 index dd2d592..0000000 --- a/resources/fontawesome/svgs/solid/sort-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sort-up.svg b/resources/fontawesome/svgs/solid/sort-up.svg deleted file mode 100644 index 610cd9b..0000000 --- a/resources/fontawesome/svgs/solid/sort-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sort.svg b/resources/fontawesome/svgs/solid/sort.svg deleted file mode 100644 index d4a891c..0000000 --- a/resources/fontawesome/svgs/solid/sort.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/spa.svg b/resources/fontawesome/svgs/solid/spa.svg deleted file mode 100644 index 80ba2a7..0000000 --- a/resources/fontawesome/svgs/solid/spa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/spaghetti-monster-flying.svg b/resources/fontawesome/svgs/solid/spaghetti-monster-flying.svg deleted file mode 100644 index 7639b9a..0000000 --- a/resources/fontawesome/svgs/solid/spaghetti-monster-flying.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/spell-check.svg b/resources/fontawesome/svgs/solid/spell-check.svg deleted file mode 100644 index 9ceeb8a..0000000 --- a/resources/fontawesome/svgs/solid/spell-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/spider.svg b/resources/fontawesome/svgs/solid/spider.svg deleted file mode 100644 index 7d100c1..0000000 --- a/resources/fontawesome/svgs/solid/spider.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/spinner.svg b/resources/fontawesome/svgs/solid/spinner.svg deleted file mode 100644 index 58c0fe0..0000000 --- a/resources/fontawesome/svgs/solid/spinner.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/splotch.svg b/resources/fontawesome/svgs/solid/splotch.svg deleted file mode 100644 index d7b4b60..0000000 --- a/resources/fontawesome/svgs/solid/splotch.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/spoon.svg b/resources/fontawesome/svgs/solid/spoon.svg deleted file mode 100644 index f06d9ef..0000000 --- a/resources/fontawesome/svgs/solid/spoon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/spray-can-sparkles.svg b/resources/fontawesome/svgs/solid/spray-can-sparkles.svg deleted file mode 100644 index 2748293..0000000 --- a/resources/fontawesome/svgs/solid/spray-can-sparkles.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/spray-can.svg b/resources/fontawesome/svgs/solid/spray-can.svg deleted file mode 100644 index 5bbe214..0000000 --- a/resources/fontawesome/svgs/solid/spray-can.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-arrow-up-right.svg b/resources/fontawesome/svgs/solid/square-arrow-up-right.svg deleted file mode 100644 index 4720e87..0000000 --- a/resources/fontawesome/svgs/solid/square-arrow-up-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-caret-down.svg b/resources/fontawesome/svgs/solid/square-caret-down.svg deleted file mode 100644 index e28244f..0000000 --- a/resources/fontawesome/svgs/solid/square-caret-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-caret-left.svg b/resources/fontawesome/svgs/solid/square-caret-left.svg deleted file mode 100644 index 735bc96..0000000 --- a/resources/fontawesome/svgs/solid/square-caret-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-caret-right.svg b/resources/fontawesome/svgs/solid/square-caret-right.svg deleted file mode 100644 index d733ded..0000000 --- a/resources/fontawesome/svgs/solid/square-caret-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-caret-up.svg b/resources/fontawesome/svgs/solid/square-caret-up.svg deleted file mode 100644 index ace78d2..0000000 --- a/resources/fontawesome/svgs/solid/square-caret-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-check.svg b/resources/fontawesome/svgs/solid/square-check.svg deleted file mode 100644 index 9a74616..0000000 --- a/resources/fontawesome/svgs/solid/square-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-envelope.svg b/resources/fontawesome/svgs/solid/square-envelope.svg deleted file mode 100644 index e5a0070..0000000 --- a/resources/fontawesome/svgs/solid/square-envelope.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-full.svg b/resources/fontawesome/svgs/solid/square-full.svg deleted file mode 100644 index 7539948..0000000 --- a/resources/fontawesome/svgs/solid/square-full.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-h.svg b/resources/fontawesome/svgs/solid/square-h.svg deleted file mode 100644 index 496d5b4..0000000 --- a/resources/fontawesome/svgs/solid/square-h.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-minus.svg b/resources/fontawesome/svgs/solid/square-minus.svg deleted file mode 100644 index 44b0f8d..0000000 --- a/resources/fontawesome/svgs/solid/square-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-nfi.svg b/resources/fontawesome/svgs/solid/square-nfi.svg deleted file mode 100644 index be087d7..0000000 --- a/resources/fontawesome/svgs/solid/square-nfi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-parking.svg b/resources/fontawesome/svgs/solid/square-parking.svg deleted file mode 100644 index b5d298a..0000000 --- a/resources/fontawesome/svgs/solid/square-parking.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-pen.svg b/resources/fontawesome/svgs/solid/square-pen.svg deleted file mode 100644 index b414f05..0000000 --- a/resources/fontawesome/svgs/solid/square-pen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-person-confined.svg b/resources/fontawesome/svgs/solid/square-person-confined.svg deleted file mode 100644 index 423d10a..0000000 --- a/resources/fontawesome/svgs/solid/square-person-confined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-phone-flip.svg b/resources/fontawesome/svgs/solid/square-phone-flip.svg deleted file mode 100644 index 298b701..0000000 --- a/resources/fontawesome/svgs/solid/square-phone-flip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-phone.svg b/resources/fontawesome/svgs/solid/square-phone.svg deleted file mode 100644 index 999d19c..0000000 --- a/resources/fontawesome/svgs/solid/square-phone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-plus.svg b/resources/fontawesome/svgs/solid/square-plus.svg deleted file mode 100644 index 026ca84..0000000 --- a/resources/fontawesome/svgs/solid/square-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-poll-horizontal.svg b/resources/fontawesome/svgs/solid/square-poll-horizontal.svg deleted file mode 100644 index 45fbac5..0000000 --- a/resources/fontawesome/svgs/solid/square-poll-horizontal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-poll-vertical.svg b/resources/fontawesome/svgs/solid/square-poll-vertical.svg deleted file mode 100644 index b19fb14..0000000 --- a/resources/fontawesome/svgs/solid/square-poll-vertical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-root-variable.svg b/resources/fontawesome/svgs/solid/square-root-variable.svg deleted file mode 100644 index 1400aac..0000000 --- a/resources/fontawesome/svgs/solid/square-root-variable.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-rss.svg b/resources/fontawesome/svgs/solid/square-rss.svg deleted file mode 100644 index 21d048f..0000000 --- a/resources/fontawesome/svgs/solid/square-rss.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-share-nodes.svg b/resources/fontawesome/svgs/solid/square-share-nodes.svg deleted file mode 100644 index da2ea54..0000000 --- a/resources/fontawesome/svgs/solid/square-share-nodes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-up-right.svg b/resources/fontawesome/svgs/solid/square-up-right.svg deleted file mode 100644 index 511faeb..0000000 --- a/resources/fontawesome/svgs/solid/square-up-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-virus.svg b/resources/fontawesome/svgs/solid/square-virus.svg deleted file mode 100644 index 8a9ff11..0000000 --- a/resources/fontawesome/svgs/solid/square-virus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square-xmark.svg b/resources/fontawesome/svgs/solid/square-xmark.svg deleted file mode 100644 index 6125674..0000000 --- a/resources/fontawesome/svgs/solid/square-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/square.svg b/resources/fontawesome/svgs/solid/square.svg deleted file mode 100644 index 004bc11..0000000 --- a/resources/fontawesome/svgs/solid/square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/staff-snake.svg b/resources/fontawesome/svgs/solid/staff-snake.svg deleted file mode 100644 index 3d29c12..0000000 --- a/resources/fontawesome/svgs/solid/staff-snake.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/stairs.svg b/resources/fontawesome/svgs/solid/stairs.svg deleted file mode 100644 index e3138e1..0000000 --- a/resources/fontawesome/svgs/solid/stairs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/stamp.svg b/resources/fontawesome/svgs/solid/stamp.svg deleted file mode 100644 index c67f3dd..0000000 --- a/resources/fontawesome/svgs/solid/stamp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/stapler.svg b/resources/fontawesome/svgs/solid/stapler.svg deleted file mode 100644 index e71a1d5..0000000 --- a/resources/fontawesome/svgs/solid/stapler.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/star-and-crescent.svg b/resources/fontawesome/svgs/solid/star-and-crescent.svg deleted file mode 100644 index 12ad577..0000000 --- a/resources/fontawesome/svgs/solid/star-and-crescent.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/star-half-stroke.svg b/resources/fontawesome/svgs/solid/star-half-stroke.svg deleted file mode 100644 index d9cd6e7..0000000 --- a/resources/fontawesome/svgs/solid/star-half-stroke.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/star-half.svg b/resources/fontawesome/svgs/solid/star-half.svg deleted file mode 100644 index d4501ad..0000000 --- a/resources/fontawesome/svgs/solid/star-half.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/star-of-david.svg b/resources/fontawesome/svgs/solid/star-of-david.svg deleted file mode 100644 index 752d0aa..0000000 --- a/resources/fontawesome/svgs/solid/star-of-david.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/star-of-life.svg b/resources/fontawesome/svgs/solid/star-of-life.svg deleted file mode 100644 index 1b00b5e..0000000 --- a/resources/fontawesome/svgs/solid/star-of-life.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/star.svg b/resources/fontawesome/svgs/solid/star.svg deleted file mode 100644 index 7f6d6c6..0000000 --- a/resources/fontawesome/svgs/solid/star.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sterling-sign.svg b/resources/fontawesome/svgs/solid/sterling-sign.svg deleted file mode 100644 index d9fb0b0..0000000 --- a/resources/fontawesome/svgs/solid/sterling-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/stethoscope.svg b/resources/fontawesome/svgs/solid/stethoscope.svg deleted file mode 100644 index c66547b..0000000 --- a/resources/fontawesome/svgs/solid/stethoscope.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/stop.svg b/resources/fontawesome/svgs/solid/stop.svg deleted file mode 100644 index 3d4b429..0000000 --- a/resources/fontawesome/svgs/solid/stop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/stopwatch-20.svg b/resources/fontawesome/svgs/solid/stopwatch-20.svg deleted file mode 100644 index 4613208..0000000 --- a/resources/fontawesome/svgs/solid/stopwatch-20.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/stopwatch.svg b/resources/fontawesome/svgs/solid/stopwatch.svg deleted file mode 100644 index c5b5cef..0000000 --- a/resources/fontawesome/svgs/solid/stopwatch.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/store-slash.svg b/resources/fontawesome/svgs/solid/store-slash.svg deleted file mode 100644 index 27ef7b6..0000000 --- a/resources/fontawesome/svgs/solid/store-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/store.svg b/resources/fontawesome/svgs/solid/store.svg deleted file mode 100644 index 44a2ffa..0000000 --- a/resources/fontawesome/svgs/solid/store.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/street-view.svg b/resources/fontawesome/svgs/solid/street-view.svg deleted file mode 100644 index 16781cc..0000000 --- a/resources/fontawesome/svgs/solid/street-view.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/strikethrough.svg b/resources/fontawesome/svgs/solid/strikethrough.svg deleted file mode 100644 index 387a464..0000000 --- a/resources/fontawesome/svgs/solid/strikethrough.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/stroopwafel.svg b/resources/fontawesome/svgs/solid/stroopwafel.svg deleted file mode 100644 index 18d970c..0000000 --- a/resources/fontawesome/svgs/solid/stroopwafel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/subscript.svg b/resources/fontawesome/svgs/solid/subscript.svg deleted file mode 100644 index b69c9ba..0000000 --- a/resources/fontawesome/svgs/solid/subscript.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/suitcase-medical.svg b/resources/fontawesome/svgs/solid/suitcase-medical.svg deleted file mode 100644 index be64391..0000000 --- a/resources/fontawesome/svgs/solid/suitcase-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/suitcase-rolling.svg b/resources/fontawesome/svgs/solid/suitcase-rolling.svg deleted file mode 100644 index b38ce7f..0000000 --- a/resources/fontawesome/svgs/solid/suitcase-rolling.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/suitcase.svg b/resources/fontawesome/svgs/solid/suitcase.svg deleted file mode 100644 index 90a4670..0000000 --- a/resources/fontawesome/svgs/solid/suitcase.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sun-plant-wilt.svg b/resources/fontawesome/svgs/solid/sun-plant-wilt.svg deleted file mode 100644 index cd0e506..0000000 --- a/resources/fontawesome/svgs/solid/sun-plant-wilt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/sun.svg b/resources/fontawesome/svgs/solid/sun.svg deleted file mode 100644 index da53fbd..0000000 --- a/resources/fontawesome/svgs/solid/sun.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/superscript.svg b/resources/fontawesome/svgs/solid/superscript.svg deleted file mode 100644 index d27495b..0000000 --- a/resources/fontawesome/svgs/solid/superscript.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/swatchbook.svg b/resources/fontawesome/svgs/solid/swatchbook.svg deleted file mode 100644 index f206cca..0000000 --- a/resources/fontawesome/svgs/solid/swatchbook.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/synagogue.svg b/resources/fontawesome/svgs/solid/synagogue.svg deleted file mode 100644 index be61956..0000000 --- a/resources/fontawesome/svgs/solid/synagogue.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/syringe.svg b/resources/fontawesome/svgs/solid/syringe.svg deleted file mode 100644 index 6712ffd..0000000 --- a/resources/fontawesome/svgs/solid/syringe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/t.svg b/resources/fontawesome/svgs/solid/t.svg deleted file mode 100644 index cca044a..0000000 --- a/resources/fontawesome/svgs/solid/t.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/table-cells-column-lock.svg b/resources/fontawesome/svgs/solid/table-cells-column-lock.svg deleted file mode 100644 index 861898c..0000000 --- a/resources/fontawesome/svgs/solid/table-cells-column-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/table-cells-large.svg b/resources/fontawesome/svgs/solid/table-cells-large.svg deleted file mode 100644 index c028875..0000000 --- a/resources/fontawesome/svgs/solid/table-cells-large.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/table-cells-row-lock.svg b/resources/fontawesome/svgs/solid/table-cells-row-lock.svg deleted file mode 100644 index 876f5e1..0000000 --- a/resources/fontawesome/svgs/solid/table-cells-row-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/table-cells.svg b/resources/fontawesome/svgs/solid/table-cells.svg deleted file mode 100644 index a301bfc..0000000 --- a/resources/fontawesome/svgs/solid/table-cells.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/table-columns.svg b/resources/fontawesome/svgs/solid/table-columns.svg deleted file mode 100644 index 4ab62e5..0000000 --- a/resources/fontawesome/svgs/solid/table-columns.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/table-list.svg b/resources/fontawesome/svgs/solid/table-list.svg deleted file mode 100644 index 90d13fc..0000000 --- a/resources/fontawesome/svgs/solid/table-list.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/table-tennis-paddle-ball.svg b/resources/fontawesome/svgs/solid/table-tennis-paddle-ball.svg deleted file mode 100644 index 9d69f92..0000000 --- a/resources/fontawesome/svgs/solid/table-tennis-paddle-ball.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/table.svg b/resources/fontawesome/svgs/solid/table.svg deleted file mode 100644 index d1a5447..0000000 --- a/resources/fontawesome/svgs/solid/table.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tablet-button.svg b/resources/fontawesome/svgs/solid/tablet-button.svg deleted file mode 100644 index 8c8e264..0000000 --- a/resources/fontawesome/svgs/solid/tablet-button.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tablet-screen-button.svg b/resources/fontawesome/svgs/solid/tablet-screen-button.svg deleted file mode 100644 index a8ae349..0000000 --- a/resources/fontawesome/svgs/solid/tablet-screen-button.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tablet.svg b/resources/fontawesome/svgs/solid/tablet.svg deleted file mode 100644 index cdb419a..0000000 --- a/resources/fontawesome/svgs/solid/tablet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tablets.svg b/resources/fontawesome/svgs/solid/tablets.svg deleted file mode 100644 index 7ea433e..0000000 --- a/resources/fontawesome/svgs/solid/tablets.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tachograph-digital.svg b/resources/fontawesome/svgs/solid/tachograph-digital.svg deleted file mode 100644 index 00a8608..0000000 --- a/resources/fontawesome/svgs/solid/tachograph-digital.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tag.svg b/resources/fontawesome/svgs/solid/tag.svg deleted file mode 100644 index 5a2e8e0..0000000 --- a/resources/fontawesome/svgs/solid/tag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tags.svg b/resources/fontawesome/svgs/solid/tags.svg deleted file mode 100644 index 1d0c556..0000000 --- a/resources/fontawesome/svgs/solid/tags.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tape.svg b/resources/fontawesome/svgs/solid/tape.svg deleted file mode 100644 index e254718..0000000 --- a/resources/fontawesome/svgs/solid/tape.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tarp-droplet.svg b/resources/fontawesome/svgs/solid/tarp-droplet.svg deleted file mode 100644 index 8834a35..0000000 --- a/resources/fontawesome/svgs/solid/tarp-droplet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tarp.svg b/resources/fontawesome/svgs/solid/tarp.svg deleted file mode 100644 index 2a6701f..0000000 --- a/resources/fontawesome/svgs/solid/tarp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/taxi.svg b/resources/fontawesome/svgs/solid/taxi.svg deleted file mode 100644 index b5f4eee..0000000 --- a/resources/fontawesome/svgs/solid/taxi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/teeth-open.svg b/resources/fontawesome/svgs/solid/teeth-open.svg deleted file mode 100644 index ac8706f..0000000 --- a/resources/fontawesome/svgs/solid/teeth-open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/teeth.svg b/resources/fontawesome/svgs/solid/teeth.svg deleted file mode 100644 index a7e8743..0000000 --- a/resources/fontawesome/svgs/solid/teeth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-arrow-down.svg b/resources/fontawesome/svgs/solid/temperature-arrow-down.svg deleted file mode 100644 index e963485..0000000 --- a/resources/fontawesome/svgs/solid/temperature-arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-arrow-up.svg b/resources/fontawesome/svgs/solid/temperature-arrow-up.svg deleted file mode 100644 index c7a3e10..0000000 --- a/resources/fontawesome/svgs/solid/temperature-arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-empty.svg b/resources/fontawesome/svgs/solid/temperature-empty.svg deleted file mode 100644 index 855639a..0000000 --- a/resources/fontawesome/svgs/solid/temperature-empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-full.svg b/resources/fontawesome/svgs/solid/temperature-full.svg deleted file mode 100644 index 29eff0b..0000000 --- a/resources/fontawesome/svgs/solid/temperature-full.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-half.svg b/resources/fontawesome/svgs/solid/temperature-half.svg deleted file mode 100644 index 7fc0fb2..0000000 --- a/resources/fontawesome/svgs/solid/temperature-half.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-high.svg b/resources/fontawesome/svgs/solid/temperature-high.svg deleted file mode 100644 index 57103c0..0000000 --- a/resources/fontawesome/svgs/solid/temperature-high.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-low.svg b/resources/fontawesome/svgs/solid/temperature-low.svg deleted file mode 100644 index a821fb2..0000000 --- a/resources/fontawesome/svgs/solid/temperature-low.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-quarter.svg b/resources/fontawesome/svgs/solid/temperature-quarter.svg deleted file mode 100644 index 2fb1c38..0000000 --- a/resources/fontawesome/svgs/solid/temperature-quarter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/temperature-three-quarters.svg b/resources/fontawesome/svgs/solid/temperature-three-quarters.svg deleted file mode 100644 index c336816..0000000 --- a/resources/fontawesome/svgs/solid/temperature-three-quarters.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tenge-sign.svg b/resources/fontawesome/svgs/solid/tenge-sign.svg deleted file mode 100644 index 5191b9e..0000000 --- a/resources/fontawesome/svgs/solid/tenge-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tent-arrow-down-to-line.svg b/resources/fontawesome/svgs/solid/tent-arrow-down-to-line.svg deleted file mode 100644 index 6e117c2..0000000 --- a/resources/fontawesome/svgs/solid/tent-arrow-down-to-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tent-arrow-left-right.svg b/resources/fontawesome/svgs/solid/tent-arrow-left-right.svg deleted file mode 100644 index 1dee5c8..0000000 --- a/resources/fontawesome/svgs/solid/tent-arrow-left-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tent-arrow-turn-left.svg b/resources/fontawesome/svgs/solid/tent-arrow-turn-left.svg deleted file mode 100644 index ec974be..0000000 --- a/resources/fontawesome/svgs/solid/tent-arrow-turn-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tent-arrows-down.svg b/resources/fontawesome/svgs/solid/tent-arrows-down.svg deleted file mode 100644 index 08f30ff..0000000 --- a/resources/fontawesome/svgs/solid/tent-arrows-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tent.svg b/resources/fontawesome/svgs/solid/tent.svg deleted file mode 100644 index 7d32006..0000000 --- a/resources/fontawesome/svgs/solid/tent.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tents.svg b/resources/fontawesome/svgs/solid/tents.svg deleted file mode 100644 index 8f6292f..0000000 --- a/resources/fontawesome/svgs/solid/tents.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/terminal.svg b/resources/fontawesome/svgs/solid/terminal.svg deleted file mode 100644 index e158952..0000000 --- a/resources/fontawesome/svgs/solid/terminal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/text-height.svg b/resources/fontawesome/svgs/solid/text-height.svg deleted file mode 100644 index aec4791..0000000 --- a/resources/fontawesome/svgs/solid/text-height.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/text-slash.svg b/resources/fontawesome/svgs/solid/text-slash.svg deleted file mode 100644 index 2cb0524..0000000 --- a/resources/fontawesome/svgs/solid/text-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/text-width.svg b/resources/fontawesome/svgs/solid/text-width.svg deleted file mode 100644 index 8f6d7fd..0000000 --- a/resources/fontawesome/svgs/solid/text-width.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/thermometer.svg b/resources/fontawesome/svgs/solid/thermometer.svg deleted file mode 100644 index 9ff009d..0000000 --- a/resources/fontawesome/svgs/solid/thermometer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/thumbs-down.svg b/resources/fontawesome/svgs/solid/thumbs-down.svg deleted file mode 100644 index b3964f7..0000000 --- a/resources/fontawesome/svgs/solid/thumbs-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/thumbs-up.svg b/resources/fontawesome/svgs/solid/thumbs-up.svg deleted file mode 100644 index 74f68eb..0000000 --- a/resources/fontawesome/svgs/solid/thumbs-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/thumbtack.svg b/resources/fontawesome/svgs/solid/thumbtack.svg deleted file mode 100644 index 72e7565..0000000 --- a/resources/fontawesome/svgs/solid/thumbtack.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ticket-simple.svg b/resources/fontawesome/svgs/solid/ticket-simple.svg deleted file mode 100644 index 7b3be64..0000000 --- a/resources/fontawesome/svgs/solid/ticket-simple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/ticket.svg b/resources/fontawesome/svgs/solid/ticket.svg deleted file mode 100644 index fea290f..0000000 --- a/resources/fontawesome/svgs/solid/ticket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/timeline.svg b/resources/fontawesome/svgs/solid/timeline.svg deleted file mode 100644 index 2452d50..0000000 --- a/resources/fontawesome/svgs/solid/timeline.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/toggle-off.svg b/resources/fontawesome/svgs/solid/toggle-off.svg deleted file mode 100644 index f83dd9e..0000000 --- a/resources/fontawesome/svgs/solid/toggle-off.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/toggle-on.svg b/resources/fontawesome/svgs/solid/toggle-on.svg deleted file mode 100644 index 188c242..0000000 --- a/resources/fontawesome/svgs/solid/toggle-on.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/toilet-paper-slash.svg b/resources/fontawesome/svgs/solid/toilet-paper-slash.svg deleted file mode 100644 index 49e8926..0000000 --- a/resources/fontawesome/svgs/solid/toilet-paper-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/toilet-paper.svg b/resources/fontawesome/svgs/solid/toilet-paper.svg deleted file mode 100644 index 76ab992..0000000 --- a/resources/fontawesome/svgs/solid/toilet-paper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/toilet-portable.svg b/resources/fontawesome/svgs/solid/toilet-portable.svg deleted file mode 100644 index 139e6b2..0000000 --- a/resources/fontawesome/svgs/solid/toilet-portable.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/toilet.svg b/resources/fontawesome/svgs/solid/toilet.svg deleted file mode 100644 index e54345d..0000000 --- a/resources/fontawesome/svgs/solid/toilet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/toilets-portable.svg b/resources/fontawesome/svgs/solid/toilets-portable.svg deleted file mode 100644 index 48404f7..0000000 --- a/resources/fontawesome/svgs/solid/toilets-portable.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/toolbox.svg b/resources/fontawesome/svgs/solid/toolbox.svg deleted file mode 100644 index de5b327..0000000 --- a/resources/fontawesome/svgs/solid/toolbox.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tooth.svg b/resources/fontawesome/svgs/solid/tooth.svg deleted file mode 100644 index 11c5ddd..0000000 --- a/resources/fontawesome/svgs/solid/tooth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/torii-gate.svg b/resources/fontawesome/svgs/solid/torii-gate.svg deleted file mode 100644 index 4fac1a8..0000000 --- a/resources/fontawesome/svgs/solid/torii-gate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tornado.svg b/resources/fontawesome/svgs/solid/tornado.svg deleted file mode 100644 index fbae365..0000000 --- a/resources/fontawesome/svgs/solid/tornado.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tower-broadcast.svg b/resources/fontawesome/svgs/solid/tower-broadcast.svg deleted file mode 100644 index 4b9729f..0000000 --- a/resources/fontawesome/svgs/solid/tower-broadcast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tower-cell.svg b/resources/fontawesome/svgs/solid/tower-cell.svg deleted file mode 100644 index 1a010dd..0000000 --- a/resources/fontawesome/svgs/solid/tower-cell.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tower-observation.svg b/resources/fontawesome/svgs/solid/tower-observation.svg deleted file mode 100644 index 315d244..0000000 --- a/resources/fontawesome/svgs/solid/tower-observation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tractor.svg b/resources/fontawesome/svgs/solid/tractor.svg deleted file mode 100644 index bdaa54b..0000000 --- a/resources/fontawesome/svgs/solid/tractor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trademark.svg b/resources/fontawesome/svgs/solid/trademark.svg deleted file mode 100644 index a8b2618..0000000 --- a/resources/fontawesome/svgs/solid/trademark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/traffic-light.svg b/resources/fontawesome/svgs/solid/traffic-light.svg deleted file mode 100644 index ddcff17..0000000 --- a/resources/fontawesome/svgs/solid/traffic-light.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trailer.svg b/resources/fontawesome/svgs/solid/trailer.svg deleted file mode 100644 index 6ff45ef..0000000 --- a/resources/fontawesome/svgs/solid/trailer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/train-subway.svg b/resources/fontawesome/svgs/solid/train-subway.svg deleted file mode 100644 index 34ad246..0000000 --- a/resources/fontawesome/svgs/solid/train-subway.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/train-tram.svg b/resources/fontawesome/svgs/solid/train-tram.svg deleted file mode 100644 index f3c113e..0000000 --- a/resources/fontawesome/svgs/solid/train-tram.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/train.svg b/resources/fontawesome/svgs/solid/train.svg deleted file mode 100644 index 7465ad2..0000000 --- a/resources/fontawesome/svgs/solid/train.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/transgender.svg b/resources/fontawesome/svgs/solid/transgender.svg deleted file mode 100644 index c139c98..0000000 --- a/resources/fontawesome/svgs/solid/transgender.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trash-arrow-up.svg b/resources/fontawesome/svgs/solid/trash-arrow-up.svg deleted file mode 100644 index d9e4da0..0000000 --- a/resources/fontawesome/svgs/solid/trash-arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trash-can-arrow-up.svg b/resources/fontawesome/svgs/solid/trash-can-arrow-up.svg deleted file mode 100644 index a7612cf..0000000 --- a/resources/fontawesome/svgs/solid/trash-can-arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trash-can.svg b/resources/fontawesome/svgs/solid/trash-can.svg deleted file mode 100644 index 47d0ea1..0000000 --- a/resources/fontawesome/svgs/solid/trash-can.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trash.svg b/resources/fontawesome/svgs/solid/trash.svg deleted file mode 100644 index ed5cf94..0000000 --- a/resources/fontawesome/svgs/solid/trash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tree-city.svg b/resources/fontawesome/svgs/solid/tree-city.svg deleted file mode 100644 index d0c8d26..0000000 --- a/resources/fontawesome/svgs/solid/tree-city.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tree.svg b/resources/fontawesome/svgs/solid/tree.svg deleted file mode 100644 index fafaad0..0000000 --- a/resources/fontawesome/svgs/solid/tree.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/triangle-exclamation.svg b/resources/fontawesome/svgs/solid/triangle-exclamation.svg deleted file mode 100644 index 014ef5e..0000000 --- a/resources/fontawesome/svgs/solid/triangle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trophy.svg b/resources/fontawesome/svgs/solid/trophy.svg deleted file mode 100644 index 94bfe49..0000000 --- a/resources/fontawesome/svgs/solid/trophy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trowel-bricks.svg b/resources/fontawesome/svgs/solid/trowel-bricks.svg deleted file mode 100644 index 31d95e2..0000000 --- a/resources/fontawesome/svgs/solid/trowel-bricks.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/trowel.svg b/resources/fontawesome/svgs/solid/trowel.svg deleted file mode 100644 index ff7a46f..0000000 --- a/resources/fontawesome/svgs/solid/trowel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-arrow-right.svg b/resources/fontawesome/svgs/solid/truck-arrow-right.svg deleted file mode 100644 index efef2c3..0000000 --- a/resources/fontawesome/svgs/solid/truck-arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-droplet.svg b/resources/fontawesome/svgs/solid/truck-droplet.svg deleted file mode 100644 index 261a3fe..0000000 --- a/resources/fontawesome/svgs/solid/truck-droplet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-fast.svg b/resources/fontawesome/svgs/solid/truck-fast.svg deleted file mode 100644 index 77b400b..0000000 --- a/resources/fontawesome/svgs/solid/truck-fast.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-field-un.svg b/resources/fontawesome/svgs/solid/truck-field-un.svg deleted file mode 100644 index 8715aef..0000000 --- a/resources/fontawesome/svgs/solid/truck-field-un.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-field.svg b/resources/fontawesome/svgs/solid/truck-field.svg deleted file mode 100644 index 7f4fb7b..0000000 --- a/resources/fontawesome/svgs/solid/truck-field.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-front.svg b/resources/fontawesome/svgs/solid/truck-front.svg deleted file mode 100644 index 855f8e5..0000000 --- a/resources/fontawesome/svgs/solid/truck-front.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-medical.svg b/resources/fontawesome/svgs/solid/truck-medical.svg deleted file mode 100644 index 1716161..0000000 --- a/resources/fontawesome/svgs/solid/truck-medical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-monster.svg b/resources/fontawesome/svgs/solid/truck-monster.svg deleted file mode 100644 index 265758e..0000000 --- a/resources/fontawesome/svgs/solid/truck-monster.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-moving.svg b/resources/fontawesome/svgs/solid/truck-moving.svg deleted file mode 100644 index 16d627a..0000000 --- a/resources/fontawesome/svgs/solid/truck-moving.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-pickup.svg b/resources/fontawesome/svgs/solid/truck-pickup.svg deleted file mode 100644 index 58f39b6..0000000 --- a/resources/fontawesome/svgs/solid/truck-pickup.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-plane.svg b/resources/fontawesome/svgs/solid/truck-plane.svg deleted file mode 100644 index 6e22edc..0000000 --- a/resources/fontawesome/svgs/solid/truck-plane.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck-ramp-box.svg b/resources/fontawesome/svgs/solid/truck-ramp-box.svg deleted file mode 100644 index f3602ce..0000000 --- a/resources/fontawesome/svgs/solid/truck-ramp-box.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/truck.svg b/resources/fontawesome/svgs/solid/truck.svg deleted file mode 100644 index 5453b04..0000000 --- a/resources/fontawesome/svgs/solid/truck.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tty.svg b/resources/fontawesome/svgs/solid/tty.svg deleted file mode 100644 index aa80493..0000000 --- a/resources/fontawesome/svgs/solid/tty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/turkish-lira-sign.svg b/resources/fontawesome/svgs/solid/turkish-lira-sign.svg deleted file mode 100644 index 45adbf8..0000000 --- a/resources/fontawesome/svgs/solid/turkish-lira-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/turn-down.svg b/resources/fontawesome/svgs/solid/turn-down.svg deleted file mode 100644 index 8eb1ec3..0000000 --- a/resources/fontawesome/svgs/solid/turn-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/turn-up.svg b/resources/fontawesome/svgs/solid/turn-up.svg deleted file mode 100644 index 3c14580..0000000 --- a/resources/fontawesome/svgs/solid/turn-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/tv.svg b/resources/fontawesome/svgs/solid/tv.svg deleted file mode 100644 index eb2e445..0000000 --- a/resources/fontawesome/svgs/solid/tv.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/u.svg b/resources/fontawesome/svgs/solid/u.svg deleted file mode 100644 index 0e64deb..0000000 --- a/resources/fontawesome/svgs/solid/u.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/umbrella-beach.svg b/resources/fontawesome/svgs/solid/umbrella-beach.svg deleted file mode 100644 index 630e3e9..0000000 --- a/resources/fontawesome/svgs/solid/umbrella-beach.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/umbrella.svg b/resources/fontawesome/svgs/solid/umbrella.svg deleted file mode 100644 index d89b595..0000000 --- a/resources/fontawesome/svgs/solid/umbrella.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/underline.svg b/resources/fontawesome/svgs/solid/underline.svg deleted file mode 100644 index c1864b0..0000000 --- a/resources/fontawesome/svgs/solid/underline.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/universal-access.svg b/resources/fontawesome/svgs/solid/universal-access.svg deleted file mode 100644 index 893120f..0000000 --- a/resources/fontawesome/svgs/solid/universal-access.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/unlock-keyhole.svg b/resources/fontawesome/svgs/solid/unlock-keyhole.svg deleted file mode 100644 index a5b76f7..0000000 --- a/resources/fontawesome/svgs/solid/unlock-keyhole.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/unlock.svg b/resources/fontawesome/svgs/solid/unlock.svg deleted file mode 100644 index ed1fea5..0000000 --- a/resources/fontawesome/svgs/solid/unlock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/up-down-left-right.svg b/resources/fontawesome/svgs/solid/up-down-left-right.svg deleted file mode 100644 index 1363a20..0000000 --- a/resources/fontawesome/svgs/solid/up-down-left-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/up-down.svg b/resources/fontawesome/svgs/solid/up-down.svg deleted file mode 100644 index c886f1d..0000000 --- a/resources/fontawesome/svgs/solid/up-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/up-long.svg b/resources/fontawesome/svgs/solid/up-long.svg deleted file mode 100644 index 2a18576..0000000 --- a/resources/fontawesome/svgs/solid/up-long.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/up-right-and-down-left-from-center.svg b/resources/fontawesome/svgs/solid/up-right-and-down-left-from-center.svg deleted file mode 100644 index 6ee8064..0000000 --- a/resources/fontawesome/svgs/solid/up-right-and-down-left-from-center.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/up-right-from-square.svg b/resources/fontawesome/svgs/solid/up-right-from-square.svg deleted file mode 100644 index b749492..0000000 --- a/resources/fontawesome/svgs/solid/up-right-from-square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/upload.svg b/resources/fontawesome/svgs/solid/upload.svg deleted file mode 100644 index b54b6a2..0000000 --- a/resources/fontawesome/svgs/solid/upload.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-astronaut.svg b/resources/fontawesome/svgs/solid/user-astronaut.svg deleted file mode 100644 index a6bd6b4..0000000 --- a/resources/fontawesome/svgs/solid/user-astronaut.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-check.svg b/resources/fontawesome/svgs/solid/user-check.svg deleted file mode 100644 index 99e1292..0000000 --- a/resources/fontawesome/svgs/solid/user-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-clock.svg b/resources/fontawesome/svgs/solid/user-clock.svg deleted file mode 100644 index 4666e20..0000000 --- a/resources/fontawesome/svgs/solid/user-clock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-doctor.svg b/resources/fontawesome/svgs/solid/user-doctor.svg deleted file mode 100644 index d942803..0000000 --- a/resources/fontawesome/svgs/solid/user-doctor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-gear.svg b/resources/fontawesome/svgs/solid/user-gear.svg deleted file mode 100644 index 51ee199..0000000 --- a/resources/fontawesome/svgs/solid/user-gear.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-graduate.svg b/resources/fontawesome/svgs/solid/user-graduate.svg deleted file mode 100644 index ecf46ac..0000000 --- a/resources/fontawesome/svgs/solid/user-graduate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-group.svg b/resources/fontawesome/svgs/solid/user-group.svg deleted file mode 100644 index 7c28bff..0000000 --- a/resources/fontawesome/svgs/solid/user-group.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-injured.svg b/resources/fontawesome/svgs/solid/user-injured.svg deleted file mode 100644 index d019c44..0000000 --- a/resources/fontawesome/svgs/solid/user-injured.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-large-slash.svg b/resources/fontawesome/svgs/solid/user-large-slash.svg deleted file mode 100644 index 6b86548..0000000 --- a/resources/fontawesome/svgs/solid/user-large-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-large.svg b/resources/fontawesome/svgs/solid/user-large.svg deleted file mode 100644 index 1dde314..0000000 --- a/resources/fontawesome/svgs/solid/user-large.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-lock.svg b/resources/fontawesome/svgs/solid/user-lock.svg deleted file mode 100644 index ace0185..0000000 --- a/resources/fontawesome/svgs/solid/user-lock.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-minus.svg b/resources/fontawesome/svgs/solid/user-minus.svg deleted file mode 100644 index 1b61d89..0000000 --- a/resources/fontawesome/svgs/solid/user-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-ninja.svg b/resources/fontawesome/svgs/solid/user-ninja.svg deleted file mode 100644 index 5d91a49..0000000 --- a/resources/fontawesome/svgs/solid/user-ninja.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-nurse.svg b/resources/fontawesome/svgs/solid/user-nurse.svg deleted file mode 100644 index a0f6823..0000000 --- a/resources/fontawesome/svgs/solid/user-nurse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-pen.svg b/resources/fontawesome/svgs/solid/user-pen.svg deleted file mode 100644 index d0cd463..0000000 --- a/resources/fontawesome/svgs/solid/user-pen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-plus.svg b/resources/fontawesome/svgs/solid/user-plus.svg deleted file mode 100644 index aad9c27..0000000 --- a/resources/fontawesome/svgs/solid/user-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-secret.svg b/resources/fontawesome/svgs/solid/user-secret.svg deleted file mode 100644 index 5100bb5..0000000 --- a/resources/fontawesome/svgs/solid/user-secret.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-shield.svg b/resources/fontawesome/svgs/solid/user-shield.svg deleted file mode 100644 index 892335a..0000000 --- a/resources/fontawesome/svgs/solid/user-shield.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-slash.svg b/resources/fontawesome/svgs/solid/user-slash.svg deleted file mode 100644 index e2b1dd4..0000000 --- a/resources/fontawesome/svgs/solid/user-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-tag.svg b/resources/fontawesome/svgs/solid/user-tag.svg deleted file mode 100644 index a60f22f..0000000 --- a/resources/fontawesome/svgs/solid/user-tag.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-tie.svg b/resources/fontawesome/svgs/solid/user-tie.svg deleted file mode 100644 index 870aad0..0000000 --- a/resources/fontawesome/svgs/solid/user-tie.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user-xmark.svg b/resources/fontawesome/svgs/solid/user-xmark.svg deleted file mode 100644 index 1ac4d67..0000000 --- a/resources/fontawesome/svgs/solid/user-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/user.svg b/resources/fontawesome/svgs/solid/user.svg deleted file mode 100644 index ae4d9cc..0000000 --- a/resources/fontawesome/svgs/solid/user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/users-between-lines.svg b/resources/fontawesome/svgs/solid/users-between-lines.svg deleted file mode 100644 index f113ed2..0000000 --- a/resources/fontawesome/svgs/solid/users-between-lines.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/users-gear.svg b/resources/fontawesome/svgs/solid/users-gear.svg deleted file mode 100644 index 2d011c8..0000000 --- a/resources/fontawesome/svgs/solid/users-gear.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/users-line.svg b/resources/fontawesome/svgs/solid/users-line.svg deleted file mode 100644 index 84ecd95..0000000 --- a/resources/fontawesome/svgs/solid/users-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/users-rays.svg b/resources/fontawesome/svgs/solid/users-rays.svg deleted file mode 100644 index 45f6b02..0000000 --- a/resources/fontawesome/svgs/solid/users-rays.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/users-rectangle.svg b/resources/fontawesome/svgs/solid/users-rectangle.svg deleted file mode 100644 index 5198d18..0000000 --- a/resources/fontawesome/svgs/solid/users-rectangle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/users-slash.svg b/resources/fontawesome/svgs/solid/users-slash.svg deleted file mode 100644 index 28c01d4..0000000 --- a/resources/fontawesome/svgs/solid/users-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/users-viewfinder.svg b/resources/fontawesome/svgs/solid/users-viewfinder.svg deleted file mode 100644 index b2b8b7a..0000000 --- a/resources/fontawesome/svgs/solid/users-viewfinder.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/users.svg b/resources/fontawesome/svgs/solid/users.svg deleted file mode 100644 index 3af4cc4..0000000 --- a/resources/fontawesome/svgs/solid/users.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/utensils.svg b/resources/fontawesome/svgs/solid/utensils.svg deleted file mode 100644 index 760c677..0000000 --- a/resources/fontawesome/svgs/solid/utensils.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/v.svg b/resources/fontawesome/svgs/solid/v.svg deleted file mode 100644 index 4cda538..0000000 --- a/resources/fontawesome/svgs/solid/v.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/van-shuttle.svg b/resources/fontawesome/svgs/solid/van-shuttle.svg deleted file mode 100644 index d5b7234..0000000 --- a/resources/fontawesome/svgs/solid/van-shuttle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vault.svg b/resources/fontawesome/svgs/solid/vault.svg deleted file mode 100644 index 31ccc3b..0000000 --- a/resources/fontawesome/svgs/solid/vault.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vector-square.svg b/resources/fontawesome/svgs/solid/vector-square.svg deleted file mode 100644 index a16c648..0000000 --- a/resources/fontawesome/svgs/solid/vector-square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/venus-double.svg b/resources/fontawesome/svgs/solid/venus-double.svg deleted file mode 100644 index ce7323f..0000000 --- a/resources/fontawesome/svgs/solid/venus-double.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/venus-mars.svg b/resources/fontawesome/svgs/solid/venus-mars.svg deleted file mode 100644 index e440c81..0000000 --- a/resources/fontawesome/svgs/solid/venus-mars.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/venus.svg b/resources/fontawesome/svgs/solid/venus.svg deleted file mode 100644 index d47abed..0000000 --- a/resources/fontawesome/svgs/solid/venus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vest-patches.svg b/resources/fontawesome/svgs/solid/vest-patches.svg deleted file mode 100644 index fdd3181..0000000 --- a/resources/fontawesome/svgs/solid/vest-patches.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vest.svg b/resources/fontawesome/svgs/solid/vest.svg deleted file mode 100644 index e0c702e..0000000 --- a/resources/fontawesome/svgs/solid/vest.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vial-circle-check.svg b/resources/fontawesome/svgs/solid/vial-circle-check.svg deleted file mode 100644 index caa4ee6..0000000 --- a/resources/fontawesome/svgs/solid/vial-circle-check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vial-virus.svg b/resources/fontawesome/svgs/solid/vial-virus.svg deleted file mode 100644 index a5b6da6..0000000 --- a/resources/fontawesome/svgs/solid/vial-virus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vial.svg b/resources/fontawesome/svgs/solid/vial.svg deleted file mode 100644 index c760c41..0000000 --- a/resources/fontawesome/svgs/solid/vial.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vials.svg b/resources/fontawesome/svgs/solid/vials.svg deleted file mode 100644 index 5f38b40..0000000 --- a/resources/fontawesome/svgs/solid/vials.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/video-slash.svg b/resources/fontawesome/svgs/solid/video-slash.svg deleted file mode 100644 index d9c396c..0000000 --- a/resources/fontawesome/svgs/solid/video-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/video.svg b/resources/fontawesome/svgs/solid/video.svg deleted file mode 100644 index 7d4e1f3..0000000 --- a/resources/fontawesome/svgs/solid/video.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vihara.svg b/resources/fontawesome/svgs/solid/vihara.svg deleted file mode 100644 index 6d7233c..0000000 --- a/resources/fontawesome/svgs/solid/vihara.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/virus-covid-slash.svg b/resources/fontawesome/svgs/solid/virus-covid-slash.svg deleted file mode 100644 index a9bf263..0000000 --- a/resources/fontawesome/svgs/solid/virus-covid-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/virus-covid.svg b/resources/fontawesome/svgs/solid/virus-covid.svg deleted file mode 100644 index b111f66..0000000 --- a/resources/fontawesome/svgs/solid/virus-covid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/virus-slash.svg b/resources/fontawesome/svgs/solid/virus-slash.svg deleted file mode 100644 index b327ed0..0000000 --- a/resources/fontawesome/svgs/solid/virus-slash.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/virus.svg b/resources/fontawesome/svgs/solid/virus.svg deleted file mode 100644 index a7fbe27..0000000 --- a/resources/fontawesome/svgs/solid/virus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/viruses.svg b/resources/fontawesome/svgs/solid/viruses.svg deleted file mode 100644 index 59c262e..0000000 --- a/resources/fontawesome/svgs/solid/viruses.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/voicemail.svg b/resources/fontawesome/svgs/solid/voicemail.svg deleted file mode 100644 index 3c39d87..0000000 --- a/resources/fontawesome/svgs/solid/voicemail.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/volcano.svg b/resources/fontawesome/svgs/solid/volcano.svg deleted file mode 100644 index 3157700..0000000 --- a/resources/fontawesome/svgs/solid/volcano.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/volleyball.svg b/resources/fontawesome/svgs/solid/volleyball.svg deleted file mode 100644 index a0ffe5f..0000000 --- a/resources/fontawesome/svgs/solid/volleyball.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/volume-high.svg b/resources/fontawesome/svgs/solid/volume-high.svg deleted file mode 100644 index 71855b2..0000000 --- a/resources/fontawesome/svgs/solid/volume-high.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/volume-low.svg b/resources/fontawesome/svgs/solid/volume-low.svg deleted file mode 100644 index 531e98d..0000000 --- a/resources/fontawesome/svgs/solid/volume-low.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/volume-off.svg b/resources/fontawesome/svgs/solid/volume-off.svg deleted file mode 100644 index 4d06eec..0000000 --- a/resources/fontawesome/svgs/solid/volume-off.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/volume-xmark.svg b/resources/fontawesome/svgs/solid/volume-xmark.svg deleted file mode 100644 index 82bb581..0000000 --- a/resources/fontawesome/svgs/solid/volume-xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/vr-cardboard.svg b/resources/fontawesome/svgs/solid/vr-cardboard.svg deleted file mode 100644 index 4d0ca14..0000000 --- a/resources/fontawesome/svgs/solid/vr-cardboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/w.svg b/resources/fontawesome/svgs/solid/w.svg deleted file mode 100644 index 1f3f57b..0000000 --- a/resources/fontawesome/svgs/solid/w.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/walkie-talkie.svg b/resources/fontawesome/svgs/solid/walkie-talkie.svg deleted file mode 100644 index 02bd516..0000000 --- a/resources/fontawesome/svgs/solid/walkie-talkie.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wallet.svg b/resources/fontawesome/svgs/solid/wallet.svg deleted file mode 100644 index a256dd6..0000000 --- a/resources/fontawesome/svgs/solid/wallet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wand-magic-sparkles.svg b/resources/fontawesome/svgs/solid/wand-magic-sparkles.svg deleted file mode 100644 index 0c4b481..0000000 --- a/resources/fontawesome/svgs/solid/wand-magic-sparkles.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wand-magic.svg b/resources/fontawesome/svgs/solid/wand-magic.svg deleted file mode 100644 index cd8fec5..0000000 --- a/resources/fontawesome/svgs/solid/wand-magic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wand-sparkles.svg b/resources/fontawesome/svgs/solid/wand-sparkles.svg deleted file mode 100644 index 02b192f..0000000 --- a/resources/fontawesome/svgs/solid/wand-sparkles.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/warehouse.svg b/resources/fontawesome/svgs/solid/warehouse.svg deleted file mode 100644 index db90588..0000000 --- a/resources/fontawesome/svgs/solid/warehouse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/water-ladder.svg b/resources/fontawesome/svgs/solid/water-ladder.svg deleted file mode 100644 index 27efa49..0000000 --- a/resources/fontawesome/svgs/solid/water-ladder.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/water.svg b/resources/fontawesome/svgs/solid/water.svg deleted file mode 100644 index ea29f51..0000000 --- a/resources/fontawesome/svgs/solid/water.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wave-square.svg b/resources/fontawesome/svgs/solid/wave-square.svg deleted file mode 100644 index a905551..0000000 --- a/resources/fontawesome/svgs/solid/wave-square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/weight-hanging.svg b/resources/fontawesome/svgs/solid/weight-hanging.svg deleted file mode 100644 index d6d09eb..0000000 --- a/resources/fontawesome/svgs/solid/weight-hanging.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/weight-scale.svg b/resources/fontawesome/svgs/solid/weight-scale.svg deleted file mode 100644 index 1dd09d2..0000000 --- a/resources/fontawesome/svgs/solid/weight-scale.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wheat-awn-circle-exclamation.svg b/resources/fontawesome/svgs/solid/wheat-awn-circle-exclamation.svg deleted file mode 100644 index ded0c62..0000000 --- a/resources/fontawesome/svgs/solid/wheat-awn-circle-exclamation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wheat-awn.svg b/resources/fontawesome/svgs/solid/wheat-awn.svg deleted file mode 100644 index 244d741..0000000 --- a/resources/fontawesome/svgs/solid/wheat-awn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wheelchair-move.svg b/resources/fontawesome/svgs/solid/wheelchair-move.svg deleted file mode 100644 index cdcb66b..0000000 --- a/resources/fontawesome/svgs/solid/wheelchair-move.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wheelchair.svg b/resources/fontawesome/svgs/solid/wheelchair.svg deleted file mode 100644 index 8f3ba9a..0000000 --- a/resources/fontawesome/svgs/solid/wheelchair.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/whiskey-glass.svg b/resources/fontawesome/svgs/solid/whiskey-glass.svg deleted file mode 100644 index 3641130..0000000 --- a/resources/fontawesome/svgs/solid/whiskey-glass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wifi.svg b/resources/fontawesome/svgs/solid/wifi.svg deleted file mode 100644 index 533946b..0000000 --- a/resources/fontawesome/svgs/solid/wifi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wind.svg b/resources/fontawesome/svgs/solid/wind.svg deleted file mode 100644 index 0c40216..0000000 --- a/resources/fontawesome/svgs/solid/wind.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/window-maximize.svg b/resources/fontawesome/svgs/solid/window-maximize.svg deleted file mode 100644 index 2d558ff..0000000 --- a/resources/fontawesome/svgs/solid/window-maximize.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/window-minimize.svg b/resources/fontawesome/svgs/solid/window-minimize.svg deleted file mode 100644 index 001d11d..0000000 --- a/resources/fontawesome/svgs/solid/window-minimize.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/window-restore.svg b/resources/fontawesome/svgs/solid/window-restore.svg deleted file mode 100644 index 07731e1..0000000 --- a/resources/fontawesome/svgs/solid/window-restore.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wine-bottle.svg b/resources/fontawesome/svgs/solid/wine-bottle.svg deleted file mode 100644 index 541d119..0000000 --- a/resources/fontawesome/svgs/solid/wine-bottle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wine-glass-empty.svg b/resources/fontawesome/svgs/solid/wine-glass-empty.svg deleted file mode 100644 index 3150b47..0000000 --- a/resources/fontawesome/svgs/solid/wine-glass-empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wine-glass.svg b/resources/fontawesome/svgs/solid/wine-glass.svg deleted file mode 100644 index e560f7b..0000000 --- a/resources/fontawesome/svgs/solid/wine-glass.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/won-sign.svg b/resources/fontawesome/svgs/solid/won-sign.svg deleted file mode 100644 index aa6b23c..0000000 --- a/resources/fontawesome/svgs/solid/won-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/worm.svg b/resources/fontawesome/svgs/solid/worm.svg deleted file mode 100644 index 2b0ef9f..0000000 --- a/resources/fontawesome/svgs/solid/worm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/wrench.svg b/resources/fontawesome/svgs/solid/wrench.svg deleted file mode 100644 index a0db05c..0000000 --- a/resources/fontawesome/svgs/solid/wrench.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/x-ray.svg b/resources/fontawesome/svgs/solid/x-ray.svg deleted file mode 100644 index a3b995f..0000000 --- a/resources/fontawesome/svgs/solid/x-ray.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/x.svg b/resources/fontawesome/svgs/solid/x.svg deleted file mode 100644 index 18b441b..0000000 --- a/resources/fontawesome/svgs/solid/x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/xmark.svg b/resources/fontawesome/svgs/solid/xmark.svg deleted file mode 100644 index 331cd75..0000000 --- a/resources/fontawesome/svgs/solid/xmark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/xmarks-lines.svg b/resources/fontawesome/svgs/solid/xmarks-lines.svg deleted file mode 100644 index 7af2144..0000000 --- a/resources/fontawesome/svgs/solid/xmarks-lines.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/y.svg b/resources/fontawesome/svgs/solid/y.svg deleted file mode 100644 index c533b52..0000000 --- a/resources/fontawesome/svgs/solid/y.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/yen-sign.svg b/resources/fontawesome/svgs/solid/yen-sign.svg deleted file mode 100644 index 73eb394..0000000 --- a/resources/fontawesome/svgs/solid/yen-sign.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/yin-yang.svg b/resources/fontawesome/svgs/solid/yin-yang.svg deleted file mode 100644 index 72899e4..0000000 --- a/resources/fontawesome/svgs/solid/yin-yang.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/svgs/solid/z.svg b/resources/fontawesome/svgs/solid/z.svg deleted file mode 100644 index 97b59ae..0000000 --- a/resources/fontawesome/svgs/solid/z.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/fontawesome/webfonts/fa-brands-400.ttf b/resources/fontawesome/webfonts/fa-brands-400.ttf deleted file mode 100644 index 1fbb1f7..0000000 Binary files a/resources/fontawesome/webfonts/fa-brands-400.ttf and /dev/null differ diff --git a/resources/fontawesome/webfonts/fa-brands-400.woff2 b/resources/fontawesome/webfonts/fa-brands-400.woff2 deleted file mode 100644 index 5d28021..0000000 Binary files a/resources/fontawesome/webfonts/fa-brands-400.woff2 and /dev/null differ diff --git a/resources/fontawesome/webfonts/fa-regular-400.ttf b/resources/fontawesome/webfonts/fa-regular-400.ttf deleted file mode 100644 index 549d68d..0000000 Binary files a/resources/fontawesome/webfonts/fa-regular-400.ttf and /dev/null differ diff --git a/resources/fontawesome/webfonts/fa-regular-400.woff2 b/resources/fontawesome/webfonts/fa-regular-400.woff2 deleted file mode 100644 index 18400d7..0000000 Binary files a/resources/fontawesome/webfonts/fa-regular-400.woff2 and /dev/null differ diff --git a/resources/fontawesome/webfonts/fa-solid-900.ttf b/resources/fontawesome/webfonts/fa-solid-900.ttf deleted file mode 100644 index bb2a869..0000000 Binary files a/resources/fontawesome/webfonts/fa-solid-900.ttf and /dev/null differ diff --git a/resources/fontawesome/webfonts/fa-solid-900.woff2 b/resources/fontawesome/webfonts/fa-solid-900.woff2 deleted file mode 100644 index 758dd4f..0000000 Binary files a/resources/fontawesome/webfonts/fa-solid-900.woff2 and /dev/null differ diff --git a/resources/fontawesome/webfonts/fa-v4compatibility.ttf b/resources/fontawesome/webfonts/fa-v4compatibility.ttf deleted file mode 100644 index 8c5864c..0000000 Binary files a/resources/fontawesome/webfonts/fa-v4compatibility.ttf and /dev/null differ diff --git a/resources/fontawesome/webfonts/fa-v4compatibility.woff2 b/resources/fontawesome/webfonts/fa-v4compatibility.woff2 deleted file mode 100644 index f94bec2..0000000 Binary files a/resources/fontawesome/webfonts/fa-v4compatibility.woff2 and /dev/null differ diff --git a/resources/html/loading-page.html b/resources/html/loading-page.html deleted file mode 100644 index 8f9ec8a..0000000 --- a/resources/html/loading-page.html +++ /dev/null @@ -1,22 +0,0 @@ - -
-
- L - Q - D - O - J -
-
\ No newline at end of file diff --git a/resources/icons/android-chrome-192x192.png b/resources/icons/android-chrome-192x192.png index eb09e5e..16b398b 100644 Binary files a/resources/icons/android-chrome-192x192.png and b/resources/icons/android-chrome-192x192.png differ diff --git a/resources/icons/android-chrome-256x256.png b/resources/icons/android-chrome-256x256.png new file mode 100644 index 0000000..5097fb9 Binary files /dev/null and b/resources/icons/android-chrome-256x256.png differ diff --git a/resources/icons/android-chrome-512x512.png b/resources/icons/android-chrome-512x512.png deleted file mode 100644 index 036f5e0..0000000 Binary files a/resources/icons/android-chrome-512x512.png and /dev/null differ diff --git a/resources/icons/apple-touch-icon.png b/resources/icons/apple-touch-icon.png index a4c7071..36adbaf 100644 Binary files a/resources/icons/apple-touch-icon.png and b/resources/icons/apple-touch-icon.png differ diff --git a/resources/icons/favicon-16x16.png b/resources/icons/favicon-16x16.png index 8352cfa..fa97db0 100644 Binary files a/resources/icons/favicon-16x16.png and b/resources/icons/favicon-16x16.png differ diff --git a/resources/icons/favicon-32x32.png b/resources/icons/favicon-32x32.png index f8f3384..923e7fe 100644 Binary files a/resources/icons/favicon-32x32.png and b/resources/icons/favicon-32x32.png differ diff --git a/resources/icons/favicon.ico b/resources/icons/favicon.ico index b1a4cae..d022282 100644 Binary files a/resources/icons/favicon.ico and b/resources/icons/favicon.ico differ diff --git a/resources/icons/icon.png b/resources/icons/icon.png index b7f38f0..4c9b81a 100644 Binary files a/resources/icons/icon.png and b/resources/icons/icon.png differ diff --git a/resources/icons/icon.svg b/resources/icons/icon.svg deleted file mode 100644 index f16e119..0000000 --- a/resources/icons/icon.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/icons/logo.png b/resources/icons/logo.png index fa74627..4e1a813 100644 Binary files a/resources/icons/logo.png and b/resources/icons/logo.png differ diff --git a/resources/icons/logo.svg b/resources/icons/logo.svg deleted file mode 100644 index 6df0467..0000000 --- a/resources/icons/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/icons/manifest.json b/resources/icons/manifest.json deleted file mode 100644 index 3b25a60..0000000 --- a/resources/icons/manifest.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "LQDOJ", - "short_name": "LQDOJ", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/resources/icons/mstile-150x150.png b/resources/icons/mstile-150x150.png new file mode 100644 index 0000000..ee7329f Binary files /dev/null and b/resources/icons/mstile-150x150.png differ diff --git a/resources/icons/old/android-chrome-144x144.png b/resources/icons/old/android-chrome-144x144.png new file mode 100644 index 0000000..6a5f355 Binary files /dev/null and b/resources/icons/old/android-chrome-144x144.png differ diff --git a/resources/icons/old/android-chrome-192x192.png b/resources/icons/old/android-chrome-192x192.png new file mode 100644 index 0000000..86ba0f1 Binary files /dev/null and b/resources/icons/old/android-chrome-192x192.png differ diff --git a/resources/icons/old/android-chrome-36x36.png b/resources/icons/old/android-chrome-36x36.png new file mode 100644 index 0000000..1afb5d0 Binary files /dev/null and b/resources/icons/old/android-chrome-36x36.png differ diff --git a/resources/icons/old/android-chrome-48x48.png b/resources/icons/old/android-chrome-48x48.png new file mode 100644 index 0000000..373c603 Binary files /dev/null and b/resources/icons/old/android-chrome-48x48.png differ diff --git a/resources/icons/old/android-chrome-72x72.png b/resources/icons/old/android-chrome-72x72.png new file mode 100644 index 0000000..47ea2ce Binary files /dev/null and b/resources/icons/old/android-chrome-72x72.png differ diff --git a/resources/icons/old/android-chrome-96x96.png b/resources/icons/old/android-chrome-96x96.png new file mode 100644 index 0000000..5d553bf Binary files /dev/null and b/resources/icons/old/android-chrome-96x96.png differ diff --git a/resources/icons/old/apple-touch-icon-114x114-precomposed.png b/resources/icons/old/apple-touch-icon-114x114-precomposed.png new file mode 100644 index 0000000..5764db5 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-114x114-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-114x114.png b/resources/icons/old/apple-touch-icon-114x114.png new file mode 100644 index 0000000..4bb4160 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-114x114.png differ diff --git a/resources/icons/old/apple-touch-icon-120x120-precomposed.png b/resources/icons/old/apple-touch-icon-120x120-precomposed.png new file mode 100644 index 0000000..2ee5716 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-120x120-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-120x120.png b/resources/icons/old/apple-touch-icon-120x120.png new file mode 100644 index 0000000..63a1fdd Binary files /dev/null and b/resources/icons/old/apple-touch-icon-120x120.png differ diff --git a/resources/icons/old/apple-touch-icon-144x144-precomposed.png b/resources/icons/old/apple-touch-icon-144x144-precomposed.png new file mode 100644 index 0000000..2fd4d8e Binary files /dev/null and b/resources/icons/old/apple-touch-icon-144x144-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-144x144.png b/resources/icons/old/apple-touch-icon-144x144.png new file mode 100644 index 0000000..cc16c9f Binary files /dev/null and b/resources/icons/old/apple-touch-icon-144x144.png differ diff --git a/resources/icons/old/apple-touch-icon-152x152-precomposed.png b/resources/icons/old/apple-touch-icon-152x152-precomposed.png new file mode 100644 index 0000000..1f6ea27 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-152x152-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-152x152.png b/resources/icons/old/apple-touch-icon-152x152.png new file mode 100644 index 0000000..300b82f Binary files /dev/null and b/resources/icons/old/apple-touch-icon-152x152.png differ diff --git a/resources/icons/old/apple-touch-icon-180x180-precomposed.png b/resources/icons/old/apple-touch-icon-180x180-precomposed.png new file mode 100644 index 0000000..8553085 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-180x180-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-180x180.png b/resources/icons/old/apple-touch-icon-180x180.png new file mode 100644 index 0000000..d737f87 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-180x180.png differ diff --git a/resources/icons/old/apple-touch-icon-57x57-precomposed.png b/resources/icons/old/apple-touch-icon-57x57-precomposed.png new file mode 100644 index 0000000..deb2048 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-57x57-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-57x57.png b/resources/icons/old/apple-touch-icon-57x57.png new file mode 100644 index 0000000..2b2533d Binary files /dev/null and b/resources/icons/old/apple-touch-icon-57x57.png differ diff --git a/resources/icons/old/apple-touch-icon-60x60-precomposed.png b/resources/icons/old/apple-touch-icon-60x60-precomposed.png new file mode 100644 index 0000000..6b16cb1 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-60x60-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-60x60.png b/resources/icons/old/apple-touch-icon-60x60.png new file mode 100644 index 0000000..7c86aba Binary files /dev/null and b/resources/icons/old/apple-touch-icon-60x60.png differ diff --git a/resources/icons/old/apple-touch-icon-72x72-precomposed.png b/resources/icons/old/apple-touch-icon-72x72-precomposed.png new file mode 100644 index 0000000..85fa920 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-72x72-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-72x72.png b/resources/icons/old/apple-touch-icon-72x72.png new file mode 100644 index 0000000..3aa3aff Binary files /dev/null and b/resources/icons/old/apple-touch-icon-72x72.png differ diff --git a/resources/icons/old/apple-touch-icon-76x76-precomposed.png b/resources/icons/old/apple-touch-icon-76x76-precomposed.png new file mode 100644 index 0000000..abaf04a Binary files /dev/null and b/resources/icons/old/apple-touch-icon-76x76-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon-76x76.png b/resources/icons/old/apple-touch-icon-76x76.png new file mode 100644 index 0000000..a2d3734 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-76x76.png differ diff --git a/resources/icons/old/apple-touch-icon-precomposed.png b/resources/icons/old/apple-touch-icon-precomposed.png new file mode 100644 index 0000000..8553085 Binary files /dev/null and b/resources/icons/old/apple-touch-icon-precomposed.png differ diff --git a/resources/icons/old/apple-touch-icon.png b/resources/icons/old/apple-touch-icon.png new file mode 100644 index 0000000..e537d48 Binary files /dev/null and b/resources/icons/old/apple-touch-icon.png differ diff --git a/resources/icons/old/browserconfig.xml b/resources/icons/old/browserconfig.xml new file mode 100644 index 0000000..df8a72d --- /dev/null +++ b/resources/icons/old/browserconfig.xml @@ -0,0 +1,11 @@ + + + + + + + + #da532c + + + diff --git a/resources/icons/old/favicon-16x16.png b/resources/icons/old/favicon-16x16.png new file mode 100644 index 0000000..aa2cc02 Binary files /dev/null and b/resources/icons/old/favicon-16x16.png differ diff --git a/resources/icons/old/favicon-32x32.png b/resources/icons/old/favicon-32x32.png new file mode 100644 index 0000000..6358f95 Binary files /dev/null and b/resources/icons/old/favicon-32x32.png differ diff --git a/resources/icons/old/favicon.ico b/resources/icons/old/favicon.ico new file mode 100644 index 0000000..06d5fd9 Binary files /dev/null and b/resources/icons/old/favicon.ico differ diff --git a/resources/icons/old/logo_old.png b/resources/icons/old/logo_old.png new file mode 100644 index 0000000..9eb296a Binary files /dev/null and b/resources/icons/old/logo_old.png differ diff --git a/resources/icons/old/mstile-144x144.png b/resources/icons/old/mstile-144x144.png new file mode 100644 index 0000000..c005af3 Binary files /dev/null and b/resources/icons/old/mstile-144x144.png differ diff --git a/resources/icons/old/mstile-150x150.png b/resources/icons/old/mstile-150x150.png new file mode 100644 index 0000000..800d2b7 Binary files /dev/null and b/resources/icons/old/mstile-150x150.png differ diff --git a/resources/icons/old/mstile-310x150.png b/resources/icons/old/mstile-310x150.png new file mode 100644 index 0000000..ffd4452 Binary files /dev/null and b/resources/icons/old/mstile-310x150.png differ diff --git a/resources/icons/old/mstile-310x310.png b/resources/icons/old/mstile-310x310.png new file mode 100644 index 0000000..cbf683a Binary files /dev/null and b/resources/icons/old/mstile-310x310.png differ diff --git a/resources/icons/old/mstile-70x70.png b/resources/icons/old/mstile-70x70.png new file mode 100644 index 0000000..86a935d Binary files /dev/null and b/resources/icons/old/mstile-70x70.png differ diff --git a/resources/icons/old/safari-pinned-tab.svg b/resources/icons/old/safari-pinned-tab.svg new file mode 100644 index 0000000..3b41b63 --- /dev/null +++ b/resources/icons/old/safari-pinned-tab.svg @@ -0,0 +1,58 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/resources/icons/safari-pinned-tab.svg b/resources/icons/safari-pinned-tab.svg new file mode 100644 index 0000000..c197c88 --- /dev/null +++ b/resources/icons/safari-pinned-tab.svg @@ -0,0 +1,30 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + diff --git a/resources/icons/site.webmanifest b/resources/icons/site.webmanifest index 45dc8a2..de65106 100644 --- a/resources/icons/site.webmanifest +++ b/resources/icons/site.webmanifest @@ -1 +1,19 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-256x256.png", + "sizes": "256x256", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/resources/ide.css b/resources/ide.css new file mode 100644 index 0000000..3f23562 --- /dev/null +++ b/resources/ide.css @@ -0,0 +1,166 @@ +html, body { + height: 100%; + min-height: 100%; + margin: 0; + padding: 0; +} + +body { + background: url(/images/judge0_background.png) no-repeat center center fixed; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} + +.ui[class*="left action"].input>.dropdown { + border-radius: 0 .28571429rem .28571429rem 0; +} + +.ui.selection.dropdown { + width: 205px; +} + +.lm_header .lm_tab { + padding-bottom: 3px; + height: 16px; + font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; + font-size: 14px; +} + +.lm_header .lm_tab.lm_active { + box-shadow: none; + padding-bottom: 3px; +} + +#site-navigation { + border-radius: 0; + margin: 0; + background: #1e1e1e; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + height: 45px; +} + +#navigation-message { + font-size: 1.2em; + white-space: nowrap; + overflow: hidden; +} + +@keyframes slide { + 0% { transform: translateX(570px); } + 100% { transform: translateX(-100%); } +} +@-webkit-keyframes slide { + 0% { transform: translateX(570px); } + 100% { transform: translateX(-100%); } +} + +.navigation-message-text { + white-space: nowrap; + -moz-animation: slide 60s linear infinite; + -webkit-animation: slide 60s linear infinite; + animation: slide 60s linear infinite; +} + +.navigation-message-text:hover { + -moz-animation-play-state: paused; + -webkit-animation-play-state: paused; + animation-play-state: paused; +} + +#navigation-message a { + color: #41a5f1; + font-weight: bold; +} + +#site-icon { + height: 40px; + vertical-align: middle; +} + +#site-header { + padding-top: 0; + padding-bottom: 0; +} + +#site-header h2 { + display: inline; + vertical-align: middle; + font-family: 'Exo 2', sans-serif; +} + +#site-content { + height: calc(100% - 45px - 19px); +} + +#site-modal .header { + color: #db2828; +} + +#site-footer { + background-color: darkslategrey; + bottom: 0; + font-family: monospace, monospace, "Droid Sans Fallback"; + font-size: 13px; + height: 19px; + padding-left: 16px; + padding-right: 16px; + position: fixed; +} + +#site-footer { + color: gray; + left: 0; + width: 100%; +} + +#editor-status-line { + background: transparent; + color: #fff; + font-family: monospace; + height: 19px; + padding: 0px 16px; + width: inherit; +} + +#editor-status-line input[type=text] { + background: transparent; + border: none; + color: #fff; + font-family: monospace; + outline: none; +} + +#editor-status-line input[type=text]::selection { + background-color: #cce2ff; + color: #fff; +} + +.blink { + animation: blinker 1s linear infinite; +} + +@keyframes blinker { + 50% { + background: #FFD700; + } +} + +.dot { + background: #0E6EB8; + position: absolute; + border-radius: 50px; + width: 5px; + height: 5px; + right: 7.5px; + top: 7.5px; +} + +.no-left-padding { + padding-left: 0 !important; +} + +input[type="text"] { + width: 205px !important; +} \ No newline at end of file diff --git a/resources/ide.js b/resources/ide.js new file mode 100644 index 0000000..08020c9 --- /dev/null +++ b/resources/ide.js @@ -0,0 +1,868 @@ +var wait = true; +var check_timeout = 300; + +var editorMode = localStorageGetItem("editorMode") || "normal"; +var redirectStderrToStdout = ((localStorageGetItem("redirectStderrToStdout") || "false") === "true"); +var editorModeObject = null; + +var fontSize = 14; + +var MonacoVim; +var MonacoEmacs; + +var layout; + +var sourceEditor; +var stdinEditor; +var stdoutEditor; +var stderrEditor; + +var isEditorDirty = false; +var currentLanguageId; + +var $selectLanguage +var $selectTheme +var $insertTemplateBtn; +var $runBtn; + +var timeStart; +var timeEnd; + +var layoutConfig = { + settings: { + showPopoutIcon: false, + reorderEnabled: true + }, + dimensions: { + borderWidth: 3, + headerHeight: 22 + }, + content: [{ + type: "row", + content: [{ + type: "component", + componentName: "source", + title: "SOURCE", + isClosable: false, + componentState: { + readOnly: false + }, + width: 60 + }, { + type: "column", + content: [{ + type: "stack", + content: [{ + type: "component", + componentName: "stdin", + title: "STDIN", + isClosable: false, + componentState: { + readOnly: false + } + }] + }, { + type: "stack", + content: [{ + type: "component", + componentName: "stdout", + title: "STDOUT", + isClosable: false, + componentState: { + readOnly: true + } + }, { + type: "component", + componentName: "stderr", + title: "STDERR", + isClosable: false, + componentState: { + readOnly: true + } + }, + ] + }] + }] + }] +}; + +function encode(str) { + return btoa(unescape(encodeURIComponent(str || ""))); +} + +function decode(bytes) { + var escaped = escape(atob(bytes || "")); + try { + return decodeURIComponent(escaped); + } catch { + return unescape(escaped); + } +} + +function localStorageSetItem(key, value) { + try { + localStorage.setItem(key, value); + } catch (ignorable) { + } +} + +function localStorageGetItem(key) { + try { + return localStorage.getItem(key); + } catch (ignorable) { + return null; + } +} + + +function showError(title, content) { + $("#site-modal #title").html(title); + $("#site-modal .content").html(content); + $("#site-modal").modal("show"); +} + +function handleError(jqXHR, textStatus, errorThrown) { + showError(`${jqXHR.statusText} (${jqXHR.status})`, `
${JSON.stringify(jqXHR, null, 4)}
`); +} + +function handleRunError(jqXHR, textStatus, errorThrown) { + handleError(jqXHR, textStatus, errorThrown); + $runBtn.removeClass("loading"); +} + +function handleResult(data) { + timeEnd = performance.now(); + + var status = data.status; + var stdout = decode(data.stdout); + var stderr = decode(data.stderr); + var compile_output = decode(data.compile_output); + var time = (data.time === null ? "-" : data.time + "s"); + var memory = (data.memory === null ? "-" : data.memory + "KB"); + + stdoutEditor.setValue(compile_output + stdout + `\n[${status.description}, ${time}, ${memory}]`); + stderrEditor.setValue(stderr); + + if (stdout !== "") { + var dot = document.getElementById("stdout-dot"); + if (!dot.parentElement.classList.contains("lm_active")) { + dot.hidden = false; + } + } + if (stderr !== "") { + var dot = document.getElementById("stderr-dot"); + if (!dot.parentElement.classList.contains("lm_active")) { + dot.hidden = false; + } + } + + $runBtn.removeClass("loading"); +} + +function getIdFromURI() { + var uri = location.search.substr(1).trim(); + return uri.split("&")[0]; +} + +function downloadSource() { + var value = parseInt($selectLanguage.val()); + download(sourceEditor.getValue(), fileNames[value], "text/plain"); +} + +function loadSavedSource() { + snippet_id = getIdFromURI(); + + if (snippet_id.length == 36) { + $.ajax({ + url: apiUrl + "/submissions/" + snippet_id + "?fields=source_code,language_id,stdin,stdout,stderr,compile_output,message,time,memory,status&base64_encoded=true", + type: "GET", + success: function(data, textStatus, jqXHR) { + sourceEditor.setValue(decode(data["source_code"])); + $selectLanguage.dropdown("set selected", data["language_id"]); + stdinEditor.setValue(decode(data["stdin"])); + stderrEditor.setValue(decode(data["stderr"])); + var time = (data.time === null ? "-" : data.time + "s"); + var memory = (data.memory === null ? "-" : data.memory + "KB"); + stdoutEditor.setValue(decode(data["compile_output"]) + decode(data["stdout"]) + `\n[${data.status.description}, ${time}, ${memory}]`); + changeEditorLanguage(); + }, + error: handleRunError + }); + } else { + loadRandomLanguage(); + } +} + +function run() { + if (sourceEditor.getValue().trim() === "") { + showError("Error", "Source code can't be empty!"); + return; + } else { + $runBtn.addClass("loading"); + } + + document.getElementById("stdout-dot").hidden = true; + document.getElementById("stderr-dot").hidden = true; + + stdoutEditor.setValue(""); + stderrEditor.setValue(""); + + var sourceValue = encode(sourceEditor.getValue()); + var stdinValue = encode(stdinEditor.getValue()); + var languageId = resolveLanguageId($selectLanguage.val()); + + if (parseInt(languageId) === 44) { + sourceValue = sourceEditor.getValue(); + } + + var data = { + source_code: sourceValue, + language_id: languageId, + stdin: stdinValue, + redirect_stderr_to_stdout: redirectStderrToStdout + }; + + var sendRequest = function(data) { + timeStart = performance.now(); + $.ajax({ + url: apiUrl + `/submissions?base64_encoded=true&wait=${wait}`, + type: "POST", + async: true, + contentType: "application/json", + data: JSON.stringify(data), + xhrFields: { + withCredentials: apiUrl.indexOf("/secure") != -1 ? true : false + }, + success: function (data, textStatus, jqXHR) { + console.log(data.token); + if (wait == true) { + handleResult(data); + } else { + setTimeout(fetchSubmission.bind(null, data.token), check_timeout); + } + }, + error: handleRunError + }); + } + + sendRequest(data); +} + +function fetchSubmission(submission_token) { + $.ajax({ + url: apiUrl + "/submissions/" + submission_token + "?base64_encoded=true", + type: "GET", + async: true, + success: function (data, textStatus, jqXHR) { + if (data.status.id <= 2) { // In Queue or Processing + setTimeout(fetchSubmission.bind(null, submission_token), check_timeout); + return; + } + handleResult(data); + }, + error: handleRunError + }); +} + +function changeEditorLanguage() { + monaco.editor.setModelLanguage(sourceEditor.getModel(), $selectLanguage.find(":selected").attr("mode")); + currentLanguageId = parseInt($selectLanguage.val()); + $(".lm_title")[0].innerText = fileNames[currentLanguageId]; + apiUrl = resolveApiUrl($selectLanguage.val()); +} + +function insertTemplate() { + currentLanguageId = parseInt($selectLanguage.val()); + sourceEditor.setValue(sources[currentLanguageId]); + changeEditorLanguage(); +} + +function loadRandomLanguage() { + var values = []; + for (var i = 0; i < $selectLanguage[0].options.length; ++i) { + values.push($selectLanguage[0].options[i].value); + } + // $selectLanguage.dropdown("set selected", values[Math.floor(Math.random() * $selectLanguage[0].length)]); + $selectLanguage.dropdown("set selected", values[9]); + apiUrl = resolveApiUrl($selectLanguage.val()); + insertTemplate(); +} + +function resizeEditor(layoutInfo) { + if (editorMode != "normal") { + var statusLineHeight = $("#editor-status-line").height(); + layoutInfo.height -= statusLineHeight; + layoutInfo.contentHeight -= statusLineHeight; + } +} + +function disposeEditorModeObject() { + try { + editorModeObject.dispose(); + editorModeObject = null; + } catch(ignorable) { + } +} + +function changeEditorMode() { + disposeEditorModeObject(); + + if (editorMode == "vim") { + editorModeObject = MonacoVim.initVimMode(sourceEditor, $("#editor-status-line")[0]); + } else if (editorMode == "emacs") { + var statusNode = $("#editor-status-line")[0]; + editorModeObject = new MonacoEmacs.EmacsExtension(sourceEditor); + editorModeObject.onDidMarkChange(function(e) { + statusNode.textContent = e ? "Mark Set!" : "Mark Unset"; + }); + editorModeObject.onDidChangeKey(function(str) { + statusNode.textContent = str; + }); + editorModeObject.start(); + } +} + +function resolveLanguageId(id) { + id = parseInt(id); + return id; +} + +function resolveApiUrl(id) { + id = parseInt(id); + return apiUrl; +} + +function editorsUpdateFontSize(fontSize) { + sourceEditor.updateOptions({fontSize: fontSize}); + stdinEditor.updateOptions({fontSize: fontSize}); + stdoutEditor.updateOptions({fontSize: fontSize}); + stderrEditor.updateOptions({fontSize: fontSize}); +} + +function getDefaultTheme() { + return localStorageGetItem('editor-theme') || 'vs-dark'; +} + +function editorsUpdateTheme(isInit) { + var theme = $selectTheme.val(); + if (isInit) { + theme = getDefaultTheme(); + $selectTheme.val(theme).change(); + } + else localStorageSetItem('editor-theme', theme); + + var $siteNavigation = $("#site-navigation"); + if (theme == 'vs') { + $siteNavigation.removeClass('inverted'); + $siteNavigation.css('background', 'white'); + } + else { + $("#site-navigation").addClass('inverted'); + $siteNavigation.css('background', '#1e1e1e'); + } + sourceEditor.updateOptions({theme: theme}); + stdinEditor.updateOptions({theme: theme}); + stdoutEditor.updateOptions({theme: theme}); + stderrEditor.updateOptions({theme: theme}); +} + +function updateScreenElements() { + var display = window.innerWidth <= 1200 ? "none" : ""; + $(".wide.screen.only").each(function(index) { + $(this).css("display", display); + }); +} + +$(window).resize(function() { + layout.updateSize(); + updateScreenElements(); +}); + +$(document).ready(function () { + updateScreenElements(); + + $selectLanguage = $("#select-language"); + $selectLanguage.change(function (e) { + if (!isEditorDirty) { + insertTemplate(); + } else { + changeEditorLanguage(); + } + }); + + $selectTheme = $("#select-theme"); + $selectTheme.change(function(e) { + editorsUpdateTheme(false); + }); + + $insertTemplateBtn = $("#insert-template-btn"); + $insertTemplateBtn.click(function (e) { + if (isEditorDirty && confirm("Are you sure? Your current changes will be lost.")) { + insertTemplate(); + } + }); + + $runBtn = $("#run-btn"); + $runBtn.click(function (e) { + run(); + }); + + $(`input[name="editor-mode"][value="${editorMode}"]`).prop("checked", true); + $("input[name=\"editor-mode\"]").on("change", function(e) { + editorMode = e.target.value; + localStorageSetItem("editorMode", editorMode); + + resizeEditor(sourceEditor.getLayoutInfo()); + changeEditorMode(); + + sourceEditor.focus(); + }); + + $("input[name=\"redirect-output\"]").prop("checked", redirectStderrToStdout) + $("input[name=\"redirect-output\"]").on("change", function(e) { + redirectStderrToStdout = e.target.checked; + localStorageSetItem("redirectStderrToStdout", redirectStderrToStdout); + }); + + $("body").keydown(function (e) { + var keyCode = e.keyCode || e.which; + if (keyCode == 120 || (event.ctrlKey && keyCode == 66)) { // F9 || ctrl B + e.preventDefault(); + run(); + } else if (event.ctrlKey && (keyCode == 107 || keyCode == 187)) { // Ctrl++ + e.preventDefault(); + fontSize += 1; + editorsUpdateFontSize(fontSize); + } else if (event.ctrlKey && (keyCode == 107 || keyCode == 189)) { // Ctrl+- + e.preventDefault(); + fontSize -= 1; + editorsUpdateFontSize(fontSize); + } + }); + + $("select.dropdown").dropdown(); + $(".ui.dropdown").dropdown(); + $(".ui.dropdown.site-links").dropdown({action: "hide", on: "hover"}); + $(".ui.checkbox").checkbox(); + $(".message .close").on("click", function () { + $(this).closest(".message").transition("fade"); + }); + + require(["vs/editor/editor.main", "monaco-vim", "monaco-emacs"], function (ignorable, MVim, MEmacs) { + layout = new GoldenLayout(layoutConfig, $("#site-content")); + + MonacoVim = MVim; + MonacoEmacs = MEmacs; + + layout.registerComponent("source", function (container, state) { + sourceEditor = monaco.editor.create(container.getElement()[0], { + automaticLayout: true, + theme: getDefaultTheme(), + scrollBeyondLastLine: true, + readOnly: state.readOnly, + language: "cpp", + minimap: { + enabled: false + }, + matchBrackets: false, + }); + + changeEditorMode(); + + sourceEditor.getModel().onDidChangeContent(function (e) { + currentLanguageId = parseInt($selectLanguage.val()); + isEditorDirty = sourceEditor.getValue() != sources[currentLanguageId]; + }); + + sourceEditor.onDidLayoutChange(resizeEditor); + + sourceEditor.onDidChangeCursorPosition(function(e) { + var line = sourceEditor.getPosition().lineNumber; + var col = sourceEditor.getPosition().column; + $('#cursor-position').html(`Line ${line}, Column ${col}`) + }) + }); + + layout.registerComponent("stdin", function (container, state) { + stdinEditor = monaco.editor.create(container.getElement()[0], { + automaticLayout: true, + theme: getDefaultTheme(), + scrollBeyondLastLine: false, + readOnly: state.readOnly, + language: "plaintext", + minimap: { + enabled: false + }, + wordWrap: "on", + }); + }); + + layout.registerComponent("stdout", function (container, state) { + stdoutEditor = monaco.editor.create(container.getElement()[0], { + automaticLayout: true, + theme: getDefaultTheme(), + scrollBeyondLastLine: false, + readOnly: state.readOnly, + language: "plaintext", + minimap: { + enabled: false + }, + wordWrap: "on", + }); + + container.on("tab", function(tab) { + tab.element.append(""); + tab.element.on("mousedown", function(e) { + e.target.closest(".lm_tab").children[3].hidden = true; + }); + }); + }); + + layout.registerComponent("stderr", function (container, state) { + stderrEditor = monaco.editor.create(container.getElement()[0], { + automaticLayout: true, + theme: getDefaultTheme(), + scrollBeyondLastLine: false, + readOnly: state.readOnly, + language: "plaintext", + minimap: { + enabled: false + }, + wordWrap: "on", + }); + + container.on("tab", function(tab) { + tab.element.append(""); + tab.element.on("mousedown", function(e) { + e.target.closest(".lm_tab").children[3].hidden = true; + }); + }); + }); + + layout.on("initialised", function () { + $(".monaco-editor")[0].appendChild($("#editor-status-line")[0]); + if (getIdFromURI()) { + loadSavedSource(); + } else { + loadRandomLanguage(); + } + $("#site-navigation").css("border-bottom", "1px solid black"); + sourceEditor.focus(); + editorsUpdateFontSize(fontSize); + editorsUpdateTheme(true); + }); + + layout.init(); + }); +}); + +// Template Sources +var assemblySource = "\ +section .text\n\ + global _start\n\ +\n\ +_start:\n\ +\n\ + xor eax, eax\n\ + lea edx, [rax+len]\n\ + mov al, 1\n\ + mov esi, msg\n\ + mov edi, eax\n\ + syscall\n\ +\n\ + xor edi, edi\n\ + lea eax, [rdi+60]\n\ + syscall\n\ +\n\ +section .rodata\n\ +\n\ +msg db 'hello, world', 0xa\n\ +len equ $ - msg\n\ +"; + +var bashSource = "echo \"hello, world\""; + +var basicSource = "PRINT \"hello, world\""; + +var cSource = "\ +// Powered by Judge0\n\ +#include \n\ +\n\ +int main(void) {\n\ + printf(\"Hello Judge0!\\n\");\n\ + return 0;\n\ +}\n\ +"; + +var csharpSource = "\ +public class Hello {\n\ + public static void Main() {\n\ + System.Console.WriteLine(\"hello, world\");\n\ + }\n\ +}\n\ +"; + +var cppSource = "\ +#include \n\ +\n\ +int main() {\n\ + std::cout << \"hello, world\" << std::endl;\n\ + return 0;\n\ +}\n\ +"; + +var clojureSource = "(println \"hello, world\")\n"; + +var cobolSource = "\ +IDENTIFICATION DIVISION.\n\ +PROGRAM-ID. MAIN.\n\ +PROCEDURE DIVISION.\n\ +DISPLAY \"hello, world\".\n\ +STOP RUN.\n\ +"; + +var lispSource = "(write-line \"hello, world\")"; + +var dSource = "\ +import std.stdio;\n\ +\n\ +void main()\n\ +{\n\ + writeln(\"hello, world\");\n\ +}\n\ +"; + +var elixirSource = "IO.puts \"hello, world\""; + +var erlangSource = "\ +main(_) ->\n\ + io:fwrite(\"hello, world\\n\").\n\ +"; + +var executableSource = "\ +Judge0 IDE assumes that content of executable is Base64 encoded.\n\ +\n\ +This means that you should Base64 encode content of your binary,\n\ +paste it here and click \"Run\".\n\ +\n\ +Here is an example of compiled \"hello, world\" NASM program.\n\ +Content of compiled binary is Base64 encoded and used as source code.\n\ +\n\ +https://ide.judge0.com/?kS_f\n\ +"; + +var fsharpSource = "printfn \"hello, world\"\n"; + +var fortranSource = "\ +program main\n\ + print *, \"hello, world\"\n\ +end\n\ +"; + +var goSource = "\ +package main\n\ +\n\ +import \"fmt\"\n\ +\n\ +func main() {\n\ + fmt.Println(\"hello, world\")\n\ +}\n\ +"; + +var groovySource = "println \"hello, world\"\n"; + +var haskellSource = "main = putStrLn \"hello, world\""; + +var javaSource = "\ +public class Main {\n\ + public static void main(String[] args) {\n\ + System.out.println(\"hello, world\");\n\ + }\n\ +}\n\ +"; + +var javaScriptSource = "console.log(\"hello, world\");"; + +var kotlinSource = "\ +fun main() {\n\ + println(\"hello, world\")\n\ +}\n\ +"; + +var luaSource = "print(\"hello, world\")"; + +var objectiveCSource = "\ +#import \n\ +\n\ +int main() {\n\ + @autoreleasepool {\n\ + char name[10];\n\ + scanf(\"%s\", name);\n\ + NSString *message = [NSString stringWithFormat:@\"hello, %s\\n\", name];\n\ + printf(\"%s\", message.UTF8String);\n\ + }\n\ + return 0;\n\ +}\n\ +"; + +var ocamlSource = "print_endline \"hello, world\""; + +var octaveSource = "printf(\"hello, world\\n\");"; + +var pascalSource = "\ +program Hello;\n\ +begin\n\ + writeln ('hello, world')\n\ +end.\n\ +"; + +var perlSource = "\ +my $name = ;\n\ +print \"hello, $name\";\n\ +"; + +var phpSource = "\ +\n\ +"; + +var plainTextSource = "hello, world\n"; + +var prologSource = "\ +:- initialization(main).\n\ +main :- write('hello, world\\n').\n\ +"; + +var pythonSource = "print(\"hello, world\")"; + +var rSource = "cat(\"hello, world\\n\")"; + +var rubySource = "puts \"hello, world\""; + +var rustSource = "\ +fn main() {\n\ + println!(\"hello, world\");\n\ +}\n\ +"; + +var scalaSource = "\ +object Main {\n\ + def main(args: Array[String]) = {\n\ + val name = scala.io.StdIn.readLine()\n\ + println(\"hello, \"+ name)\n\ + }\n\ +}\n\ +"; + +var swiftSource = "\ +import Foundation\n\ +let name = readLine()\n\ +print(\"hello, \\(name!)\")\n\ +"; + +var typescriptSource = "console.log(\"hello, world\");"; + +var vbSource = "\ +Public Module Program\n\ + Public Sub Main()\n\ + Console.WriteLine(\"hello, world\")\n\ + End Sub\n\ +End Module\n\ +"; + +var sources = { + 45: assemblySource, + 46: bashSource, + 47: basicSource, + 48: cSource, + 49: cSource, + 50: cSource, + 51: csharpSource, + 52: cppSource, + 53: cppSource, + 54: cppSource, + 55: lispSource, + 56: dSource, + 57: elixirSource, + 58: erlangSource, + 44: executableSource, + 59: fortranSource, + 60: goSource, + 61: haskellSource, + 62: javaSource, + 63: javaScriptSource, + 64: luaSource, + 65: ocamlSource, + 66: octaveSource, + 67: pascalSource, + 68: phpSource, + 43: plainTextSource, + 69: prologSource, + 70: pythonSource, + 71: pythonSource, + 72: rubySource, + 73: rustSource, + 74: typescriptSource, + 75: cSource, + 76: cppSource, + 77: cobolSource, + 78: kotlinSource, + 79: objectiveCSource, + 80: rSource, + 81: scalaSource, + 83: swiftSource, + 84: vbSource, + 85: perlSource, + 86: clojureSource, + 87: fsharpSource, + 88: groovySource, +}; + +var fileNames = { + 45: "main.asm", + 46: "script.sh", + 47: "main.bas", + 48: "main.c", + 49: "main.c", + 50: "main.c", + 51: "Main.cs", + 52: "main.cpp", + 53: "main.cpp", + 54: "main.cpp", + 55: "script.lisp", + 56: "main.d", + 57: "script.exs", + 58: "main.erl", + 44: "a.out", + 59: "main.f90", + 60: "main.go", + 61: "main.hs", + 62: "Main.java", + 63: "script.js", + 64: "script.lua", + 65: "main.ml", + 66: "script.m", + 67: "main.pas", + 68: "script.php", + 43: "text.txt", + 69: "main.pro", + 70: "script.py", + 71: "script.py", + 72: "script.rb", + 73: "main.rs", + 74: "script.ts", + 75: "main.c", + 76: "main.cpp", + 77: "main.cob", + 78: "Main.kt", + 79: "main.m", + 80: "script.r", + 81: "Main.scala", + 83: "Main.swift", + 84: "Main.vb", + 85: "script.pl", + 86: "main.clj", + 87: "script.fsx", + 88: "script.groovy", +}; \ No newline at end of file diff --git a/resources/jszip/jszip.min.js b/resources/jszip/jszip.min.js deleted file mode 100644 index 62775e2..0000000 --- a/resources/jszip/jszip.min.js +++ /dev/null @@ -1,13 +0,0 @@ -/*! - -JSZip v3.10.0 - A JavaScript class for generating and reading zip files - - -(c) 2009-2016 Stuart Knightley -Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. - -JSZip uses the library pako released under the MIT license : -https://github.com/nodeca/pako/blob/main/LICENSE -*/ - -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(e){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(e){},lastIndexOfSignature:function(e){},readAndCheckSignature:function(e){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(e){if(this.extraFields[1]){var t=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=t.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=t.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=t.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=t.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r { - var maths = document.querySelectorAll('.arithmatex'), - tex; - for (var i = 0; i < maths.length; i++) { - tex = maths[i].textContent || maths[i].innerText; - if (tex.startsWith('\\(') && tex.endsWith('\\)')) { - katex.render(tex.slice(2, -2), maths[i], { - 'displayMode': false, - 'throwOnError': false, - 'strict': false, - }); - } else if (tex.startsWith('\\[') && tex.endsWith('\\]')) { - katex.render(tex.slice(2, -2), maths[i], { - 'displayMode': true, - 'throwOnError': false, - 'strict': false, - }); - } - } -} \ No newline at end of file diff --git a/resources/libs b/resources/libs new file mode 160000 index 0000000..2681309 --- /dev/null +++ b/resources/libs @@ -0,0 +1 @@ +Subproject commit 2681309a5ddd3482e699761488c75d9a62a626ec diff --git a/resources/libs/README.md b/resources/libs/README.md deleted file mode 100644 index 8f4e171..0000000 --- a/resources/libs/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# site-assets -DMOJ site assets. diff --git a/resources/libs/chart.js/Chart.js b/resources/libs/chart.js/Chart.js deleted file mode 100644 index 4c50e09..0000000 --- a/resources/libs/chart.js/Chart.js +++ /dev/null @@ -1,14680 +0,0 @@ -/*! - * Chart.js v2.8.0 - * https://www.chartjs.org - * (c) 2019 Chart.js Contributors - * Released under the MIT License - */ -(function (global, factory) { -typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function() { try { return require('moment'); } catch(e) { } }()) : -typeof define === 'function' && define.amd ? define(['require'], function(require) { return factory(function() { try { return require('moment'); } catch(e) { } }()); }) : -(global.Chart = factory(global.moment)); -}(this, (function (moment) { 'use strict'; - -moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment; - -/* MIT license */ - -var conversions = { - rgb2hsl: rgb2hsl, - rgb2hsv: rgb2hsv, - rgb2hwb: rgb2hwb, - rgb2cmyk: rgb2cmyk, - rgb2keyword: rgb2keyword, - rgb2xyz: rgb2xyz, - rgb2lab: rgb2lab, - rgb2lch: rgb2lch, - - hsl2rgb: hsl2rgb, - hsl2hsv: hsl2hsv, - hsl2hwb: hsl2hwb, - hsl2cmyk: hsl2cmyk, - hsl2keyword: hsl2keyword, - - hsv2rgb: hsv2rgb, - hsv2hsl: hsv2hsl, - hsv2hwb: hsv2hwb, - hsv2cmyk: hsv2cmyk, - hsv2keyword: hsv2keyword, - - hwb2rgb: hwb2rgb, - hwb2hsl: hwb2hsl, - hwb2hsv: hwb2hsv, - hwb2cmyk: hwb2cmyk, - hwb2keyword: hwb2keyword, - - cmyk2rgb: cmyk2rgb, - cmyk2hsl: cmyk2hsl, - cmyk2hsv: cmyk2hsv, - cmyk2hwb: cmyk2hwb, - cmyk2keyword: cmyk2keyword, - - keyword2rgb: keyword2rgb, - keyword2hsl: keyword2hsl, - keyword2hsv: keyword2hsv, - keyword2hwb: keyword2hwb, - keyword2cmyk: keyword2cmyk, - keyword2lab: keyword2lab, - keyword2xyz: keyword2xyz, - - xyz2rgb: xyz2rgb, - xyz2lab: xyz2lab, - xyz2lch: xyz2lch, - - lab2xyz: lab2xyz, - lab2rgb: lab2rgb, - lab2lch: lab2lch, - - lch2lab: lch2lab, - lch2xyz: lch2xyz, - lch2rgb: lch2rgb -}; - - -function rgb2hsl(rgb) { - var r = rgb[0]/255, - g = rgb[1]/255, - b = rgb[2]/255, - min = Math.min(r, g, b), - max = Math.max(r, g, b), - delta = max - min, - h, s, l; - - if (max == min) - h = 0; - else if (r == max) - h = (g - b) / delta; - else if (g == max) - h = 2 + (b - r) / delta; - else if (b == max) - h = 4 + (r - g)/ delta; - - h = Math.min(h * 60, 360); - - if (h < 0) - h += 360; - - l = (min + max) / 2; - - if (max == min) - s = 0; - else if (l <= 0.5) - s = delta / (max + min); - else - s = delta / (2 - max - min); - - return [h, s * 100, l * 100]; -} - -function rgb2hsv(rgb) { - var r = rgb[0], - g = rgb[1], - b = rgb[2], - min = Math.min(r, g, b), - max = Math.max(r, g, b), - delta = max - min, - h, s, v; - - if (max == 0) - s = 0; - else - s = (delta/max * 1000)/10; - - if (max == min) - h = 0; - else if (r == max) - h = (g - b) / delta; - else if (g == max) - h = 2 + (b - r) / delta; - else if (b == max) - h = 4 + (r - g) / delta; - - h = Math.min(h * 60, 360); - - if (h < 0) - h += 360; - - v = ((max / 255) * 1000) / 10; - - return [h, s, v]; -} - -function rgb2hwb(rgb) { - var r = rgb[0], - g = rgb[1], - b = rgb[2], - h = rgb2hsl(rgb)[0], - w = 1/255 * Math.min(r, Math.min(g, b)), - b = 1 - 1/255 * Math.max(r, Math.max(g, b)); - - return [h, w * 100, b * 100]; -} - -function rgb2cmyk(rgb) { - var r = rgb[0] / 255, - g = rgb[1] / 255, - b = rgb[2] / 255, - c, m, y, k; - - k = Math.min(1 - r, 1 - g, 1 - b); - c = (1 - r - k) / (1 - k) || 0; - m = (1 - g - k) / (1 - k) || 0; - y = (1 - b - k) / (1 - k) || 0; - return [c * 100, m * 100, y * 100, k * 100]; -} - -function rgb2keyword(rgb) { - return reverseKeywords[JSON.stringify(rgb)]; -} - -function rgb2xyz(rgb) { - var r = rgb[0] / 255, - g = rgb[1] / 255, - b = rgb[2] / 255; - - // assume sRGB - r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); - g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); - b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); - - var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); - - return [x * 100, y *100, z * 100]; -} - -function rgb2lab(rgb) { - var xyz = rgb2xyz(rgb), - x = xyz[0], - y = xyz[1], - z = xyz[2], - l, a, b; - - x /= 95.047; - y /= 100; - z /= 108.883; - - x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); - - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - - return [l, a, b]; -} - -function rgb2lch(args) { - return lab2lch(rgb2lab(args)); -} - -function hsl2rgb(hsl) { - var h = hsl[0] / 360, - s = hsl[1] / 100, - l = hsl[2] / 100, - t1, t2, t3, rgb, val; - - if (s == 0) { - val = l * 255; - return [val, val, val]; - } - - if (l < 0.5) - t2 = l * (1 + s); - else - t2 = l + s - l * s; - t1 = 2 * l - t2; - - rgb = [0, 0, 0]; - for (var i = 0; i < 3; i++) { - t3 = h + 1 / 3 * - (i - 1); - t3 < 0 && t3++; - t3 > 1 && t3--; - - if (6 * t3 < 1) - val = t1 + (t2 - t1) * 6 * t3; - else if (2 * t3 < 1) - val = t2; - else if (3 * t3 < 2) - val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; - else - val = t1; - - rgb[i] = val * 255; - } - - return rgb; -} - -function hsl2hsv(hsl) { - var h = hsl[0], - s = hsl[1] / 100, - l = hsl[2] / 100, - sv, v; - - if(l === 0) { - // no need to do calc on black - // also avoids divide by 0 error - return [0, 0, 0]; - } - - l *= 2; - s *= (l <= 1) ? l : 2 - l; - v = (l + s) / 2; - sv = (2 * s) / (l + s); - return [h, sv * 100, v * 100]; -} - -function hsl2hwb(args) { - return rgb2hwb(hsl2rgb(args)); -} - -function hsl2cmyk(args) { - return rgb2cmyk(hsl2rgb(args)); -} - -function hsl2keyword(args) { - return rgb2keyword(hsl2rgb(args)); -} - - -function hsv2rgb(hsv) { - var h = hsv[0] / 60, - s = hsv[1] / 100, - v = hsv[2] / 100, - hi = Math.floor(h) % 6; - - var f = h - Math.floor(h), - p = 255 * v * (1 - s), - q = 255 * v * (1 - (s * f)), - t = 255 * v * (1 - (s * (1 - f))), - v = 255 * v; - - switch(hi) { - case 0: - return [v, t, p]; - case 1: - return [q, v, p]; - case 2: - return [p, v, t]; - case 3: - return [p, q, v]; - case 4: - return [t, p, v]; - case 5: - return [v, p, q]; - } -} - -function hsv2hsl(hsv) { - var h = hsv[0], - s = hsv[1] / 100, - v = hsv[2] / 100, - sl, l; - - l = (2 - s) * v; - sl = s * v; - sl /= (l <= 1) ? l : 2 - l; - sl = sl || 0; - l /= 2; - return [h, sl * 100, l * 100]; -} - -function hsv2hwb(args) { - return rgb2hwb(hsv2rgb(args)) -} - -function hsv2cmyk(args) { - return rgb2cmyk(hsv2rgb(args)); -} - -function hsv2keyword(args) { - return rgb2keyword(hsv2rgb(args)); -} - -// http://dev.w3.org/csswg/css-color/#hwb-to-rgb -function hwb2rgb(hwb) { - var h = hwb[0] / 360, - wh = hwb[1] / 100, - bl = hwb[2] / 100, - ratio = wh + bl, - i, v, f, n; - - // wh + bl cant be > 1 - if (ratio > 1) { - wh /= ratio; - bl /= ratio; - } - - i = Math.floor(6 * h); - v = 1 - bl; - f = 6 * h - i; - if ((i & 0x01) != 0) { - f = 1 - f; - } - n = wh + f * (v - wh); // linear interpolation - - switch (i) { - default: - case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; - } - - return [r * 255, g * 255, b * 255]; -} - -function hwb2hsl(args) { - return rgb2hsl(hwb2rgb(args)); -} - -function hwb2hsv(args) { - return rgb2hsv(hwb2rgb(args)); -} - -function hwb2cmyk(args) { - return rgb2cmyk(hwb2rgb(args)); -} - -function hwb2keyword(args) { - return rgb2keyword(hwb2rgb(args)); -} - -function cmyk2rgb(cmyk) { - var c = cmyk[0] / 100, - m = cmyk[1] / 100, - y = cmyk[2] / 100, - k = cmyk[3] / 100, - r, g, b; - - r = 1 - Math.min(1, c * (1 - k) + k); - g = 1 - Math.min(1, m * (1 - k) + k); - b = 1 - Math.min(1, y * (1 - k) + k); - return [r * 255, g * 255, b * 255]; -} - -function cmyk2hsl(args) { - return rgb2hsl(cmyk2rgb(args)); -} - -function cmyk2hsv(args) { - return rgb2hsv(cmyk2rgb(args)); -} - -function cmyk2hwb(args) { - return rgb2hwb(cmyk2rgb(args)); -} - -function cmyk2keyword(args) { - return rgb2keyword(cmyk2rgb(args)); -} - - -function xyz2rgb(xyz) { - var x = xyz[0] / 100, - y = xyz[1] / 100, - z = xyz[2] / 100, - r, g, b; - - r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); - g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); - b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); - - // assume sRGB - r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) - : r = (r * 12.92); - - g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) - : g = (g * 12.92); - - b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) - : b = (b * 12.92); - - r = Math.min(Math.max(0, r), 1); - g = Math.min(Math.max(0, g), 1); - b = Math.min(Math.max(0, b), 1); - - return [r * 255, g * 255, b * 255]; -} - -function xyz2lab(xyz) { - var x = xyz[0], - y = xyz[1], - z = xyz[2], - l, a, b; - - x /= 95.047; - y /= 100; - z /= 108.883; - - x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); - - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - - return [l, a, b]; -} - -function xyz2lch(args) { - return lab2lch(xyz2lab(args)); -} - -function lab2xyz(lab) { - var l = lab[0], - a = lab[1], - b = lab[2], - x, y, z, y2; - - if (l <= 8) { - y = (l * 100) / 903.3; - y2 = (7.787 * (y / 100)) + (16 / 116); - } else { - y = 100 * Math.pow((l + 16) / 116, 3); - y2 = Math.pow(y / 100, 1/3); - } - - x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); - - z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); - - return [x, y, z]; -} - -function lab2lch(lab) { - var l = lab[0], - a = lab[1], - b = lab[2], - hr, h, c; - - hr = Math.atan2(b, a); - h = hr * 360 / 2 / Math.PI; - if (h < 0) { - h += 360; - } - c = Math.sqrt(a * a + b * b); - return [l, c, h]; -} - -function lab2rgb(args) { - return xyz2rgb(lab2xyz(args)); -} - -function lch2lab(lch) { - var l = lch[0], - c = lch[1], - h = lch[2], - a, b, hr; - - hr = h / 360 * 2 * Math.PI; - a = c * Math.cos(hr); - b = c * Math.sin(hr); - return [l, a, b]; -} - -function lch2xyz(args) { - return lab2xyz(lch2lab(args)); -} - -function lch2rgb(args) { - return lab2rgb(lch2lab(args)); -} - -function keyword2rgb(keyword) { - return cssKeywords[keyword]; -} - -function keyword2hsl(args) { - return rgb2hsl(keyword2rgb(args)); -} - -function keyword2hsv(args) { - return rgb2hsv(keyword2rgb(args)); -} - -function keyword2hwb(args) { - return rgb2hwb(keyword2rgb(args)); -} - -function keyword2cmyk(args) { - return rgb2cmyk(keyword2rgb(args)); -} - -function keyword2lab(args) { - return rgb2lab(keyword2rgb(args)); -} - -function keyword2xyz(args) { - return rgb2xyz(keyword2rgb(args)); -} - -var cssKeywords = { - aliceblue: [240,248,255], - antiquewhite: [250,235,215], - aqua: [0,255,255], - aquamarine: [127,255,212], - azure: [240,255,255], - beige: [245,245,220], - bisque: [255,228,196], - black: [0,0,0], - blanchedalmond: [255,235,205], - blue: [0,0,255], - blueviolet: [138,43,226], - brown: [165,42,42], - burlywood: [222,184,135], - cadetblue: [95,158,160], - chartreuse: [127,255,0], - chocolate: [210,105,30], - coral: [255,127,80], - cornflowerblue: [100,149,237], - cornsilk: [255,248,220], - crimson: [220,20,60], - cyan: [0,255,255], - darkblue: [0,0,139], - darkcyan: [0,139,139], - darkgoldenrod: [184,134,11], - darkgray: [169,169,169], - darkgreen: [0,100,0], - darkgrey: [169,169,169], - darkkhaki: [189,183,107], - darkmagenta: [139,0,139], - darkolivegreen: [85,107,47], - darkorange: [255,140,0], - darkorchid: [153,50,204], - darkred: [139,0,0], - darksalmon: [233,150,122], - darkseagreen: [143,188,143], - darkslateblue: [72,61,139], - darkslategray: [47,79,79], - darkslategrey: [47,79,79], - darkturquoise: [0,206,209], - darkviolet: [148,0,211], - deeppink: [255,20,147], - deepskyblue: [0,191,255], - dimgray: [105,105,105], - dimgrey: [105,105,105], - dodgerblue: [30,144,255], - firebrick: [178,34,34], - floralwhite: [255,250,240], - forestgreen: [34,139,34], - fuchsia: [255,0,255], - gainsboro: [220,220,220], - ghostwhite: [248,248,255], - gold: [255,215,0], - goldenrod: [218,165,32], - gray: [128,128,128], - green: [0,128,0], - greenyellow: [173,255,47], - grey: [128,128,128], - honeydew: [240,255,240], - hotpink: [255,105,180], - indianred: [205,92,92], - indigo: [75,0,130], - ivory: [255,255,240], - khaki: [240,230,140], - lavender: [230,230,250], - lavenderblush: [255,240,245], - lawngreen: [124,252,0], - lemonchiffon: [255,250,205], - lightblue: [173,216,230], - lightcoral: [240,128,128], - lightcyan: [224,255,255], - lightgoldenrodyellow: [250,250,210], - lightgray: [211,211,211], - lightgreen: [144,238,144], - lightgrey: [211,211,211], - lightpink: [255,182,193], - lightsalmon: [255,160,122], - lightseagreen: [32,178,170], - lightskyblue: [135,206,250], - lightslategray: [119,136,153], - lightslategrey: [119,136,153], - lightsteelblue: [176,196,222], - lightyellow: [255,255,224], - lime: [0,255,0], - limegreen: [50,205,50], - linen: [250,240,230], - magenta: [255,0,255], - maroon: [128,0,0], - mediumaquamarine: [102,205,170], - mediumblue: [0,0,205], - mediumorchid: [186,85,211], - mediumpurple: [147,112,219], - mediumseagreen: [60,179,113], - mediumslateblue: [123,104,238], - mediumspringgreen: [0,250,154], - mediumturquoise: [72,209,204], - mediumvioletred: [199,21,133], - midnightblue: [25,25,112], - mintcream: [245,255,250], - mistyrose: [255,228,225], - moccasin: [255,228,181], - navajowhite: [255,222,173], - navy: [0,0,128], - oldlace: [253,245,230], - olive: [128,128,0], - olivedrab: [107,142,35], - orange: [255,165,0], - orangered: [255,69,0], - orchid: [218,112,214], - palegoldenrod: [238,232,170], - palegreen: [152,251,152], - paleturquoise: [175,238,238], - palevioletred: [219,112,147], - papayawhip: [255,239,213], - peachpuff: [255,218,185], - peru: [205,133,63], - pink: [255,192,203], - plum: [221,160,221], - powderblue: [176,224,230], - purple: [128,0,128], - rebeccapurple: [102, 51, 153], - red: [255,0,0], - rosybrown: [188,143,143], - royalblue: [65,105,225], - saddlebrown: [139,69,19], - salmon: [250,128,114], - sandybrown: [244,164,96], - seagreen: [46,139,87], - seashell: [255,245,238], - sienna: [160,82,45], - silver: [192,192,192], - skyblue: [135,206,235], - slateblue: [106,90,205], - slategray: [112,128,144], - slategrey: [112,128,144], - snow: [255,250,250], - springgreen: [0,255,127], - steelblue: [70,130,180], - tan: [210,180,140], - teal: [0,128,128], - thistle: [216,191,216], - tomato: [255,99,71], - turquoise: [64,224,208], - violet: [238,130,238], - wheat: [245,222,179], - white: [255,255,255], - whitesmoke: [245,245,245], - yellow: [255,255,0], - yellowgreen: [154,205,50] -}; - -var reverseKeywords = {}; -for (var key in cssKeywords) { - reverseKeywords[JSON.stringify(cssKeywords[key])] = key; -} - -var convert = function() { - return new Converter(); -}; - -for (var func in conversions) { - // export Raw versions - convert[func + "Raw"] = (function(func) { - // accept array or plain args - return function(arg) { - if (typeof arg == "number") - arg = Array.prototype.slice.call(arguments); - return conversions[func](arg); - } - })(func); - - var pair = /(\w+)2(\w+)/.exec(func), - from = pair[1], - to = pair[2]; - - // export rgb2hsl and ["rgb"]["hsl"] - convert[from] = convert[from] || {}; - - convert[from][to] = convert[func] = (function(func) { - return function(arg) { - if (typeof arg == "number") - arg = Array.prototype.slice.call(arguments); - - var val = conversions[func](arg); - if (typeof val == "string" || val === undefined) - return val; // keyword - - for (var i = 0; i < val.length; i++) - val[i] = Math.round(val[i]); - return val; - } - })(func); -} - - -/* Converter does lazy conversion and caching */ -var Converter = function() { - this.convs = {}; -}; - -/* Either get the values for a space or - set the values for a space, depending on args */ -Converter.prototype.routeSpace = function(space, args) { - var values = args[0]; - if (values === undefined) { - // color.rgb() - return this.getValues(space); - } - // color.rgb(10, 10, 10) - if (typeof values == "number") { - values = Array.prototype.slice.call(args); - } - - return this.setValues(space, values); -}; - -/* Set the values for a space, invalidating cache */ -Converter.prototype.setValues = function(space, values) { - this.space = space; - this.convs = {}; - this.convs[space] = values; - return this; -}; - -/* Get the values for a space. If there's already - a conversion for the space, fetch it, otherwise - compute it */ -Converter.prototype.getValues = function(space) { - var vals = this.convs[space]; - if (!vals) { - var fspace = this.space, - from = this.convs[fspace]; - vals = convert[fspace][space](from); - - this.convs[space] = vals; - } - return vals; -}; - -["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { - Converter.prototype[space] = function(vals) { - return this.routeSpace(space, arguments); - }; -}); - -var colorConvert = convert; - -var colorName = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; - -/* MIT license */ - - -var colorString = { - getRgba: getRgba, - getHsla: getHsla, - getRgb: getRgb, - getHsl: getHsl, - getHwb: getHwb, - getAlpha: getAlpha, - - hexString: hexString, - rgbString: rgbString, - rgbaString: rgbaString, - percentString: percentString, - percentaString: percentaString, - hslString: hslString, - hslaString: hslaString, - hwbString: hwbString, - keyword: keyword -}; - -function getRgba(string) { - if (!string) { - return; - } - var abbr = /^#([a-fA-F0-9]{3,4})$/i, - hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i, - rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, - per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, - keyword = /(\w+)/; - - var rgb = [0, 0, 0], - a = 1, - match = string.match(abbr), - hexAlpha = ""; - if (match) { - match = match[1]; - hexAlpha = match[3]; - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match[i] + match[i], 16); - } - if (hexAlpha) { - a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100; - } - } - else if (match = string.match(hex)) { - hexAlpha = match[2]; - match = match[1]; - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); - } - if (hexAlpha) { - a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100; - } - } - else if (match = string.match(rgba)) { - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match[i + 1]); - } - a = parseFloat(match[4]); - } - else if (match = string.match(per)) { - for (var i = 0; i < rgb.length; i++) { - rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); - } - a = parseFloat(match[4]); - } - else if (match = string.match(keyword)) { - if (match[1] == "transparent") { - return [0, 0, 0, 0]; - } - rgb = colorName[match[1]]; - if (!rgb) { - return; - } - } - - for (var i = 0; i < rgb.length; i++) { - rgb[i] = scale(rgb[i], 0, 255); - } - if (!a && a != 0) { - a = 1; - } - else { - a = scale(a, 0, 1); - } - rgb[3] = a; - return rgb; -} - -function getHsla(string) { - if (!string) { - return; - } - var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; - var match = string.match(hsl); - if (match) { - var alpha = parseFloat(match[4]); - var h = scale(parseInt(match[1]), 0, 360), - s = scale(parseFloat(match[2]), 0, 100), - l = scale(parseFloat(match[3]), 0, 100), - a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, s, l, a]; - } -} - -function getHwb(string) { - if (!string) { - return; - } - var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; - var match = string.match(hwb); - if (match) { - var alpha = parseFloat(match[4]); - var h = scale(parseInt(match[1]), 0, 360), - w = scale(parseFloat(match[2]), 0, 100), - b = scale(parseFloat(match[3]), 0, 100), - a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, w, b, a]; - } -} - -function getRgb(string) { - var rgba = getRgba(string); - return rgba && rgba.slice(0, 3); -} - -function getHsl(string) { - var hsla = getHsla(string); - return hsla && hsla.slice(0, 3); -} - -function getAlpha(string) { - var vals = getRgba(string); - if (vals) { - return vals[3]; - } - else if (vals = getHsla(string)) { - return vals[3]; - } - else if (vals = getHwb(string)) { - return vals[3]; - } -} - -// generators -function hexString(rgba, a) { - var a = (a !== undefined && rgba.length === 3) ? a : rgba[3]; - return "#" + hexDouble(rgba[0]) - + hexDouble(rgba[1]) - + hexDouble(rgba[2]) - + ( - (a >= 0 && a < 1) - ? hexDouble(Math.round(a * 255)) - : "" - ); -} - -function rgbString(rgba, alpha) { - if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { - return rgbaString(rgba, alpha); - } - return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; -} - -function rgbaString(rgba, alpha) { - if (alpha === undefined) { - alpha = (rgba[3] !== undefined ? rgba[3] : 1); - } - return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] - + ", " + alpha + ")"; -} - -function percentString(rgba, alpha) { - if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { - return percentaString(rgba, alpha); - } - var r = Math.round(rgba[0]/255 * 100), - g = Math.round(rgba[1]/255 * 100), - b = Math.round(rgba[2]/255 * 100); - - return "rgb(" + r + "%, " + g + "%, " + b + "%)"; -} - -function percentaString(rgba, alpha) { - var r = Math.round(rgba[0]/255 * 100), - g = Math.round(rgba[1]/255 * 100), - b = Math.round(rgba[2]/255 * 100); - return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; -} - -function hslString(hsla, alpha) { - if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { - return hslaString(hsla, alpha); - } - return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; -} - -function hslaString(hsla, alpha) { - if (alpha === undefined) { - alpha = (hsla[3] !== undefined ? hsla[3] : 1); - } - return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " - + alpha + ")"; -} - -// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax -// (hwb have alpha optional & 1 is default value) -function hwbString(hwb, alpha) { - if (alpha === undefined) { - alpha = (hwb[3] !== undefined ? hwb[3] : 1); - } - return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" - + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; -} - -function keyword(rgb) { - return reverseNames[rgb.slice(0, 3)]; -} - -// helpers -function scale(num, min, max) { - return Math.min(Math.max(min, num), max); -} - -function hexDouble(num) { - var str = num.toString(16).toUpperCase(); - return (str.length < 2) ? "0" + str : str; -} - - -//create a list of reverse color names -var reverseNames = {}; -for (var name in colorName) { - reverseNames[colorName[name]] = name; -} - -/* MIT license */ - - - -var Color = function (obj) { - if (obj instanceof Color) { - return obj; - } - if (!(this instanceof Color)) { - return new Color(obj); - } - - this.valid = false; - this.values = { - rgb: [0, 0, 0], - hsl: [0, 0, 0], - hsv: [0, 0, 0], - hwb: [0, 0, 0], - cmyk: [0, 0, 0, 0], - alpha: 1 - }; - - // parse Color() argument - var vals; - if (typeof obj === 'string') { - vals = colorString.getRgba(obj); - if (vals) { - this.setValues('rgb', vals); - } else if (vals = colorString.getHsla(obj)) { - this.setValues('hsl', vals); - } else if (vals = colorString.getHwb(obj)) { - this.setValues('hwb', vals); - } - } else if (typeof obj === 'object') { - vals = obj; - if (vals.r !== undefined || vals.red !== undefined) { - this.setValues('rgb', vals); - } else if (vals.l !== undefined || vals.lightness !== undefined) { - this.setValues('hsl', vals); - } else if (vals.v !== undefined || vals.value !== undefined) { - this.setValues('hsv', vals); - } else if (vals.w !== undefined || vals.whiteness !== undefined) { - this.setValues('hwb', vals); - } else if (vals.c !== undefined || vals.cyan !== undefined) { - this.setValues('cmyk', vals); - } - } -}; - -Color.prototype = { - isValid: function () { - return this.valid; - }, - rgb: function () { - return this.setSpace('rgb', arguments); - }, - hsl: function () { - return this.setSpace('hsl', arguments); - }, - hsv: function () { - return this.setSpace('hsv', arguments); - }, - hwb: function () { - return this.setSpace('hwb', arguments); - }, - cmyk: function () { - return this.setSpace('cmyk', arguments); - }, - - rgbArray: function () { - return this.values.rgb; - }, - hslArray: function () { - return this.values.hsl; - }, - hsvArray: function () { - return this.values.hsv; - }, - hwbArray: function () { - var values = this.values; - if (values.alpha !== 1) { - return values.hwb.concat([values.alpha]); - } - return values.hwb; - }, - cmykArray: function () { - return this.values.cmyk; - }, - rgbaArray: function () { - var values = this.values; - return values.rgb.concat([values.alpha]); - }, - hslaArray: function () { - var values = this.values; - return values.hsl.concat([values.alpha]); - }, - alpha: function (val) { - if (val === undefined) { - return this.values.alpha; - } - this.setValues('alpha', val); - return this; - }, - - red: function (val) { - return this.setChannel('rgb', 0, val); - }, - green: function (val) { - return this.setChannel('rgb', 1, val); - }, - blue: function (val) { - return this.setChannel('rgb', 2, val); - }, - hue: function (val) { - if (val) { - val %= 360; - val = val < 0 ? 360 + val : val; - } - return this.setChannel('hsl', 0, val); - }, - saturation: function (val) { - return this.setChannel('hsl', 1, val); - }, - lightness: function (val) { - return this.setChannel('hsl', 2, val); - }, - saturationv: function (val) { - return this.setChannel('hsv', 1, val); - }, - whiteness: function (val) { - return this.setChannel('hwb', 1, val); - }, - blackness: function (val) { - return this.setChannel('hwb', 2, val); - }, - value: function (val) { - return this.setChannel('hsv', 2, val); - }, - cyan: function (val) { - return this.setChannel('cmyk', 0, val); - }, - magenta: function (val) { - return this.setChannel('cmyk', 1, val); - }, - yellow: function (val) { - return this.setChannel('cmyk', 2, val); - }, - black: function (val) { - return this.setChannel('cmyk', 3, val); - }, - - hexString: function () { - return colorString.hexString(this.values.rgb); - }, - rgbString: function () { - return colorString.rgbString(this.values.rgb, this.values.alpha); - }, - rgbaString: function () { - return colorString.rgbaString(this.values.rgb, this.values.alpha); - }, - percentString: function () { - return colorString.percentString(this.values.rgb, this.values.alpha); - }, - hslString: function () { - return colorString.hslString(this.values.hsl, this.values.alpha); - }, - hslaString: function () { - return colorString.hslaString(this.values.hsl, this.values.alpha); - }, - hwbString: function () { - return colorString.hwbString(this.values.hwb, this.values.alpha); - }, - keyword: function () { - return colorString.keyword(this.values.rgb, this.values.alpha); - }, - - rgbNumber: function () { - var rgb = this.values.rgb; - return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; - }, - - luminosity: function () { - // http://www.w3.org/TR/WCAG20/#relativeluminancedef - var rgb = this.values.rgb; - var lum = []; - for (var i = 0; i < rgb.length; i++) { - var chan = rgb[i] / 255; - lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); - } - return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; - }, - - contrast: function (color2) { - // http://www.w3.org/TR/WCAG20/#contrast-ratiodef - var lum1 = this.luminosity(); - var lum2 = color2.luminosity(); - if (lum1 > lum2) { - return (lum1 + 0.05) / (lum2 + 0.05); - } - return (lum2 + 0.05) / (lum1 + 0.05); - }, - - level: function (color2) { - var contrastRatio = this.contrast(color2); - if (contrastRatio >= 7.1) { - return 'AAA'; - } - - return (contrastRatio >= 4.5) ? 'AA' : ''; - }, - - dark: function () { - // YIQ equation from http://24ways.org/2010/calculating-color-contrast - var rgb = this.values.rgb; - var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; - return yiq < 128; - }, - - light: function () { - return !this.dark(); - }, - - negate: function () { - var rgb = []; - for (var i = 0; i < 3; i++) { - rgb[i] = 255 - this.values.rgb[i]; - } - this.setValues('rgb', rgb); - return this; - }, - - lighten: function (ratio) { - var hsl = this.values.hsl; - hsl[2] += hsl[2] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - darken: function (ratio) { - var hsl = this.values.hsl; - hsl[2] -= hsl[2] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - saturate: function (ratio) { - var hsl = this.values.hsl; - hsl[1] += hsl[1] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - desaturate: function (ratio) { - var hsl = this.values.hsl; - hsl[1] -= hsl[1] * ratio; - this.setValues('hsl', hsl); - return this; - }, - - whiten: function (ratio) { - var hwb = this.values.hwb; - hwb[1] += hwb[1] * ratio; - this.setValues('hwb', hwb); - return this; - }, - - blacken: function (ratio) { - var hwb = this.values.hwb; - hwb[2] += hwb[2] * ratio; - this.setValues('hwb', hwb); - return this; - }, - - greyscale: function () { - var rgb = this.values.rgb; - // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale - var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; - this.setValues('rgb', [val, val, val]); - return this; - }, - - clearer: function (ratio) { - var alpha = this.values.alpha; - this.setValues('alpha', alpha - (alpha * ratio)); - return this; - }, - - opaquer: function (ratio) { - var alpha = this.values.alpha; - this.setValues('alpha', alpha + (alpha * ratio)); - return this; - }, - - rotate: function (degrees) { - var hsl = this.values.hsl; - var hue = (hsl[0] + degrees) % 360; - hsl[0] = hue < 0 ? 360 + hue : hue; - this.setValues('hsl', hsl); - return this; - }, - - /** - * Ported from sass implementation in C - * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 - */ - mix: function (mixinColor, weight) { - var color1 = this; - var color2 = mixinColor; - var p = weight === undefined ? 0.5 : weight; - - var w = 2 * p - 1; - var a = color1.alpha() - color2.alpha(); - - var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - return this - .rgb( - w1 * color1.red() + w2 * color2.red(), - w1 * color1.green() + w2 * color2.green(), - w1 * color1.blue() + w2 * color2.blue() - ) - .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); - }, - - toJSON: function () { - return this.rgb(); - }, - - clone: function () { - // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, - // making the final build way to big to embed in Chart.js. So let's do it manually, - // assuming that values to clone are 1 dimension arrays containing only numbers, - // except 'alpha' which is a number. - var result = new Color(); - var source = this.values; - var target = result.values; - var value, type; - - for (var prop in source) { - if (source.hasOwnProperty(prop)) { - value = source[prop]; - type = ({}).toString.call(value); - if (type === '[object Array]') { - target[prop] = value.slice(0); - } else if (type === '[object Number]') { - target[prop] = value; - } else { - console.error('unexpected color value:', value); - } - } - } - - return result; - } -}; - -Color.prototype.spaces = { - rgb: ['red', 'green', 'blue'], - hsl: ['hue', 'saturation', 'lightness'], - hsv: ['hue', 'saturation', 'value'], - hwb: ['hue', 'whiteness', 'blackness'], - cmyk: ['cyan', 'magenta', 'yellow', 'black'] -}; - -Color.prototype.maxes = { - rgb: [255, 255, 255], - hsl: [360, 100, 100], - hsv: [360, 100, 100], - hwb: [360, 100, 100], - cmyk: [100, 100, 100, 100] -}; - -Color.prototype.getValues = function (space) { - var values = this.values; - var vals = {}; - - for (var i = 0; i < space.length; i++) { - vals[space.charAt(i)] = values[space][i]; - } - - if (values.alpha !== 1) { - vals.a = values.alpha; - } - - // {r: 255, g: 255, b: 255, a: 0.4} - return vals; -}; - -Color.prototype.setValues = function (space, vals) { - var values = this.values; - var spaces = this.spaces; - var maxes = this.maxes; - var alpha = 1; - var i; - - this.valid = true; - - if (space === 'alpha') { - alpha = vals; - } else if (vals.length) { - // [10, 10, 10] - values[space] = vals.slice(0, space.length); - alpha = vals[space.length]; - } else if (vals[space.charAt(0)] !== undefined) { - // {r: 10, g: 10, b: 10} - for (i = 0; i < space.length; i++) { - values[space][i] = vals[space.charAt(i)]; - } - - alpha = vals.a; - } else if (vals[spaces[space][0]] !== undefined) { - // {red: 10, green: 10, blue: 10} - var chans = spaces[space]; - - for (i = 0; i < space.length; i++) { - values[space][i] = vals[chans[i]]; - } - - alpha = vals.alpha; - } - - values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); - - if (space === 'alpha') { - return false; - } - - var capped; - - // cap values of the space prior converting all values - for (i = 0; i < space.length; i++) { - capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); - values[space][i] = Math.round(capped); - } - - // convert to all the other color spaces - for (var sname in spaces) { - if (sname !== space) { - values[sname] = colorConvert[space][sname](values[space]); - } - } - - return true; -}; - -Color.prototype.setSpace = function (space, args) { - var vals = args[0]; - - if (vals === undefined) { - // color.rgb() - return this.getValues(space); - } - - // color.rgb(10, 10, 10) - if (typeof vals === 'number') { - vals = Array.prototype.slice.call(args); - } - - this.setValues(space, vals); - return this; -}; - -Color.prototype.setChannel = function (space, index, val) { - var svalues = this.values[space]; - if (val === undefined) { - // color.red() - return svalues[index]; - } else if (val === svalues[index]) { - // color.red(color.red()) - return this; - } - - // color.red(100) - svalues[index] = val; - this.setValues(space, svalues); - - return this; -}; - -if (typeof window !== 'undefined') { - window.Color = Color; -} - -var chartjsColor = Color; - -/** - * @namespace Chart.helpers - */ -var helpers = { - /** - * An empty function that can be used, for example, for optional callback. - */ - noop: function() {}, - - /** - * Returns a unique id, sequentially generated from a global variable. - * @returns {number} - * @function - */ - uid: (function() { - var id = 0; - return function() { - return id++; - }; - }()), - - /** - * Returns true if `value` is neither null nor undefined, else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @since 2.7.0 - */ - isNullOrUndef: function(value) { - return value === null || typeof value === 'undefined'; - }, - - /** - * Returns true if `value` is an array (including typed arrays), else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @function - */ - isArray: function(value) { - if (Array.isArray && Array.isArray(value)) { - return true; - } - var type = Object.prototype.toString.call(value); - if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { - return true; - } - return false; - }, - - /** - * Returns true if `value` is an object (excluding null), else returns false. - * @param {*} value - The value to test. - * @returns {boolean} - * @since 2.7.0 - */ - isObject: function(value) { - return value !== null && Object.prototype.toString.call(value) === '[object Object]'; - }, - - /** - * Returns true if `value` is a finite number, else returns false - * @param {*} value - The value to test. - * @returns {boolean} - */ - isFinite: function(value) { - return (typeof value === 'number' || value instanceof Number) && isFinite(value); - }, - - /** - * Returns `value` if defined, else returns `defaultValue`. - * @param {*} value - The value to return if defined. - * @param {*} defaultValue - The value to return if `value` is undefined. - * @returns {*} - */ - valueOrDefault: function(value, defaultValue) { - return typeof value === 'undefined' ? defaultValue : value; - }, - - /** - * Returns value at the given `index` in array if defined, else returns `defaultValue`. - * @param {Array} value - The array to lookup for value at `index`. - * @param {number} index - The index in `value` to lookup for value. - * @param {*} defaultValue - The value to return if `value[index]` is undefined. - * @returns {*} - */ - valueAtIndexOrDefault: function(value, index, defaultValue) { - return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); - }, - - /** - * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the - * value returned by `fn`. If `fn` is not a function, this method returns undefined. - * @param {function} fn - The function to call. - * @param {Array|undefined|null} args - The arguments with which `fn` should be called. - * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. - * @returns {*} - */ - callback: function(fn, args, thisArg) { - if (fn && typeof fn.call === 'function') { - return fn.apply(thisArg, args); - } - }, - - /** - * Note(SB) for performance sake, this method should only be used when loopable type - * is unknown or in none intensive code (not called often and small loopable). Else - * it's preferable to use a regular for() loop and save extra function calls. - * @param {object|Array} loopable - The object or array to be iterated. - * @param {function} fn - The function to call for each item. - * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. - * @param {boolean} [reverse] - If true, iterates backward on the loopable. - */ - each: function(loopable, fn, thisArg, reverse) { - var i, len, keys; - if (helpers.isArray(loopable)) { - len = loopable.length; - if (reverse) { - for (i = len - 1; i >= 0; i--) { - fn.call(thisArg, loopable[i], i); - } - } else { - for (i = 0; i < len; i++) { - fn.call(thisArg, loopable[i], i); - } - } - } else if (helpers.isObject(loopable)) { - keys = Object.keys(loopable); - len = keys.length; - for (i = 0; i < len; i++) { - fn.call(thisArg, loopable[keys[i]], keys[i]); - } - } - }, - - /** - * Returns true if the `a0` and `a1` arrays have the same content, else returns false. - * @see https://stackoverflow.com/a/14853974 - * @param {Array} a0 - The array to compare - * @param {Array} a1 - The array to compare - * @returns {boolean} - */ - arrayEquals: function(a0, a1) { - var i, ilen, v0, v1; - - if (!a0 || !a1 || a0.length !== a1.length) { - return false; - } - - for (i = 0, ilen = a0.length; i < ilen; ++i) { - v0 = a0[i]; - v1 = a1[i]; - - if (v0 instanceof Array && v1 instanceof Array) { - if (!helpers.arrayEquals(v0, v1)) { - return false; - } - } else if (v0 !== v1) { - // NOTE: two different object instances will never be equal: {x:20} != {x:20} - return false; - } - } - - return true; - }, - - /** - * Returns a deep copy of `source` without keeping references on objects and arrays. - * @param {*} source - The value to clone. - * @returns {*} - */ - clone: function(source) { - if (helpers.isArray(source)) { - return source.map(helpers.clone); - } - - if (helpers.isObject(source)) { - var target = {}; - var keys = Object.keys(source); - var klen = keys.length; - var k = 0; - - for (; k < klen; ++k) { - target[keys[k]] = helpers.clone(source[keys[k]]); - } - - return target; - } - - return source; - }, - - /** - * The default merger when Chart.helpers.merge is called without merger option. - * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback. - * @private - */ - _merger: function(key, target, source, options) { - var tval = target[key]; - var sval = source[key]; - - if (helpers.isObject(tval) && helpers.isObject(sval)) { - helpers.merge(tval, sval, options); - } else { - target[key] = helpers.clone(sval); - } - }, - - /** - * Merges source[key] in target[key] only if target[key] is undefined. - * @private - */ - _mergerIf: function(key, target, source) { - var tval = target[key]; - var sval = source[key]; - - if (helpers.isObject(tval) && helpers.isObject(sval)) { - helpers.mergeIf(tval, sval); - } else if (!target.hasOwnProperty(key)) { - target[key] = helpers.clone(sval); - } - }, - - /** - * Recursively deep copies `source` properties into `target` with the given `options`. - * IMPORTANT: `target` is not cloned and will be updated with `source` properties. - * @param {object} target - The target object in which all sources are merged into. - * @param {object|object[]} source - Object(s) to merge into `target`. - * @param {object} [options] - Merging options: - * @param {function} [options.merger] - The merge method (key, target, source, options) - * @returns {object} The `target` object. - */ - merge: function(target, source, options) { - var sources = helpers.isArray(source) ? source : [source]; - var ilen = sources.length; - var merge, i, keys, klen, k; - - if (!helpers.isObject(target)) { - return target; - } - - options = options || {}; - merge = options.merger || helpers._merger; - - for (i = 0; i < ilen; ++i) { - source = sources[i]; - if (!helpers.isObject(source)) { - continue; - } - - keys = Object.keys(source); - for (k = 0, klen = keys.length; k < klen; ++k) { - merge(keys[k], target, source, options); - } - } - - return target; - }, - - /** - * Recursively deep copies `source` properties into `target` *only* if not defined in target. - * IMPORTANT: `target` is not cloned and will be updated with `source` properties. - * @param {object} target - The target object in which all sources are merged into. - * @param {object|object[]} source - Object(s) to merge into `target`. - * @returns {object} The `target` object. - */ - mergeIf: function(target, source) { - return helpers.merge(target, source, {merger: helpers._mergerIf}); - }, - - /** - * Applies the contents of two or more objects together into the first object. - * @param {object} target - The target object in which all objects are merged into. - * @param {object} arg1 - Object containing additional properties to merge in target. - * @param {object} argN - Additional objects containing properties to merge in target. - * @returns {object} The `target` object. - */ - extend: function(target) { - var setFn = function(value, key) { - target[key] = value; - }; - for (var i = 1, ilen = arguments.length; i < ilen; ++i) { - helpers.each(arguments[i], setFn); - } - return target; - }, - - /** - * Basic javascript inheritance based on the model created in Backbone.js - */ - inherits: function(extensions) { - var me = this; - var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { - return me.apply(this, arguments); - }; - - var Surrogate = function() { - this.constructor = ChartElement; - }; - - Surrogate.prototype = me.prototype; - ChartElement.prototype = new Surrogate(); - ChartElement.extend = helpers.inherits; - - if (extensions) { - helpers.extend(ChartElement.prototype, extensions); - } - - ChartElement.__super__ = me.prototype; - return ChartElement; - } -}; - -var helpers_core = helpers; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.callback instead. - * @function Chart.helpers.callCallback - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ -helpers.callCallback = helpers.callback; - -/** - * Provided for backward compatibility, use Array.prototype.indexOf instead. - * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ - * @function Chart.helpers.indexOf - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.indexOf = function(array, item, fromIndex) { - return Array.prototype.indexOf.call(array, item, fromIndex); -}; - -/** - * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. - * @function Chart.helpers.getValueOrDefault - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.getValueOrDefault = helpers.valueOrDefault; - -/** - * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. - * @function Chart.helpers.getValueAtIndexOrDefault - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; - -/** - * Easing functions adapted from Robert Penner's easing equations. - * @namespace Chart.helpers.easingEffects - * @see http://www.robertpenner.com/easing/ - */ -var effects = { - linear: function(t) { - return t; - }, - - easeInQuad: function(t) { - return t * t; - }, - - easeOutQuad: function(t) { - return -t * (t - 2); - }, - - easeInOutQuad: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t; - } - return -0.5 * ((--t) * (t - 2) - 1); - }, - - easeInCubic: function(t) { - return t * t * t; - }, - - easeOutCubic: function(t) { - return (t = t - 1) * t * t + 1; - }, - - easeInOutCubic: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t; - } - return 0.5 * ((t -= 2) * t * t + 2); - }, - - easeInQuart: function(t) { - return t * t * t * t; - }, - - easeOutQuart: function(t) { - return -((t = t - 1) * t * t * t - 1); - }, - - easeInOutQuart: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t * t; - } - return -0.5 * ((t -= 2) * t * t * t - 2); - }, - - easeInQuint: function(t) { - return t * t * t * t * t; - }, - - easeOutQuint: function(t) { - return (t = t - 1) * t * t * t * t + 1; - }, - - easeInOutQuint: function(t) { - if ((t /= 0.5) < 1) { - return 0.5 * t * t * t * t * t; - } - return 0.5 * ((t -= 2) * t * t * t * t + 2); - }, - - easeInSine: function(t) { - return -Math.cos(t * (Math.PI / 2)) + 1; - }, - - easeOutSine: function(t) { - return Math.sin(t * (Math.PI / 2)); - }, - - easeInOutSine: function(t) { - return -0.5 * (Math.cos(Math.PI * t) - 1); - }, - - easeInExpo: function(t) { - return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); - }, - - easeOutExpo: function(t) { - return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; - }, - - easeInOutExpo: function(t) { - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if ((t /= 0.5) < 1) { - return 0.5 * Math.pow(2, 10 * (t - 1)); - } - return 0.5 * (-Math.pow(2, -10 * --t) + 2); - }, - - easeInCirc: function(t) { - if (t >= 1) { - return t; - } - return -(Math.sqrt(1 - t * t) - 1); - }, - - easeOutCirc: function(t) { - return Math.sqrt(1 - (t = t - 1) * t); - }, - - easeInOutCirc: function(t) { - if ((t /= 0.5) < 1) { - return -0.5 * (Math.sqrt(1 - t * t) - 1); - } - return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); - }, - - easeInElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if (!p) { - p = 0.3; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); - }, - - easeOutElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if (!p) { - p = 0.3; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; - }, - - easeInOutElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if ((t /= 0.5) === 2) { - return 1; - } - if (!p) { - p = 0.45; - } - if (a < 1) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - if (t < 1) { - return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); - } - return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; - }, - easeInBack: function(t) { - var s = 1.70158; - return t * t * ((s + 1) * t - s); - }, - - easeOutBack: function(t) { - var s = 1.70158; - return (t = t - 1) * t * ((s + 1) * t + s) + 1; - }, - - easeInOutBack: function(t) { - var s = 1.70158; - if ((t /= 0.5) < 1) { - return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); - } - return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); - }, - - easeInBounce: function(t) { - return 1 - effects.easeOutBounce(1 - t); - }, - - easeOutBounce: function(t) { - if (t < (1 / 2.75)) { - return 7.5625 * t * t; - } - if (t < (2 / 2.75)) { - return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; - } - if (t < (2.5 / 2.75)) { - return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; - } - return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; - }, - - easeInOutBounce: function(t) { - if (t < 0.5) { - return effects.easeInBounce(t * 2) * 0.5; - } - return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; - } -}; - -var helpers_easing = { - effects: effects -}; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.easing.effects instead. - * @function Chart.helpers.easingEffects - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.easingEffects = effects; - -var PI = Math.PI; -var RAD_PER_DEG = PI / 180; -var DOUBLE_PI = PI * 2; -var HALF_PI = PI / 2; -var QUARTER_PI = PI / 4; -var TWO_THIRDS_PI = PI * 2 / 3; - -/** - * @namespace Chart.helpers.canvas - */ -var exports$1 = { - /** - * Clears the entire canvas associated to the given `chart`. - * @param {Chart} chart - The chart for which to clear the canvas. - */ - clear: function(chart) { - chart.ctx.clearRect(0, 0, chart.width, chart.height); - }, - - /** - * Creates a "path" for a rectangle with rounded corners at position (x, y) with a - * given size (width, height) and the same `radius` for all corners. - * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. - * @param {number} x - The x axis of the coordinate for the rectangle starting point. - * @param {number} y - The y axis of the coordinate for the rectangle starting point. - * @param {number} width - The rectangle's width. - * @param {number} height - The rectangle's height. - * @param {number} radius - The rounded amount (in pixels) for the four corners. - * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? - */ - roundedRect: function(ctx, x, y, width, height, radius) { - if (radius) { - var r = Math.min(radius, height / 2, width / 2); - var left = x + r; - var top = y + r; - var right = x + width - r; - var bottom = y + height - r; - - ctx.moveTo(x, top); - if (left < right && top < bottom) { - ctx.arc(left, top, r, -PI, -HALF_PI); - ctx.arc(right, top, r, -HALF_PI, 0); - ctx.arc(right, bottom, r, 0, HALF_PI); - ctx.arc(left, bottom, r, HALF_PI, PI); - } else if (left < right) { - ctx.moveTo(left, y); - ctx.arc(right, top, r, -HALF_PI, HALF_PI); - ctx.arc(left, top, r, HALF_PI, PI + HALF_PI); - } else if (top < bottom) { - ctx.arc(left, top, r, -PI, 0); - ctx.arc(left, bottom, r, 0, PI); - } else { - ctx.arc(left, top, r, -PI, PI); - } - ctx.closePath(); - ctx.moveTo(x, y); - } else { - ctx.rect(x, y, width, height); - } - }, - - drawPoint: function(ctx, style, radius, x, y, rotation) { - var type, xOffset, yOffset, size, cornerRadius; - var rad = (rotation || 0) * RAD_PER_DEG; - - if (style && typeof style === 'object') { - type = style.toString(); - if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { - ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); - return; - } - } - - if (isNaN(radius) || radius <= 0) { - return; - } - - ctx.beginPath(); - - switch (style) { - // Default includes circle - default: - ctx.arc(x, y, radius, 0, DOUBLE_PI); - ctx.closePath(); - break; - case 'triangle': - ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - rad += TWO_THIRDS_PI; - ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); - ctx.closePath(); - break; - case 'rectRounded': - // NOTE: the rounded rect implementation changed to use `arc` instead of - // `quadraticCurveTo` since it generates better results when rect is - // almost a circle. 0.516 (instead of 0.5) produces results with visually - // closer proportion to the previous impl and it is inscribed in the - // circle with `radius`. For more details, see the following PRs: - // https://github.com/chartjs/Chart.js/issues/5597 - // https://github.com/chartjs/Chart.js/issues/5858 - cornerRadius = radius * 0.516; - size = radius - cornerRadius; - xOffset = Math.cos(rad + QUARTER_PI) * size; - yOffset = Math.sin(rad + QUARTER_PI) * size; - ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); - ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); - ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); - ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); - ctx.closePath(); - break; - case 'rect': - if (!rotation) { - size = Math.SQRT1_2 * radius; - ctx.rect(x - size, y - size, 2 * size, 2 * size); - break; - } - rad += QUARTER_PI; - /* falls through */ - case 'rectRot': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + yOffset, y - xOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.lineTo(x - yOffset, y + xOffset); - ctx.closePath(); - break; - case 'crossRot': - rad += QUARTER_PI; - /* falls through */ - case 'cross': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'star': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - rad += QUARTER_PI; - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x + yOffset, y - xOffset); - ctx.lineTo(x - yOffset, y + xOffset); - break; - case 'line': - xOffset = Math.cos(rad) * radius; - yOffset = Math.sin(rad) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - break; - case 'dash': - ctx.moveTo(x, y); - ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius); - break; - } - - ctx.fill(); - ctx.stroke(); - }, - - /** - * Returns true if the point is inside the rectangle - * @param {object} point - The point to test - * @param {object} area - The rectangle - * @returns {boolean} - * @private - */ - _isPointInArea: function(point, area) { - var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. - - return point.x > area.left - epsilon && point.x < area.right + epsilon && - point.y > area.top - epsilon && point.y < area.bottom + epsilon; - }, - - clipArea: function(ctx, area) { - ctx.save(); - ctx.beginPath(); - ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); - ctx.clip(); - }, - - unclipArea: function(ctx) { - ctx.restore(); - }, - - lineTo: function(ctx, previous, target, flip) { - var stepped = target.steppedLine; - if (stepped) { - if (stepped === 'middle') { - var midpoint = (previous.x + target.x) / 2.0; - ctx.lineTo(midpoint, flip ? target.y : previous.y); - ctx.lineTo(midpoint, flip ? previous.y : target.y); - } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) { - ctx.lineTo(previous.x, target.y); - } else { - ctx.lineTo(target.x, previous.y); - } - ctx.lineTo(target.x, target.y); - return; - } - - if (!target.tension) { - ctx.lineTo(target.x, target.y); - return; - } - - ctx.bezierCurveTo( - flip ? previous.controlPointPreviousX : previous.controlPointNextX, - flip ? previous.controlPointPreviousY : previous.controlPointNextY, - flip ? target.controlPointNextX : target.controlPointPreviousX, - flip ? target.controlPointNextY : target.controlPointPreviousY, - target.x, - target.y); - } -}; - -var helpers_canvas = exports$1; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. - * @namespace Chart.helpers.clear - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.clear = exports$1.clear; - -/** - * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. - * @namespace Chart.helpers.drawRoundedRectangle - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers_core.drawRoundedRectangle = function(ctx) { - ctx.beginPath(); - exports$1.roundedRect.apply(exports$1, arguments); -}; - -var defaults = { - /** - * @private - */ - _set: function(scope, values) { - return helpers_core.merge(this[scope] || (this[scope] = {}), values); - } -}; - -defaults._set('global', { - defaultColor: 'rgba(0,0,0,0.1)', - defaultFontColor: '#666', - defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - defaultFontSize: 12, - defaultFontStyle: 'normal', - defaultLineHeight: 1.2, - showLines: true -}); - -var core_defaults = defaults; - -var valueOrDefault = helpers_core.valueOrDefault; - -/** - * Converts the given font object into a CSS font string. - * @param {object} font - A font object. - * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font - * @private - */ -function toFontString(font) { - if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) { - return null; - } - - return (font.style ? font.style + ' ' : '') - + (font.weight ? font.weight + ' ' : '') - + font.size + 'px ' - + font.family; -} - -/** - * @alias Chart.helpers.options - * @namespace - */ -var helpers_options = { - /** - * Converts the given line height `value` in pixels for a specific font `size`. - * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). - * @param {number} size - The font size (in pixels) used to resolve relative `value`. - * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid). - * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height - * @since 2.7.0 - */ - toLineHeight: function(value, size) { - var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); - if (!matches || matches[1] === 'normal') { - return size * 1.2; - } - - value = +matches[2]; - - switch (matches[3]) { - case 'px': - return value; - case '%': - value /= 100; - break; - default: - break; - } - - return size * value; - }, - - /** - * Converts the given value into a padding object with pre-computed width/height. - * @param {number|object} value - If a number, set the value to all TRBL component, - * else, if and object, use defined properties and sets undefined ones to 0. - * @returns {object} The padding values (top, right, bottom, left, width, height) - * @since 2.7.0 - */ - toPadding: function(value) { - var t, r, b, l; - - if (helpers_core.isObject(value)) { - t = +value.top || 0; - r = +value.right || 0; - b = +value.bottom || 0; - l = +value.left || 0; - } else { - t = r = b = l = +value || 0; - } - - return { - top: t, - right: r, - bottom: b, - left: l, - height: t + b, - width: l + r - }; - }, - - /** - * Parses font options and returns the font object. - * @param {object} options - A object that contains font options to be parsed. - * @return {object} The font object. - * @todo Support font.* options and renamed to toFont(). - * @private - */ - _parseFont: function(options) { - var globalDefaults = core_defaults.global; - var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); - var font = { - family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily), - lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size), - size: size, - style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle), - weight: null, - string: '' - }; - - font.string = toFontString(font); - return font; - }, - - /** - * Evaluates the given `inputs` sequentially and returns the first defined value. - * @param {Array} inputs - An array of values, falling back to the last value. - * @param {object} [context] - If defined and the current value is a function, the value - * is called with `context` as first argument and the result becomes the new input. - * @param {number} [index] - If defined and the current value is an array, the value - * at `index` become the new input. - * @since 2.7.0 - */ - resolve: function(inputs, context, index) { - var i, ilen, value; - - for (i = 0, ilen = inputs.length; i < ilen; ++i) { - value = inputs[i]; - if (value === undefined) { - continue; - } - if (context !== undefined && typeof value === 'function') { - value = value(context); - } - if (index !== undefined && helpers_core.isArray(value)) { - value = value[index]; - } - if (value !== undefined) { - return value; - } - } - } -}; - -var helpers$1 = helpers_core; -var easing = helpers_easing; -var canvas = helpers_canvas; -var options = helpers_options; -helpers$1.easing = easing; -helpers$1.canvas = canvas; -helpers$1.options = options; - -function interpolate(start, view, model, ease) { - var keys = Object.keys(model); - var i, ilen, key, actual, origin, target, type, c0, c1; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - - target = model[key]; - - // if a value is added to the model after pivot() has been called, the view - // doesn't contain it, so let's initialize the view to the target value. - if (!view.hasOwnProperty(key)) { - view[key] = target; - } - - actual = view[key]; - - if (actual === target || key[0] === '_') { - continue; - } - - if (!start.hasOwnProperty(key)) { - start[key] = actual; - } - - origin = start[key]; - - type = typeof target; - - if (type === typeof origin) { - if (type === 'string') { - c0 = chartjsColor(origin); - if (c0.valid) { - c1 = chartjsColor(target); - if (c1.valid) { - view[key] = c1.mix(c0, ease).rgbString(); - continue; - } - } - } else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) { - view[key] = origin + (target - origin) * ease; - continue; - } - } - - view[key] = target; - } -} - -var Element = function(configuration) { - helpers$1.extend(this, configuration); - this.initialize.apply(this, arguments); -}; - -helpers$1.extend(Element.prototype, { - - initialize: function() { - this.hidden = false; - }, - - pivot: function() { - var me = this; - if (!me._view) { - me._view = helpers$1.clone(me._model); - } - me._start = {}; - return me; - }, - - transition: function(ease) { - var me = this; - var model = me._model; - var start = me._start; - var view = me._view; - - // No animation -> No Transition - if (!model || ease === 1) { - me._view = model; - me._start = null; - return me; - } - - if (!view) { - view = me._view = {}; - } - - if (!start) { - start = me._start = {}; - } - - interpolate(start, view, model, ease); - - return me; - }, - - tooltipPosition: function() { - return { - x: this._model.x, - y: this._model.y - }; - }, - - hasValue: function() { - return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y); - } -}); - -Element.extend = helpers$1.inherits; - -var core_element = Element; - -var exports$2 = core_element.extend({ - chart: null, // the animation associated chart instance - currentStep: 0, // the current animation step - numSteps: 60, // default number of steps - easing: '', // the easing to use for this animation - render: null, // render function used by the animation service - - onAnimationProgress: null, // user specified callback to fire on each step of the animation - onAnimationComplete: null, // user specified callback to fire when the animation finishes -}); - -var core_animation = exports$2; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart.Animation instead - * @prop Chart.Animation#animationObject - * @deprecated since version 2.6.0 - * @todo remove at version 3 - */ -Object.defineProperty(exports$2.prototype, 'animationObject', { - get: function() { - return this; - } -}); - -/** - * Provided for backward compatibility, use Chart.Animation#chart instead - * @prop Chart.Animation#chartInstance - * @deprecated since version 2.6.0 - * @todo remove at version 3 - */ -Object.defineProperty(exports$2.prototype, 'chartInstance', { - get: function() { - return this.chart; - }, - set: function(value) { - this.chart = value; - } -}); - -core_defaults._set('global', { - animation: { - duration: 1000, - easing: 'easeOutQuart', - onProgress: helpers$1.noop, - onComplete: helpers$1.noop - } -}); - -var core_animations = { - animations: [], - request: null, - - /** - * @param {Chart} chart - The chart to animate. - * @param {Chart.Animation} animation - The animation that we will animate. - * @param {number} duration - The animation duration in ms. - * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions - */ - addAnimation: function(chart, animation, duration, lazy) { - var animations = this.animations; - var i, ilen; - - animation.chart = chart; - animation.startTime = Date.now(); - animation.duration = duration; - - if (!lazy) { - chart.animating = true; - } - - for (i = 0, ilen = animations.length; i < ilen; ++i) { - if (animations[i].chart === chart) { - animations[i] = animation; - return; - } - } - - animations.push(animation); - - // If there are no animations queued, manually kickstart a digest, for lack of a better word - if (animations.length === 1) { - this.requestAnimationFrame(); - } - }, - - cancelAnimation: function(chart) { - var index = helpers$1.findIndex(this.animations, function(animation) { - return animation.chart === chart; - }); - - if (index !== -1) { - this.animations.splice(index, 1); - chart.animating = false; - } - }, - - requestAnimationFrame: function() { - var me = this; - if (me.request === null) { - // Skip animation frame requests until the active one is executed. - // This can happen when processing mouse events, e.g. 'mousemove' - // and 'mouseout' events will trigger multiple renders. - me.request = helpers$1.requestAnimFrame.call(window, function() { - me.request = null; - me.startDigest(); - }); - } - }, - - /** - * @private - */ - startDigest: function() { - var me = this; - - me.advance(); - - // Do we have more stuff to animate? - if (me.animations.length > 0) { - me.requestAnimationFrame(); - } - }, - - /** - * @private - */ - advance: function() { - var animations = this.animations; - var animation, chart, numSteps, nextStep; - var i = 0; - - // 1 animation per chart, so we are looping charts here - while (i < animations.length) { - animation = animations[i]; - chart = animation.chart; - numSteps = animation.numSteps; - - // Make sure that currentStep starts at 1 - // https://github.com/chartjs/Chart.js/issues/6104 - nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1; - animation.currentStep = Math.min(nextStep, numSteps); - - helpers$1.callback(animation.render, [chart, animation], chart); - helpers$1.callback(animation.onAnimationProgress, [animation], chart); - - if (animation.currentStep >= numSteps) { - helpers$1.callback(animation.onAnimationComplete, [animation], chart); - chart.animating = false; - animations.splice(i, 1); - } else { - ++i; - } - } - } -}; - -var resolve = helpers$1.options.resolve; - -var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; - -/** - * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', - * 'unshift') and notify the listener AFTER the array has been altered. Listeners are - * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. - */ -function listenArrayEvents(array, listener) { - if (array._chartjs) { - array._chartjs.listeners.push(listener); - return; - } - - Object.defineProperty(array, '_chartjs', { - configurable: true, - enumerable: false, - value: { - listeners: [listener] - } - }); - - arrayEvents.forEach(function(key) { - var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); - var base = array[key]; - - Object.defineProperty(array, key, { - configurable: true, - enumerable: false, - value: function() { - var args = Array.prototype.slice.call(arguments); - var res = base.apply(this, args); - - helpers$1.each(array._chartjs.listeners, function(object) { - if (typeof object[method] === 'function') { - object[method].apply(object, args); - } - }); - - return res; - } - }); - }); -} - -/** - * Removes the given array event listener and cleanup extra attached properties (such as - * the _chartjs stub and overridden methods) if array doesn't have any more listeners. - */ -function unlistenArrayEvents(array, listener) { - var stub = array._chartjs; - if (!stub) { - return; - } - - var listeners = stub.listeners; - var index = listeners.indexOf(listener); - if (index !== -1) { - listeners.splice(index, 1); - } - - if (listeners.length > 0) { - return; - } - - arrayEvents.forEach(function(key) { - delete array[key]; - }); - - delete array._chartjs; -} - -// Base class for all dataset controllers (line, bar, etc) -var DatasetController = function(chart, datasetIndex) { - this.initialize(chart, datasetIndex); -}; - -helpers$1.extend(DatasetController.prototype, { - - /** - * Element type used to generate a meta dataset (e.g. Chart.element.Line). - * @type {Chart.core.element} - */ - datasetElementType: null, - - /** - * Element type used to generate a meta data (e.g. Chart.element.Point). - * @type {Chart.core.element} - */ - dataElementType: null, - - initialize: function(chart, datasetIndex) { - var me = this; - me.chart = chart; - me.index = datasetIndex; - me.linkScales(); - me.addElements(); - }, - - updateIndex: function(datasetIndex) { - this.index = datasetIndex; - }, - - linkScales: function() { - var me = this; - var meta = me.getMeta(); - var dataset = me.getDataset(); - - if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) { - meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; - } - if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) { - meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; - } - }, - - getDataset: function() { - return this.chart.data.datasets[this.index]; - }, - - getMeta: function() { - return this.chart.getDatasetMeta(this.index); - }, - - getScaleForId: function(scaleID) { - return this.chart.scales[scaleID]; - }, - - /** - * @private - */ - _getValueScaleId: function() { - return this.getMeta().yAxisID; - }, - - /** - * @private - */ - _getIndexScaleId: function() { - return this.getMeta().xAxisID; - }, - - /** - * @private - */ - _getValueScale: function() { - return this.getScaleForId(this._getValueScaleId()); - }, - - /** - * @private - */ - _getIndexScale: function() { - return this.getScaleForId(this._getIndexScaleId()); - }, - - reset: function() { - this.update(true); - }, - - /** - * @private - */ - destroy: function() { - if (this._data) { - unlistenArrayEvents(this._data, this); - } - }, - - createMetaDataset: function() { - var me = this; - var type = me.datasetElementType; - return type && new type({ - _chart: me.chart, - _datasetIndex: me.index - }); - }, - - createMetaData: function(index) { - var me = this; - var type = me.dataElementType; - return type && new type({ - _chart: me.chart, - _datasetIndex: me.index, - _index: index - }); - }, - - addElements: function() { - var me = this; - var meta = me.getMeta(); - var data = me.getDataset().data || []; - var metaData = meta.data; - var i, ilen; - - for (i = 0, ilen = data.length; i < ilen; ++i) { - metaData[i] = metaData[i] || me.createMetaData(i); - } - - meta.dataset = meta.dataset || me.createMetaDataset(); - }, - - addElementAndReset: function(index) { - var element = this.createMetaData(index); - this.getMeta().data.splice(index, 0, element); - this.updateElement(element, index, true); - }, - - buildOrUpdateElements: function() { - var me = this; - var dataset = me.getDataset(); - var data = dataset.data || (dataset.data = []); - - // In order to correctly handle data addition/deletion animation (an thus simulate - // real-time charts), we need to monitor these data modifications and synchronize - // the internal meta data accordingly. - if (me._data !== data) { - if (me._data) { - // This case happens when the user replaced the data array instance. - unlistenArrayEvents(me._data, me); - } - - if (data && Object.isExtensible(data)) { - listenArrayEvents(data, me); - } - me._data = data; - } - - // Re-sync meta data in case the user replaced the data array or if we missed - // any updates and so make sure that we handle number of datapoints changing. - me.resyncElements(); - }, - - update: helpers$1.noop, - - transition: function(easingValue) { - var meta = this.getMeta(); - var elements = meta.data || []; - var ilen = elements.length; - var i = 0; - - for (; i < ilen; ++i) { - elements[i].transition(easingValue); - } - - if (meta.dataset) { - meta.dataset.transition(easingValue); - } - }, - - draw: function() { - var meta = this.getMeta(); - var elements = meta.data || []; - var ilen = elements.length; - var i = 0; - - if (meta.dataset) { - meta.dataset.draw(); - } - - for (; i < ilen; ++i) { - elements[i].draw(); - } - }, - - removeHoverStyle: function(element) { - helpers$1.merge(element._model, element.$previousStyle || {}); - delete element.$previousStyle; - }, - - setHoverStyle: function(element) { - var dataset = this.chart.data.datasets[element._datasetIndex]; - var index = element._index; - var custom = element.custom || {}; - var model = element._model; - var getHoverColor = helpers$1.getHoverColor; - - element.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth - }; - - model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index); - model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index); - model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index); - }, - - /** - * @private - */ - resyncElements: function() { - var me = this; - var meta = me.getMeta(); - var data = me.getDataset().data; - var numMeta = meta.data.length; - var numData = data.length; - - if (numData < numMeta) { - meta.data.splice(numData, numMeta - numData); - } else if (numData > numMeta) { - me.insertElements(numMeta, numData - numMeta); - } - }, - - /** - * @private - */ - insertElements: function(start, count) { - for (var i = 0; i < count; ++i) { - this.addElementAndReset(start + i); - } - }, - - /** - * @private - */ - onDataPush: function() { - var count = arguments.length; - this.insertElements(this.getDataset().data.length - count, count); - }, - - /** - * @private - */ - onDataPop: function() { - this.getMeta().data.pop(); - }, - - /** - * @private - */ - onDataShift: function() { - this.getMeta().data.shift(); - }, - - /** - * @private - */ - onDataSplice: function(start, count) { - this.getMeta().data.splice(start, count); - this.insertElements(start, arguments.length - 2); - }, - - /** - * @private - */ - onDataUnshift: function() { - this.insertElements(0, arguments.length); - } -}); - -DatasetController.extend = helpers$1.inherits; - -var core_datasetController = DatasetController; - -core_defaults._set('global', { - elements: { - arc: { - backgroundColor: core_defaults.global.defaultColor, - borderColor: '#fff', - borderWidth: 2, - borderAlign: 'center' - } - } -}); - -var element_arc = core_element.extend({ - inLabelRange: function(mouseX) { - var vm = this._view; - - if (vm) { - return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); - } - return false; - }, - - inRange: function(chartX, chartY) { - var vm = this._view; - - if (vm) { - var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY}); - var angle = pointRelativePosition.angle; - var distance = pointRelativePosition.distance; - - // Sanitise angle range - var startAngle = vm.startAngle; - var endAngle = vm.endAngle; - while (endAngle < startAngle) { - endAngle += 2.0 * Math.PI; - } - while (angle > endAngle) { - angle -= 2.0 * Math.PI; - } - while (angle < startAngle) { - angle += 2.0 * Math.PI; - } - - // Check if within the range of the open/close angle - var betweenAngles = (angle >= startAngle && angle <= endAngle); - var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); - - return (betweenAngles && withinRadius); - } - return false; - }, - - getCenterPoint: function() { - var vm = this._view; - var halfAngle = (vm.startAngle + vm.endAngle) / 2; - var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; - return { - x: vm.x + Math.cos(halfAngle) * halfRadius, - y: vm.y + Math.sin(halfAngle) * halfRadius - }; - }, - - getArea: function() { - var vm = this._view; - return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); - }, - - tooltipPosition: function() { - var vm = this._view; - var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); - var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; - - return { - x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), - y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) - }; - }, - - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - var sA = vm.startAngle; - var eA = vm.endAngle; - var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0; - var angleMargin; - - ctx.save(); - - ctx.beginPath(); - ctx.arc(vm.x, vm.y, Math.max(vm.outerRadius - pixelMargin, 0), sA, eA); - ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); - ctx.closePath(); - - ctx.fillStyle = vm.backgroundColor; - ctx.fill(); - - if (vm.borderWidth) { - if (vm.borderAlign === 'inner') { - // Draw an inner border by cliping the arc and drawing a double-width border - // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders - ctx.beginPath(); - angleMargin = pixelMargin / vm.outerRadius; - ctx.arc(vm.x, vm.y, vm.outerRadius, sA - angleMargin, eA + angleMargin); - if (vm.innerRadius > pixelMargin) { - angleMargin = pixelMargin / vm.innerRadius; - ctx.arc(vm.x, vm.y, vm.innerRadius - pixelMargin, eA + angleMargin, sA - angleMargin, true); - } else { - ctx.arc(vm.x, vm.y, pixelMargin, eA + Math.PI / 2, sA - Math.PI / 2); - } - ctx.closePath(); - ctx.clip(); - - ctx.beginPath(); - ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); - ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); - ctx.closePath(); - - ctx.lineWidth = vm.borderWidth * 2; - ctx.lineJoin = 'round'; - } else { - ctx.lineWidth = vm.borderWidth; - ctx.lineJoin = 'bevel'; - } - - ctx.strokeStyle = vm.borderColor; - ctx.stroke(); - } - - ctx.restore(); - } -}); - -var valueOrDefault$1 = helpers$1.valueOrDefault; - -var defaultColor = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - line: { - tension: 0.4, - backgroundColor: defaultColor, - borderWidth: 3, - borderColor: defaultColor, - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0.0, - borderJoinStyle: 'miter', - capBezierPoints: true, - fill: true, // do we fill in the area between the line and its base axis - } - } -}); - -var element_line = core_element.extend({ - draw: function() { - var me = this; - var vm = me._view; - var ctx = me._chart.ctx; - var spanGaps = vm.spanGaps; - var points = me._children.slice(); // clone array - var globalDefaults = core_defaults.global; - var globalOptionLineElements = globalDefaults.elements.line; - var lastDrawnIndex = -1; - var index, current, previous, currentVM; - - // If we are looping, adding the first point again - if (me._loop && points.length) { - points.push(points[0]); - } - - ctx.save(); - - // Stroke Line Options - ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; - - // IE 9 and 10 do not support line dash - if (ctx.setLineDash) { - ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); - } - - ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset); - ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; - ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth); - ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; - - // Stroke Line - ctx.beginPath(); - lastDrawnIndex = -1; - - for (index = 0; index < points.length; ++index) { - current = points[index]; - previous = helpers$1.previousItem(points, index); - currentVM = current._view; - - // First point moves to it's starting position no matter what - if (index === 0) { - if (!currentVM.skip) { - ctx.moveTo(currentVM.x, currentVM.y); - lastDrawnIndex = index; - } - } else { - previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; - - if (!currentVM.skip) { - if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { - // There was a gap and this is the first point after the gap - ctx.moveTo(currentVM.x, currentVM.y); - } else { - // Line to next point - helpers$1.canvas.lineTo(ctx, previous._view, current._view); - } - lastDrawnIndex = index; - } - } - } - - ctx.stroke(); - ctx.restore(); - } -}); - -var valueOrDefault$2 = helpers$1.valueOrDefault; - -var defaultColor$1 = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - point: { - radius: 3, - pointStyle: 'circle', - backgroundColor: defaultColor$1, - borderColor: defaultColor$1, - borderWidth: 1, - // Hover - hitRadius: 1, - hoverRadius: 4, - hoverBorderWidth: 1 - } - } -}); - -function xRange(mouseX) { - var vm = this._view; - return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; -} - -function yRange(mouseY) { - var vm = this._view; - return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; -} - -var element_point = core_element.extend({ - inRange: function(mouseX, mouseY) { - var vm = this._view; - return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; - }, - - inLabelRange: xRange, - inXRange: xRange, - inYRange: yRange, - - getCenterPoint: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y - }; - }, - - getArea: function() { - return Math.PI * Math.pow(this._view.radius, 2); - }, - - tooltipPosition: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y, - padding: vm.radius + vm.borderWidth - }; - }, - - draw: function(chartArea) { - var vm = this._view; - var ctx = this._chart.ctx; - var pointStyle = vm.pointStyle; - var rotation = vm.rotation; - var radius = vm.radius; - var x = vm.x; - var y = vm.y; - var globalDefaults = core_defaults.global; - var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow - - if (vm.skip) { - return; - } - - // Clipping for Points. - if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) { - ctx.strokeStyle = vm.borderColor || defaultColor; - ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth); - ctx.fillStyle = vm.backgroundColor || defaultColor; - helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation); - } - } -}); - -var defaultColor$2 = core_defaults.global.defaultColor; - -core_defaults._set('global', { - elements: { - rectangle: { - backgroundColor: defaultColor$2, - borderColor: defaultColor$2, - borderSkipped: 'bottom', - borderWidth: 0 - } - } -}); - -function isVertical(vm) { - return vm && vm.width !== undefined; -} - -/** - * Helper function to get the bounds of the bar regardless of the orientation - * @param bar {Chart.Element.Rectangle} the bar - * @return {Bounds} bounds of the bar - * @private - */ -function getBarBounds(vm) { - var x1, x2, y1, y2, half; - - if (isVertical(vm)) { - half = vm.width / 2; - x1 = vm.x - half; - x2 = vm.x + half; - y1 = Math.min(vm.y, vm.base); - y2 = Math.max(vm.y, vm.base); - } else { - half = vm.height / 2; - x1 = Math.min(vm.x, vm.base); - x2 = Math.max(vm.x, vm.base); - y1 = vm.y - half; - y2 = vm.y + half; - } - - return { - left: x1, - top: y1, - right: x2, - bottom: y2 - }; -} - -function swap(orig, v1, v2) { - return orig === v1 ? v2 : orig === v2 ? v1 : orig; -} - -function parseBorderSkipped(vm) { - var edge = vm.borderSkipped; - var res = {}; - - if (!edge) { - return res; - } - - if (vm.horizontal) { - if (vm.base > vm.x) { - edge = swap(edge, 'left', 'right'); - } - } else if (vm.base < vm.y) { - edge = swap(edge, 'bottom', 'top'); - } - - res[edge] = true; - return res; -} - -function parseBorderWidth(vm, maxW, maxH) { - var value = vm.borderWidth; - var skip = parseBorderSkipped(vm); - var t, r, b, l; - - if (helpers$1.isObject(value)) { - t = +value.top || 0; - r = +value.right || 0; - b = +value.bottom || 0; - l = +value.left || 0; - } else { - t = r = b = l = +value || 0; - } - - return { - t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t, - r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r, - b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b, - l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l - }; -} - -function boundingRects(vm) { - var bounds = getBarBounds(vm); - var width = bounds.right - bounds.left; - var height = bounds.bottom - bounds.top; - var border = parseBorderWidth(vm, width / 2, height / 2); - - return { - outer: { - x: bounds.left, - y: bounds.top, - w: width, - h: height - }, - inner: { - x: bounds.left + border.l, - y: bounds.top + border.t, - w: width - border.l - border.r, - h: height - border.t - border.b - } - }; -} - -function inRange(vm, x, y) { - var skipX = x === null; - var skipY = y === null; - var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm); - - return bounds - && (skipX || x >= bounds.left && x <= bounds.right) - && (skipY || y >= bounds.top && y <= bounds.bottom); -} - -var element_rectangle = core_element.extend({ - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - var rects = boundingRects(vm); - var outer = rects.outer; - var inner = rects.inner; - - ctx.fillStyle = vm.backgroundColor; - ctx.fillRect(outer.x, outer.y, outer.w, outer.h); - - if (outer.w === inner.w && outer.h === inner.h) { - return; - } - - ctx.save(); - ctx.beginPath(); - ctx.rect(outer.x, outer.y, outer.w, outer.h); - ctx.clip(); - ctx.fillStyle = vm.borderColor; - ctx.rect(inner.x, inner.y, inner.w, inner.h); - ctx.fill('evenodd'); - ctx.restore(); - }, - - height: function() { - var vm = this._view; - return vm.base - vm.y; - }, - - inRange: function(mouseX, mouseY) { - return inRange(this._view, mouseX, mouseY); - }, - - inLabelRange: function(mouseX, mouseY) { - var vm = this._view; - return isVertical(vm) - ? inRange(vm, mouseX, null) - : inRange(vm, null, mouseY); - }, - - inXRange: function(mouseX) { - return inRange(this._view, mouseX, null); - }, - - inYRange: function(mouseY) { - return inRange(this._view, null, mouseY); - }, - - getCenterPoint: function() { - var vm = this._view; - var x, y; - if (isVertical(vm)) { - x = vm.x; - y = (vm.y + vm.base) / 2; - } else { - x = (vm.x + vm.base) / 2; - y = vm.y; - } - - return {x: x, y: y}; - }, - - getArea: function() { - var vm = this._view; - - return isVertical(vm) - ? vm.width * Math.abs(vm.y - vm.base) - : vm.height * Math.abs(vm.x - vm.base); - }, - - tooltipPosition: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y - }; - } -}); - -var elements = {}; -var Arc = element_arc; -var Line = element_line; -var Point = element_point; -var Rectangle = element_rectangle; -elements.Arc = Arc; -elements.Line = Line; -elements.Point = Point; -elements.Rectangle = Rectangle; - -var resolve$1 = helpers$1.options.resolve; - -core_defaults._set('bar', { - hover: { - mode: 'label' - }, - - scales: { - xAxes: [{ - type: 'category', - categoryPercentage: 0.8, - barPercentage: 0.9, - offset: true, - gridLines: { - offsetGridLines: true - } - }], - - yAxes: [{ - type: 'linear' - }] - } -}); - -/** - * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. - * @private - */ -function computeMinSampleSize(scale, pixels) { - var min = scale.isHorizontal() ? scale.width : scale.height; - var ticks = scale.getTicks(); - var prev, curr, i, ilen; - - for (i = 1, ilen = pixels.length; i < ilen; ++i) { - min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1])); - } - - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - curr = scale.getPixelForTick(i); - min = i > 0 ? Math.min(min, curr - prev) : min; - prev = curr; - } - - return min; -} - -/** - * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, - * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This - * mode currently always generates bars equally sized (until we introduce scriptable options?). - * @private - */ -function computeFitCategoryTraits(index, ruler, options) { - var thickness = options.barThickness; - var count = ruler.stackCount; - var curr = ruler.pixels[index]; - var size, ratio; - - if (helpers$1.isNullOrUndef(thickness)) { - size = ruler.min * options.categoryPercentage; - ratio = options.barPercentage; - } else { - // When bar thickness is enforced, category and bar percentages are ignored. - // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') - // and deprecate barPercentage since this value is ignored when thickness is absolute. - size = thickness * count; - ratio = 1; - } - - return { - chunk: size / count, - ratio: ratio, - start: curr - (size / 2) - }; -} - -/** - * Computes an "optimal" category that globally arranges bars side by side (no gap when - * percentage options are 1), based on the previous and following categories. This mode - * generates bars with different widths when data are not evenly spaced. - * @private - */ -function computeFlexCategoryTraits(index, ruler, options) { - var pixels = ruler.pixels; - var curr = pixels[index]; - var prev = index > 0 ? pixels[index - 1] : null; - var next = index < pixels.length - 1 ? pixels[index + 1] : null; - var percent = options.categoryPercentage; - var start, size; - - if (prev === null) { - // first data: its size is double based on the next point or, - // if it's also the last data, we use the scale size. - prev = curr - (next === null ? ruler.end - ruler.start : next - curr); - } - - if (next === null) { - // last data: its size is also double based on the previous point. - next = curr + curr - prev; - } - - start = curr - (curr - Math.min(prev, next)) / 2 * percent; - size = Math.abs(next - prev) / 2 * percent; - - return { - chunk: size / ruler.stackCount, - ratio: options.barPercentage, - start: start - }; -} - -var controller_bar = core_datasetController.extend({ - - dataElementType: elements.Rectangle, - - initialize: function() { - var me = this; - var meta; - - core_datasetController.prototype.initialize.apply(me, arguments); - - meta = me.getMeta(); - meta.stack = me.getDataset().stack; - meta.bar = true; - }, - - update: function(reset) { - var me = this; - var rects = me.getMeta().data; - var i, ilen; - - me._ruler = me.getRuler(); - - for (i = 0, ilen = rects.length; i < ilen; ++i) { - me.updateElement(rects[i], i, reset); - } - }, - - updateElement: function(rectangle, index, reset) { - var me = this; - var meta = me.getMeta(); - var dataset = me.getDataset(); - var options = me._resolveElementOptions(rectangle, index); - - rectangle._xScale = me.getScaleForId(meta.xAxisID); - rectangle._yScale = me.getScaleForId(meta.yAxisID); - rectangle._datasetIndex = me.index; - rectangle._index = index; - rectangle._model = { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderSkipped: options.borderSkipped, - borderWidth: options.borderWidth, - datasetLabel: dataset.label, - label: me.chart.data.labels[index] - }; - - me._updateElementGeometry(rectangle, index, reset); - - rectangle.pivot(); - }, - - /** - * @private - */ - _updateElementGeometry: function(rectangle, index, reset) { - var me = this; - var model = rectangle._model; - var vscale = me._getValueScale(); - var base = vscale.getBasePixel(); - var horizontal = vscale.isHorizontal(); - var ruler = me._ruler || me.getRuler(); - var vpixels = me.calculateBarValuePixels(me.index, index); - var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); - - model.horizontal = horizontal; - model.base = reset ? base : vpixels.base; - model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; - model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; - model.height = horizontal ? ipixels.size : undefined; - model.width = horizontal ? undefined : ipixels.size; - }, - - /** - * Returns the stacks based on groups and bar visibility. - * @param {number} [last] - The dataset index - * @returns {string[]} The list of stack IDs - * @private - */ - _getStacks: function(last) { - var me = this; - var chart = me.chart; - var scale = me._getIndexScale(); - var stacked = scale.options.stacked; - var ilen = last === undefined ? chart.data.datasets.length : last + 1; - var stacks = []; - var i, meta; - - for (i = 0; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - if (meta.bar && chart.isDatasetVisible(i) && - (stacked === false || - (stacked === true && stacks.indexOf(meta.stack) === -1) || - (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { - stacks.push(meta.stack); - } - } - - return stacks; - }, - - /** - * Returns the effective number of stacks based on groups and bar visibility. - * @private - */ - getStackCount: function() { - return this._getStacks().length; - }, - - /** - * Returns the stack index for the given dataset based on groups and bar visibility. - * @param {number} [datasetIndex] - The dataset index - * @param {string} [name] - The stack name to find - * @returns {number} The stack index - * @private - */ - getStackIndex: function(datasetIndex, name) { - var stacks = this._getStacks(datasetIndex); - var index = (name !== undefined) - ? stacks.indexOf(name) - : -1; // indexOf returns -1 if element is not present - - return (index === -1) - ? stacks.length - 1 - : index; - }, - - /** - * @private - */ - getRuler: function() { - var me = this; - var scale = me._getIndexScale(); - var stackCount = me.getStackCount(); - var datasetIndex = me.index; - var isHorizontal = scale.isHorizontal(); - var start = isHorizontal ? scale.left : scale.top; - var end = start + (isHorizontal ? scale.width : scale.height); - var pixels = []; - var i, ilen, min; - - for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { - pixels.push(scale.getPixelForValue(null, i, datasetIndex)); - } - - min = helpers$1.isNullOrUndef(scale.options.barThickness) - ? computeMinSampleSize(scale, pixels) - : -1; - - return { - min: min, - pixels: pixels, - start: start, - end: end, - stackCount: stackCount, - scale: scale - }; - }, - - /** - * Note: pixel values are not clamped to the scale area. - * @private - */ - calculateBarValuePixels: function(datasetIndex, index) { - var me = this; - var chart = me.chart; - var meta = me.getMeta(); - var scale = me._getValueScale(); - var isHorizontal = scale.isHorizontal(); - var datasets = chart.data.datasets; - var value = +scale.getRightValue(datasets[datasetIndex].data[index]); - var minBarLength = scale.options.minBarLength; - var stacked = scale.options.stacked; - var stack = meta.stack; - var start = 0; - var i, imeta, ivalue, base, head, size; - - if (stacked || (stacked === undefined && stack !== undefined)) { - for (i = 0; i < datasetIndex; ++i) { - imeta = chart.getDatasetMeta(i); - - if (imeta.bar && - imeta.stack === stack && - imeta.controller._getValueScaleId() === scale.id && - chart.isDatasetVisible(i)) { - - ivalue = +scale.getRightValue(datasets[i].data[index]); - if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { - start += ivalue; - } - } - } - } - - base = scale.getPixelForValue(start); - head = scale.getPixelForValue(start + value); - size = head - base; - - if (minBarLength !== undefined && Math.abs(size) < minBarLength) { - size = minBarLength; - if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) { - head = base - minBarLength; - } else { - head = base + minBarLength; - } - } - - return { - size: size, - base: base, - head: head, - center: head + size / 2 - }; - }, - - /** - * @private - */ - calculateBarIndexPixels: function(datasetIndex, index, ruler) { - var me = this; - var options = ruler.scale.options; - var range = options.barThickness === 'flex' - ? computeFlexCategoryTraits(index, ruler, options) - : computeFitCategoryTraits(index, ruler, options); - - var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); - var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); - var size = Math.min( - helpers$1.valueOrDefault(options.maxBarThickness, Infinity), - range.chunk * range.ratio); - - return { - base: center - size / 2, - head: center + size / 2, - center: center, - size: size - }; - }, - - draw: function() { - var me = this; - var chart = me.chart; - var scale = me._getValueScale(); - var rects = me.getMeta().data; - var dataset = me.getDataset(); - var ilen = rects.length; - var i = 0; - - helpers$1.canvas.clipArea(chart.ctx, chart.chartArea); - - for (; i < ilen; ++i) { - if (!isNaN(scale.getRightValue(dataset.data[i]))) { - rects[i].draw(); - } - } - - helpers$1.canvas.unclipArea(chart.ctx); - }, - - /** - * @private - */ - _resolveElementOptions: function(rectangle, index) { - var me = this; - var chart = me.chart; - var datasets = chart.data.datasets; - var dataset = datasets[me.index]; - var custom = rectangle.custom || {}; - var options = chart.options.elements.rectangle; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderSkipped', - 'borderWidth' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$1([ - custom[key], - dataset[key], - options[key] - ], context, index); - } - - return values; - } -}); - -var valueOrDefault$3 = helpers$1.valueOrDefault; -var resolve$2 = helpers$1.options.resolve; - -core_defaults._set('bubble', { - hover: { - mode: 'single' - }, - - scales: { - xAxes: [{ - type: 'linear', // bubble should probably use a linear scale by default - position: 'bottom', - id: 'x-axis-0' // need an ID so datasets can reference the scale - }], - yAxes: [{ - type: 'linear', - position: 'left', - id: 'y-axis-0' - }] - }, - - tooltips: { - callbacks: { - title: function() { - // Title doesn't make sense for scatter since we format the data as a point - return ''; - }, - label: function(item, data) { - var datasetLabel = data.datasets[item.datasetIndex].label || ''; - var dataPoint = data.datasets[item.datasetIndex].data[item.index]; - return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; - } - } - } -}); - -var controller_bubble = core_datasetController.extend({ - /** - * @protected - */ - dataElementType: elements.Point, - - /** - * @protected - */ - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var points = meta.data; - - // Update Points - helpers$1.each(points, function(point, index) { - me.updateElement(point, index, reset); - }); - }, - - /** - * @protected - */ - updateElement: function(point, index, reset) { - var me = this; - var meta = me.getMeta(); - var custom = point.custom || {}; - var xScale = me.getScaleForId(meta.xAxisID); - var yScale = me.getScaleForId(meta.yAxisID); - var options = me._resolveElementOptions(point, index); - var data = me.getDataset().data[index]; - var dsIndex = me.index; - - var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); - var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); - - point._xScale = xScale; - point._yScale = yScale; - point._options = options; - point._datasetIndex = dsIndex; - point._index = index; - point._model = { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - hitRadius: options.hitRadius, - pointStyle: options.pointStyle, - rotation: options.rotation, - radius: reset ? 0 : options.radius, - skip: custom.skip || isNaN(x) || isNaN(y), - x: x, - y: y, - }; - - point.pivot(); - }, - - /** - * @protected - */ - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$3(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$3(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$3(options.hoverBorderWidth, options.borderWidth); - model.radius = options.radius + options.hoverRadius; - }, - - /** - * @private - */ - _resolveElementOptions: function(point, index) { - var me = this; - var chart = me.chart; - var datasets = chart.data.datasets; - var dataset = datasets[me.index]; - var custom = point.custom || {}; - var options = chart.options.elements.point; - var data = dataset.data[index]; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - 'hoverRadius', - 'hitRadius', - 'pointStyle', - 'rotation' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$2([ - custom[key], - dataset[key], - options[key] - ], context, index); - } - - // Custom radius resolution - values.radius = resolve$2([ - custom.radius, - data ? data.r : undefined, - dataset.radius, - options.radius - ], context, index); - - return values; - } -}); - -var resolve$3 = helpers$1.options.resolve; -var valueOrDefault$4 = helpers$1.valueOrDefault; - -core_defaults._set('doughnut', { - animation: { - // Boolean - Whether we animate the rotation of the Doughnut - animateRotate: true, - // Boolean - Whether we animate scaling the Doughnut from the centre - animateScale: false - }, - hover: { - mode: 'single' - }, - legendCallback: function(chart) { - var text = []; - text.push('
    '); - - var data = chart.data; - var datasets = data.datasets; - var labels = data.labels; - - if (datasets.length) { - for (var i = 0; i < datasets[0].data.length; ++i) { - text.push('
  • '); - if (labels[i]) { - text.push(labels[i]); - } - text.push('
  • '); - } - } - - text.push('
'); - return text.join(''); - }, - legend: { - labels: { - generateLabels: function(chart) { - var data = chart.data; - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var meta = chart.getDatasetMeta(0); - var ds = data.datasets[0]; - var arc = meta.data[i]; - var custom = arc && arc.custom || {}; - var arcOpts = chart.options.elements.arc; - var fill = resolve$3([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i); - var stroke = resolve$3([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i); - var bw = resolve$3([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i); - - return { - text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(ds.data[i]) || meta.data[i].hidden, - - // Extra data used for toggling the correct item - index: i - }; - }); - } - return []; - } - }, - - onClick: function(e, legendItem) { - var index = legendItem.index; - var chart = this.chart; - var i, ilen, meta; - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - // toggle visibility of index if exists - if (meta.data[index]) { - meta.data[index].hidden = !meta.data[index].hidden; - } - } - - chart.update(); - } - }, - - // The percentage of the chart that we cut out of the middle. - cutoutPercentage: 50, - - // The rotation of the chart, where the first data arc begins. - rotation: Math.PI * -0.5, - - // The total circumference of the chart. - circumference: Math.PI * 2.0, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(tooltipItem, data) { - var dataLabel = data.labels[tooltipItem.index]; - var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; - - if (helpers$1.isArray(dataLabel)) { - // show value on first line of multiline label - // need to clone because we are changing the value - dataLabel = dataLabel.slice(); - dataLabel[0] += value; - } else { - dataLabel += value; - } - - return dataLabel; - } - } - } -}); - -var controller_doughnut = core_datasetController.extend({ - - dataElementType: elements.Arc, - - linkScales: helpers$1.noop, - - // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly - getRingIndex: function(datasetIndex) { - var ringIndex = 0; - - for (var j = 0; j < datasetIndex; ++j) { - if (this.chart.isDatasetVisible(j)) { - ++ringIndex; - } - } - - return ringIndex; - }, - - update: function(reset) { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var availableWidth = chartArea.right - chartArea.left; - var availableHeight = chartArea.bottom - chartArea.top; - var minSize = Math.min(availableWidth, availableHeight); - var offset = {x: 0, y: 0}; - var meta = me.getMeta(); - var arcs = meta.data; - var cutoutPercentage = opts.cutoutPercentage; - var circumference = opts.circumference; - var chartWeight = me._getRingWeight(me.index); - var i, ilen; - - // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc - if (circumference < Math.PI * 2.0) { - var startAngle = opts.rotation % (Math.PI * 2.0); - startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); - var endAngle = startAngle + circumference; - var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; - var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; - var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); - var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); - var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); - var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); - var cutout = cutoutPercentage / 100.0; - var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; - var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; - var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; - minSize = Math.min(availableWidth / size.width, availableHeight / size.height); - offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arcs[i]._options = me._resolveElementOptions(arcs[i], i); - } - - chart.borderWidth = me.getMaxBorderWidth(); - chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); - chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); - chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1); - chart.offsetX = offset.x * chart.outerRadius; - chart.offsetY = offset.y * chart.outerRadius; - - meta.total = me.calculateTotal(); - - me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index); - me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0); - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - me.updateElement(arcs[i], i, reset); - } - }, - - updateElement: function(arc, index, reset) { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var animationOpts = opts.animation; - var centerX = (chartArea.left + chartArea.right) / 2; - var centerY = (chartArea.top + chartArea.bottom) / 2; - var startAngle = opts.rotation; // non reset case handled later - var endAngle = opts.rotation; // non reset case handled later - var dataset = me.getDataset(); - var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); - var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; - var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; - var options = arc._options || {}; - - helpers$1.extend(arc, { - // Utility - _datasetIndex: me.index, - _index: index, - - // Desired view properties - _model: { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - borderAlign: options.borderAlign, - x: centerX + chart.offsetX, - y: centerY + chart.offsetY, - startAngle: startAngle, - endAngle: endAngle, - circumference: circumference, - outerRadius: outerRadius, - innerRadius: innerRadius, - label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) - } - }); - - var model = arc._model; - - // Set correct angles if not resetting - if (!reset || !animationOpts.animateRotate) { - if (index === 0) { - model.startAngle = opts.rotation; - } else { - model.startAngle = me.getMeta().data[index - 1]._model.endAngle; - } - - model.endAngle = model.startAngle + model.circumference; - } - - arc.pivot(); - }, - - calculateTotal: function() { - var dataset = this.getDataset(); - var meta = this.getMeta(); - var total = 0; - var value; - - helpers$1.each(meta.data, function(element, index) { - value = dataset.data[index]; - if (!isNaN(value) && !element.hidden) { - total += Math.abs(value); - } - }); - - /* if (total === 0) { - total = NaN; - }*/ - - return total; - }, - - calculateCircumference: function(value) { - var total = this.getMeta().total; - if (total > 0 && !isNaN(value)) { - return (Math.PI * 2.0) * (Math.abs(value) / total); - } - return 0; - }, - - // gets the max border or hover width to properly scale pie charts - getMaxBorderWidth: function(arcs) { - var me = this; - var max = 0; - var chart = me.chart; - var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth; - - if (!arcs) { - // Find the outmost visible dataset - for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - meta = chart.getDatasetMeta(i); - arcs = meta.data; - if (i !== me.index) { - controller = meta.controller; - } - break; - } - } - } - - if (!arcs) { - return 0; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arc = arcs[i]; - options = controller ? controller._resolveElementOptions(arc, i) : arc._options; - if (options.borderAlign !== 'inner') { - borderWidth = options.borderWidth; - hoverWidth = options.hoverBorderWidth; - - max = borderWidth > max ? borderWidth : max; - max = hoverWidth > max ? hoverWidth : max; - } - } - return max; - }, - - /** - * @protected - */ - setHoverStyle: function(arc) { - var model = arc._model; - var options = arc._options; - var getHoverColor = helpers$1.getHoverColor; - - arc.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - }; - - model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth); - }, - - /** - * @private - */ - _resolveElementOptions: function(arc, index) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var custom = arc.custom || {}; - var options = chart.options.elements.arc; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'borderAlign', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$3([ - custom[key], - dataset[key], - options[key] - ], context, index); - } - - return values; - }, - - /** - * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly - * @private - */ - _getRingWeightOffset: function(datasetIndex) { - var ringWeightOffset = 0; - - for (var i = 0; i < datasetIndex; ++i) { - if (this.chart.isDatasetVisible(i)) { - ringWeightOffset += this._getRingWeight(i); - } - } - - return ringWeightOffset; - }, - - /** - * @private - */ - _getRingWeight: function(dataSetIndex) { - return Math.max(valueOrDefault$4(this.chart.data.datasets[dataSetIndex].weight, 1), 0); - }, - - /** - * Returns the sum of all visibile data set weights. This value can be 0. - * @private - */ - _getVisibleDatasetWeightTotal: function() { - return this._getRingWeightOffset(this.chart.data.datasets.length); - } -}); - -core_defaults._set('horizontalBar', { - hover: { - mode: 'index', - axis: 'y' - }, - - scales: { - xAxes: [{ - type: 'linear', - position: 'bottom' - }], - - yAxes: [{ - type: 'category', - position: 'left', - categoryPercentage: 0.8, - barPercentage: 0.9, - offset: true, - gridLines: { - offsetGridLines: true - } - }] - }, - - elements: { - rectangle: { - borderSkipped: 'left' - } - }, - - tooltips: { - mode: 'index', - axis: 'y' - } -}); - -var controller_horizontalBar = controller_bar.extend({ - /** - * @private - */ - _getValueScaleId: function() { - return this.getMeta().xAxisID; - }, - - /** - * @private - */ - _getIndexScaleId: function() { - return this.getMeta().yAxisID; - } -}); - -var valueOrDefault$5 = helpers$1.valueOrDefault; -var resolve$4 = helpers$1.options.resolve; -var isPointInArea = helpers$1.canvas._isPointInArea; - -core_defaults._set('line', { - showLines: true, - spanGaps: false, - - hover: { - mode: 'label' - }, - - scales: { - xAxes: [{ - type: 'category', - id: 'x-axis-0' - }], - yAxes: [{ - type: 'linear', - id: 'y-axis-0' - }] - } -}); - -function lineEnabled(dataset, options) { - return valueOrDefault$5(dataset.showLine, options.showLines); -} - -var controller_line = core_datasetController.extend({ - - datasetElementType: elements.Line, - - dataElementType: elements.Point, - - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var line = meta.dataset; - var points = meta.data || []; - var scale = me.getScaleForId(meta.yAxisID); - var dataset = me.getDataset(); - var showLine = lineEnabled(dataset, me.chart.options); - var i, ilen; - - // Update Line - if (showLine) { - // Compatibility: If the properties are defined with only the old name, use those values - if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { - dataset.lineTension = dataset.tension; - } - - // Utility - line._scale = scale; - line._datasetIndex = me.index; - // Data - line._children = points; - // Model - line._model = me._resolveLineOptions(line); - - line.pivot(); - } - - // Update Points - for (i = 0, ilen = points.length; i < ilen; ++i) { - me.updateElement(points[i], i, reset); - } - - if (showLine && line._model.tension !== 0) { - me.updateBezierControlPoints(); - } - - // Now pivot the point for animation - for (i = 0, ilen = points.length; i < ilen; ++i) { - points[i].pivot(); - } - }, - - updateElement: function(point, index, reset) { - var me = this; - var meta = me.getMeta(); - var custom = point.custom || {}; - var dataset = me.getDataset(); - var datasetIndex = me.index; - var value = dataset.data[index]; - var yScale = me.getScaleForId(meta.yAxisID); - var xScale = me.getScaleForId(meta.xAxisID); - var lineModel = meta.dataset._model; - var x, y; - - var options = me._resolvePointOptions(point, index); - - x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); - y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); - - // Utility - point._xScale = xScale; - point._yScale = yScale; - point._options = options; - point._datasetIndex = datasetIndex; - point._index = index; - - // Desired view properties - point._model = { - x: x, - y: y, - skip: custom.skip || isNaN(x) || isNaN(y), - // Appearance - radius: options.radius, - pointStyle: options.pointStyle, - rotation: options.rotation, - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - tension: valueOrDefault$5(custom.tension, lineModel ? lineModel.tension : 0), - steppedLine: lineModel ? lineModel.steppedLine : false, - // Tooltip - hitRadius: options.hitRadius - }; - }, - - /** - * @private - */ - _resolvePointOptions: function(element, index) { - var me = this; - var chart = me.chart; - var dataset = chart.data.datasets[me.index]; - var custom = element.custom || {}; - var options = chart.options.elements.point; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var ELEMENT_OPTIONS = { - backgroundColor: 'pointBackgroundColor', - borderColor: 'pointBorderColor', - borderWidth: 'pointBorderWidth', - hitRadius: 'pointHitRadius', - hoverBackgroundColor: 'pointHoverBackgroundColor', - hoverBorderColor: 'pointHoverBorderColor', - hoverBorderWidth: 'pointHoverBorderWidth', - hoverRadius: 'pointHoverRadius', - pointStyle: 'pointStyle', - radius: 'pointRadius', - rotation: 'pointRotation' - }; - var keys = Object.keys(ELEMENT_OPTIONS); - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$4([ - custom[key], - dataset[ELEMENT_OPTIONS[key]], - dataset[key], - options[key] - ], context, index); - } - - return values; - }, - - /** - * @private - */ - _resolveLineOptions: function(element) { - var me = this; - var chart = me.chart; - var dataset = chart.data.datasets[me.index]; - var custom = element.custom || {}; - var options = chart.options; - var elementOptions = options.elements.line; - var values = {}; - var i, ilen, key; - - var keys = [ - 'backgroundColor', - 'borderWidth', - 'borderColor', - 'borderCapStyle', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'fill', - 'cubicInterpolationMode' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$4([ - custom[key], - dataset[key], - elementOptions[key] - ]); - } - - // The default behavior of lines is to break at null values, according - // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 - // This option gives lines the ability to span gaps - values.spanGaps = valueOrDefault$5(dataset.spanGaps, options.spanGaps); - values.tension = valueOrDefault$5(dataset.lineTension, elementOptions.tension); - values.steppedLine = resolve$4([custom.steppedLine, dataset.steppedLine, elementOptions.stepped]); - - return values; - }, - - calculatePointY: function(value, index, datasetIndex) { - var me = this; - var chart = me.chart; - var meta = me.getMeta(); - var yScale = me.getScaleForId(meta.yAxisID); - var sumPos = 0; - var sumNeg = 0; - var i, ds, dsMeta; - - if (yScale.options.stacked) { - for (i = 0; i < datasetIndex; i++) { - ds = chart.data.datasets[i]; - dsMeta = chart.getDatasetMeta(i); - if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { - var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); - if (stackedRightValue < 0) { - sumNeg += stackedRightValue || 0; - } else { - sumPos += stackedRightValue || 0; - } - } - } - - var rightValue = Number(yScale.getRightValue(value)); - if (rightValue < 0) { - return yScale.getPixelForValue(sumNeg + rightValue); - } - return yScale.getPixelForValue(sumPos + rightValue); - } - - return yScale.getPixelForValue(value); - }, - - updateBezierControlPoints: function() { - var me = this; - var chart = me.chart; - var meta = me.getMeta(); - var lineModel = meta.dataset._model; - var area = chart.chartArea; - var points = meta.data || []; - var i, ilen, model, controlPoints; - - // Only consider points that are drawn in case the spanGaps option is used - if (lineModel.spanGaps) { - points = points.filter(function(pt) { - return !pt._model.skip; - }); - } - - function capControlPoint(pt, min, max) { - return Math.max(Math.min(pt, max), min); - } - - if (lineModel.cubicInterpolationMode === 'monotone') { - helpers$1.splineCurveMonotone(points); - } else { - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - controlPoints = helpers$1.splineCurve( - helpers$1.previousItem(points, i)._model, - model, - helpers$1.nextItem(points, i)._model, - lineModel.tension - ); - model.controlPointPreviousX = controlPoints.previous.x; - model.controlPointPreviousY = controlPoints.previous.y; - model.controlPointNextX = controlPoints.next.x; - model.controlPointNextY = controlPoints.next.y; - } - } - - if (chart.options.elements.line.capBezierPoints) { - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - if (isPointInArea(model, area)) { - if (i > 0 && isPointInArea(points[i - 1]._model, area)) { - model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); - model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); - } - if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) { - model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); - model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); - } - } - } - } - }, - - draw: function() { - var me = this; - var chart = me.chart; - var meta = me.getMeta(); - var points = meta.data || []; - var area = chart.chartArea; - var ilen = points.length; - var halfBorderWidth; - var i = 0; - - if (lineEnabled(me.getDataset(), chart.options)) { - halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2; - - helpers$1.canvas.clipArea(chart.ctx, { - left: area.left, - right: area.right, - top: area.top - halfBorderWidth, - bottom: area.bottom + halfBorderWidth - }); - - meta.dataset.draw(); - - helpers$1.canvas.unclipArea(chart.ctx); - } - - // Draw the points - for (; i < ilen; ++i) { - points[i].draw(area); - } - }, - - /** - * @protected - */ - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth); - model.radius = valueOrDefault$5(options.hoverRadius, options.radius); - }, -}); - -var resolve$5 = helpers$1.options.resolve; - -core_defaults._set('polarArea', { - scale: { - type: 'radialLinear', - angleLines: { - display: false - }, - gridLines: { - circular: true - }, - pointLabels: { - display: false - }, - ticks: { - beginAtZero: true - } - }, - - // Boolean - Whether to animate the rotation of the chart - animation: { - animateRotate: true, - animateScale: true - }, - - startAngle: -0.5 * Math.PI, - legendCallback: function(chart) { - var text = []; - text.push('
    '); - - var data = chart.data; - var datasets = data.datasets; - var labels = data.labels; - - if (datasets.length) { - for (var i = 0; i < datasets[0].data.length; ++i) { - text.push('
  • '); - if (labels[i]) { - text.push(labels[i]); - } - text.push('
  • '); - } - } - - text.push('
'); - return text.join(''); - }, - legend: { - labels: { - generateLabels: function(chart) { - var data = chart.data; - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var meta = chart.getDatasetMeta(0); - var ds = data.datasets[0]; - var arc = meta.data[i]; - var custom = arc.custom || {}; - var arcOpts = chart.options.elements.arc; - var fill = resolve$5([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i); - var stroke = resolve$5([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i); - var bw = resolve$5([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i); - - return { - text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(ds.data[i]) || meta.data[i].hidden, - - // Extra data used for toggling the correct item - index: i - }; - }); - } - return []; - } - }, - - onClick: function(e, legendItem) { - var index = legendItem.index; - var chart = this.chart; - var i, ilen, meta; - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - meta.data[index].hidden = !meta.data[index].hidden; - } - - chart.update(); - } - }, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(item, data) { - return data.labels[item.index] + ': ' + item.yLabel; - } - } - } -}); - -var controller_polarArea = core_datasetController.extend({ - - dataElementType: elements.Arc, - - linkScales: helpers$1.noop, - - update: function(reset) { - var me = this; - var dataset = me.getDataset(); - var meta = me.getMeta(); - var start = me.chart.options.startAngle || 0; - var starts = me._starts = []; - var angles = me._angles = []; - var arcs = meta.data; - var i, ilen, angle; - - me._updateRadius(); - - meta.count = me.countVisibleElements(); - - for (i = 0, ilen = dataset.data.length; i < ilen; i++) { - starts[i] = start; - angle = me._computeAngle(i); - angles[i] = angle; - start += angle; - } - - for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arcs[i]._options = me._resolveElementOptions(arcs[i], i); - me.updateElement(arcs[i], i, reset); - } - }, - - /** - * @private - */ - _updateRadius: function() { - var me = this; - var chart = me.chart; - var chartArea = chart.chartArea; - var opts = chart.options; - var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); - - chart.outerRadius = Math.max(minSize / 2, 0); - chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); - chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); - - me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); - me.innerRadius = me.outerRadius - chart.radiusLength; - }, - - updateElement: function(arc, index, reset) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var opts = chart.options; - var animationOpts = opts.animation; - var scale = chart.scale; - var labels = chart.data.labels; - - var centerX = scale.xCenter; - var centerY = scale.yCenter; - - // var negHalfPI = -0.5 * Math.PI; - var datasetStartAngle = opts.startAngle; - var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); - var startAngle = me._starts[index]; - var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]); - - var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); - var options = arc._options || {}; - - helpers$1.extend(arc, { - // Utility - _datasetIndex: me.index, - _index: index, - _scale: scale, - - // Desired view properties - _model: { - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - borderAlign: options.borderAlign, - x: centerX, - y: centerY, - innerRadius: 0, - outerRadius: reset ? resetRadius : distance, - startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, - endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, - label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index]) - } - }); - - arc.pivot(); - }, - - countVisibleElements: function() { - var dataset = this.getDataset(); - var meta = this.getMeta(); - var count = 0; - - helpers$1.each(meta.data, function(element, index) { - if (!isNaN(dataset.data[index]) && !element.hidden) { - count++; - } - }); - - return count; - }, - - /** - * @protected - */ - setHoverStyle: function(arc) { - var model = arc._model; - var options = arc._options; - var getHoverColor = helpers$1.getHoverColor; - var valueOrDefault = helpers$1.valueOrDefault; - - arc.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - }; - - model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth); - }, - - /** - * @private - */ - _resolveElementOptions: function(arc, index) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var custom = arc.custom || {}; - var options = chart.options.elements.arc; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'borderAlign', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$5([ - custom[key], - dataset[key], - options[key] - ], context, index); - } - - return values; - }, - - /** - * @private - */ - _computeAngle: function(index) { - var me = this; - var count = this.getMeta().count; - var dataset = me.getDataset(); - var meta = me.getMeta(); - - if (isNaN(dataset.data[index]) || meta.data[index].hidden) { - return 0; - } - - // Scriptable options - var context = { - chart: me.chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - return resolve$5([ - me.chart.options.elements.arc.angle, - (2 * Math.PI) / count - ], context, index); - } -}); - -core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut)); -core_defaults._set('pie', { - cutoutPercentage: 0 -}); - -// Pie charts are Doughnut chart with different defaults -var controller_pie = controller_doughnut; - -var valueOrDefault$6 = helpers$1.valueOrDefault; -var resolve$6 = helpers$1.options.resolve; - -core_defaults._set('radar', { - scale: { - type: 'radialLinear' - }, - elements: { - line: { - tension: 0 // no bezier in radar - } - } -}); - -var controller_radar = core_datasetController.extend({ - - datasetElementType: elements.Line, - - dataElementType: elements.Point, - - linkScales: helpers$1.noop, - - update: function(reset) { - var me = this; - var meta = me.getMeta(); - var line = meta.dataset; - var points = meta.data || []; - var scale = me.chart.scale; - var dataset = me.getDataset(); - var i, ilen; - - // Compatibility: If the properties are defined with only the old name, use those values - if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { - dataset.lineTension = dataset.tension; - } - - // Utility - line._scale = scale; - line._datasetIndex = me.index; - // Data - line._children = points; - line._loop = true; - // Model - line._model = me._resolveLineOptions(line); - - line.pivot(); - - // Update Points - for (i = 0, ilen = points.length; i < ilen; ++i) { - me.updateElement(points[i], i, reset); - } - - // Update bezier control points - me.updateBezierControlPoints(); - - // Now pivot the point for animation - for (i = 0, ilen = points.length; i < ilen; ++i) { - points[i].pivot(); - } - }, - - updateElement: function(point, index, reset) { - var me = this; - var custom = point.custom || {}; - var dataset = me.getDataset(); - var scale = me.chart.scale; - var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); - var options = me._resolvePointOptions(point, index); - var lineModel = me.getMeta().dataset._model; - var x = reset ? scale.xCenter : pointPosition.x; - var y = reset ? scale.yCenter : pointPosition.y; - - // Utility - point._scale = scale; - point._options = options; - point._datasetIndex = me.index; - point._index = index; - - // Desired view properties - point._model = { - x: x, // value not used in dataset scale, but we want a consistent API between scales - y: y, - skip: custom.skip || isNaN(x) || isNaN(y), - // Appearance - radius: options.radius, - pointStyle: options.pointStyle, - rotation: options.rotation, - backgroundColor: options.backgroundColor, - borderColor: options.borderColor, - borderWidth: options.borderWidth, - tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0), - - // Tooltip - hitRadius: options.hitRadius - }; - }, - - /** - * @private - */ - _resolvePointOptions: function(element, index) { - var me = this; - var chart = me.chart; - var dataset = chart.data.datasets[me.index]; - var custom = element.custom || {}; - var options = chart.options.elements.point; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var ELEMENT_OPTIONS = { - backgroundColor: 'pointBackgroundColor', - borderColor: 'pointBorderColor', - borderWidth: 'pointBorderWidth', - hitRadius: 'pointHitRadius', - hoverBackgroundColor: 'pointHoverBackgroundColor', - hoverBorderColor: 'pointHoverBorderColor', - hoverBorderWidth: 'pointHoverBorderWidth', - hoverRadius: 'pointHoverRadius', - pointStyle: 'pointStyle', - radius: 'pointRadius', - rotation: 'pointRotation' - }; - var keys = Object.keys(ELEMENT_OPTIONS); - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$6([ - custom[key], - dataset[ELEMENT_OPTIONS[key]], - dataset[key], - options[key] - ], context, index); - } - - return values; - }, - - /** - * @private - */ - _resolveLineOptions: function(element) { - var me = this; - var chart = me.chart; - var dataset = chart.data.datasets[me.index]; - var custom = element.custom || {}; - var options = chart.options.elements.line; - var values = {}; - var i, ilen, key; - - var keys = [ - 'backgroundColor', - 'borderWidth', - 'borderColor', - 'borderCapStyle', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'fill' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$6([ - custom[key], - dataset[key], - options[key] - ]); - } - - values.tension = valueOrDefault$6(dataset.lineTension, options.tension); - - return values; - }, - - updateBezierControlPoints: function() { - var me = this; - var meta = me.getMeta(); - var area = me.chart.chartArea; - var points = meta.data || []; - var i, ilen, model, controlPoints; - - function capControlPoint(pt, min, max) { - return Math.max(Math.min(pt, max), min); - } - - for (i = 0, ilen = points.length; i < ilen; ++i) { - model = points[i]._model; - controlPoints = helpers$1.splineCurve( - helpers$1.previousItem(points, i, true)._model, - model, - helpers$1.nextItem(points, i, true)._model, - model.tension - ); - - // Prevent the bezier going outside of the bounds of the graph - model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right); - model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom); - model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right); - model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom); - } - }, - - setHoverStyle: function(point) { - var model = point._model; - var options = point._options; - var getHoverColor = helpers$1.getHoverColor; - - point.$previousStyle = { - backgroundColor: model.backgroundColor, - borderColor: model.borderColor, - borderWidth: model.borderWidth, - radius: model.radius - }; - - model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth); - model.radius = valueOrDefault$6(options.hoverRadius, options.radius); - } -}); - -core_defaults._set('scatter', { - hover: { - mode: 'single' - }, - - scales: { - xAxes: [{ - id: 'x-axis-1', // need an ID so datasets can reference the scale - type: 'linear', // scatter should not use a category axis - position: 'bottom' - }], - yAxes: [{ - id: 'y-axis-1', - type: 'linear', - position: 'left' - }] - }, - - showLines: false, - - tooltips: { - callbacks: { - title: function() { - return ''; // doesn't make sense for scatter since data are formatted as a point - }, - label: function(item) { - return '(' + item.xLabel + ', ' + item.yLabel + ')'; - } - } - } -}); - -// Scatter charts use line controllers -var controller_scatter = controller_line; - -// NOTE export a map in which the key represents the controller type, not -// the class, and so must be CamelCase in order to be correctly retrieved -// by the controller in core.controller.js (`controllers[meta.type]`). - -var controllers = { - bar: controller_bar, - bubble: controller_bubble, - doughnut: controller_doughnut, - horizontalBar: controller_horizontalBar, - line: controller_line, - polarArea: controller_polarArea, - pie: controller_pie, - radar: controller_radar, - scatter: controller_scatter -}; - -/** - * Helper function to get relative position for an event - * @param {Event|IEvent} event - The event to get the position for - * @param {Chart} chart - The chart - * @returns {object} the event position - */ -function getRelativePosition(e, chart) { - if (e.native) { - return { - x: e.x, - y: e.y - }; - } - - return helpers$1.getRelativePosition(e, chart); -} - -/** - * Helper function to traverse all of the visible elements in the chart - * @param {Chart} chart - the chart - * @param {function} handler - the callback to execute for each visible item - */ -function parseVisibleItems(chart, handler) { - var datasets = chart.data.datasets; - var meta, i, j, ilen, jlen; - - for (i = 0, ilen = datasets.length; i < ilen; ++i) { - if (!chart.isDatasetVisible(i)) { - continue; - } - - meta = chart.getDatasetMeta(i); - for (j = 0, jlen = meta.data.length; j < jlen; ++j) { - var element = meta.data[j]; - if (!element._view.skip) { - handler(element); - } - } - } -} - -/** - * Helper function to get the items that intersect the event position - * @param {ChartElement[]} items - elements to filter - * @param {object} position - the point to be nearest to - * @return {ChartElement[]} the nearest items - */ -function getIntersectItems(chart, position) { - var elements = []; - - parseVisibleItems(chart, function(element) { - if (element.inRange(position.x, position.y)) { - elements.push(element); - } - }); - - return elements; -} - -/** - * Helper function to get the items nearest to the event position considering all visible items in teh chart - * @param {Chart} chart - the chart to look at elements from - * @param {object} position - the point to be nearest to - * @param {boolean} intersect - if true, only consider items that intersect the position - * @param {function} distanceMetric - function to provide the distance between points - * @return {ChartElement[]} the nearest items - */ -function getNearestItems(chart, position, intersect, distanceMetric) { - var minDistance = Number.POSITIVE_INFINITY; - var nearestItems = []; - - parseVisibleItems(chart, function(element) { - if (intersect && !element.inRange(position.x, position.y)) { - return; - } - - var center = element.getCenterPoint(); - var distance = distanceMetric(position, center); - if (distance < minDistance) { - nearestItems = [element]; - minDistance = distance; - } else if (distance === minDistance) { - // Can have multiple items at the same distance in which case we sort by size - nearestItems.push(element); - } - }); - - return nearestItems; -} - -/** - * Get a distance metric function for two points based on the - * axis mode setting - * @param {string} axis - the axis mode. x|y|xy - */ -function getDistanceMetricForAxis(axis) { - var useX = axis.indexOf('x') !== -1; - var useY = axis.indexOf('y') !== -1; - - return function(pt1, pt2) { - var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; - var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; - return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); - }; -} - -function indexMode(chart, e, options) { - var position = getRelativePosition(e, chart); - // Default axis for index mode is 'x' to match old behaviour - options.axis = options.axis || 'x'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); - var elements = []; - - if (!items.length) { - return []; - } - - chart.data.datasets.forEach(function(dataset, datasetIndex) { - if (chart.isDatasetVisible(datasetIndex)) { - var meta = chart.getDatasetMeta(datasetIndex); - var element = meta.data[items[0]._index]; - - // don't count items that are skipped (null data) - if (element && !element._view.skip) { - elements.push(element); - } - } - }); - - return elements; -} - -/** - * @interface IInteractionOptions - */ -/** - * If true, only consider items that intersect the point - * @name IInterfaceOptions#boolean - * @type Boolean - */ - -/** - * Contains interaction related functions - * @namespace Chart.Interaction - */ -var core_interaction = { - // Helper function for different modes - modes: { - single: function(chart, e) { - var position = getRelativePosition(e, chart); - var elements = []; - - parseVisibleItems(chart, function(element) { - if (element.inRange(position.x, position.y)) { - elements.push(element); - return elements; - } - }); - - return elements.slice(0, 1); - }, - - /** - * @function Chart.Interaction.modes.label - * @deprecated since version 2.4.0 - * @todo remove at version 3 - * @private - */ - label: indexMode, - - /** - * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something - * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item - * @function Chart.Interaction.modes.index - * @since v2.4.0 - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use during interaction - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - index: indexMode, - - /** - * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something - * If the options.intersect is false, we find the nearest item and return the items in that dataset - * @function Chart.Interaction.modes.dataset - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use during interaction - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - dataset: function(chart, e, options) { - var position = getRelativePosition(e, chart); - options.axis = options.axis || 'xy'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); - - if (items.length > 0) { - items = chart.getDatasetMeta(items[0]._datasetIndex).data; - } - - return items; - }, - - /** - * @function Chart.Interaction.modes.x-axis - * @deprecated since version 2.4.0. Use index mode and intersect == true - * @todo remove at version 3 - * @private - */ - 'x-axis': function(chart, e) { - return indexMode(chart, e, {intersect: false}); - }, - - /** - * Point mode returns all elements that hit test based on the event position - * of the event - * @function Chart.Interaction.modes.intersect - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - point: function(chart, e) { - var position = getRelativePosition(e, chart); - return getIntersectItems(chart, position); - }, - - /** - * nearest mode returns the element closest to the point - * @function Chart.Interaction.modes.intersect - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - nearest: function(chart, e, options) { - var position = getRelativePosition(e, chart); - options.axis = options.axis || 'xy'; - var distanceMetric = getDistanceMetricForAxis(options.axis); - return getNearestItems(chart, position, options.intersect, distanceMetric); - }, - - /** - * x mode returns the elements that hit-test at the current x coordinate - * @function Chart.Interaction.modes.x - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - x: function(chart, e, options) { - var position = getRelativePosition(e, chart); - var items = []; - var intersectsItem = false; - - parseVisibleItems(chart, function(element) { - if (element.inXRange(position.x)) { - items.push(element); - } - - if (element.inRange(position.x, position.y)) { - intersectsItem = true; - } - }); - - // If we want to trigger on an intersect and we don't have any items - // that intersect the position, return nothing - if (options.intersect && !intersectsItem) { - items = []; - } - return items; - }, - - /** - * y mode returns the elements that hit-test at the current y coordinate - * @function Chart.Interaction.modes.y - * @param {Chart} chart - the chart we are returning items from - * @param {Event} e - the event we are find things at - * @param {IInteractionOptions} options - options to use - * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned - */ - y: function(chart, e, options) { - var position = getRelativePosition(e, chart); - var items = []; - var intersectsItem = false; - - parseVisibleItems(chart, function(element) { - if (element.inYRange(position.y)) { - items.push(element); - } - - if (element.inRange(position.x, position.y)) { - intersectsItem = true; - } - }); - - // If we want to trigger on an intersect and we don't have any items - // that intersect the position, return nothing - if (options.intersect && !intersectsItem) { - items = []; - } - return items; - } - } -}; - -function filterByPosition(array, position) { - return helpers$1.where(array, function(v) { - return v.position === position; - }); -} - -function sortByWeight(array, reverse) { - array.forEach(function(v, i) { - v._tmpIndex_ = i; - return v; - }); - array.sort(function(a, b) { - var v0 = reverse ? b : a; - var v1 = reverse ? a : b; - return v0.weight === v1.weight ? - v0._tmpIndex_ - v1._tmpIndex_ : - v0.weight - v1.weight; - }); - array.forEach(function(v) { - delete v._tmpIndex_; - }); -} - -function findMaxPadding(boxes) { - var top = 0; - var left = 0; - var bottom = 0; - var right = 0; - helpers$1.each(boxes, function(box) { - if (box.getPadding) { - var boxPadding = box.getPadding(); - top = Math.max(top, boxPadding.top); - left = Math.max(left, boxPadding.left); - bottom = Math.max(bottom, boxPadding.bottom); - right = Math.max(right, boxPadding.right); - } - }); - return { - top: top, - left: left, - bottom: bottom, - right: right - }; -} - -function addSizeByPosition(boxes, size) { - helpers$1.each(boxes, function(box) { - size[box.position] += box.isHorizontal() ? box.height : box.width; - }); -} - -core_defaults._set('global', { - layout: { - padding: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } - } -}); - -/** - * @interface ILayoutItem - * @prop {string} position - The position of the item in the chart layout. Possible values are - * 'left', 'top', 'right', 'bottom', and 'chartArea' - * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area - * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down - * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) - * @prop {function} update - Takes two parameters: width and height. Returns size of item - * @prop {function} getPadding - Returns an object with padding on the edges - * @prop {number} width - Width of item. Must be valid after update() - * @prop {number} height - Height of item. Must be valid after update() - * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update - * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update - * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update - * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update - */ - -// The layout service is very self explanatory. It's responsible for the layout within a chart. -// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need -// It is this service's responsibility of carrying out that layout. -var core_layouts = { - defaults: {}, - - /** - * Register a box to a chart. - * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. - * @param {Chart} chart - the chart to use - * @param {ILayoutItem} item - the item to add to be layed out - */ - addBox: function(chart, item) { - if (!chart.boxes) { - chart.boxes = []; - } - - // initialize item with default values - item.fullWidth = item.fullWidth || false; - item.position = item.position || 'top'; - item.weight = item.weight || 0; - - chart.boxes.push(item); - }, - - /** - * Remove a layoutItem from a chart - * @param {Chart} chart - the chart to remove the box from - * @param {ILayoutItem} layoutItem - the item to remove from the layout - */ - removeBox: function(chart, layoutItem) { - var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; - if (index !== -1) { - chart.boxes.splice(index, 1); - } - }, - - /** - * Sets (or updates) options on the given `item`. - * @param {Chart} chart - the chart in which the item lives (or will be added to) - * @param {ILayoutItem} item - the item to configure with the given options - * @param {object} options - the new item options. - */ - configure: function(chart, item, options) { - var props = ['fullWidth', 'position', 'weight']; - var ilen = props.length; - var i = 0; - var prop; - - for (; i < ilen; ++i) { - prop = props[i]; - if (options.hasOwnProperty(prop)) { - item[prop] = options[prop]; - } - } - }, - - /** - * Fits boxes of the given chart into the given size by having each box measure itself - * then running a fitting algorithm - * @param {Chart} chart - the chart - * @param {number} width - the width to fit into - * @param {number} height - the height to fit into - */ - update: function(chart, width, height) { - if (!chart) { - return; - } - - var layoutOptions = chart.options.layout || {}; - var padding = helpers$1.options.toPadding(layoutOptions.padding); - var leftPadding = padding.left; - var rightPadding = padding.right; - var topPadding = padding.top; - var bottomPadding = padding.bottom; - - var leftBoxes = filterByPosition(chart.boxes, 'left'); - var rightBoxes = filterByPosition(chart.boxes, 'right'); - var topBoxes = filterByPosition(chart.boxes, 'top'); - var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); - var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); - - // Sort boxes by weight. A higher weight is further away from the chart area - sortByWeight(leftBoxes, true); - sortByWeight(rightBoxes, false); - sortByWeight(topBoxes, true); - sortByWeight(bottomBoxes, false); - - var verticalBoxes = leftBoxes.concat(rightBoxes); - var horizontalBoxes = topBoxes.concat(bottomBoxes); - var outerBoxes = verticalBoxes.concat(horizontalBoxes); - - // Essentially we now have any number of boxes on each of the 4 sides. - // Our canvas looks like the following. - // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and - // B1 is the bottom axis - // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays - // These locations are single-box locations only, when trying to register a chartArea location that is already taken, - // an error will be thrown. - // - // |----------------------------------------------------| - // | T1 (Full Width) | - // |----------------------------------------------------| - // | | | T2 | | - // | |----|-------------------------------------|----| - // | | | C1 | | C2 | | - // | | |----| |----| | - // | | | | | - // | L1 | L2 | ChartArea (C0) | R1 | - // | | | | | - // | | |----| |----| | - // | | | C3 | | C4 | | - // | |----|-------------------------------------|----| - // | | | B1 | | - // |----------------------------------------------------| - // | B2 (Full Width) | - // |----------------------------------------------------| - // - // What we do to find the best sizing, we do the following - // 1. Determine the minimum size of the chart area. - // 2. Split the remaining width equally between each vertical axis - // 3. Split the remaining height equally between each horizontal axis - // 4. Give each layout the maximum size it can be. The layout will return it's minimum size - // 5. Adjust the sizes of each axis based on it's minimum reported size. - // 6. Refit each axis - // 7. Position each axis in the final location - // 8. Tell the chart the final location of the chart area - // 9. Tell any axes that overlay the chart area the positions of the chart area - - // Step 1 - var chartWidth = width - leftPadding - rightPadding; - var chartHeight = height - topPadding - bottomPadding; - var chartAreaWidth = chartWidth / 2; // min 50% - - // Step 2 - var verticalBoxWidth = (width - chartAreaWidth) / verticalBoxes.length; - - // Step 3 - // TODO re-limit horizontal axis height (this limit has affected only padding calculation since PR 1837) - // var horizontalBoxHeight = (height - chartAreaHeight) / horizontalBoxes.length; - - // Step 4 - var maxChartAreaWidth = chartWidth; - var maxChartAreaHeight = chartHeight; - var outerBoxSizes = {top: topPadding, left: leftPadding, bottom: bottomPadding, right: rightPadding}; - var minBoxSizes = []; - var maxPadding; - - function getMinimumBoxSize(box) { - var minSize; - var isHorizontal = box.isHorizontal(); - - if (isHorizontal) { - minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2); - maxChartAreaHeight -= minSize.height; - } else { - minSize = box.update(verticalBoxWidth, maxChartAreaHeight); - maxChartAreaWidth -= minSize.width; - } - - minBoxSizes.push({ - horizontal: isHorizontal, - width: minSize.width, - box: box, - }); - } - - helpers$1.each(outerBoxes, getMinimumBoxSize); - - // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) - maxPadding = findMaxPadding(outerBoxes); - - // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could - // be if the axes are drawn at their minimum sizes. - // Steps 5 & 6 - - // Function to fit a box - function fitBox(box) { - var minBoxSize = helpers$1.findNextWhere(minBoxSizes, function(minBox) { - return minBox.box === box; - }); - - if (minBoxSize) { - if (minBoxSize.horizontal) { - var scaleMargin = { - left: Math.max(outerBoxSizes.left, maxPadding.left), - right: Math.max(outerBoxSizes.right, maxPadding.right), - top: 0, - bottom: 0 - }; - - // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends - // on the margin. Sometimes they need to increase in size slightly - box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); - } else { - box.update(minBoxSize.width, maxChartAreaHeight); - } - } - } - - // Update, and calculate the left and right margins for the horizontal boxes - helpers$1.each(verticalBoxes, fitBox); - addSizeByPosition(verticalBoxes, outerBoxSizes); - - // Set the Left and Right margins for the horizontal boxes - helpers$1.each(horizontalBoxes, fitBox); - addSizeByPosition(horizontalBoxes, outerBoxSizes); - - function finalFitVerticalBox(box) { - var minBoxSize = helpers$1.findNextWhere(minBoxSizes, function(minSize) { - return minSize.box === box; - }); - - var scaleMargin = { - left: 0, - right: 0, - top: outerBoxSizes.top, - bottom: outerBoxSizes.bottom - }; - - if (minBoxSize) { - box.update(minBoxSize.width, maxChartAreaHeight, scaleMargin); - } - } - - // Let the left layout know the final margin - helpers$1.each(verticalBoxes, finalFitVerticalBox); - - // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) - outerBoxSizes = {top: topPadding, left: leftPadding, bottom: bottomPadding, right: rightPadding}; - addSizeByPosition(outerBoxes, outerBoxSizes); - - // We may be adding some padding to account for rotated x axis labels - var leftPaddingAddition = Math.max(maxPadding.left - outerBoxSizes.left, 0); - outerBoxSizes.left += leftPaddingAddition; - outerBoxSizes.right += Math.max(maxPadding.right - outerBoxSizes.right, 0); - - var topPaddingAddition = Math.max(maxPadding.top - outerBoxSizes.top, 0); - outerBoxSizes.top += topPaddingAddition; - outerBoxSizes.bottom += Math.max(maxPadding.bottom - outerBoxSizes.bottom, 0); - - // Figure out if our chart area changed. This would occur if the dataset layout label rotation - // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do - // without calling `fit` again - var newMaxChartAreaHeight = height - outerBoxSizes.top - outerBoxSizes.bottom; - var newMaxChartAreaWidth = width - outerBoxSizes.left - outerBoxSizes.right; - - if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { - helpers$1.each(verticalBoxes, function(box) { - box.height = newMaxChartAreaHeight; - }); - - helpers$1.each(horizontalBoxes, function(box) { - if (!box.fullWidth) { - box.width = newMaxChartAreaWidth; - } - }); - - maxChartAreaHeight = newMaxChartAreaHeight; - maxChartAreaWidth = newMaxChartAreaWidth; - } - - // Step 7 - Position the boxes - var left = leftPadding + leftPaddingAddition; - var top = topPadding + topPaddingAddition; - - function placeBox(box) { - if (box.isHorizontal()) { - box.left = box.fullWidth ? leftPadding : outerBoxSizes.left; - box.right = box.fullWidth ? width - rightPadding : outerBoxSizes.left + maxChartAreaWidth; - box.top = top; - box.bottom = top + box.height; - - // Move to next point - top = box.bottom; - - } else { - - box.left = left; - box.right = left + box.width; - box.top = outerBoxSizes.top; - box.bottom = outerBoxSizes.top + maxChartAreaHeight; - - // Move to next point - left = box.right; - } - } - - helpers$1.each(leftBoxes.concat(topBoxes), placeBox); - - // Account for chart width and height - left += maxChartAreaWidth; - top += maxChartAreaHeight; - - helpers$1.each(rightBoxes, placeBox); - helpers$1.each(bottomBoxes, placeBox); - - // Step 8 - chart.chartArea = { - left: outerBoxSizes.left, - top: outerBoxSizes.top, - right: outerBoxSizes.left + maxChartAreaWidth, - bottom: outerBoxSizes.top + maxChartAreaHeight - }; - - // Step 9 - helpers$1.each(chartAreaBoxes, function(box) { - box.left = chart.chartArea.left; - box.top = chart.chartArea.top; - box.right = chart.chartArea.right; - box.bottom = chart.chartArea.bottom; - - box.update(maxChartAreaWidth, maxChartAreaHeight); - }); - } -}; - -/** - * Platform fallback implementation (minimal). - * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 - */ - -var platform_basic = { - acquireContext: function(item) { - if (item && item.canvas) { - // Support for any object associated to a canvas (including a context2d) - item = item.canvas; - } - - return item && item.getContext('2d') || null; - } -}; - -var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n"; - -var platform_dom$1 = /*#__PURE__*/Object.freeze({ -default: platform_dom -}); - -function getCjsExportFromNamespace (n) { - return n && n.default || n; -} - -var stylesheet = getCjsExportFromNamespace(platform_dom$1); - -var EXPANDO_KEY = '$chartjs'; -var CSS_PREFIX = 'chartjs-'; -var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor'; -var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; -var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; -var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; - -/** - * DOM event types -> Chart.js event types. - * Note: only events with different types are mapped. - * @see https://developer.mozilla.org/en-US/docs/Web/Events - */ -var EVENT_TYPES = { - touchstart: 'mousedown', - touchmove: 'mousemove', - touchend: 'mouseup', - pointerenter: 'mouseenter', - pointerdown: 'mousedown', - pointermove: 'mousemove', - pointerup: 'mouseup', - pointerleave: 'mouseout', - pointerout: 'mouseout' -}; - -/** - * The "used" size is the final value of a dimension property after all calculations have - * been performed. This method uses the computed style of `element` but returns undefined - * if the computed style is not expressed in pixels. That can happen in some cases where - * `element` has a size relative to its parent and this last one is not yet displayed, - * for example because of `display: none` on a parent node. - * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value - * @returns {number} Size in pixels or undefined if unknown. - */ -function readUsedSize(element, property) { - var value = helpers$1.getStyle(element, property); - var matches = value && value.match(/^(\d+)(\.\d+)?px$/); - return matches ? Number(matches[1]) : undefined; -} - -/** - * Initializes the canvas style and render size without modifying the canvas display size, - * since responsiveness is handled by the controller.resize() method. The config is used - * to determine the aspect ratio to apply in case no explicit height has been specified. - */ -function initCanvas(canvas, config) { - var style = canvas.style; - - // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it - // returns null or '' if no explicit value has been set to the canvas attribute. - var renderHeight = canvas.getAttribute('height'); - var renderWidth = canvas.getAttribute('width'); - - // Chart.js modifies some canvas values that we want to restore on destroy - canvas[EXPANDO_KEY] = { - initial: { - height: renderHeight, - width: renderWidth, - style: { - display: style.display, - height: style.height, - width: style.width - } - } - }; - - // Force canvas to display as block to avoid extra space caused by inline - // elements, which would interfere with the responsive resize process. - // https://github.com/chartjs/Chart.js/issues/2538 - style.display = style.display || 'block'; - - if (renderWidth === null || renderWidth === '') { - var displayWidth = readUsedSize(canvas, 'width'); - if (displayWidth !== undefined) { - canvas.width = displayWidth; - } - } - - if (renderHeight === null || renderHeight === '') { - if (canvas.style.height === '') { - // If no explicit render height and style height, let's apply the aspect ratio, - // which one can be specified by the user but also by charts as default option - // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. - canvas.height = canvas.width / (config.options.aspectRatio || 2); - } else { - var displayHeight = readUsedSize(canvas, 'height'); - if (displayWidth !== undefined) { - canvas.height = displayHeight; - } - } - } - - return canvas; -} - -/** - * Detects support for options object argument in addEventListener. - * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support - * @private - */ -var supportsEventListenerOptions = (function() { - var supports = false; - try { - var options = Object.defineProperty({}, 'passive', { - // eslint-disable-next-line getter-return - get: function() { - supports = true; - } - }); - window.addEventListener('e', null, options); - } catch (e) { - // continue regardless of error - } - return supports; -}()); - -// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. -// https://github.com/chartjs/Chart.js/issues/4287 -var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; - -function addListener(node, type, listener) { - node.addEventListener(type, listener, eventListenerOptions); -} - -function removeListener(node, type, listener) { - node.removeEventListener(type, listener, eventListenerOptions); -} - -function createEvent(type, chart, x, y, nativeEvent) { - return { - type: type, - chart: chart, - native: nativeEvent || null, - x: x !== undefined ? x : null, - y: y !== undefined ? y : null, - }; -} - -function fromNativeEvent(event, chart) { - var type = EVENT_TYPES[event.type] || event.type; - var pos = helpers$1.getRelativePosition(event, chart); - return createEvent(type, chart, pos.x, pos.y, event); -} - -function throttled(fn, thisArg) { - var ticking = false; - var args = []; - - return function() { - args = Array.prototype.slice.call(arguments); - thisArg = thisArg || this; - - if (!ticking) { - ticking = true; - helpers$1.requestAnimFrame.call(window, function() { - ticking = false; - fn.apply(thisArg, args); - }); - } - }; -} - -function createDiv(cls) { - var el = document.createElement('div'); - el.className = cls || ''; - return el; -} - -// Implementation based on https://github.com/marcj/css-element-queries -function createResizer(handler) { - var maxSize = 1000000; - - // NOTE(SB) Don't use innerHTML because it could be considered unsafe. - // https://github.com/chartjs/Chart.js/issues/5902 - var resizer = createDiv(CSS_SIZE_MONITOR); - var expand = createDiv(CSS_SIZE_MONITOR + '-expand'); - var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink'); - - expand.appendChild(createDiv()); - shrink.appendChild(createDiv()); - - resizer.appendChild(expand); - resizer.appendChild(shrink); - resizer._reset = function() { - expand.scrollLeft = maxSize; - expand.scrollTop = maxSize; - shrink.scrollLeft = maxSize; - shrink.scrollTop = maxSize; - }; - - var onScroll = function() { - resizer._reset(); - handler(); - }; - - addListener(expand, 'scroll', onScroll.bind(expand, 'expand')); - addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); - - return resizer; -} - -// https://davidwalsh.name/detect-node-insertion -function watchForRender(node, handler) { - var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); - var proxy = expando.renderProxy = function(e) { - if (e.animationName === CSS_RENDER_ANIMATION) { - handler(); - } - }; - - helpers$1.each(ANIMATION_START_EVENTS, function(type) { - addListener(node, type, proxy); - }); - - // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class - // is removed then added back immediately (same animation frame?). Accessing the - // `offsetParent` property will force a reflow and re-evaluate the CSS animation. - // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics - // https://github.com/chartjs/Chart.js/issues/4737 - expando.reflow = !!node.offsetParent; - - node.classList.add(CSS_RENDER_MONITOR); -} - -function unwatchForRender(node) { - var expando = node[EXPANDO_KEY] || {}; - var proxy = expando.renderProxy; - - if (proxy) { - helpers$1.each(ANIMATION_START_EVENTS, function(type) { - removeListener(node, type, proxy); - }); - - delete expando.renderProxy; - } - - node.classList.remove(CSS_RENDER_MONITOR); -} - -function addResizeListener(node, listener, chart) { - var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); - - // Let's keep track of this added resizer and thus avoid DOM query when removing it. - var resizer = expando.resizer = createResizer(throttled(function() { - if (expando.resizer) { - var container = chart.options.maintainAspectRatio && node.parentNode; - var w = container ? container.clientWidth : 0; - listener(createEvent('resize', chart)); - if (container && container.clientWidth < w && chart.canvas) { - // If the container size shrank during chart resize, let's assume - // scrollbar appeared. So we resize again with the scrollbar visible - - // effectively making chart smaller and the scrollbar hidden again. - // Because we are inside `throttled`, and currently `ticking`, scroll - // events are ignored during this whole 2 resize process. - // If we assumed wrong and something else happened, we are resizing - // twice in a frame (potential performance issue) - listener(createEvent('resize', chart)); - } - } - })); - - // The resizer needs to be attached to the node parent, so we first need to be - // sure that `node` is attached to the DOM before injecting the resizer element. - watchForRender(node, function() { - if (expando.resizer) { - var container = node.parentNode; - if (container && container !== resizer.parentNode) { - container.insertBefore(resizer, container.firstChild); - } - - // The container size might have changed, let's reset the resizer state. - resizer._reset(); - } - }); -} - -function removeResizeListener(node) { - var expando = node[EXPANDO_KEY] || {}; - var resizer = expando.resizer; - - delete expando.resizer; - unwatchForRender(node); - - if (resizer && resizer.parentNode) { - resizer.parentNode.removeChild(resizer); - } -} - -function injectCSS(platform, css) { - // https://stackoverflow.com/q/3922139 - var style = platform._style || document.createElement('style'); - if (!platform._style) { - platform._style = style; - css = '/* Chart.js */\n' + css; - style.setAttribute('type', 'text/css'); - document.getElementsByTagName('head')[0].appendChild(style); - } - - style.appendChild(document.createTextNode(css)); -} - -var platform_dom$2 = { - /** - * When `true`, prevents the automatic injection of the stylesheet required to - * correctly detect when the chart is added to the DOM and then resized. This - * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`) - * to be manually imported to make this library compatible with any CSP. - * See https://github.com/chartjs/Chart.js/issues/5208 - */ - disableCSSInjection: false, - - /** - * This property holds whether this platform is enabled for the current environment. - * Currently used by platform.js to select the proper implementation. - * @private - */ - _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', - - /** - * @private - */ - _ensureLoaded: function() { - if (this._loaded) { - return; - } - - this._loaded = true; - - // https://github.com/chartjs/Chart.js/issues/5208 - if (!this.disableCSSInjection) { - injectCSS(this, stylesheet); - } - }, - - acquireContext: function(item, config) { - if (typeof item === 'string') { - item = document.getElementById(item); - } else if (item.length) { - // Support for array based queries (such as jQuery) - item = item[0]; - } - - if (item && item.canvas) { - // Support for any object associated to a canvas (including a context2d) - item = item.canvas; - } - - // To prevent canvas fingerprinting, some add-ons undefine the getContext - // method, for example: https://github.com/kkapsner/CanvasBlocker - // https://github.com/chartjs/Chart.js/issues/2807 - var context = item && item.getContext && item.getContext('2d'); - - // Load platform resources on first chart creation, to make possible to change - // platform options after importing the library (e.g. `disableCSSInjection`). - this._ensureLoaded(); - - // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is - // inside an iframe or when running in a protected environment. We could guess the - // types from their toString() value but let's keep things flexible and assume it's - // a sufficient condition if the item has a context2D which has item as `canvas`. - // https://github.com/chartjs/Chart.js/issues/3887 - // https://github.com/chartjs/Chart.js/issues/4102 - // https://github.com/chartjs/Chart.js/issues/4152 - if (context && context.canvas === item) { - initCanvas(item, config); - return context; - } - - return null; - }, - - releaseContext: function(context) { - var canvas = context.canvas; - if (!canvas[EXPANDO_KEY]) { - return; - } - - var initial = canvas[EXPANDO_KEY].initial; - ['height', 'width'].forEach(function(prop) { - var value = initial[prop]; - if (helpers$1.isNullOrUndef(value)) { - canvas.removeAttribute(prop); - } else { - canvas.setAttribute(prop, value); - } - }); - - helpers$1.each(initial.style || {}, function(value, key) { - canvas.style[key] = value; - }); - - // The canvas render size might have been changed (and thus the state stack discarded), - // we can't use save() and restore() to restore the initial state. So make sure that at - // least the canvas context is reset to the default state by setting the canvas width. - // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html - // eslint-disable-next-line no-self-assign - canvas.width = canvas.width; - - delete canvas[EXPANDO_KEY]; - }, - - addEventListener: function(chart, type, listener) { - var canvas = chart.canvas; - if (type === 'resize') { - // Note: the resize event is not supported on all browsers. - addResizeListener(canvas, listener, chart); - return; - } - - var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); - var proxies = expando.proxies || (expando.proxies = {}); - var proxy = proxies[chart.id + '_' + type] = function(event) { - listener(fromNativeEvent(event, chart)); - }; - - addListener(canvas, type, proxy); - }, - - removeEventListener: function(chart, type, listener) { - var canvas = chart.canvas; - if (type === 'resize') { - // Note: the resize event is not supported on all browsers. - removeResizeListener(canvas); - return; - } - - var expando = listener[EXPANDO_KEY] || {}; - var proxies = expando.proxies || {}; - var proxy = proxies[chart.id + '_' + type]; - if (!proxy) { - return; - } - - removeListener(canvas, type, proxy); - } -}; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use EventTarget.addEventListener instead. - * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ - * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener - * @function Chart.helpers.addEvent - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers$1.addEvent = addListener; - -/** - * Provided for backward compatibility, use EventTarget.removeEventListener instead. - * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ - * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener - * @function Chart.helpers.removeEvent - * @deprecated since version 2.7.0 - * @todo remove at version 3 - * @private - */ -helpers$1.removeEvent = removeListener; - -// @TODO Make possible to select another platform at build time. -var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic; - -/** - * @namespace Chart.platform - * @see https://chartjs.gitbooks.io/proposals/content/Platform.html - * @since 2.4.0 - */ -var platform = helpers$1.extend({ - /** - * @since 2.7.0 - */ - initialize: function() {}, - - /** - * Called at chart construction time, returns a context2d instance implementing - * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. - * @param {*} item - The native item from which to acquire context (platform specific) - * @param {object} options - The chart options - * @returns {CanvasRenderingContext2D} context2d instance - */ - acquireContext: function() {}, - - /** - * Called at chart destruction time, releases any resources associated to the context - * previously returned by the acquireContext() method. - * @param {CanvasRenderingContext2D} context - The context2d instance - * @returns {boolean} true if the method succeeded, else false - */ - releaseContext: function() {}, - - /** - * Registers the specified listener on the given chart. - * @param {Chart} chart - Chart from which to listen for event - * @param {string} type - The ({@link IEvent}) type to listen for - * @param {function} listener - Receives a notification (an object that implements - * the {@link IEvent} interface) when an event of the specified type occurs. - */ - addEventListener: function() {}, - - /** - * Removes the specified listener previously registered with addEventListener. - * @param {Chart} chart - Chart from which to remove the listener - * @param {string} type - The ({@link IEvent}) type to remove - * @param {function} listener - The listener function to remove from the event target. - */ - removeEventListener: function() {} - -}, implementation); - -core_defaults._set('global', { - plugins: {} -}); - -/** - * The plugin service singleton - * @namespace Chart.plugins - * @since 2.1.0 - */ -var core_plugins = { - /** - * Globally registered plugins. - * @private - */ - _plugins: [], - - /** - * This identifier is used to invalidate the descriptors cache attached to each chart - * when a global plugin is registered or unregistered. In this case, the cache ID is - * incremented and descriptors are regenerated during following API calls. - * @private - */ - _cacheId: 0, - - /** - * Registers the given plugin(s) if not already registered. - * @param {IPlugin[]|IPlugin} plugins plugin instance(s). - */ - register: function(plugins) { - var p = this._plugins; - ([]).concat(plugins).forEach(function(plugin) { - if (p.indexOf(plugin) === -1) { - p.push(plugin); - } - }); - - this._cacheId++; - }, - - /** - * Unregisters the given plugin(s) only if registered. - * @param {IPlugin[]|IPlugin} plugins plugin instance(s). - */ - unregister: function(plugins) { - var p = this._plugins; - ([]).concat(plugins).forEach(function(plugin) { - var idx = p.indexOf(plugin); - if (idx !== -1) { - p.splice(idx, 1); - } - }); - - this._cacheId++; - }, - - /** - * Remove all registered plugins. - * @since 2.1.5 - */ - clear: function() { - this._plugins = []; - this._cacheId++; - }, - - /** - * Returns the number of registered plugins? - * @returns {number} - * @since 2.1.5 - */ - count: function() { - return this._plugins.length; - }, - - /** - * Returns all registered plugin instances. - * @returns {IPlugin[]} array of plugin objects. - * @since 2.1.5 - */ - getAll: function() { - return this._plugins; - }, - - /** - * Calls enabled plugins for `chart` on the specified hook and with the given args. - * This method immediately returns as soon as a plugin explicitly returns false. The - * returned value can be used, for instance, to interrupt the current action. - * @param {Chart} chart - The chart instance for which plugins should be called. - * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). - * @param {Array} [args] - Extra arguments to apply to the hook call. - * @returns {boolean} false if any of the plugins return false, else returns true. - */ - notify: function(chart, hook, args) { - var descriptors = this.descriptors(chart); - var ilen = descriptors.length; - var i, descriptor, plugin, params, method; - - for (i = 0; i < ilen; ++i) { - descriptor = descriptors[i]; - plugin = descriptor.plugin; - method = plugin[hook]; - if (typeof method === 'function') { - params = [chart].concat(args || []); - params.push(descriptor.options); - if (method.apply(plugin, params) === false) { - return false; - } - } - } - - return true; - }, - - /** - * Returns descriptors of enabled plugins for the given chart. - * @returns {object[]} [{ plugin, options }] - * @private - */ - descriptors: function(chart) { - var cache = chart.$plugins || (chart.$plugins = {}); - if (cache.id === this._cacheId) { - return cache.descriptors; - } - - var plugins = []; - var descriptors = []; - var config = (chart && chart.config) || {}; - var options = (config.options && config.options.plugins) || {}; - - this._plugins.concat(config.plugins || []).forEach(function(plugin) { - var idx = plugins.indexOf(plugin); - if (idx !== -1) { - return; - } - - var id = plugin.id; - var opts = options[id]; - if (opts === false) { - return; - } - - if (opts === true) { - opts = helpers$1.clone(core_defaults.global.plugins[id]); - } - - plugins.push(plugin); - descriptors.push({ - plugin: plugin, - options: opts || {} - }); - }); - - cache.descriptors = descriptors; - cache.id = this._cacheId; - return descriptors; - }, - - /** - * Invalidates cache for the given chart: descriptors hold a reference on plugin option, - * but in some cases, this reference can be changed by the user when updating options. - * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 - * @private - */ - _invalidate: function(chart) { - delete chart.$plugins; - } -}; - -var core_scaleService = { - // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then - // use the new chart options to grab the correct scale - constructors: {}, - // Use a registration function so that we can move to an ES6 map when we no longer need to support - // old browsers - - // Scale config defaults - defaults: {}, - registerScaleType: function(type, scaleConstructor, scaleDefaults) { - this.constructors[type] = scaleConstructor; - this.defaults[type] = helpers$1.clone(scaleDefaults); - }, - getScaleConstructor: function(type) { - return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; - }, - getScaleDefaults: function(type) { - // Return the scale defaults merged with the global settings so that we always use the latest ones - return this.defaults.hasOwnProperty(type) ? helpers$1.merge({}, [core_defaults.scale, this.defaults[type]]) : {}; - }, - updateScaleDefaults: function(type, additions) { - var me = this; - if (me.defaults.hasOwnProperty(type)) { - me.defaults[type] = helpers$1.extend(me.defaults[type], additions); - } - }, - addScalesToLayout: function(chart) { - // Adds each scale to the chart.boxes array to be sized accordingly - helpers$1.each(chart.scales, function(scale) { - // Set ILayoutItem parameters for backwards compatibility - scale.fullWidth = scale.options.fullWidth; - scale.position = scale.options.position; - scale.weight = scale.options.weight; - core_layouts.addBox(chart, scale); - }); - } -}; - -var valueOrDefault$7 = helpers$1.valueOrDefault; - -core_defaults._set('global', { - tooltips: { - enabled: true, - custom: null, - mode: 'nearest', - position: 'average', - intersect: true, - backgroundColor: 'rgba(0,0,0,0.8)', - titleFontStyle: 'bold', - titleSpacing: 2, - titleMarginBottom: 6, - titleFontColor: '#fff', - titleAlign: 'left', - bodySpacing: 2, - bodyFontColor: '#fff', - bodyAlign: 'left', - footerFontStyle: 'bold', - footerSpacing: 2, - footerMarginTop: 6, - footerFontColor: '#fff', - footerAlign: 'left', - yPadding: 6, - xPadding: 6, - caretPadding: 2, - caretSize: 5, - cornerRadius: 6, - multiKeyBackground: '#fff', - displayColors: true, - borderColor: 'rgba(0,0,0,0)', - borderWidth: 0, - callbacks: { - // Args are: (tooltipItems, data) - beforeTitle: helpers$1.noop, - title: function(tooltipItems, data) { - var title = ''; - var labels = data.labels; - var labelCount = labels ? labels.length : 0; - - if (tooltipItems.length > 0) { - var item = tooltipItems[0]; - if (item.label) { - title = item.label; - } else if (item.xLabel) { - title = item.xLabel; - } else if (labelCount > 0 && item.index < labelCount) { - title = labels[item.index]; - } - } - - return title; - }, - afterTitle: helpers$1.noop, - - // Args are: (tooltipItems, data) - beforeBody: helpers$1.noop, - - // Args are: (tooltipItem, data) - beforeLabel: helpers$1.noop, - label: function(tooltipItem, data) { - var label = data.datasets[tooltipItem.datasetIndex].label || ''; - - if (label) { - label += ': '; - } - if (!helpers$1.isNullOrUndef(tooltipItem.value)) { - label += tooltipItem.value; - } else { - label += tooltipItem.yLabel; - } - return label; - }, - labelColor: function(tooltipItem, chart) { - var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); - var activeElement = meta.data[tooltipItem.index]; - var view = activeElement._view; - return { - borderColor: view.borderColor, - backgroundColor: view.backgroundColor - }; - }, - labelTextColor: function() { - return this._options.bodyFontColor; - }, - afterLabel: helpers$1.noop, - - // Args are: (tooltipItems, data) - afterBody: helpers$1.noop, - - // Args are: (tooltipItems, data) - beforeFooter: helpers$1.noop, - footer: helpers$1.noop, - afterFooter: helpers$1.noop - } - } -}); - -var positioners = { - /** - * Average mode places the tooltip at the average position of the elements shown - * @function Chart.Tooltip.positioners.average - * @param elements {ChartElement[]} the elements being displayed in the tooltip - * @returns {object} tooltip position - */ - average: function(elements) { - if (!elements.length) { - return false; - } - - var i, len; - var x = 0; - var y = 0; - var count = 0; - - for (i = 0, len = elements.length; i < len; ++i) { - var el = elements[i]; - if (el && el.hasValue()) { - var pos = el.tooltipPosition(); - x += pos.x; - y += pos.y; - ++count; - } - } - - return { - x: x / count, - y: y / count - }; - }, - - /** - * Gets the tooltip position nearest of the item nearest to the event position - * @function Chart.Tooltip.positioners.nearest - * @param elements {Chart.Element[]} the tooltip elements - * @param eventPosition {object} the position of the event in canvas coordinates - * @returns {object} the tooltip position - */ - nearest: function(elements, eventPosition) { - var x = eventPosition.x; - var y = eventPosition.y; - var minDistance = Number.POSITIVE_INFINITY; - var i, len, nearestElement; - - for (i = 0, len = elements.length; i < len; ++i) { - var el = elements[i]; - if (el && el.hasValue()) { - var center = el.getCenterPoint(); - var d = helpers$1.distanceBetweenPoints(eventPosition, center); - - if (d < minDistance) { - minDistance = d; - nearestElement = el; - } - } - } - - if (nearestElement) { - var tp = nearestElement.tooltipPosition(); - x = tp.x; - y = tp.y; - } - - return { - x: x, - y: y - }; - } -}; - -// Helper to push or concat based on if the 2nd parameter is an array or not -function pushOrConcat(base, toPush) { - if (toPush) { - if (helpers$1.isArray(toPush)) { - // base = base.concat(toPush); - Array.prototype.push.apply(base, toPush); - } else { - base.push(toPush); - } - } - - return base; -} - -/** - * Returns array of strings split by newline - * @param {string} value - The value to split by newline. - * @returns {string[]} value if newline present - Returned from String split() method - * @function - */ -function splitNewlines(str) { - if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) { - return str.split('\n'); - } - return str; -} - - -/** - * Private helper to create a tooltip item model - * @param element - the chart element (point, arc, bar) to create the tooltip item for - * @return new tooltip item - */ -function createTooltipItem(element) { - var xScale = element._xScale; - var yScale = element._yScale || element._scale; // handle radar || polarArea charts - var index = element._index; - var datasetIndex = element._datasetIndex; - var controller = element._chart.getDatasetMeta(datasetIndex).controller; - var indexScale = controller._getIndexScale(); - var valueScale = controller._getValueScale(); - - return { - xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', - yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', - label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '', - value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '', - index: index, - datasetIndex: datasetIndex, - x: element._model.x, - y: element._model.y - }; -} - -/** - * Helper to get the reset model for the tooltip - * @param tooltipOpts {object} the tooltip options - */ -function getBaseModel(tooltipOpts) { - var globalDefaults = core_defaults.global; - - return { - // Positioning - xPadding: tooltipOpts.xPadding, - yPadding: tooltipOpts.yPadding, - xAlign: tooltipOpts.xAlign, - yAlign: tooltipOpts.yAlign, - - // Body - bodyFontColor: tooltipOpts.bodyFontColor, - _bodyFontFamily: valueOrDefault$7(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), - _bodyFontStyle: valueOrDefault$7(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), - _bodyAlign: tooltipOpts.bodyAlign, - bodyFontSize: valueOrDefault$7(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), - bodySpacing: tooltipOpts.bodySpacing, - - // Title - titleFontColor: tooltipOpts.titleFontColor, - _titleFontFamily: valueOrDefault$7(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), - _titleFontStyle: valueOrDefault$7(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), - titleFontSize: valueOrDefault$7(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), - _titleAlign: tooltipOpts.titleAlign, - titleSpacing: tooltipOpts.titleSpacing, - titleMarginBottom: tooltipOpts.titleMarginBottom, - - // Footer - footerFontColor: tooltipOpts.footerFontColor, - _footerFontFamily: valueOrDefault$7(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), - _footerFontStyle: valueOrDefault$7(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), - footerFontSize: valueOrDefault$7(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), - _footerAlign: tooltipOpts.footerAlign, - footerSpacing: tooltipOpts.footerSpacing, - footerMarginTop: tooltipOpts.footerMarginTop, - - // Appearance - caretSize: tooltipOpts.caretSize, - cornerRadius: tooltipOpts.cornerRadius, - backgroundColor: tooltipOpts.backgroundColor, - opacity: 0, - legendColorBackground: tooltipOpts.multiKeyBackground, - displayColors: tooltipOpts.displayColors, - borderColor: tooltipOpts.borderColor, - borderWidth: tooltipOpts.borderWidth - }; -} - -/** - * Get the size of the tooltip - */ -function getTooltipSize(tooltip, model) { - var ctx = tooltip._chart.ctx; - - var height = model.yPadding * 2; // Tooltip Padding - var width = 0; - - // Count of all lines in the body - var body = model.body; - var combinedBodyLength = body.reduce(function(count, bodyItem) { - return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; - }, 0); - combinedBodyLength += model.beforeBody.length + model.afterBody.length; - - var titleLineCount = model.title.length; - var footerLineCount = model.footer.length; - var titleFontSize = model.titleFontSize; - var bodyFontSize = model.bodyFontSize; - var footerFontSize = model.footerFontSize; - - height += titleLineCount * titleFontSize; // Title Lines - height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing - height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin - height += combinedBodyLength * bodyFontSize; // Body Lines - height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing - height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin - height += footerLineCount * (footerFontSize); // Footer Lines - height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing - - // Title width - var widthPadding = 0; - var maxLineWidth = function(line) { - width = Math.max(width, ctx.measureText(line).width + widthPadding); - }; - - ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); - helpers$1.each(model.title, maxLineWidth); - - // Body width - ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); - helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth); - - // Body lines may include some extra width due to the color box - widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; - helpers$1.each(body, function(bodyItem) { - helpers$1.each(bodyItem.before, maxLineWidth); - helpers$1.each(bodyItem.lines, maxLineWidth); - helpers$1.each(bodyItem.after, maxLineWidth); - }); - - // Reset back to 0 - widthPadding = 0; - - // Footer width - ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); - helpers$1.each(model.footer, maxLineWidth); - - // Add padding - width += 2 * model.xPadding; - - return { - width: width, - height: height - }; -} - -/** - * Helper to get the alignment of a tooltip given the size - */ -function determineAlignment(tooltip, size) { - var model = tooltip._model; - var chart = tooltip._chart; - var chartArea = tooltip._chart.chartArea; - var xAlign = 'center'; - var yAlign = 'center'; - - if (model.y < size.height) { - yAlign = 'top'; - } else if (model.y > (chart.height - size.height)) { - yAlign = 'bottom'; - } - - var lf, rf; // functions to determine left, right alignment - var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart - var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges - var midX = (chartArea.left + chartArea.right) / 2; - var midY = (chartArea.top + chartArea.bottom) / 2; - - if (yAlign === 'center') { - lf = function(x) { - return x <= midX; - }; - rf = function(x) { - return x > midX; - }; - } else { - lf = function(x) { - return x <= (size.width / 2); - }; - rf = function(x) { - return x >= (chart.width - (size.width / 2)); - }; - } - - olf = function(x) { - return x + size.width + model.caretSize + model.caretPadding > chart.width; - }; - orf = function(x) { - return x - size.width - model.caretSize - model.caretPadding < 0; - }; - yf = function(y) { - return y <= midY ? 'top' : 'bottom'; - }; - - if (lf(model.x)) { - xAlign = 'left'; - - // Is tooltip too wide and goes over the right side of the chart.? - if (olf(model.x)) { - xAlign = 'center'; - yAlign = yf(model.y); - } - } else if (rf(model.x)) { - xAlign = 'right'; - - // Is tooltip too wide and goes outside left edge of canvas? - if (orf(model.x)) { - xAlign = 'center'; - yAlign = yf(model.y); - } - } - - var opts = tooltip._options; - return { - xAlign: opts.xAlign ? opts.xAlign : xAlign, - yAlign: opts.yAlign ? opts.yAlign : yAlign - }; -} - -/** - * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment - */ -function getBackgroundPoint(vm, size, alignment, chart) { - // Background Position - var x = vm.x; - var y = vm.y; - - var caretSize = vm.caretSize; - var caretPadding = vm.caretPadding; - var cornerRadius = vm.cornerRadius; - var xAlign = alignment.xAlign; - var yAlign = alignment.yAlign; - var paddingAndSize = caretSize + caretPadding; - var radiusAndPadding = cornerRadius + caretPadding; - - if (xAlign === 'right') { - x -= size.width; - } else if (xAlign === 'center') { - x -= (size.width / 2); - if (x + size.width > chart.width) { - x = chart.width - size.width; - } - if (x < 0) { - x = 0; - } - } - - if (yAlign === 'top') { - y += paddingAndSize; - } else if (yAlign === 'bottom') { - y -= size.height + paddingAndSize; - } else { - y -= (size.height / 2); - } - - if (yAlign === 'center') { - if (xAlign === 'left') { - x += paddingAndSize; - } else if (xAlign === 'right') { - x -= paddingAndSize; - } - } else if (xAlign === 'left') { - x -= radiusAndPadding; - } else if (xAlign === 'right') { - x += radiusAndPadding; - } - - return { - x: x, - y: y - }; -} - -function getAlignedX(vm, align) { - return align === 'center' - ? vm.x + vm.width / 2 - : align === 'right' - ? vm.x + vm.width - vm.xPadding - : vm.x + vm.xPadding; -} - -/** - * Helper to build before and after body lines - */ -function getBeforeAfterBodyLines(callback) { - return pushOrConcat([], splitNewlines(callback)); -} - -var exports$3 = core_element.extend({ - initialize: function() { - this._model = getBaseModel(this._options); - this._lastActive = []; - }, - - // Get the title - // Args are: (tooltipItem, data) - getTitle: function() { - var me = this; - var opts = me._options; - var callbacks = opts.callbacks; - - var beforeTitle = callbacks.beforeTitle.apply(me, arguments); - var title = callbacks.title.apply(me, arguments); - var afterTitle = callbacks.afterTitle.apply(me, arguments); - - var lines = []; - lines = pushOrConcat(lines, splitNewlines(beforeTitle)); - lines = pushOrConcat(lines, splitNewlines(title)); - lines = pushOrConcat(lines, splitNewlines(afterTitle)); - - return lines; - }, - - // Args are: (tooltipItem, data) - getBeforeBody: function() { - return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments)); - }, - - // Args are: (tooltipItem, data) - getBody: function(tooltipItems, data) { - var me = this; - var callbacks = me._options.callbacks; - var bodyItems = []; - - helpers$1.each(tooltipItems, function(tooltipItem) { - var bodyItem = { - before: [], - lines: [], - after: [] - }; - pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data))); - pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); - pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data))); - - bodyItems.push(bodyItem); - }); - - return bodyItems; - }, - - // Args are: (tooltipItem, data) - getAfterBody: function() { - return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments)); - }, - - // Get the footer and beforeFooter and afterFooter lines - // Args are: (tooltipItem, data) - getFooter: function() { - var me = this; - var callbacks = me._options.callbacks; - - var beforeFooter = callbacks.beforeFooter.apply(me, arguments); - var footer = callbacks.footer.apply(me, arguments); - var afterFooter = callbacks.afterFooter.apply(me, arguments); - - var lines = []; - lines = pushOrConcat(lines, splitNewlines(beforeFooter)); - lines = pushOrConcat(lines, splitNewlines(footer)); - lines = pushOrConcat(lines, splitNewlines(afterFooter)); - - return lines; - }, - - update: function(changed) { - var me = this; - var opts = me._options; - - // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition - // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time - // which breaks any animations. - var existingModel = me._model; - var model = me._model = getBaseModel(opts); - var active = me._active; - - var data = me._data; - - // In the case where active.length === 0 we need to keep these at existing values for good animations - var alignment = { - xAlign: existingModel.xAlign, - yAlign: existingModel.yAlign - }; - var backgroundPoint = { - x: existingModel.x, - y: existingModel.y - }; - var tooltipSize = { - width: existingModel.width, - height: existingModel.height - }; - var tooltipPosition = { - x: existingModel.caretX, - y: existingModel.caretY - }; - - var i, len; - - if (active.length) { - model.opacity = 1; - - var labelColors = []; - var labelTextColors = []; - tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition); - - var tooltipItems = []; - for (i = 0, len = active.length; i < len; ++i) { - tooltipItems.push(createTooltipItem(active[i])); - } - - // If the user provided a filter function, use it to modify the tooltip items - if (opts.filter) { - tooltipItems = tooltipItems.filter(function(a) { - return opts.filter(a, data); - }); - } - - // If the user provided a sorting function, use it to modify the tooltip items - if (opts.itemSort) { - tooltipItems = tooltipItems.sort(function(a, b) { - return opts.itemSort(a, b, data); - }); - } - - // Determine colors for boxes - helpers$1.each(tooltipItems, function(tooltipItem) { - labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); - labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); - }); - - - // Build the Text Lines - model.title = me.getTitle(tooltipItems, data); - model.beforeBody = me.getBeforeBody(tooltipItems, data); - model.body = me.getBody(tooltipItems, data); - model.afterBody = me.getAfterBody(tooltipItems, data); - model.footer = me.getFooter(tooltipItems, data); - - // Initial positioning and colors - model.x = tooltipPosition.x; - model.y = tooltipPosition.y; - model.caretPadding = opts.caretPadding; - model.labelColors = labelColors; - model.labelTextColors = labelTextColors; - - // data points - model.dataPoints = tooltipItems; - - // We need to determine alignment of the tooltip - tooltipSize = getTooltipSize(this, model); - alignment = determineAlignment(this, tooltipSize); - // Final Size and Position - backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); - } else { - model.opacity = 0; - } - - model.xAlign = alignment.xAlign; - model.yAlign = alignment.yAlign; - model.x = backgroundPoint.x; - model.y = backgroundPoint.y; - model.width = tooltipSize.width; - model.height = tooltipSize.height; - - // Point where the caret on the tooltip points to - model.caretX = tooltipPosition.x; - model.caretY = tooltipPosition.y; - - me._model = model; - - if (changed && opts.custom) { - opts.custom.call(me, model); - } - - return me; - }, - - drawCaret: function(tooltipPoint, size) { - var ctx = this._chart.ctx; - var vm = this._view; - var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); - - ctx.lineTo(caretPosition.x1, caretPosition.y1); - ctx.lineTo(caretPosition.x2, caretPosition.y2); - ctx.lineTo(caretPosition.x3, caretPosition.y3); - }, - getCaretPosition: function(tooltipPoint, size, vm) { - var x1, x2, x3, y1, y2, y3; - var caretSize = vm.caretSize; - var cornerRadius = vm.cornerRadius; - var xAlign = vm.xAlign; - var yAlign = vm.yAlign; - var ptX = tooltipPoint.x; - var ptY = tooltipPoint.y; - var width = size.width; - var height = size.height; - - if (yAlign === 'center') { - y2 = ptY + (height / 2); - - if (xAlign === 'left') { - x1 = ptX; - x2 = x1 - caretSize; - x3 = x1; - - y1 = y2 + caretSize; - y3 = y2 - caretSize; - } else { - x1 = ptX + width; - x2 = x1 + caretSize; - x3 = x1; - - y1 = y2 - caretSize; - y3 = y2 + caretSize; - } - } else { - if (xAlign === 'left') { - x2 = ptX + cornerRadius + (caretSize); - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } else if (xAlign === 'right') { - x2 = ptX + width - cornerRadius - caretSize; - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } else { - x2 = vm.caretX; - x1 = x2 - caretSize; - x3 = x2 + caretSize; - } - if (yAlign === 'top') { - y1 = ptY; - y2 = y1 - caretSize; - y3 = y1; - } else { - y1 = ptY + height; - y2 = y1 + caretSize; - y3 = y1; - // invert drawing order - var tmp = x3; - x3 = x1; - x1 = tmp; - } - } - return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; - }, - - drawTitle: function(pt, vm, ctx) { - var title = vm.title; - - if (title.length) { - pt.x = getAlignedX(vm, vm._titleAlign); - - ctx.textAlign = vm._titleAlign; - ctx.textBaseline = 'top'; - - var titleFontSize = vm.titleFontSize; - var titleSpacing = vm.titleSpacing; - - ctx.fillStyle = vm.titleFontColor; - ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); - - var i, len; - for (i = 0, len = title.length; i < len; ++i) { - ctx.fillText(title[i], pt.x, pt.y); - pt.y += titleFontSize + titleSpacing; // Line Height and spacing - - if (i + 1 === title.length) { - pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing - } - } - } - }, - - drawBody: function(pt, vm, ctx) { - var bodyFontSize = vm.bodyFontSize; - var bodySpacing = vm.bodySpacing; - var bodyAlign = vm._bodyAlign; - var body = vm.body; - var drawColorBoxes = vm.displayColors; - var labelColors = vm.labelColors; - var xLinePadding = 0; - var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0; - var textColor; - - ctx.textAlign = bodyAlign; - ctx.textBaseline = 'top'; - ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); - - pt.x = getAlignedX(vm, bodyAlign); - - // Before Body - var fillLineOfText = function(line) { - ctx.fillText(line, pt.x + xLinePadding, pt.y); - pt.y += bodyFontSize + bodySpacing; - }; - - // Before body lines - ctx.fillStyle = vm.bodyFontColor; - helpers$1.each(vm.beforeBody, fillLineOfText); - - xLinePadding = drawColorBoxes && bodyAlign !== 'right' - ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2) - : 0; - - // Draw body lines now - helpers$1.each(body, function(bodyItem, i) { - textColor = vm.labelTextColors[i]; - ctx.fillStyle = textColor; - helpers$1.each(bodyItem.before, fillLineOfText); - - helpers$1.each(bodyItem.lines, function(line) { - // Draw Legend-like boxes if needed - if (drawColorBoxes) { - // Fill a white rect so that colours merge nicely if the opacity is < 1 - ctx.fillStyle = vm.legendColorBackground; - ctx.fillRect(colorX, pt.y, bodyFontSize, bodyFontSize); - - // Border - ctx.lineWidth = 1; - ctx.strokeStyle = labelColors[i].borderColor; - ctx.strokeRect(colorX, pt.y, bodyFontSize, bodyFontSize); - - // Inner square - ctx.fillStyle = labelColors[i].backgroundColor; - ctx.fillRect(colorX + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); - ctx.fillStyle = textColor; - } - - fillLineOfText(line); - }); - - helpers$1.each(bodyItem.after, fillLineOfText); - }); - - // Reset back to 0 for after body - xLinePadding = 0; - - // After body lines - helpers$1.each(vm.afterBody, fillLineOfText); - pt.y -= bodySpacing; // Remove last body spacing - }, - - drawFooter: function(pt, vm, ctx) { - var footer = vm.footer; - - if (footer.length) { - pt.x = getAlignedX(vm, vm._footerAlign); - pt.y += vm.footerMarginTop; - - ctx.textAlign = vm._footerAlign; - ctx.textBaseline = 'top'; - - ctx.fillStyle = vm.footerFontColor; - ctx.font = helpers$1.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); - - helpers$1.each(footer, function(line) { - ctx.fillText(line, pt.x, pt.y); - pt.y += vm.footerFontSize + vm.footerSpacing; - }); - } - }, - - drawBackground: function(pt, vm, ctx, tooltipSize) { - ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = vm.borderWidth; - var xAlign = vm.xAlign; - var yAlign = vm.yAlign; - var x = pt.x; - var y = pt.y; - var width = tooltipSize.width; - var height = tooltipSize.height; - var radius = vm.cornerRadius; - - ctx.beginPath(); - ctx.moveTo(x + radius, y); - if (yAlign === 'top') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - if (yAlign === 'center' && xAlign === 'right') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - if (yAlign === 'bottom') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - if (yAlign === 'center' && xAlign === 'left') { - this.drawCaret(pt, tooltipSize); - } - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - - ctx.fill(); - - if (vm.borderWidth > 0) { - ctx.stroke(); - } - }, - - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - - if (vm.opacity === 0) { - return; - } - - var tooltipSize = { - width: vm.width, - height: vm.height - }; - var pt = { - x: vm.x, - y: vm.y - }; - - // IE11/Edge does not like very small opacities, so snap to 0 - var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; - - // Truthy/falsey value for empty tooltip - var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; - - if (this._options.enabled && hasTooltipContent) { - ctx.save(); - ctx.globalAlpha = opacity; - - // Draw Background - this.drawBackground(pt, vm, ctx, tooltipSize); - - // Draw Title, Body, and Footer - pt.y += vm.yPadding; - - // Titles - this.drawTitle(pt, vm, ctx); - - // Body - this.drawBody(pt, vm, ctx); - - // Footer - this.drawFooter(pt, vm, ctx); - - ctx.restore(); - } - }, - - /** - * Handle an event - * @private - * @param {IEvent} event - The event to handle - * @returns {boolean} true if the tooltip changed - */ - handleEvent: function(e) { - var me = this; - var options = me._options; - var changed = false; - - me._lastActive = me._lastActive || []; - - // Find Active Elements for tooltips - if (e.type === 'mouseout') { - me._active = []; - } else { - me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); - } - - // Remember Last Actives - changed = !helpers$1.arrayEquals(me._active, me._lastActive); - - // Only handle target event on tooltip change - if (changed) { - me._lastActive = me._active; - - if (options.enabled || options.custom) { - me._eventPosition = { - x: e.x, - y: e.y - }; - - me.update(true); - me.pivot(); - } - } - - return changed; - } -}); - -/** - * @namespace Chart.Tooltip.positioners - */ -var positioners_1 = positioners; - -var core_tooltip = exports$3; -core_tooltip.positioners = positioners_1; - -var valueOrDefault$8 = helpers$1.valueOrDefault; - -core_defaults._set('global', { - elements: {}, - events: [ - 'mousemove', - 'mouseout', - 'click', - 'touchstart', - 'touchmove' - ], - hover: { - onHover: null, - mode: 'nearest', - intersect: true, - animationDuration: 400 - }, - onClick: null, - maintainAspectRatio: true, - responsive: true, - responsiveAnimationDuration: 0 -}); - -/** - * Recursively merge the given config objects representing the `scales` option - * by incorporating scale defaults in `xAxes` and `yAxes` array items, then - * returns a deep copy of the result, thus doesn't alter inputs. - */ -function mergeScaleConfig(/* config objects ... */) { - return helpers$1.merge({}, [].slice.call(arguments), { - merger: function(key, target, source, options) { - if (key === 'xAxes' || key === 'yAxes') { - var slen = source[key].length; - var i, type, scale; - - if (!target[key]) { - target[key] = []; - } - - for (i = 0; i < slen; ++i) { - scale = source[key][i]; - type = valueOrDefault$8(scale.type, key === 'xAxes' ? 'category' : 'linear'); - - if (i >= target[key].length) { - target[key].push({}); - } - - if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { - // new/untyped scale or type changed: let's apply the new defaults - // then merge source scale to correctly overwrite the defaults. - helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]); - } else { - // scales type are the same - helpers$1.merge(target[key][i], scale); - } - } - } else { - helpers$1._merger(key, target, source, options); - } - } - }); -} - -/** - * Recursively merge the given config objects as the root options by handling - * default scale options for the `scales` and `scale` properties, then returns - * a deep copy of the result, thus doesn't alter inputs. - */ -function mergeConfig(/* config objects ... */) { - return helpers$1.merge({}, [].slice.call(arguments), { - merger: function(key, target, source, options) { - var tval = target[key] || {}; - var sval = source[key]; - - if (key === 'scales') { - // scale config merging is complex. Add our own function here for that - target[key] = mergeScaleConfig(tval, sval); - } else if (key === 'scale') { - // used in polar area & radar charts since there is only one scale - target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]); - } else { - helpers$1._merger(key, target, source, options); - } - } - }); -} - -function initConfig(config) { - config = config || {}; - - // Do NOT use mergeConfig for the data object because this method merges arrays - // and so would change references to labels and datasets, preventing data updates. - var data = config.data = config.data || {}; - data.datasets = data.datasets || []; - data.labels = data.labels || []; - - config.options = mergeConfig( - core_defaults.global, - core_defaults[config.type], - config.options || {}); - - return config; -} - -function updateConfig(chart) { - var newOptions = chart.options; - - helpers$1.each(chart.scales, function(scale) { - core_layouts.removeBox(chart, scale); - }); - - newOptions = mergeConfig( - core_defaults.global, - core_defaults[chart.config.type], - newOptions); - - chart.options = chart.config.options = newOptions; - chart.ensureScalesHaveIDs(); - chart.buildOrUpdateScales(); - - // Tooltip - chart.tooltip._options = newOptions.tooltips; - chart.tooltip.initialize(); -} - -function positionIsHorizontal(position) { - return position === 'top' || position === 'bottom'; -} - -var Chart = function(item, config) { - this.construct(item, config); - return this; -}; - -helpers$1.extend(Chart.prototype, /** @lends Chart */ { - /** - * @private - */ - construct: function(item, config) { - var me = this; - - config = initConfig(config); - - var context = platform.acquireContext(item, config); - var canvas = context && context.canvas; - var height = canvas && canvas.height; - var width = canvas && canvas.width; - - me.id = helpers$1.uid(); - me.ctx = context; - me.canvas = canvas; - me.config = config; - me.width = width; - me.height = height; - me.aspectRatio = height ? width / height : null; - me.options = config.options; - me._bufferedRender = false; - - /** - * Provided for backward compatibility, Chart and Chart.Controller have been merged, - * the "instance" still need to be defined since it might be called from plugins. - * @prop Chart#chart - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ - me.chart = me; - me.controller = me; // chart.chart.controller #inception - - // Add the chart instance to the global namespace - Chart.instances[me.id] = me; - - // Define alias to the config data: `chart.data === chart.config.data` - Object.defineProperty(me, 'data', { - get: function() { - return me.config.data; - }, - set: function(value) { - me.config.data = value; - } - }); - - if (!context || !canvas) { - // The given item is not a compatible context2d element, let's return before finalizing - // the chart initialization but after setting basic chart / controller properties that - // can help to figure out that the chart is not valid (e.g chart.canvas !== null); - // https://github.com/chartjs/Chart.js/issues/2807 - console.error("Failed to create chart: can't acquire context from the given item"); - return; - } - - me.initialize(); - me.update(); - }, - - /** - * @private - */ - initialize: function() { - var me = this; - - // Before init plugin notification - core_plugins.notify(me, 'beforeInit'); - - helpers$1.retinaScale(me, me.options.devicePixelRatio); - - me.bindEvents(); - - if (me.options.responsive) { - // Initial resize before chart draws (must be silent to preserve initial animations). - me.resize(true); - } - - // Make sure scales have IDs and are built before we build any controllers. - me.ensureScalesHaveIDs(); - me.buildOrUpdateScales(); - me.initToolTip(); - - // After init plugin notification - core_plugins.notify(me, 'afterInit'); - - return me; - }, - - clear: function() { - helpers$1.canvas.clear(this); - return this; - }, - - stop: function() { - // Stops any current animation loop occurring - core_animations.cancelAnimation(this); - return this; - }, - - resize: function(silent) { - var me = this; - var options = me.options; - var canvas = me.canvas; - var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; - - // the canvas render width and height will be casted to integers so make sure that - // the canvas display style uses the same integer values to avoid blurring effect. - - // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed - var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas))); - var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas))); - - if (me.width === newWidth && me.height === newHeight) { - return; - } - - canvas.width = me.width = newWidth; - canvas.height = me.height = newHeight; - canvas.style.width = newWidth + 'px'; - canvas.style.height = newHeight + 'px'; - - helpers$1.retinaScale(me, options.devicePixelRatio); - - if (!silent) { - // Notify any plugins about the resize - var newSize = {width: newWidth, height: newHeight}; - core_plugins.notify(me, 'resize', [newSize]); - - // Notify of resize - if (options.onResize) { - options.onResize(me, newSize); - } - - me.stop(); - me.update({ - duration: options.responsiveAnimationDuration - }); - } - }, - - ensureScalesHaveIDs: function() { - var options = this.options; - var scalesOptions = options.scales || {}; - var scaleOptions = options.scale; - - helpers$1.each(scalesOptions.xAxes, function(xAxisOptions, index) { - xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); - }); - - helpers$1.each(scalesOptions.yAxes, function(yAxisOptions, index) { - yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); - }); - - if (scaleOptions) { - scaleOptions.id = scaleOptions.id || 'scale'; - } - }, - - /** - * Builds a map of scale ID to scale object for future lookup. - */ - buildOrUpdateScales: function() { - var me = this; - var options = me.options; - var scales = me.scales || {}; - var items = []; - var updated = Object.keys(scales).reduce(function(obj, id) { - obj[id] = false; - return obj; - }, {}); - - if (options.scales) { - items = items.concat( - (options.scales.xAxes || []).map(function(xAxisOptions) { - return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; - }), - (options.scales.yAxes || []).map(function(yAxisOptions) { - return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; - }) - ); - } - - if (options.scale) { - items.push({ - options: options.scale, - dtype: 'radialLinear', - isDefault: true, - dposition: 'chartArea' - }); - } - - helpers$1.each(items, function(item) { - var scaleOptions = item.options; - var id = scaleOptions.id; - var scaleType = valueOrDefault$8(scaleOptions.type, item.dtype); - - if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { - scaleOptions.position = item.dposition; - } - - updated[id] = true; - var scale = null; - if (id in scales && scales[id].type === scaleType) { - scale = scales[id]; - scale.options = scaleOptions; - scale.ctx = me.ctx; - scale.chart = me; - } else { - var scaleClass = core_scaleService.getScaleConstructor(scaleType); - if (!scaleClass) { - return; - } - scale = new scaleClass({ - id: id, - type: scaleType, - options: scaleOptions, - ctx: me.ctx, - chart: me - }); - scales[scale.id] = scale; - } - - scale.mergeTicksOptions(); - - // TODO(SB): I think we should be able to remove this custom case (options.scale) - // and consider it as a regular scale part of the "scales"" map only! This would - // make the logic easier and remove some useless? custom code. - if (item.isDefault) { - me.scale = scale; - } - }); - // clear up discarded scales - helpers$1.each(updated, function(hasUpdated, id) { - if (!hasUpdated) { - delete scales[id]; - } - }); - - me.scales = scales; - - core_scaleService.addScalesToLayout(this); - }, - - buildOrUpdateControllers: function() { - var me = this; - var newControllers = []; - - helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { - var meta = me.getDatasetMeta(datasetIndex); - var type = dataset.type || me.config.type; - - if (meta.type && meta.type !== type) { - me.destroyDatasetMeta(datasetIndex); - meta = me.getDatasetMeta(datasetIndex); - } - meta.type = type; - - if (meta.controller) { - meta.controller.updateIndex(datasetIndex); - meta.controller.linkScales(); - } else { - var ControllerClass = controllers[meta.type]; - if (ControllerClass === undefined) { - throw new Error('"' + meta.type + '" is not a chart type.'); - } - - meta.controller = new ControllerClass(me, datasetIndex); - newControllers.push(meta.controller); - } - }, me); - - return newControllers; - }, - - /** - * Reset the elements of all datasets - * @private - */ - resetElements: function() { - var me = this; - helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { - me.getDatasetMeta(datasetIndex).controller.reset(); - }, me); - }, - - /** - * Resets the chart back to it's state before the initial animation - */ - reset: function() { - this.resetElements(); - this.tooltip.initialize(); - }, - - update: function(config) { - var me = this; - - if (!config || typeof config !== 'object') { - // backwards compatibility - config = { - duration: config, - lazy: arguments[1] - }; - } - - updateConfig(me); - - // plugins options references might have change, let's invalidate the cache - // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 - core_plugins._invalidate(me); - - if (core_plugins.notify(me, 'beforeUpdate') === false) { - return; - } - - // In case the entire data object changed - me.tooltip._data = me.data; - - // Make sure dataset controllers are updated and new controllers are reset - var newControllers = me.buildOrUpdateControllers(); - - // Make sure all dataset controllers have correct meta data counts - helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { - me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); - }, me); - - me.updateLayout(); - - // Can only reset the new controllers after the scales have been updated - if (me.options.animation && me.options.animation.duration) { - helpers$1.each(newControllers, function(controller) { - controller.reset(); - }); - } - - me.updateDatasets(); - - // Need to reset tooltip in case it is displayed with elements that are removed - // after update. - me.tooltip.initialize(); - - // Last active contains items that were previously in the tooltip. - // When we reset the tooltip, we need to clear it - me.lastActive = []; - - // Do this before render so that any plugins that need final scale updates can use it - core_plugins.notify(me, 'afterUpdate'); - - if (me._bufferedRender) { - me._bufferedRequest = { - duration: config.duration, - easing: config.easing, - lazy: config.lazy - }; - } else { - me.render(config); - } - }, - - /** - * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` - * hook, in which case, plugins will not be called on `afterLayout`. - * @private - */ - updateLayout: function() { - var me = this; - - if (core_plugins.notify(me, 'beforeLayout') === false) { - return; - } - - core_layouts.update(this, this.width, this.height); - - /** - * Provided for backward compatibility, use `afterLayout` instead. - * @method IPlugin#afterScaleUpdate - * @deprecated since version 2.5.0 - * @todo remove at version 3 - * @private - */ - core_plugins.notify(me, 'afterScaleUpdate'); - core_plugins.notify(me, 'afterLayout'); - }, - - /** - * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` - * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. - * @private - */ - updateDatasets: function() { - var me = this; - - if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) { - return; - } - - for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { - me.updateDataset(i); - } - - core_plugins.notify(me, 'afterDatasetsUpdate'); - }, - - /** - * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` - * hook, in which case, plugins will not be called on `afterDatasetUpdate`. - * @private - */ - updateDataset: function(index) { - var me = this; - var meta = me.getDatasetMeta(index); - var args = { - meta: meta, - index: index - }; - - if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { - return; - } - - meta.controller.update(); - - core_plugins.notify(me, 'afterDatasetUpdate', [args]); - }, - - render: function(config) { - var me = this; - - if (!config || typeof config !== 'object') { - // backwards compatibility - config = { - duration: config, - lazy: arguments[1] - }; - } - - var animationOptions = me.options.animation; - var duration = valueOrDefault$8(config.duration, animationOptions && animationOptions.duration); - var lazy = config.lazy; - - if (core_plugins.notify(me, 'beforeRender') === false) { - return; - } - - var onComplete = function(animation) { - core_plugins.notify(me, 'afterRender'); - helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me); - }; - - if (animationOptions && duration) { - var animation = new core_animation({ - numSteps: duration / 16.66, // 60 fps - easing: config.easing || animationOptions.easing, - - render: function(chart, animationObject) { - var easingFunction = helpers$1.easing.effects[animationObject.easing]; - var currentStep = animationObject.currentStep; - var stepDecimal = currentStep / animationObject.numSteps; - - chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); - }, - - onAnimationProgress: animationOptions.onProgress, - onAnimationComplete: onComplete - }); - - core_animations.addAnimation(me, animation, duration, lazy); - } else { - me.draw(); - - // See https://github.com/chartjs/Chart.js/issues/3781 - onComplete(new core_animation({numSteps: 0, chart: me})); - } - - return me; - }, - - draw: function(easingValue) { - var me = this; - - me.clear(); - - if (helpers$1.isNullOrUndef(easingValue)) { - easingValue = 1; - } - - me.transition(easingValue); - - if (me.width <= 0 || me.height <= 0) { - return; - } - - if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) { - return; - } - - // Draw all the scales - helpers$1.each(me.boxes, function(box) { - box.draw(me.chartArea); - }, me); - - me.drawDatasets(easingValue); - me._drawTooltip(easingValue); - - core_plugins.notify(me, 'afterDraw', [easingValue]); - }, - - /** - * @private - */ - transition: function(easingValue) { - var me = this; - - for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { - if (me.isDatasetVisible(i)) { - me.getDatasetMeta(i).controller.transition(easingValue); - } - } - - me.tooltip.transition(easingValue); - }, - - /** - * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` - * hook, in which case, plugins will not be called on `afterDatasetsDraw`. - * @private - */ - drawDatasets: function(easingValue) { - var me = this; - - if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { - return; - } - - // Draw datasets reversed to support proper line stacking - for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { - if (me.isDatasetVisible(i)) { - me.drawDataset(i, easingValue); - } - } - - core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]); - }, - - /** - * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` - * hook, in which case, plugins will not be called on `afterDatasetDraw`. - * @private - */ - drawDataset: function(index, easingValue) { - var me = this; - var meta = me.getDatasetMeta(index); - var args = { - meta: meta, - index: index, - easingValue: easingValue - }; - - if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { - return; - } - - meta.controller.draw(easingValue); - - core_plugins.notify(me, 'afterDatasetDraw', [args]); - }, - - /** - * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` - * hook, in which case, plugins will not be called on `afterTooltipDraw`. - * @private - */ - _drawTooltip: function(easingValue) { - var me = this; - var tooltip = me.tooltip; - var args = { - tooltip: tooltip, - easingValue: easingValue - }; - - if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { - return; - } - - tooltip.draw(); - - core_plugins.notify(me, 'afterTooltipDraw', [args]); - }, - - /** - * Get the single element that was clicked on - * @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw - */ - getElementAtEvent: function(e) { - return core_interaction.modes.single(this, e); - }, - - getElementsAtEvent: function(e) { - return core_interaction.modes.label(this, e, {intersect: true}); - }, - - getElementsAtXAxis: function(e) { - return core_interaction.modes['x-axis'](this, e, {intersect: true}); - }, - - getElementsAtEventForMode: function(e, mode, options) { - var method = core_interaction.modes[mode]; - if (typeof method === 'function') { - return method(this, e, options); - } - - return []; - }, - - getDatasetAtEvent: function(e) { - return core_interaction.modes.dataset(this, e, {intersect: true}); - }, - - getDatasetMeta: function(datasetIndex) { - var me = this; - var dataset = me.data.datasets[datasetIndex]; - if (!dataset._meta) { - dataset._meta = {}; - } - - var meta = dataset._meta[me.id]; - if (!meta) { - meta = dataset._meta[me.id] = { - type: null, - data: [], - dataset: null, - controller: null, - hidden: null, // See isDatasetVisible() comment - xAxisID: null, - yAxisID: null - }; - } - - return meta; - }, - - getVisibleDatasetCount: function() { - var count = 0; - for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { - if (this.isDatasetVisible(i)) { - count++; - } - } - return count; - }, - - isDatasetVisible: function(datasetIndex) { - var meta = this.getDatasetMeta(datasetIndex); - - // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, - // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. - return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; - }, - - generateLegend: function() { - return this.options.legendCallback(this); - }, - - /** - * @private - */ - destroyDatasetMeta: function(datasetIndex) { - var id = this.id; - var dataset = this.data.datasets[datasetIndex]; - var meta = dataset._meta && dataset._meta[id]; - - if (meta) { - meta.controller.destroy(); - delete dataset._meta[id]; - } - }, - - destroy: function() { - var me = this; - var canvas = me.canvas; - var i, ilen; - - me.stop(); - - // dataset controllers need to cleanup associated data - for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { - me.destroyDatasetMeta(i); - } - - if (canvas) { - me.unbindEvents(); - helpers$1.canvas.clear(me); - platform.releaseContext(me.ctx); - me.canvas = null; - me.ctx = null; - } - - core_plugins.notify(me, 'destroy'); - - delete Chart.instances[me.id]; - }, - - toBase64Image: function() { - return this.canvas.toDataURL.apply(this.canvas, arguments); - }, - - initToolTip: function() { - var me = this; - me.tooltip = new core_tooltip({ - _chart: me, - _chartInstance: me, // deprecated, backward compatibility - _data: me.data, - _options: me.options.tooltips - }, me); - }, - - /** - * @private - */ - bindEvents: function() { - var me = this; - var listeners = me._listeners = {}; - var listener = function() { - me.eventHandler.apply(me, arguments); - }; - - helpers$1.each(me.options.events, function(type) { - platform.addEventListener(me, type, listener); - listeners[type] = listener; - }); - - // Elements used to detect size change should not be injected for non responsive charts. - // See https://github.com/chartjs/Chart.js/issues/2210 - if (me.options.responsive) { - listener = function() { - me.resize(); - }; - - platform.addEventListener(me, 'resize', listener); - listeners.resize = listener; - } - }, - - /** - * @private - */ - unbindEvents: function() { - var me = this; - var listeners = me._listeners; - if (!listeners) { - return; - } - - delete me._listeners; - helpers$1.each(listeners, function(listener, type) { - platform.removeEventListener(me, type, listener); - }); - }, - - updateHoverStyle: function(elements, mode, enabled) { - var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; - var element, i, ilen; - - for (i = 0, ilen = elements.length; i < ilen; ++i) { - element = elements[i]; - if (element) { - this.getDatasetMeta(element._datasetIndex).controller[method](element); - } - } - }, - - /** - * @private - */ - eventHandler: function(e) { - var me = this; - var tooltip = me.tooltip; - - if (core_plugins.notify(me, 'beforeEvent', [e]) === false) { - return; - } - - // Buffer any update calls so that renders do not occur - me._bufferedRender = true; - me._bufferedRequest = null; - - var changed = me.handleEvent(e); - // for smooth tooltip animations issue #4989 - // the tooltip should be the source of change - // Animation check workaround: - // tooltip._start will be null when tooltip isn't animating - if (tooltip) { - changed = tooltip._start - ? tooltip.handleEvent(e) - : changed | tooltip.handleEvent(e); - } - - core_plugins.notify(me, 'afterEvent', [e]); - - var bufferedRequest = me._bufferedRequest; - if (bufferedRequest) { - // If we have an update that was triggered, we need to do a normal render - me.render(bufferedRequest); - } else if (changed && !me.animating) { - // If entering, leaving, or changing elements, animate the change via pivot - me.stop(); - - // We only need to render at this point. Updating will cause scales to be - // recomputed generating flicker & using more memory than necessary. - me.render({ - duration: me.options.hover.animationDuration, - lazy: true - }); - } - - me._bufferedRender = false; - me._bufferedRequest = null; - - return me; - }, - - /** - * Handle an event - * @private - * @param {IEvent} event the event to handle - * @return {boolean} true if the chart needs to re-render - */ - handleEvent: function(e) { - var me = this; - var options = me.options || {}; - var hoverOptions = options.hover; - var changed = false; - - me.lastActive = me.lastActive || []; - - // Find Active Elements for hover and tooltips - if (e.type === 'mouseout') { - me.active = []; - } else { - me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); - } - - // Invoke onHover hook - // Need to call with native event here to not break backwards compatibility - helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); - - if (e.type === 'mouseup' || e.type === 'click') { - if (options.onClick) { - // Use e.native here for backwards compatibility - options.onClick.call(me, e.native, me.active); - } - } - - // Remove styling for last active (even if it may still be active) - if (me.lastActive.length) { - me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); - } - - // Built in hover styling - if (me.active.length && hoverOptions.mode) { - me.updateHoverStyle(me.active, hoverOptions.mode, true); - } - - changed = !helpers$1.arrayEquals(me.active, me.lastActive); - - // Remember Last Actives - me.lastActive = me.active; - - return changed; - } -}); - -/** - * NOTE(SB) We actually don't use this container anymore but we need to keep it - * for backward compatibility. Though, it can still be useful for plugins that - * would need to work on multiple charts?! - */ -Chart.instances = {}; - -var core_controller = Chart; - -// DEPRECATIONS - -/** - * Provided for backward compatibility, use Chart instead. - * @class Chart.Controller - * @deprecated since version 2.6 - * @todo remove at version 3 - * @private - */ -Chart.Controller = Chart; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart - * @deprecated since version 2.8 - * @todo remove at version 3 - * @private - */ -Chart.types = {}; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.helpers.configMerge - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -helpers$1.configMerge = mergeConfig; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.helpers.scaleMerge - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -helpers$1.scaleMerge = mergeScaleConfig; - -var core_helpers = function() { - - // -- Basic js utility methods - - helpers$1.where = function(collection, filterCallback) { - if (helpers$1.isArray(collection) && Array.prototype.filter) { - return collection.filter(filterCallback); - } - var filtered = []; - - helpers$1.each(collection, function(item) { - if (filterCallback(item)) { - filtered.push(item); - } - }); - - return filtered; - }; - helpers$1.findIndex = Array.prototype.findIndex ? - function(array, callback, scope) { - return array.findIndex(callback, scope); - } : - function(array, callback, scope) { - scope = scope === undefined ? array : scope; - for (var i = 0, ilen = array.length; i < ilen; ++i) { - if (callback.call(scope, array[i], i, array)) { - return i; - } - } - return -1; - }; - helpers$1.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { - // Default to start of the array - if (helpers$1.isNullOrUndef(startIndex)) { - startIndex = -1; - } - for (var i = startIndex + 1; i < arrayToSearch.length; i++) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)) { - return currentItem; - } - } - }; - helpers$1.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { - // Default to end of the array - if (helpers$1.isNullOrUndef(startIndex)) { - startIndex = arrayToSearch.length; - } - for (var i = startIndex - 1; i >= 0; i--) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)) { - return currentItem; - } - } - }; - - // -- Math methods - helpers$1.isNumber = function(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - }; - helpers$1.almostEquals = function(x, y, epsilon) { - return Math.abs(x - y) < epsilon; - }; - helpers$1.almostWhole = function(x, epsilon) { - var rounded = Math.round(x); - return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); - }; - helpers$1.max = function(array) { - return array.reduce(function(max, value) { - if (!isNaN(value)) { - return Math.max(max, value); - } - return max; - }, Number.NEGATIVE_INFINITY); - }; - helpers$1.min = function(array) { - return array.reduce(function(min, value) { - if (!isNaN(value)) { - return Math.min(min, value); - } - return min; - }, Number.POSITIVE_INFINITY); - }; - helpers$1.sign = Math.sign ? - function(x) { - return Math.sign(x); - } : - function(x) { - x = +x; // convert to a number - if (x === 0 || isNaN(x)) { - return x; - } - return x > 0 ? 1 : -1; - }; - helpers$1.log10 = Math.log10 ? - function(x) { - return Math.log10(x); - } : - function(x) { - var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. - // Check for whole powers of 10, - // which due to floating point rounding error should be corrected. - var powerOf10 = Math.round(exponent); - var isPowerOf10 = x === Math.pow(10, powerOf10); - - return isPowerOf10 ? powerOf10 : exponent; - }; - helpers$1.toRadians = function(degrees) { - return degrees * (Math.PI / 180); - }; - helpers$1.toDegrees = function(radians) { - return radians * (180 / Math.PI); - }; - - /** - * Returns the number of decimal places - * i.e. the number of digits after the decimal point, of the value of this Number. - * @param {number} x - A number. - * @returns {number} The number of decimal places. - * @private - */ - helpers$1._decimalPlaces = function(x) { - if (!helpers$1.isFinite(x)) { - return; - } - var e = 1; - var p = 0; - while (Math.round(x * e) / e !== x) { - e *= 10; - p++; - } - return p; - }; - - // Gets the angle from vertical upright to the point about a centre. - helpers$1.getAngleFromPoint = function(centrePoint, anglePoint) { - var distanceFromXCenter = anglePoint.x - centrePoint.x; - var distanceFromYCenter = anglePoint.y - centrePoint.y; - var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); - - var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); - - if (angle < (-0.5 * Math.PI)) { - angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] - } - - return { - angle: angle, - distance: radialDistanceFromCenter - }; - }; - helpers$1.distanceBetweenPoints = function(pt1, pt2) { - return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); - }; - - /** - * Provided for backward compatibility, not available anymore - * @function Chart.helpers.aliasPixel - * @deprecated since version 2.8.0 - * @todo remove at version 3 - */ - helpers$1.aliasPixel = function(pixelWidth) { - return (pixelWidth % 2 === 0) ? 0 : 0.5; - }; - - /** - * Returns the aligned pixel value to avoid anti-aliasing blur - * @param {Chart} chart - The chart instance. - * @param {number} pixel - A pixel value. - * @param {number} width - The width of the element. - * @returns {number} The aligned pixel value. - * @private - */ - helpers$1._alignPixel = function(chart, pixel, width) { - var devicePixelRatio = chart.currentDevicePixelRatio; - var halfWidth = width / 2; - return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth; - }; - - helpers$1.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { - // Props to Rob Spencer at scaled innovation for his post on splining between points - // http://scaledinnovation.com/analytics/splines/aboutSplines.html - - // This function must also respect "skipped" points - - var previous = firstPoint.skip ? middlePoint : firstPoint; - var current = middlePoint; - var next = afterPoint.skip ? middlePoint : afterPoint; - - var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); - var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); - - var s01 = d01 / (d01 + d12); - var s12 = d12 / (d01 + d12); - - // If all points are the same, s01 & s02 will be inf - s01 = isNaN(s01) ? 0 : s01; - s12 = isNaN(s12) ? 0 : s12; - - var fa = t * s01; // scaling factor for triangle Ta - var fb = t * s12; - - return { - previous: { - x: current.x - fa * (next.x - previous.x), - y: current.y - fa * (next.y - previous.y) - }, - next: { - x: current.x + fb * (next.x - previous.x), - y: current.y + fb * (next.y - previous.y) - } - }; - }; - helpers$1.EPSILON = Number.EPSILON || 1e-14; - helpers$1.splineCurveMonotone = function(points) { - // This function calculates Bézier control points in a similar way than |splineCurve|, - // but preserves monotonicity of the provided data and ensures no local extremums are added - // between the dataset discrete points due to the interpolation. - // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation - - var pointsWithTangents = (points || []).map(function(point) { - return { - model: point._model, - deltaK: 0, - mK: 0 - }; - }); - - // Calculate slopes (deltaK) and initialize tangents (mK) - var pointsLen = pointsWithTangents.length; - var i, pointBefore, pointCurrent, pointAfter; - for (i = 0; i < pointsLen; ++i) { - pointCurrent = pointsWithTangents[i]; - if (pointCurrent.model.skip) { - continue; - } - - pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; - pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; - if (pointAfter && !pointAfter.model.skip) { - var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); - - // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 - pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; - } - - if (!pointBefore || pointBefore.model.skip) { - pointCurrent.mK = pointCurrent.deltaK; - } else if (!pointAfter || pointAfter.model.skip) { - pointCurrent.mK = pointBefore.deltaK; - } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { - pointCurrent.mK = 0; - } else { - pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; - } - } - - // Adjust tangents to ensure monotonic properties - var alphaK, betaK, tauK, squaredMagnitude; - for (i = 0; i < pointsLen - 1; ++i) { - pointCurrent = pointsWithTangents[i]; - pointAfter = pointsWithTangents[i + 1]; - if (pointCurrent.model.skip || pointAfter.model.skip) { - continue; - } - - if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { - pointCurrent.mK = pointAfter.mK = 0; - continue; - } - - alphaK = pointCurrent.mK / pointCurrent.deltaK; - betaK = pointAfter.mK / pointCurrent.deltaK; - squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); - if (squaredMagnitude <= 9) { - continue; - } - - tauK = 3 / Math.sqrt(squaredMagnitude); - pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; - pointAfter.mK = betaK * tauK * pointCurrent.deltaK; - } - - // Compute control points - var deltaX; - for (i = 0; i < pointsLen; ++i) { - pointCurrent = pointsWithTangents[i]; - if (pointCurrent.model.skip) { - continue; - } - - pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; - pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; - if (pointBefore && !pointBefore.model.skip) { - deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; - pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; - pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; - } - if (pointAfter && !pointAfter.model.skip) { - deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; - pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; - pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; - } - } - }; - helpers$1.nextItem = function(collection, index, loop) { - if (loop) { - return index >= collection.length - 1 ? collection[0] : collection[index + 1]; - } - return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; - }; - helpers$1.previousItem = function(collection, index, loop) { - if (loop) { - return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; - } - return index <= 0 ? collection[0] : collection[index - 1]; - }; - // Implementation of the nice number algorithm used in determining where axis labels will go - helpers$1.niceNum = function(range, round) { - var exponent = Math.floor(helpers$1.log10(range)); - var fraction = range / Math.pow(10, exponent); - var niceFraction; - - if (round) { - if (fraction < 1.5) { - niceFraction = 1; - } else if (fraction < 3) { - niceFraction = 2; - } else if (fraction < 7) { - niceFraction = 5; - } else { - niceFraction = 10; - } - } else if (fraction <= 1.0) { - niceFraction = 1; - } else if (fraction <= 2) { - niceFraction = 2; - } else if (fraction <= 5) { - niceFraction = 5; - } else { - niceFraction = 10; - } - - return niceFraction * Math.pow(10, exponent); - }; - // Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ - helpers$1.requestAnimFrame = (function() { - if (typeof window === 'undefined') { - return function(callback) { - callback(); - }; - } - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - }()); - // -- DOM methods - helpers$1.getRelativePosition = function(evt, chart) { - var mouseX, mouseY; - var e = evt.originalEvent || evt; - var canvas = evt.target || evt.srcElement; - var boundingRect = canvas.getBoundingClientRect(); - - var touches = e.touches; - if (touches && touches.length > 0) { - mouseX = touches[0].clientX; - mouseY = touches[0].clientY; - - } else { - mouseX = e.clientX; - mouseY = e.clientY; - } - - // Scale mouse coordinates into canvas coordinates - // by following the pattern laid out by 'jerryj' in the comments of - // https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ - var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left')); - var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top')); - var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right')); - var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom')); - var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; - var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; - - // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However - // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here - mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); - mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); - - return { - x: mouseX, - y: mouseY - }; - - }; - - // Private helper function to convert max-width/max-height values that may be percentages into a number - function parseMaxStyle(styleValue, node, parentProperty) { - var valueInPixels; - if (typeof styleValue === 'string') { - valueInPixels = parseInt(styleValue, 10); - - if (styleValue.indexOf('%') !== -1) { - // percentage * size in dimension - valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; - } - } else { - valueInPixels = styleValue; - } - - return valueInPixels; - } - - /** - * Returns if the given value contains an effective constraint. - * @private - */ - function isConstrainedValue(value) { - return value !== undefined && value !== null && value !== 'none'; - } - - /** - * Returns the max width or height of the given DOM node in a cross-browser compatible fashion - * @param {HTMLElement} domNode - the node to check the constraint on - * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height') - * @param {string} percentageProperty - property of parent to use when calculating width as a percentage - * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser} - */ - function getConstraintDimension(domNode, maxStyle, percentageProperty) { - var view = document.defaultView; - var parentNode = helpers$1._getParentNode(domNode); - var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; - var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; - var hasCNode = isConstrainedValue(constrainedNode); - var hasCContainer = isConstrainedValue(constrainedContainer); - var infinity = Number.POSITIVE_INFINITY; - - if (hasCNode || hasCContainer) { - return Math.min( - hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, - hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); - } - - return 'none'; - } - // returns Number or undefined if no constraint - helpers$1.getConstraintWidth = function(domNode) { - return getConstraintDimension(domNode, 'max-width', 'clientWidth'); - }; - // returns Number or undefined if no constraint - helpers$1.getConstraintHeight = function(domNode) { - return getConstraintDimension(domNode, 'max-height', 'clientHeight'); - }; - /** - * @private - */ - helpers$1._calculatePadding = function(container, padding, parentDimension) { - padding = helpers$1.getStyle(container, padding); - - return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10); - }; - /** - * @private - */ - helpers$1._getParentNode = function(domNode) { - var parent = domNode.parentNode; - if (parent && parent.toString() === '[object ShadowRoot]') { - parent = parent.host; - } - return parent; - }; - helpers$1.getMaximumWidth = function(domNode) { - var container = helpers$1._getParentNode(domNode); - if (!container) { - return domNode.clientWidth; - } - - var clientWidth = container.clientWidth; - var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth); - var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth); - - var w = clientWidth - paddingLeft - paddingRight; - var cw = helpers$1.getConstraintWidth(domNode); - return isNaN(cw) ? w : Math.min(w, cw); - }; - helpers$1.getMaximumHeight = function(domNode) { - var container = helpers$1._getParentNode(domNode); - if (!container) { - return domNode.clientHeight; - } - - var clientHeight = container.clientHeight; - var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight); - var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight); - - var h = clientHeight - paddingTop - paddingBottom; - var ch = helpers$1.getConstraintHeight(domNode); - return isNaN(ch) ? h : Math.min(h, ch); - }; - helpers$1.getStyle = function(el, property) { - return el.currentStyle ? - el.currentStyle[property] : - document.defaultView.getComputedStyle(el, null).getPropertyValue(property); - }; - helpers$1.retinaScale = function(chart, forceRatio) { - var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1; - if (pixelRatio === 1) { - return; - } - - var canvas = chart.canvas; - var height = chart.height; - var width = chart.width; - - canvas.height = height * pixelRatio; - canvas.width = width * pixelRatio; - chart.ctx.scale(pixelRatio, pixelRatio); - - // If no style has been set on the canvas, the render size is used as display size, - // making the chart visually bigger, so let's enforce it to the "correct" values. - // See https://github.com/chartjs/Chart.js/issues/3575 - if (!canvas.style.height && !canvas.style.width) { - canvas.style.height = height + 'px'; - canvas.style.width = width + 'px'; - } - }; - // -- Canvas methods - helpers$1.fontString = function(pixelSize, fontStyle, fontFamily) { - return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; - }; - helpers$1.longestText = function(ctx, font, arrayOfThings, cache) { - cache = cache || {}; - var data = cache.data = cache.data || {}; - var gc = cache.garbageCollect = cache.garbageCollect || []; - - if (cache.font !== font) { - data = cache.data = {}; - gc = cache.garbageCollect = []; - cache.font = font; - } - - ctx.font = font; - var longest = 0; - helpers$1.each(arrayOfThings, function(thing) { - // Undefined strings and arrays should not be measured - if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) { - longest = helpers$1.measureText(ctx, data, gc, longest, thing); - } else if (helpers$1.isArray(thing)) { - // if it is an array lets measure each element - // to do maybe simplify this function a bit so we can do this more recursively? - helpers$1.each(thing, function(nestedThing) { - // Undefined strings and arrays should not be measured - if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) { - longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing); - } - }); - } - }); - - var gcLen = gc.length / 2; - if (gcLen > arrayOfThings.length) { - for (var i = 0; i < gcLen; i++) { - delete data[gc[i]]; - } - gc.splice(0, gcLen); - } - return longest; - }; - helpers$1.measureText = function(ctx, data, gc, longest, string) { - var textWidth = data[string]; - if (!textWidth) { - textWidth = data[string] = ctx.measureText(string).width; - gc.push(string); - } - if (textWidth > longest) { - longest = textWidth; - } - return longest; - }; - helpers$1.numberOfLabelLines = function(arrayOfThings) { - var numberOfLines = 1; - helpers$1.each(arrayOfThings, function(thing) { - if (helpers$1.isArray(thing)) { - if (thing.length > numberOfLines) { - numberOfLines = thing.length; - } - } - }); - return numberOfLines; - }; - - helpers$1.color = !chartjsColor ? - function(value) { - console.error('Color.js not found!'); - return value; - } : - function(value) { - /* global CanvasGradient */ - if (value instanceof CanvasGradient) { - value = core_defaults.global.defaultColor; - } - - return chartjsColor(value); - }; - - helpers$1.getHoverColor = function(colorValue) { - /* global CanvasPattern */ - return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ? - colorValue : - helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString(); - }; -}; - -function abstract() { - throw new Error( - 'This method is not implemented: either no adapter can ' + - 'be found or an incomplete integration was provided.' - ); -} - -/** - * Date adapter (current used by the time scale) - * @namespace Chart._adapters._date - * @memberof Chart._adapters - * @private - */ - -/** - * Currently supported unit string values. - * @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')} - * @memberof Chart._adapters._date - * @name Unit - */ - -/** - * @class - */ -function DateAdapter(options) { - this.options = options || {}; -} - -helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ { - /** - * Returns a map of time formats for the supported formatting units defined - * in Unit as well as 'datetime' representing a detailed date/time string. - * @returns {{string: string}} - */ - formats: abstract, - - /** - * Parses the given `value` and return the associated timestamp. - * @param {any} value - the value to parse (usually comes from the data) - * @param {string} [format] - the expected data format - * @returns {(number|null)} - * @function - */ - parse: abstract, - - /** - * Returns the formatted date in the specified `format` for a given `timestamp`. - * @param {number} timestamp - the timestamp to format - * @param {string} format - the date/time token - * @return {string} - * @function - */ - format: abstract, - - /** - * Adds the specified `amount` of `unit` to the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {number} amount - the amount to add - * @param {Unit} unit - the unit as string - * @return {number} - * @function - */ - add: abstract, - - /** - * Returns the number of `unit` between the given timestamps. - * @param {number} max - the input timestamp (reference) - * @param {number} min - the timestamp to substract - * @param {Unit} unit - the unit as string - * @return {number} - * @function - */ - diff: abstract, - - /** - * Returns start of `unit` for the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {Unit} unit - the unit as string - * @param {number} [weekday] - the ISO day of the week with 1 being Monday - * and 7 being Sunday (only needed if param *unit* is `isoWeek`). - * @function - */ - startOf: abstract, - - /** - * Returns end of `unit` for the given `timestamp`. - * @param {number} timestamp - the input timestamp - * @param {Unit} unit - the unit as string - * @function - */ - endOf: abstract, - - // DEPRECATIONS - - /** - * Provided for backward compatibility for scale.getValueForPixel(), - * this method should be overridden only by the moment adapter. - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ - _create: function(value) { - return value; - } -}); - -DateAdapter.override = function(members) { - helpers$1.extend(DateAdapter.prototype, members); -}; - -var _date = DateAdapter; - -var core_adapters = { - _date: _date -}; - -/** - * Namespace to hold static tick generation functions - * @namespace Chart.Ticks - */ -var core_ticks = { - /** - * Namespace to hold formatters for different types of ticks - * @namespace Chart.Ticks.formatters - */ - formatters: { - /** - * Formatter for value labels - * @method Chart.Ticks.formatters.values - * @param value the value to display - * @return {string|string[]} the label to display - */ - values: function(value) { - return helpers$1.isArray(value) ? value : '' + value; - }, - - /** - * Formatter for linear numeric ticks - * @method Chart.Ticks.formatters.linear - * @param tickValue {number} the value to be formatted - * @param index {number} the position of the tickValue parameter in the ticks array - * @param ticks {number[]} the list of ticks being converted - * @return {string} string representation of the tickValue parameter - */ - linear: function(tickValue, index, ticks) { - // If we have lots of ticks, don't use the ones - var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; - - // If we have a number like 2.5 as the delta, figure out how many decimal places we need - if (Math.abs(delta) > 1) { - if (tickValue !== Math.floor(tickValue)) { - // not an integer - delta = tickValue - Math.floor(tickValue); - } - } - - var logDelta = helpers$1.log10(Math.abs(delta)); - var tickString = ''; - - if (tickValue !== 0) { - var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1])); - if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation - var logTick = helpers$1.log10(Math.abs(tickValue)); - tickString = tickValue.toExponential(Math.floor(logTick) - Math.floor(logDelta)); - } else { - var numDecimal = -1 * Math.floor(logDelta); - numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places - tickString = tickValue.toFixed(numDecimal); - } - } else { - tickString = '0'; // never show decimal places for 0 - } - - return tickString; - }, - - logarithmic: function(tickValue, index, ticks) { - var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue)))); - - if (tickValue === 0) { - return '0'; - } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { - return tickValue.toExponential(); - } - return ''; - } - } -}; - -var valueOrDefault$9 = helpers$1.valueOrDefault; -var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault; - -core_defaults._set('scale', { - display: true, - position: 'left', - offset: false, - - // grid line settings - gridLines: { - display: true, - color: 'rgba(0, 0, 0, 0.1)', - lineWidth: 1, - drawBorder: true, - drawOnChartArea: true, - drawTicks: true, - tickMarkLength: 10, - zeroLineWidth: 1, - zeroLineColor: 'rgba(0,0,0,0.25)', - zeroLineBorderDash: [], - zeroLineBorderDashOffset: 0.0, - offsetGridLines: false, - borderDash: [], - borderDashOffset: 0.0 - }, - - // scale label - scaleLabel: { - // display property - display: false, - - // actual label - labelString: '', - - // top/bottom padding - padding: { - top: 4, - bottom: 4 - } - }, - - // label settings - ticks: { - beginAtZero: false, - minRotation: 0, - maxRotation: 50, - mirror: false, - padding: 0, - reverse: false, - display: true, - autoSkip: true, - autoSkipPadding: 0, - labelOffset: 0, - // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. - callback: core_ticks.formatters.values, - minor: {}, - major: {} - } -}); - -function labelsFromTicks(ticks) { - var labels = []; - var i, ilen; - - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - labels.push(ticks[i].label); - } - - return labels; -} - -function getPixelForGridLine(scale, index, offsetGridLines) { - var lineValue = scale.getPixelForTick(index); - - if (offsetGridLines) { - if (scale.getTicks().length === 1) { - lineValue -= scale.isHorizontal() ? - Math.max(lineValue - scale.left, scale.right - lineValue) : - Math.max(lineValue - scale.top, scale.bottom - lineValue); - } else if (index === 0) { - lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; - } else { - lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; - } - } - return lineValue; -} - -function computeTextSize(context, tick, font) { - return helpers$1.isArray(tick) ? - helpers$1.longestText(context, font, tick) : - context.measureText(tick).width; -} - -var core_scale = core_element.extend({ - /** - * Get the padding needed for the scale - * @method getPadding - * @private - * @returns {Padding} the necessary padding - */ - getPadding: function() { - var me = this; - return { - left: me.paddingLeft || 0, - top: me.paddingTop || 0, - right: me.paddingRight || 0, - bottom: me.paddingBottom || 0 - }; - }, - - /** - * Returns the scale tick objects ({label, major}) - * @since 2.7 - */ - getTicks: function() { - return this._ticks; - }, - - // These methods are ordered by lifecyle. Utilities then follow. - // Any function defined here is inherited by all scale types. - // Any function can be extended by the scale type - - mergeTicksOptions: function() { - var ticks = this.options.ticks; - if (ticks.minor === false) { - ticks.minor = { - display: false - }; - } - if (ticks.major === false) { - ticks.major = { - display: false - }; - } - for (var key in ticks) { - if (key !== 'major' && key !== 'minor') { - if (typeof ticks.minor[key] === 'undefined') { - ticks.minor[key] = ticks[key]; - } - if (typeof ticks.major[key] === 'undefined') { - ticks.major[key] = ticks[key]; - } - } - } - }, - beforeUpdate: function() { - helpers$1.callback(this.options.beforeUpdate, [this]); - }, - - update: function(maxWidth, maxHeight, margins) { - var me = this; - var i, ilen, labels, label, ticks, tick; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = helpers$1.extend({ - left: 0, - right: 0, - top: 0, - bottom: 0 - }, margins); - - me._maxLabelLines = 0; - me.longestLabelWidth = 0; - me.longestTextCache = me.longestTextCache || {}; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - - // Data min/max - me.beforeDataLimits(); - me.determineDataLimits(); - me.afterDataLimits(); - - // Ticks - `this.ticks` is now DEPRECATED! - // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member - // and must not be accessed directly from outside this class. `this.ticks` being - // around for long time and not marked as private, we can't change its structure - // without unexpected breaking changes. If you need to access the scale ticks, - // use scale.getTicks() instead. - - me.beforeBuildTicks(); - - // New implementations should return an array of objects but for BACKWARD COMPAT, - // we still support no return (`this.ticks` internally set by calling this method). - ticks = me.buildTicks() || []; - - // Allow modification of ticks in callback. - ticks = me.afterBuildTicks(ticks) || ticks; - - me.beforeTickToLabelConversion(); - - // New implementations should return the formatted tick labels but for BACKWARD - // COMPAT, we still support no return (`this.ticks` internally changed by calling - // this method and supposed to contain only string values). - labels = me.convertTicksToLabels(ticks) || me.ticks; - - me.afterTickToLabelConversion(); - - me.ticks = labels; // BACKWARD COMPATIBILITY - - // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! - - // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) - for (i = 0, ilen = labels.length; i < ilen; ++i) { - label = labels[i]; - tick = ticks[i]; - if (!tick) { - ticks.push(tick = { - label: label, - major: false - }); - } else { - tick.label = label; - } - } - - me._ticks = ticks; - - // Tick Rotation - me.beforeCalculateTickRotation(); - me.calculateTickRotation(); - me.afterCalculateTickRotation(); - // Fit - me.beforeFit(); - me.fit(); - me.afterFit(); - // - me.afterUpdate(); - - return me.minSize; - - }, - afterUpdate: function() { - helpers$1.callback(this.options.afterUpdate, [this]); - }, - - // - - beforeSetDimensions: function() { - helpers$1.callback(this.options.beforeSetDimensions, [this]); - }, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - }, - afterSetDimensions: function() { - helpers$1.callback(this.options.afterSetDimensions, [this]); - }, - - // Data limits - beforeDataLimits: function() { - helpers$1.callback(this.options.beforeDataLimits, [this]); - }, - determineDataLimits: helpers$1.noop, - afterDataLimits: function() { - helpers$1.callback(this.options.afterDataLimits, [this]); - }, - - // - beforeBuildTicks: function() { - helpers$1.callback(this.options.beforeBuildTicks, [this]); - }, - buildTicks: helpers$1.noop, - afterBuildTicks: function(ticks) { - var me = this; - // ticks is empty for old axis implementations here - if (helpers$1.isArray(ticks) && ticks.length) { - return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]); - } - // Support old implementations (that modified `this.ticks` directly in buildTicks) - me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks; - return ticks; - }, - - beforeTickToLabelConversion: function() { - helpers$1.callback(this.options.beforeTickToLabelConversion, [this]); - }, - convertTicksToLabels: function() { - var me = this; - // Convert ticks to strings - var tickOpts = me.options.ticks; - me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); - }, - afterTickToLabelConversion: function() { - helpers$1.callback(this.options.afterTickToLabelConversion, [this]); - }, - - // - - beforeCalculateTickRotation: function() { - helpers$1.callback(this.options.beforeCalculateTickRotation, [this]); - }, - calculateTickRotation: function() { - var me = this; - var context = me.ctx; - var tickOpts = me.options.ticks; - var labels = labelsFromTicks(me._ticks); - - // Get the width of each grid by calculating the difference - // between x offsets between 0 and 1. - var tickFont = helpers$1.options._parseFont(tickOpts); - context.font = tickFont.string; - - var labelRotation = tickOpts.minRotation || 0; - - if (labels.length && me.options.display && me.isHorizontal()) { - var originalLabelWidth = helpers$1.longestText(context, tickFont.string, labels, me.longestTextCache); - var labelWidth = originalLabelWidth; - var cosRotation, sinRotation; - - // Allow 3 pixels x2 padding either side for label readability - var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; - - // Max label rotation can be set or default to 90 - also act as a loop counter - while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { - var angleRadians = helpers$1.toRadians(labelRotation); - cosRotation = Math.cos(angleRadians); - sinRotation = Math.sin(angleRadians); - - if (sinRotation * originalLabelWidth > me.maxHeight) { - // go back one step - labelRotation--; - break; - } - - labelRotation++; - labelWidth = cosRotation * originalLabelWidth; - } - } - - me.labelRotation = labelRotation; - }, - afterCalculateTickRotation: function() { - helpers$1.callback(this.options.afterCalculateTickRotation, [this]); - }, - - // - - beforeFit: function() { - helpers$1.callback(this.options.beforeFit, [this]); - }, - fit: function() { - var me = this; - // Reset - var minSize = me.minSize = { - width: 0, - height: 0 - }; - - var labels = labelsFromTicks(me._ticks); - - var opts = me.options; - var tickOpts = opts.ticks; - var scaleLabelOpts = opts.scaleLabel; - var gridLineOpts = opts.gridLines; - var display = me._isVisible(); - var position = opts.position; - var isHorizontal = me.isHorizontal(); - - var parseFont = helpers$1.options._parseFont; - var tickFont = parseFont(tickOpts); - var tickMarkLength = opts.gridLines.tickMarkLength; - - // Width - if (isHorizontal) { - // subtract the margins to line up with the chartArea if we are a full width scale - minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; - } else { - minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; - } - - // height - if (isHorizontal) { - minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; - } else { - minSize.height = me.maxHeight; // fill all the height - } - - // Are we showing a title for the scale? - if (scaleLabelOpts.display && display) { - var scaleLabelFont = parseFont(scaleLabelOpts); - var scaleLabelPadding = helpers$1.options.toPadding(scaleLabelOpts.padding); - var deltaHeight = scaleLabelFont.lineHeight + scaleLabelPadding.height; - - if (isHorizontal) { - minSize.height += deltaHeight; - } else { - minSize.width += deltaHeight; - } - } - - // Don't bother fitting the ticks if we are not showing the labels - if (tickOpts.display && display) { - var largestTextWidth = helpers$1.longestText(me.ctx, tickFont.string, labels, me.longestTextCache); - var tallestLabelHeightInLines = helpers$1.numberOfLabelLines(labels); - var lineSpace = tickFont.size * 0.5; - var tickPadding = me.options.ticks.padding; - - // Store max number of lines and widest label for _autoSkip - me._maxLabelLines = tallestLabelHeightInLines; - me.longestLabelWidth = largestTextWidth; - - if (isHorizontal) { - var angleRadians = helpers$1.toRadians(me.labelRotation); - var cosRotation = Math.cos(angleRadians); - var sinRotation = Math.sin(angleRadians); - - // TODO - improve this calculation - var labelHeight = (sinRotation * largestTextWidth) - + (tickFont.lineHeight * tallestLabelHeightInLines) - + lineSpace; // padding - - minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); - - me.ctx.font = tickFont.string; - var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.string); - var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.string); - var offsetLeft = me.getPixelForTick(0) - me.left; - var offsetRight = me.right - me.getPixelForTick(labels.length - 1); - var paddingLeft, paddingRight; - - // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned - // which means that the right padding is dominated by the font height - if (me.labelRotation !== 0) { - paddingLeft = position === 'bottom' ? (cosRotation * firstLabelWidth) : (cosRotation * lineSpace); - paddingRight = position === 'bottom' ? (cosRotation * lineSpace) : (cosRotation * lastLabelWidth); - } else { - paddingLeft = firstLabelWidth / 2; - paddingRight = lastLabelWidth / 2; - } - me.paddingLeft = Math.max(paddingLeft - offsetLeft, 0) + 3; // add 3 px to move away from canvas edges - me.paddingRight = Math.max(paddingRight - offsetRight, 0) + 3; - } else { - // A vertical axis is more constrained by the width. Labels are the - // dominant factor here, so get that length first and account for padding - if (tickOpts.mirror) { - largestTextWidth = 0; - } else { - // use lineSpace for consistency with horizontal axis - // tickPadding is not implemented for horizontal - largestTextWidth += tickPadding + lineSpace; - } - - minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); - - me.paddingTop = tickFont.size / 2; - me.paddingBottom = tickFont.size / 2; - } - } - - me.handleMargins(); - - me.width = minSize.width; - me.height = minSize.height; - }, - - /** - * Handle margins and padding interactions - * @private - */ - handleMargins: function() { - var me = this; - if (me.margins) { - me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); - me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); - me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); - me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); - } - }, - - afterFit: function() { - helpers$1.callback(this.options.afterFit, [this]); - }, - - // Shared Methods - isHorizontal: function() { - return this.options.position === 'top' || this.options.position === 'bottom'; - }, - isFullWidth: function() { - return (this.options.fullWidth); - }, - - // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not - getRightValue: function(rawValue) { - // Null and undefined values first - if (helpers$1.isNullOrUndef(rawValue)) { - return NaN; - } - // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values - if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) { - return NaN; - } - // If it is in fact an object, dive in one more level - if (rawValue) { - if (this.isHorizontal()) { - if (rawValue.x !== undefined) { - return this.getRightValue(rawValue.x); - } - } else if (rawValue.y !== undefined) { - return this.getRightValue(rawValue.y); - } - } - - // Value is good, return it - return rawValue; - }, - - /** - * Used to get the value to display in the tooltip for the data at the given index - * @param index - * @param datasetIndex - */ - getLabelForIndex: helpers$1.noop, - - /** - * Returns the location of the given data point. Value can either be an index or a numerical value - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param value - * @param index - * @param datasetIndex - */ - getPixelForValue: helpers$1.noop, - - /** - * Used to get the data value from a given pixel. This is the inverse of getPixelForValue - * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param pixel - */ - getValueForPixel: helpers$1.noop, - - /** - * Returns the location of the tick at the given index - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getPixelForTick: function(index) { - var me = this; - var offset = me.options.offset; - if (me.isHorizontal()) { - var innerWidth = me.width - (me.paddingLeft + me.paddingRight); - var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); - var pixel = (tickWidth * index) + me.paddingLeft; - - if (offset) { - pixel += tickWidth / 2; - } - - var finalVal = me.left + pixel; - finalVal += me.isFullWidth() ? me.margins.left : 0; - return finalVal; - } - var innerHeight = me.height - (me.paddingTop + me.paddingBottom); - return me.top + (index * (innerHeight / (me._ticks.length - 1))); - }, - - /** - * Utility for getting the pixel location of a percentage of scale - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getPixelForDecimal: function(decimal) { - var me = this; - if (me.isHorizontal()) { - var innerWidth = me.width - (me.paddingLeft + me.paddingRight); - var valueOffset = (innerWidth * decimal) + me.paddingLeft; - - var finalVal = me.left + valueOffset; - finalVal += me.isFullWidth() ? me.margins.left : 0; - return finalVal; - } - return me.top + (decimal * me.height); - }, - - /** - * Returns the pixel for the minimum chart value - * The coordinate (0, 0) is at the upper-left corner of the canvas - */ - getBasePixel: function() { - return this.getPixelForValue(this.getBaseValue()); - }, - - getBaseValue: function() { - var me = this; - var min = me.min; - var max = me.max; - - return me.beginAtZero ? 0 : - min < 0 && max < 0 ? max : - min > 0 && max > 0 ? min : - 0; - }, - - /** - * Returns a subset of ticks to be plotted to avoid overlapping labels. - * @private - */ - _autoSkip: function(ticks) { - var me = this; - var isHorizontal = me.isHorizontal(); - var optionTicks = me.options.ticks.minor; - var tickCount = ticks.length; - var skipRatio = false; - var maxTicks = optionTicks.maxTicksLimit; - - // Total space needed to display all ticks. First and last ticks are - // drawn as their center at end of axis, so tickCount-1 - var ticksLength = me._tickSize() * (tickCount - 1); - - // Axis length - var axisLength = isHorizontal - ? me.width - (me.paddingLeft + me.paddingRight) - : me.height - (me.paddingTop + me.PaddingBottom); - - var result = []; - var i, tick; - - if (ticksLength > axisLength) { - skipRatio = 1 + Math.floor(ticksLength / axisLength); - } - - // if they defined a max number of optionTicks, - // increase skipRatio until that number is met - if (tickCount > maxTicks) { - skipRatio = Math.max(skipRatio, 1 + Math.floor(tickCount / maxTicks)); - } - - for (i = 0; i < tickCount; i++) { - tick = ticks[i]; - - if (skipRatio > 1 && i % skipRatio > 0) { - // leave tick in place but make sure it's not displayed (#4635) - delete tick.label; - } - result.push(tick); - } - return result; - }, - - /** - * @private - */ - _tickSize: function() { - var me = this; - var isHorizontal = me.isHorizontal(); - var optionTicks = me.options.ticks.minor; - - // Calculate space needed by label in axis direction. - var rot = helpers$1.toRadians(me.labelRotation); - var cos = Math.abs(Math.cos(rot)); - var sin = Math.abs(Math.sin(rot)); - - var padding = optionTicks.autoSkipPadding || 0; - var w = (me.longestLabelWidth + padding) || 0; - - var tickFont = helpers$1.options._parseFont(optionTicks); - var h = (me._maxLabelLines * tickFont.lineHeight + padding) || 0; - - // Calculate space needed for 1 tick in axis direction. - return isHorizontal - ? h * cos > w * sin ? w / cos : h / sin - : h * sin < w * cos ? h / cos : w / sin; - }, - - /** - * @private - */ - _isVisible: function() { - var me = this; - var chart = me.chart; - var display = me.options.display; - var i, ilen, meta; - - if (display !== 'auto') { - return !!display; - } - - // When 'auto', the scale is visible if at least one associated dataset is visible. - for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - meta = chart.getDatasetMeta(i); - if (meta.xAxisID === me.id || meta.yAxisID === me.id) { - return true; - } - } - } - - return false; - }, - - /** - * Actually draw the scale on the canvas - * @param {object} chartArea - the area of the chart to draw full grid lines on - */ - draw: function(chartArea) { - var me = this; - var options = me.options; - - if (!me._isVisible()) { - return; - } - - var chart = me.chart; - var context = me.ctx; - var globalDefaults = core_defaults.global; - var defaultFontColor = globalDefaults.defaultFontColor; - var optionTicks = options.ticks.minor; - var optionMajorTicks = options.ticks.major || optionTicks; - var gridLines = options.gridLines; - var scaleLabel = options.scaleLabel; - var position = options.position; - - var isRotated = me.labelRotation !== 0; - var isMirrored = optionTicks.mirror; - var isHorizontal = me.isHorizontal(); - - var parseFont = helpers$1.options._parseFont; - var ticks = optionTicks.display && optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); - var tickFontColor = valueOrDefault$9(optionTicks.fontColor, defaultFontColor); - var tickFont = parseFont(optionTicks); - var lineHeight = tickFont.lineHeight; - var majorTickFontColor = valueOrDefault$9(optionMajorTicks.fontColor, defaultFontColor); - var majorTickFont = parseFont(optionMajorTicks); - var tickPadding = optionTicks.padding; - var labelOffset = optionTicks.labelOffset; - - var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; - - var scaleLabelFontColor = valueOrDefault$9(scaleLabel.fontColor, defaultFontColor); - var scaleLabelFont = parseFont(scaleLabel); - var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding); - var labelRotationRadians = helpers$1.toRadians(me.labelRotation); - - var itemsToDraw = []; - - var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; - var alignPixel = helpers$1._alignPixel; - var borderValue, tickStart, tickEnd; - - if (position === 'top') { - borderValue = alignPixel(chart, me.bottom, axisWidth); - tickStart = me.bottom - tl; - tickEnd = borderValue - axisWidth / 2; - } else if (position === 'bottom') { - borderValue = alignPixel(chart, me.top, axisWidth); - tickStart = borderValue + axisWidth / 2; - tickEnd = me.top + tl; - } else if (position === 'left') { - borderValue = alignPixel(chart, me.right, axisWidth); - tickStart = me.right - tl; - tickEnd = borderValue - axisWidth / 2; - } else { - borderValue = alignPixel(chart, me.left, axisWidth); - tickStart = borderValue + axisWidth / 2; - tickEnd = me.left + tl; - } - - var epsilon = 0.0000001; // 0.0000001 is margin in pixels for Accumulated error. - - helpers$1.each(ticks, function(tick, index) { - // autoskipper skipped this tick (#4635) - if (helpers$1.isNullOrUndef(tick.label)) { - return; - } - - var label = tick.label; - var lineWidth, lineColor, borderDash, borderDashOffset; - if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { - // Draw the first index specially - lineWidth = gridLines.zeroLineWidth; - lineColor = gridLines.zeroLineColor; - borderDash = gridLines.zeroLineBorderDash || []; - borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0; - } else { - lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, index); - lineColor = valueAtIndexOrDefault(gridLines.color, index); - borderDash = gridLines.borderDash || []; - borderDashOffset = gridLines.borderDashOffset || 0.0; - } - - // Common properties - var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY, textOffset, textAlign; - var labelCount = helpers$1.isArray(label) ? label.length : 1; - var lineValue = getPixelForGridLine(me, index, gridLines.offsetGridLines); - - if (isHorizontal) { - var labelYOffset = tl + tickPadding; - - if (lineValue < me.left - epsilon) { - lineColor = 'rgba(0,0,0,0)'; - } - - tx1 = tx2 = x1 = x2 = alignPixel(chart, lineValue, lineWidth); - ty1 = tickStart; - ty2 = tickEnd; - labelX = me.getPixelForTick(index) + labelOffset; // x values for optionTicks (need to consider offsetLabel option) - - if (position === 'top') { - y1 = alignPixel(chart, chartArea.top, axisWidth) + axisWidth / 2; - y2 = chartArea.bottom; - textOffset = ((!isRotated ? 0.5 : 1) - labelCount) * lineHeight; - textAlign = !isRotated ? 'center' : 'left'; - labelY = me.bottom - labelYOffset; - } else { - y1 = chartArea.top; - y2 = alignPixel(chart, chartArea.bottom, axisWidth) - axisWidth / 2; - textOffset = (!isRotated ? 0.5 : 0) * lineHeight; - textAlign = !isRotated ? 'center' : 'right'; - labelY = me.top + labelYOffset; - } - } else { - var labelXOffset = (isMirrored ? 0 : tl) + tickPadding; - - if (lineValue < me.top - epsilon) { - lineColor = 'rgba(0,0,0,0)'; - } - - tx1 = tickStart; - tx2 = tickEnd; - ty1 = ty2 = y1 = y2 = alignPixel(chart, lineValue, lineWidth); - labelY = me.getPixelForTick(index) + labelOffset; - textOffset = (1 - labelCount) * lineHeight / 2; - - if (position === 'left') { - x1 = alignPixel(chart, chartArea.left, axisWidth) + axisWidth / 2; - x2 = chartArea.right; - textAlign = isMirrored ? 'left' : 'right'; - labelX = me.right - labelXOffset; - } else { - x1 = chartArea.left; - x2 = alignPixel(chart, chartArea.right, axisWidth) - axisWidth / 2; - textAlign = isMirrored ? 'right' : 'left'; - labelX = me.left + labelXOffset; - } - } - - itemsToDraw.push({ - tx1: tx1, - ty1: ty1, - tx2: tx2, - ty2: ty2, - x1: x1, - y1: y1, - x2: x2, - y2: y2, - labelX: labelX, - labelY: labelY, - glWidth: lineWidth, - glColor: lineColor, - glBorderDash: borderDash, - glBorderDashOffset: borderDashOffset, - rotation: -1 * labelRotationRadians, - label: label, - major: tick.major, - textOffset: textOffset, - textAlign: textAlign - }); - }); - - // Draw all of the tick labels, tick marks, and grid lines at the correct places - helpers$1.each(itemsToDraw, function(itemToDraw) { - var glWidth = itemToDraw.glWidth; - var glColor = itemToDraw.glColor; - - if (gridLines.display && glWidth && glColor) { - context.save(); - context.lineWidth = glWidth; - context.strokeStyle = glColor; - if (context.setLineDash) { - context.setLineDash(itemToDraw.glBorderDash); - context.lineDashOffset = itemToDraw.glBorderDashOffset; - } - - context.beginPath(); - - if (gridLines.drawTicks) { - context.moveTo(itemToDraw.tx1, itemToDraw.ty1); - context.lineTo(itemToDraw.tx2, itemToDraw.ty2); - } - - if (gridLines.drawOnChartArea) { - context.moveTo(itemToDraw.x1, itemToDraw.y1); - context.lineTo(itemToDraw.x2, itemToDraw.y2); - } - - context.stroke(); - context.restore(); - } - - if (optionTicks.display) { - // Make sure we draw text in the correct color and font - context.save(); - context.translate(itemToDraw.labelX, itemToDraw.labelY); - context.rotate(itemToDraw.rotation); - context.font = itemToDraw.major ? majorTickFont.string : tickFont.string; - context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; - context.textBaseline = 'middle'; - context.textAlign = itemToDraw.textAlign; - - var label = itemToDraw.label; - var y = itemToDraw.textOffset; - if (helpers$1.isArray(label)) { - for (var i = 0; i < label.length; ++i) { - // We just make sure the multiline element is a string here.. - context.fillText('' + label[i], 0, y); - y += lineHeight; - } - } else { - context.fillText(label, 0, y); - } - context.restore(); - } - }); - - if (scaleLabel.display) { - // Draw the scale label - var scaleLabelX; - var scaleLabelY; - var rotation = 0; - var halfLineHeight = scaleLabelFont.lineHeight / 2; - - if (isHorizontal) { - scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width - scaleLabelY = position === 'bottom' - ? me.bottom - halfLineHeight - scaleLabelPadding.bottom - : me.top + halfLineHeight + scaleLabelPadding.top; - } else { - var isLeft = position === 'left'; - scaleLabelX = isLeft - ? me.left + halfLineHeight + scaleLabelPadding.top - : me.right - halfLineHeight - scaleLabelPadding.top; - scaleLabelY = me.top + ((me.bottom - me.top) / 2); - rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; - } - - context.save(); - context.translate(scaleLabelX, scaleLabelY); - context.rotate(rotation); - context.textAlign = 'center'; - context.textBaseline = 'middle'; - context.fillStyle = scaleLabelFontColor; // render in correct colour - context.font = scaleLabelFont.string; - context.fillText(scaleLabel.labelString, 0, 0); - context.restore(); - } - - if (axisWidth) { - // Draw the line at the edge of the axis - var firstLineWidth = axisWidth; - var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, ticks.length - 1, 0); - var x1, x2, y1, y2; - - if (isHorizontal) { - x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2; - x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2; - y1 = y2 = borderValue; - } else { - y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2; - y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2; - x1 = x2 = borderValue; - } - - context.lineWidth = axisWidth; - context.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0); - context.beginPath(); - context.moveTo(x1, y1); - context.lineTo(x2, y2); - context.stroke(); - } - } -}); - -var defaultConfig = { - position: 'bottom' -}; - -var scale_category = core_scale.extend({ - /** - * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those - * else fall back to data.labels - * @private - */ - getLabels: function() { - var data = this.chart.data; - return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; - }, - - determineDataLimits: function() { - var me = this; - var labels = me.getLabels(); - me.minIndex = 0; - me.maxIndex = labels.length - 1; - var findIndex; - - if (me.options.ticks.min !== undefined) { - // user specified min value - findIndex = labels.indexOf(me.options.ticks.min); - me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; - } - - if (me.options.ticks.max !== undefined) { - // user specified max value - findIndex = labels.indexOf(me.options.ticks.max); - me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; - } - - me.min = labels[me.minIndex]; - me.max = labels[me.maxIndex]; - }, - - buildTicks: function() { - var me = this; - var labels = me.getLabels(); - // If we are viewing some subset of labels, slice the original array - me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); - }, - - getLabelForIndex: function(index, datasetIndex) { - var me = this; - var chart = me.chart; - - if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) { - return me.getRightValue(chart.data.datasets[datasetIndex].data[index]); - } - - return me.ticks[index - me.minIndex]; - }, - - // Used to get data value locations. Value can either be an index or a numerical value - getPixelForValue: function(value, index) { - var me = this; - var offset = me.options.offset; - // 1 is added because we need the length but we have the indexes - var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); - - // If value is a data object, then index is the index in the data array, - // not the index of the scale. We need to change that. - var valueCategory; - if (value !== undefined && value !== null) { - valueCategory = me.isHorizontal() ? value.x : value.y; - } - if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { - var labels = me.getLabels(); - value = valueCategory || value; - var idx = labels.indexOf(value); - index = idx !== -1 ? idx : index; - } - - if (me.isHorizontal()) { - var valueWidth = me.width / offsetAmt; - var widthOffset = (valueWidth * (index - me.minIndex)); - - if (offset) { - widthOffset += (valueWidth / 2); - } - - return me.left + widthOffset; - } - var valueHeight = me.height / offsetAmt; - var heightOffset = (valueHeight * (index - me.minIndex)); - - if (offset) { - heightOffset += (valueHeight / 2); - } - - return me.top + heightOffset; - }, - - getPixelForTick: function(index) { - return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); - }, - - getValueForPixel: function(pixel) { - var me = this; - var offset = me.options.offset; - var value; - var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); - var horz = me.isHorizontal(); - var valueDimension = (horz ? me.width : me.height) / offsetAmt; - - pixel -= horz ? me.left : me.top; - - if (offset) { - pixel -= (valueDimension / 2); - } - - if (pixel <= 0) { - value = 0; - } else { - value = Math.round(pixel / valueDimension); - } - - return value + me.minIndex; - }, - - getBasePixel: function() { - return this.bottom; - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults = defaultConfig; -scale_category._defaults = _defaults; - -var noop = helpers$1.noop; -var isNullOrUndef = helpers$1.isNullOrUndef; - -/** - * Generate a set of linear ticks - * @param generationOptions the options used to generate the ticks - * @param dataRange the range of the data - * @returns {number[]} array of tick values - */ -function generateTicks(generationOptions, dataRange) { - var ticks = []; - // To get a "nice" value for the tick spacing, we will use the appropriately named - // "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks - // for details. - - var MIN_SPACING = 1e-14; - var stepSize = generationOptions.stepSize; - var unit = stepSize || 1; - var maxNumSpaces = generationOptions.maxTicks - 1; - var min = generationOptions.min; - var max = generationOptions.max; - var precision = generationOptions.precision; - var rmin = dataRange.min; - var rmax = dataRange.max; - var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit; - var factor, niceMin, niceMax, numSpaces; - - // Beyond MIN_SPACING floating point numbers being to lose precision - // such that we can't do the math necessary to generate ticks - if (spacing < MIN_SPACING && isNullOrUndef(min) && isNullOrUndef(max)) { - return [rmin, rmax]; - } - - numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing); - if (numSpaces > maxNumSpaces) { - // If the calculated num of spaces exceeds maxNumSpaces, recalculate it - spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit; - } - - if (stepSize || isNullOrUndef(precision)) { - // If a precision is not specified, calculate factor based on spacing - factor = Math.pow(10, helpers$1._decimalPlaces(spacing)); - } else { - // If the user specified a precision, round to that number of decimal places - factor = Math.pow(10, precision); - spacing = Math.ceil(spacing * factor) / factor; - } - - niceMin = Math.floor(rmin / spacing) * spacing; - niceMax = Math.ceil(rmax / spacing) * spacing; - - // If min, max and stepSize is set and they make an evenly spaced scale use it. - if (stepSize) { - // If very close to our whole number, use it. - if (!isNullOrUndef(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) { - niceMin = min; - } - if (!isNullOrUndef(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) { - niceMax = max; - } - } - - numSpaces = (niceMax - niceMin) / spacing; - // If very close to our rounded value, use it. - if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { - numSpaces = Math.round(numSpaces); - } else { - numSpaces = Math.ceil(numSpaces); - } - - niceMin = Math.round(niceMin * factor) / factor; - niceMax = Math.round(niceMax * factor) / factor; - ticks.push(isNullOrUndef(min) ? niceMin : min); - for (var j = 1; j < numSpaces; ++j) { - ticks.push(Math.round((niceMin + j * spacing) * factor) / factor); - } - ticks.push(isNullOrUndef(max) ? niceMax : max); - - return ticks; -} - -var scale_linearbase = core_scale.extend({ - getRightValue: function(value) { - if (typeof value === 'string') { - return +value; - } - return core_scale.prototype.getRightValue.call(this, value); - }, - - handleTickRangeOptions: function() { - var me = this; - var opts = me.options; - var tickOpts = opts.ticks; - - // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, - // do nothing since that would make the chart weird. If the user really wants a weird chart - // axis, they can manually override it - if (tickOpts.beginAtZero) { - var minSign = helpers$1.sign(me.min); - var maxSign = helpers$1.sign(me.max); - - if (minSign < 0 && maxSign < 0) { - // move the top up to 0 - me.max = 0; - } else if (minSign > 0 && maxSign > 0) { - // move the bottom down to 0 - me.min = 0; - } - } - - var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; - var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; - - if (tickOpts.min !== undefined) { - me.min = tickOpts.min; - } else if (tickOpts.suggestedMin !== undefined) { - if (me.min === null) { - me.min = tickOpts.suggestedMin; - } else { - me.min = Math.min(me.min, tickOpts.suggestedMin); - } - } - - if (tickOpts.max !== undefined) { - me.max = tickOpts.max; - } else if (tickOpts.suggestedMax !== undefined) { - if (me.max === null) { - me.max = tickOpts.suggestedMax; - } else { - me.max = Math.max(me.max, tickOpts.suggestedMax); - } - } - - if (setMin !== setMax) { - // We set the min or the max but not both. - // So ensure that our range is good - // Inverted or 0 length range can happen when - // ticks.min is set, and no datasets are visible - if (me.min >= me.max) { - if (setMin) { - me.max = me.min + 1; - } else { - me.min = me.max - 1; - } - } - } - - if (me.min === me.max) { - me.max++; - - if (!tickOpts.beginAtZero) { - me.min--; - } - } - }, - - getTickLimit: function() { - var me = this; - var tickOpts = me.options.ticks; - var stepSize = tickOpts.stepSize; - var maxTicksLimit = tickOpts.maxTicksLimit; - var maxTicks; - - if (stepSize) { - maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1; - } else { - maxTicks = me._computeTickLimit(); - maxTicksLimit = maxTicksLimit || 11; - } - - if (maxTicksLimit) { - maxTicks = Math.min(maxTicksLimit, maxTicks); - } - - return maxTicks; - }, - - _computeTickLimit: function() { - return Number.POSITIVE_INFINITY; - }, - - handleDirectionalChanges: noop, - - buildTicks: function() { - var me = this; - var opts = me.options; - var tickOpts = opts.ticks; - - // Figure out what the max number of ticks we can support it is based on the size of - // the axis area. For now, we say that the minimum tick spacing in pixels must be 40 - // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on - // the graph. Make sure we always have at least 2 ticks - var maxTicks = me.getTickLimit(); - maxTicks = Math.max(2, maxTicks); - - var numericGeneratorOptions = { - maxTicks: maxTicks, - min: tickOpts.min, - max: tickOpts.max, - precision: tickOpts.precision, - stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) - }; - var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); - - me.handleDirectionalChanges(); - - // At this point, we need to update our max and min given the tick values since we have expanded the - // range of the scale - me.max = helpers$1.max(ticks); - me.min = helpers$1.min(ticks); - - if (tickOpts.reverse) { - ticks.reverse(); - - me.start = me.max; - me.end = me.min; - } else { - me.start = me.min; - me.end = me.max; - } - }, - - convertTicksToLabels: function() { - var me = this; - me.ticksAsNumbers = me.ticks.slice(); - me.zeroLineIndex = me.ticks.indexOf(0); - - core_scale.prototype.convertTicksToLabels.call(me); - } -}); - -var defaultConfig$1 = { - position: 'left', - ticks: { - callback: core_ticks.formatters.linear - } -}; - -var scale_linear = scale_linearbase.extend({ - determineDataLimits: function() { - var me = this; - var opts = me.options; - var chart = me.chart; - var data = chart.data; - var datasets = data.datasets; - var isHorizontal = me.isHorizontal(); - var DEFAULT_MIN = 0; - var DEFAULT_MAX = 1; - - function IDMatches(meta) { - return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; - } - - // First Calculate the range - me.min = null; - me.max = null; - - var hasStacks = opts.stacked; - if (hasStacks === undefined) { - helpers$1.each(datasets, function(dataset, datasetIndex) { - if (hasStacks) { - return; - } - - var meta = chart.getDatasetMeta(datasetIndex); - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && - meta.stack !== undefined) { - hasStacks = true; - } - }); - } - - if (opts.stacked || hasStacks) { - var valuesPerStack = {}; - - helpers$1.each(datasets, function(dataset, datasetIndex) { - var meta = chart.getDatasetMeta(datasetIndex); - var key = [ - meta.type, - // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined - ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), - meta.stack - ].join('.'); - - if (valuesPerStack[key] === undefined) { - valuesPerStack[key] = { - positiveValues: [], - negativeValues: [] - }; - } - - // Store these per type - var positiveValues = valuesPerStack[key].positiveValues; - var negativeValues = valuesPerStack[key].negativeValues; - - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { - helpers$1.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); - if (isNaN(value) || meta.data[index].hidden) { - return; - } - - positiveValues[index] = positiveValues[index] || 0; - negativeValues[index] = negativeValues[index] || 0; - - if (opts.relativePoints) { - positiveValues[index] = 100; - } else if (value < 0) { - negativeValues[index] += value; - } else { - positiveValues[index] += value; - } - }); - } - }); - - helpers$1.each(valuesPerStack, function(valuesForType) { - var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); - var minVal = helpers$1.min(values); - var maxVal = helpers$1.max(values); - me.min = me.min === null ? minVal : Math.min(me.min, minVal); - me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); - }); - - } else { - helpers$1.each(datasets, function(dataset, datasetIndex) { - var meta = chart.getDatasetMeta(datasetIndex); - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { - helpers$1.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); - if (isNaN(value) || meta.data[index].hidden) { - return; - } - - if (me.min === null) { - me.min = value; - } else if (value < me.min) { - me.min = value; - } - - if (me.max === null) { - me.max = value; - } else if (value > me.max) { - me.max = value; - } - }); - } - }); - } - - me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; - me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; - - // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero - this.handleTickRangeOptions(); - }, - - // Returns the maximum number of ticks based on the scale dimension - _computeTickLimit: function() { - var me = this; - var tickFont; - - if (me.isHorizontal()) { - return Math.ceil(me.width / 40); - } - tickFont = helpers$1.options._parseFont(me.options.ticks); - return Math.ceil(me.height / tickFont.lineHeight); - }, - - // Called after the ticks are built. We need - handleDirectionalChanges: function() { - if (!this.isHorizontal()) { - // We are in a vertical orientation. The top value is the highest. So reverse the array - this.ticks.reverse(); - } - }, - - getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); - }, - - // Utils - getPixelForValue: function(value) { - // This must be called after fit has been run so that - // this.left, this.top, this.right, and this.bottom have been defined - var me = this; - var start = me.start; - - var rightValue = +me.getRightValue(value); - var pixel; - var range = me.end - start; - - if (me.isHorizontal()) { - pixel = me.left + (me.width / range * (rightValue - start)); - } else { - pixel = me.bottom - (me.height / range * (rightValue - start)); - } - return pixel; - }, - - getValueForPixel: function(pixel) { - var me = this; - var isHorizontal = me.isHorizontal(); - var innerDimension = isHorizontal ? me.width : me.height; - var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; - return me.start + ((me.end - me.start) * offset); - }, - - getPixelForTick: function(index) { - return this.getPixelForValue(this.ticksAsNumbers[index]); - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$1 = defaultConfig$1; -scale_linear._defaults = _defaults$1; - -var valueOrDefault$a = helpers$1.valueOrDefault; - -/** - * Generate a set of logarithmic ticks - * @param generationOptions the options used to generate the ticks - * @param dataRange the range of the data - * @returns {number[]} array of tick values - */ -function generateTicks$1(generationOptions, dataRange) { - var ticks = []; - - var tickVal = valueOrDefault$a(generationOptions.min, Math.pow(10, Math.floor(helpers$1.log10(dataRange.min)))); - - var endExp = Math.floor(helpers$1.log10(dataRange.max)); - var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); - var exp, significand; - - if (tickVal === 0) { - exp = Math.floor(helpers$1.log10(dataRange.minNotZero)); - significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); - - ticks.push(tickVal); - tickVal = significand * Math.pow(10, exp); - } else { - exp = Math.floor(helpers$1.log10(tickVal)); - significand = Math.floor(tickVal / Math.pow(10, exp)); - } - var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; - - do { - ticks.push(tickVal); - - ++significand; - if (significand === 10) { - significand = 1; - ++exp; - precision = exp >= 0 ? 1 : precision; - } - - tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; - } while (exp < endExp || (exp === endExp && significand < endSignificand)); - - var lastTick = valueOrDefault$a(generationOptions.max, tickVal); - ticks.push(lastTick); - - return ticks; -} - -var defaultConfig$2 = { - position: 'left', - - // label settings - ticks: { - callback: core_ticks.formatters.logarithmic - } -}; - -// TODO(v3): change this to positiveOrDefault -function nonNegativeOrDefault(value, defaultValue) { - return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue; -} - -var scale_logarithmic = core_scale.extend({ - determineDataLimits: function() { - var me = this; - var opts = me.options; - var chart = me.chart; - var data = chart.data; - var datasets = data.datasets; - var isHorizontal = me.isHorizontal(); - function IDMatches(meta) { - return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; - } - - // Calculate Range - me.min = null; - me.max = null; - me.minNotZero = null; - - var hasStacks = opts.stacked; - if (hasStacks === undefined) { - helpers$1.each(datasets, function(dataset, datasetIndex) { - if (hasStacks) { - return; - } - - var meta = chart.getDatasetMeta(datasetIndex); - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && - meta.stack !== undefined) { - hasStacks = true; - } - }); - } - - if (opts.stacked || hasStacks) { - var valuesPerStack = {}; - - helpers$1.each(datasets, function(dataset, datasetIndex) { - var meta = chart.getDatasetMeta(datasetIndex); - var key = [ - meta.type, - // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined - ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), - meta.stack - ].join('.'); - - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { - if (valuesPerStack[key] === undefined) { - valuesPerStack[key] = []; - } - - helpers$1.each(dataset.data, function(rawValue, index) { - var values = valuesPerStack[key]; - var value = +me.getRightValue(rawValue); - // invalid, hidden and negative values are ignored - if (isNaN(value) || meta.data[index].hidden || value < 0) { - return; - } - values[index] = values[index] || 0; - values[index] += value; - }); - } - }); - - helpers$1.each(valuesPerStack, function(valuesForType) { - if (valuesForType.length > 0) { - var minVal = helpers$1.min(valuesForType); - var maxVal = helpers$1.max(valuesForType); - me.min = me.min === null ? minVal : Math.min(me.min, minVal); - me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); - } - }); - - } else { - helpers$1.each(datasets, function(dataset, datasetIndex) { - var meta = chart.getDatasetMeta(datasetIndex); - if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { - helpers$1.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); - // invalid, hidden and negative values are ignored - if (isNaN(value) || meta.data[index].hidden || value < 0) { - return; - } - - if (me.min === null) { - me.min = value; - } else if (value < me.min) { - me.min = value; - } - - if (me.max === null) { - me.max = value; - } else if (value > me.max) { - me.max = value; - } - - if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { - me.minNotZero = value; - } - }); - } - }); - } - - // Common base implementation to handle ticks.min, ticks.max - this.handleTickRangeOptions(); - }, - - handleTickRangeOptions: function() { - var me = this; - var tickOpts = me.options.ticks; - var DEFAULT_MIN = 1; - var DEFAULT_MAX = 10; - - me.min = nonNegativeOrDefault(tickOpts.min, me.min); - me.max = nonNegativeOrDefault(tickOpts.max, me.max); - - if (me.min === me.max) { - if (me.min !== 0 && me.min !== null) { - me.min = Math.pow(10, Math.floor(helpers$1.log10(me.min)) - 1); - me.max = Math.pow(10, Math.floor(helpers$1.log10(me.max)) + 1); - } else { - me.min = DEFAULT_MIN; - me.max = DEFAULT_MAX; - } - } - if (me.min === null) { - me.min = Math.pow(10, Math.floor(helpers$1.log10(me.max)) - 1); - } - if (me.max === null) { - me.max = me.min !== 0 - ? Math.pow(10, Math.floor(helpers$1.log10(me.min)) + 1) - : DEFAULT_MAX; - } - if (me.minNotZero === null) { - if (me.min > 0) { - me.minNotZero = me.min; - } else if (me.max < 1) { - me.minNotZero = Math.pow(10, Math.floor(helpers$1.log10(me.max))); - } else { - me.minNotZero = DEFAULT_MIN; - } - } - }, - - buildTicks: function() { - var me = this; - var tickOpts = me.options.ticks; - var reverse = !me.isHorizontal(); - - var generationOptions = { - min: nonNegativeOrDefault(tickOpts.min), - max: nonNegativeOrDefault(tickOpts.max) - }; - var ticks = me.ticks = generateTicks$1(generationOptions, me); - - // At this point, we need to update our max and min given the tick values since we have expanded the - // range of the scale - me.max = helpers$1.max(ticks); - me.min = helpers$1.min(ticks); - - if (tickOpts.reverse) { - reverse = !reverse; - me.start = me.max; - me.end = me.min; - } else { - me.start = me.min; - me.end = me.max; - } - if (reverse) { - ticks.reverse(); - } - }, - - convertTicksToLabels: function() { - this.tickValues = this.ticks.slice(); - - core_scale.prototype.convertTicksToLabels.call(this); - }, - - // Get the correct tooltip label - getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); - }, - - getPixelForTick: function(index) { - return this.getPixelForValue(this.tickValues[index]); - }, - - /** - * Returns the value of the first tick. - * @param {number} value - The minimum not zero value. - * @return {number} The first tick value. - * @private - */ - _getFirstTickValue: function(value) { - var exp = Math.floor(helpers$1.log10(value)); - var significand = Math.floor(value / Math.pow(10, exp)); - - return significand * Math.pow(10, exp); - }, - - getPixelForValue: function(value) { - var me = this; - var tickOpts = me.options.ticks; - var reverse = tickOpts.reverse; - var log10 = helpers$1.log10; - var firstTickValue = me._getFirstTickValue(me.minNotZero); - var offset = 0; - var innerDimension, pixel, start, end, sign; - - value = +me.getRightValue(value); - if (reverse) { - start = me.end; - end = me.start; - sign = -1; - } else { - start = me.start; - end = me.end; - sign = 1; - } - if (me.isHorizontal()) { - innerDimension = me.width; - pixel = reverse ? me.right : me.left; - } else { - innerDimension = me.height; - sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0) - pixel = reverse ? me.top : me.bottom; - } - if (value !== start) { - if (start === 0) { // include zero tick - offset = valueOrDefault$a(tickOpts.fontSize, core_defaults.global.defaultFontSize); - innerDimension -= offset; - start = firstTickValue; - } - if (value !== 0) { - offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start)); - } - pixel += sign * offset; - } - return pixel; - }, - - getValueForPixel: function(pixel) { - var me = this; - var tickOpts = me.options.ticks; - var reverse = tickOpts.reverse; - var log10 = helpers$1.log10; - var firstTickValue = me._getFirstTickValue(me.minNotZero); - var innerDimension, start, end, value; - - if (reverse) { - start = me.end; - end = me.start; - } else { - start = me.start; - end = me.end; - } - if (me.isHorizontal()) { - innerDimension = me.width; - value = reverse ? me.right - pixel : pixel - me.left; - } else { - innerDimension = me.height; - value = reverse ? pixel - me.top : me.bottom - pixel; - } - if (value !== start) { - if (start === 0) { // include zero tick - var offset = valueOrDefault$a(tickOpts.fontSize, core_defaults.global.defaultFontSize); - value -= offset; - innerDimension -= offset; - start = firstTickValue; - } - value *= log10(end) - log10(start); - value /= innerDimension; - value = Math.pow(10, log10(start) + value); - } - return value; - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$2 = defaultConfig$2; -scale_logarithmic._defaults = _defaults$2; - -var valueOrDefault$b = helpers$1.valueOrDefault; -var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault; -var resolve$7 = helpers$1.options.resolve; - -var defaultConfig$3 = { - display: true, - - // Boolean - Whether to animate scaling the chart from the centre - animate: true, - position: 'chartArea', - - angleLines: { - display: true, - color: 'rgba(0, 0, 0, 0.1)', - lineWidth: 1, - borderDash: [], - borderDashOffset: 0.0 - }, - - gridLines: { - circular: false - }, - - // label settings - ticks: { - // Boolean - Show a backdrop to the scale label - showLabelBackdrop: true, - - // String - The colour of the label backdrop - backdropColor: 'rgba(255,255,255,0.75)', - - // Number - The backdrop padding above & below the label in pixels - backdropPaddingY: 2, - - // Number - The backdrop padding to the side of the label in pixels - backdropPaddingX: 2, - - callback: core_ticks.formatters.linear - }, - - pointLabels: { - // Boolean - if true, show point labels - display: true, - - // Number - Point label font size in pixels - fontSize: 10, - - // Function - Used to convert point labels - callback: function(label) { - return label; - } - } -}; - -function getValueCount(scale) { - var opts = scale.options; - return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; -} - -function getTickBackdropHeight(opts) { - var tickOpts = opts.ticks; - - if (tickOpts.display && opts.display) { - return valueOrDefault$b(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2; - } - return 0; -} - -function measureLabelSize(ctx, lineHeight, label) { - if (helpers$1.isArray(label)) { - return { - w: helpers$1.longestText(ctx, ctx.font, label), - h: label.length * lineHeight - }; - } - - return { - w: ctx.measureText(label).width, - h: lineHeight - }; -} - -function determineLimits(angle, pos, size, min, max) { - if (angle === min || angle === max) { - return { - start: pos - (size / 2), - end: pos + (size / 2) - }; - } else if (angle < min || angle > max) { - return { - start: pos - size, - end: pos - }; - } - - return { - start: pos, - end: pos + size - }; -} - -/** - * Helper function to fit a radial linear scale with point labels - */ -function fitWithPointLabels(scale) { - - // Right, this is really confusing and there is a lot of maths going on here - // The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 - // - // Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif - // - // Solution: - // - // We assume the radius of the polygon is half the size of the canvas at first - // at each index we check if the text overlaps. - // - // Where it does, we store that angle and that index. - // - // After finding the largest index and angle we calculate how much we need to remove - // from the shape radius to move the point inwards by that x. - // - // We average the left and right distances to get the maximum shape radius that can fit in the box - // along with labels. - // - // Once we have that, we can find the centre point for the chart, by taking the x text protrusion - // on each side, removing that from the size, halving it and adding the left x protrusion width. - // - // This will mean we have a shape fitted to the canvas, as large as it can be with the labels - // and position it in the most space efficient manner - // - // https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif - - var plFont = helpers$1.options._parseFont(scale.options.pointLabels); - - // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. - // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points - var furthestLimits = { - l: 0, - r: scale.width, - t: 0, - b: scale.height - scale.paddingTop - }; - var furthestAngles = {}; - var i, textSize, pointPosition; - - scale.ctx.font = plFont.string; - scale._pointLabelSizes = []; - - var valueCount = getValueCount(scale); - for (i = 0; i < valueCount; i++) { - pointPosition = scale.getPointPosition(i, scale.drawingArea + 5); - textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i] || ''); - scale._pointLabelSizes[i] = textSize; - - // Add quarter circle to make degree 0 mean top of circle - var angleRadians = scale.getIndexAngle(i); - var angle = helpers$1.toDegrees(angleRadians) % 360; - var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); - var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); - - if (hLimits.start < furthestLimits.l) { - furthestLimits.l = hLimits.start; - furthestAngles.l = angleRadians; - } - - if (hLimits.end > furthestLimits.r) { - furthestLimits.r = hLimits.end; - furthestAngles.r = angleRadians; - } - - if (vLimits.start < furthestLimits.t) { - furthestLimits.t = vLimits.start; - furthestAngles.t = angleRadians; - } - - if (vLimits.end > furthestLimits.b) { - furthestLimits.b = vLimits.end; - furthestAngles.b = angleRadians; - } - } - - scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles); -} - -function getTextAlignForAngle(angle) { - if (angle === 0 || angle === 180) { - return 'center'; - } else if (angle < 180) { - return 'left'; - } - - return 'right'; -} - -function fillText(ctx, text, position, lineHeight) { - var y = position.y + lineHeight / 2; - var i, ilen; - - if (helpers$1.isArray(text)) { - for (i = 0, ilen = text.length; i < ilen; ++i) { - ctx.fillText(text[i], position.x, y); - y += lineHeight; - } - } else { - ctx.fillText(text, position.x, y); - } -} - -function adjustPointPositionForLabelHeight(angle, textSize, position) { - if (angle === 90 || angle === 270) { - position.y -= (textSize.h / 2); - } else if (angle > 270 || angle < 90) { - position.y -= textSize.h; - } -} - -function drawPointLabels(scale) { - var ctx = scale.ctx; - var opts = scale.options; - var angleLineOpts = opts.angleLines; - var gridLineOpts = opts.gridLines; - var pointLabelOpts = opts.pointLabels; - var lineWidth = valueOrDefault$b(angleLineOpts.lineWidth, gridLineOpts.lineWidth); - var lineColor = valueOrDefault$b(angleLineOpts.color, gridLineOpts.color); - var tickBackdropHeight = getTickBackdropHeight(opts); - - ctx.save(); - ctx.lineWidth = lineWidth; - ctx.strokeStyle = lineColor; - if (ctx.setLineDash) { - ctx.setLineDash(resolve$7([angleLineOpts.borderDash, gridLineOpts.borderDash, []])); - ctx.lineDashOffset = resolve$7([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]); - } - - var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); - - // Point Label Font - var plFont = helpers$1.options._parseFont(pointLabelOpts); - - ctx.font = plFont.string; - ctx.textBaseline = 'middle'; - - for (var i = getValueCount(scale) - 1; i >= 0; i--) { - if (angleLineOpts.display && lineWidth && lineColor) { - var outerPosition = scale.getPointPosition(i, outerDistance); - ctx.beginPath(); - ctx.moveTo(scale.xCenter, scale.yCenter); - ctx.lineTo(outerPosition.x, outerPosition.y); - ctx.stroke(); - } - - if (pointLabelOpts.display) { - // Extra pixels out for some label spacing - var extra = (i === 0 ? tickBackdropHeight / 2 : 0); - var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5); - - // Keep this in loop since we may support array properties here - var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor); - ctx.fillStyle = pointLabelFontColor; - - var angleRadians = scale.getIndexAngle(i); - var angle = helpers$1.toDegrees(angleRadians); - ctx.textAlign = getTextAlignForAngle(angle); - adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); - fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight); - } - } - ctx.restore(); -} - -function drawRadiusLine(scale, gridLineOpts, radius, index) { - var ctx = scale.ctx; - var circular = gridLineOpts.circular; - var valueCount = getValueCount(scale); - var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1); - var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1); - var pointPosition; - - if ((!circular && !valueCount) || !lineColor || !lineWidth) { - return; - } - - ctx.save(); - ctx.strokeStyle = lineColor; - ctx.lineWidth = lineWidth; - if (ctx.setLineDash) { - ctx.setLineDash(gridLineOpts.borderDash || []); - ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0; - } - - ctx.beginPath(); - if (circular) { - // Draw circular arcs between the points - ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); - } else { - // Draw straight lines connecting each index - pointPosition = scale.getPointPosition(0, radius); - ctx.moveTo(pointPosition.x, pointPosition.y); - - for (var i = 1; i < valueCount; i++) { - pointPosition = scale.getPointPosition(i, radius); - ctx.lineTo(pointPosition.x, pointPosition.y); - } - } - ctx.closePath(); - ctx.stroke(); - ctx.restore(); -} - -function numberOrZero(param) { - return helpers$1.isNumber(param) ? param : 0; -} - -var scale_radialLinear = scale_linearbase.extend({ - setDimensions: function() { - var me = this; - - // Set the unconstrained dimension before label rotation - me.width = me.maxWidth; - me.height = me.maxHeight; - me.paddingTop = getTickBackdropHeight(me.options) / 2; - me.xCenter = Math.floor(me.width / 2); - me.yCenter = Math.floor((me.height - me.paddingTop) / 2); - me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2; - }, - - determineDataLimits: function() { - var me = this; - var chart = me.chart; - var min = Number.POSITIVE_INFINITY; - var max = Number.NEGATIVE_INFINITY; - - helpers$1.each(chart.data.datasets, function(dataset, datasetIndex) { - if (chart.isDatasetVisible(datasetIndex)) { - var meta = chart.getDatasetMeta(datasetIndex); - - helpers$1.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); - if (isNaN(value) || meta.data[index].hidden) { - return; - } - - min = Math.min(value, min); - max = Math.max(value, max); - }); - } - }); - - me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); - me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); - - // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero - me.handleTickRangeOptions(); - }, - - // Returns the maximum number of ticks based on the scale dimension - _computeTickLimit: function() { - return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); - }, - - convertTicksToLabels: function() { - var me = this; - - scale_linearbase.prototype.convertTicksToLabels.call(me); - - // Point labels - me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); - }, - - getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); - }, - - fit: function() { - var me = this; - var opts = me.options; - - if (opts.display && opts.pointLabels.display) { - fitWithPointLabels(me); - } else { - me.setCenterPoint(0, 0, 0, 0); - } - }, - - /** - * Set radius reductions and determine new radius and center point - * @private - */ - setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { - var me = this; - var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); - var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); - var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); - var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b); - - radiusReductionLeft = numberOrZero(radiusReductionLeft); - radiusReductionRight = numberOrZero(radiusReductionRight); - radiusReductionTop = numberOrZero(radiusReductionTop); - radiusReductionBottom = numberOrZero(radiusReductionBottom); - - me.drawingArea = Math.min( - Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), - Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); - me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); - }, - - setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { - var me = this; - var maxRight = me.width - rightMovement - me.drawingArea; - var maxLeft = leftMovement + me.drawingArea; - var maxTop = topMovement + me.drawingArea; - var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea; - - me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left); - me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop); - }, - - getIndexAngle: function(index) { - var angleMultiplier = (Math.PI * 2) / getValueCount(this); - var startAngle = this.chart.options && this.chart.options.startAngle ? - this.chart.options.startAngle : - 0; - - var startAngleRadians = startAngle * Math.PI * 2 / 360; - - // Start from the top instead of right, so remove a quarter of the circle - return index * angleMultiplier + startAngleRadians; - }, - - getDistanceFromCenterForValue: function(value) { - var me = this; - - if (value === null) { - return 0; // null always in center - } - - // Take into account half font size + the yPadding of the top value - var scalingFactor = me.drawingArea / (me.max - me.min); - if (me.options.ticks.reverse) { - return (me.max - value) * scalingFactor; - } - return (value - me.min) * scalingFactor; - }, - - getPointPosition: function(index, distanceFromCenter) { - var me = this; - var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); - return { - x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter, - y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter - }; - }, - - getPointPositionForValue: function(index, value) { - return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); - }, - - getBasePosition: function() { - var me = this; - var min = me.min; - var max = me.max; - - return me.getPointPositionForValue(0, - me.beginAtZero ? 0 : - min < 0 && max < 0 ? max : - min > 0 && max > 0 ? min : - 0); - }, - - draw: function() { - var me = this; - var opts = me.options; - var gridLineOpts = opts.gridLines; - var tickOpts = opts.ticks; - - if (opts.display) { - var ctx = me.ctx; - var startAngle = this.getIndexAngle(0); - var tickFont = helpers$1.options._parseFont(tickOpts); - - if (opts.angleLines.display || opts.pointLabels.display) { - drawPointLabels(me); - } - - helpers$1.each(me.ticks, function(label, index) { - // Don't draw a centre value (if it is minimum) - if (index > 0 || tickOpts.reverse) { - var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); - - // Draw circular lines around the scale - if (gridLineOpts.display && index !== 0) { - drawRadiusLine(me, gridLineOpts, yCenterOffset, index); - } - - if (tickOpts.display) { - var tickFontColor = valueOrDefault$b(tickOpts.fontColor, core_defaults.global.defaultFontColor); - ctx.font = tickFont.string; - - ctx.save(); - ctx.translate(me.xCenter, me.yCenter); - ctx.rotate(startAngle); - - if (tickOpts.showLabelBackdrop) { - var labelWidth = ctx.measureText(label).width; - ctx.fillStyle = tickOpts.backdropColor; - ctx.fillRect( - -labelWidth / 2 - tickOpts.backdropPaddingX, - -yCenterOffset - tickFont.size / 2 - tickOpts.backdropPaddingY, - labelWidth + tickOpts.backdropPaddingX * 2, - tickFont.size + tickOpts.backdropPaddingY * 2 - ); - } - - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = tickFontColor; - ctx.fillText(label, 0, -yCenterOffset); - ctx.restore(); - } - } - }); - } - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$3 = defaultConfig$3; -scale_radialLinear._defaults = _defaults$3; - -var valueOrDefault$c = helpers$1.valueOrDefault; - -// Integer constants are from the ES6 spec. -var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; -var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; - -var INTERVALS = { - millisecond: { - common: true, - size: 1, - steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] - }, - second: { - common: true, - size: 1000, - steps: [1, 2, 5, 10, 15, 30] - }, - minute: { - common: true, - size: 60000, - steps: [1, 2, 5, 10, 15, 30] - }, - hour: { - common: true, - size: 3600000, - steps: [1, 2, 3, 6, 12] - }, - day: { - common: true, - size: 86400000, - steps: [1, 2, 5] - }, - week: { - common: false, - size: 604800000, - steps: [1, 2, 3, 4] - }, - month: { - common: true, - size: 2.628e9, - steps: [1, 2, 3] - }, - quarter: { - common: false, - size: 7.884e9, - steps: [1, 2, 3, 4] - }, - year: { - common: true, - size: 3.154e10 - } -}; - -var UNITS = Object.keys(INTERVALS); - -function sorter(a, b) { - return a - b; -} - -function arrayUnique(items) { - var hash = {}; - var out = []; - var i, ilen, item; - - for (i = 0, ilen = items.length; i < ilen; ++i) { - item = items[i]; - if (!hash[item]) { - hash[item] = true; - out.push(item); - } - } - - return out; -} - -/** - * Returns an array of {time, pos} objects used to interpolate a specific `time` or position - * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is - * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other - * extremity (left + width or top + height). Note that it would be more optimized to directly - * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need - * to create the lookup table. The table ALWAYS contains at least two items: min and max. - * - * @param {number[]} timestamps - timestamps sorted from lowest to highest. - * @param {string} distribution - If 'linear', timestamps will be spread linearly along the min - * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. - * If 'series', timestamps will be positioned at the same distance from each other. In this - * case, only timestamps that break the time linearity are registered, meaning that in the - * best case, all timestamps are linear, the table contains only min and max. - */ -function buildLookupTable(timestamps, min, max, distribution) { - if (distribution === 'linear' || !timestamps.length) { - return [ - {time: min, pos: 0}, - {time: max, pos: 1} - ]; - } - - var table = []; - var items = [min]; - var i, ilen, prev, curr, next; - - for (i = 0, ilen = timestamps.length; i < ilen; ++i) { - curr = timestamps[i]; - if (curr > min && curr < max) { - items.push(curr); - } - } - - items.push(max); - - for (i = 0, ilen = items.length; i < ilen; ++i) { - next = items[i + 1]; - prev = items[i - 1]; - curr = items[i]; - - // only add points that breaks the scale linearity - if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { - table.push({time: curr, pos: i / (ilen - 1)}); - } - } - - return table; -} - -// @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ -function lookup(table, key, value) { - var lo = 0; - var hi = table.length - 1; - var mid, i0, i1; - - while (lo >= 0 && lo <= hi) { - mid = (lo + hi) >> 1; - i0 = table[mid - 1] || null; - i1 = table[mid]; - - if (!i0) { - // given value is outside table (before first item) - return {lo: null, hi: i1}; - } else if (i1[key] < value) { - lo = mid + 1; - } else if (i0[key] > value) { - hi = mid - 1; - } else { - return {lo: i0, hi: i1}; - } - } - - // given value is outside table (after last item) - return {lo: i1, hi: null}; -} - -/** - * Linearly interpolates the given source `value` using the table items `skey` values and - * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') - * returns the position for a timestamp equal to 42. If value is out of bounds, values at - * index [0, 1] or [n - 1, n] are used for the interpolation. - */ -function interpolate$1(table, skey, sval, tkey) { - var range = lookup(table, skey, sval); - - // Note: the lookup table ALWAYS contains at least 2 items (min and max) - var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; - var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; - - var span = next[skey] - prev[skey]; - var ratio = span ? (sval - prev[skey]) / span : 0; - var offset = (next[tkey] - prev[tkey]) * ratio; - - return prev[tkey] + offset; -} - -function toTimestamp(scale, input) { - var adapter = scale._adapter; - var options = scale.options.time; - var parser = options.parser; - var format = parser || options.format; - var value = input; - - if (typeof parser === 'function') { - value = parser(value); - } - - // Only parse if its not a timestamp already - if (!helpers$1.isFinite(value)) { - value = typeof format === 'string' - ? adapter.parse(value, format) - : adapter.parse(value); - } - - if (value !== null) { - return +value; - } - - // Labels are in an incompatible format and no `parser` has been provided. - // The user might still use the deprecated `format` option for parsing. - if (!parser && typeof format === 'function') { - value = format(input); - - // `format` could return something else than a timestamp, if so, parse it - if (!helpers$1.isFinite(value)) { - value = adapter.parse(value); - } - } - - return value; -} - -function parse(scale, input) { - if (helpers$1.isNullOrUndef(input)) { - return null; - } - - var options = scale.options.time; - var value = toTimestamp(scale, scale.getRightValue(input)); - if (value === null) { - return value; - } - - if (options.round) { - value = +scale._adapter.startOf(value, options.round); - } - - return value; -} - -/** - * Returns the number of unit to skip to be able to display up to `capacity` number of ticks - * in `unit` for the given `min` / `max` range and respecting the interval steps constraints. - */ -function determineStepSize(min, max, unit, capacity) { - var range = max - min; - var interval = INTERVALS[unit]; - var milliseconds = interval.size; - var steps = interval.steps; - var i, ilen, factor; - - if (!steps) { - return Math.ceil(range / (capacity * milliseconds)); - } - - for (i = 0, ilen = steps.length; i < ilen; ++i) { - factor = steps[i]; - if (Math.ceil(range / (milliseconds * factor)) <= capacity) { - break; - } - } - - return factor; -} - -/** - * Figures out what unit results in an appropriate number of auto-generated ticks - */ -function determineUnitForAutoTicks(minUnit, min, max, capacity) { - var ilen = UNITS.length; - var i, interval, factor; - - for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { - interval = INTERVALS[UNITS[i]]; - factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; - - if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { - return UNITS[i]; - } - } - - return UNITS[ilen - 1]; -} - -/** - * Figures out what unit to format a set of ticks with - */ -function determineUnitForFormatting(scale, ticks, minUnit, min, max) { - var ilen = UNITS.length; - var i, unit; - - for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) { - unit = UNITS[i]; - if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= ticks.length) { - return unit; - } - } - - return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; -} - -function determineMajorUnit(unit) { - for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { - if (INTERVALS[UNITS[i]].common) { - return UNITS[i]; - } - } -} - -/** - * Generates a maximum of `capacity` timestamps between min and max, rounded to the - * `minor` unit, aligned on the `major` unit and using the given scale time `options`. - * Important: this method can return ticks outside the min and max range, it's the - * responsibility of the calling code to clamp values if needed. - */ -function generate(scale, min, max, capacity) { - var adapter = scale._adapter; - var options = scale.options; - var timeOpts = options.time; - var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); - var major = determineMajorUnit(minor); - var stepSize = valueOrDefault$c(timeOpts.stepSize, timeOpts.unitStepSize); - var weekday = minor === 'week' ? timeOpts.isoWeekday : false; - var majorTicksEnabled = options.ticks.major.enabled; - var interval = INTERVALS[minor]; - var first = min; - var last = max; - var ticks = []; - var time; - - if (!stepSize) { - stepSize = determineStepSize(min, max, minor, capacity); - } - - // For 'week' unit, handle the first day of week option - if (weekday) { - first = +adapter.startOf(first, 'isoWeek', weekday); - last = +adapter.startOf(last, 'isoWeek', weekday); - } - - // Align first/last ticks on unit - first = +adapter.startOf(first, weekday ? 'day' : minor); - last = +adapter.startOf(last, weekday ? 'day' : minor); - - // Make sure that the last tick include max - if (last < max) { - last = +adapter.add(last, 1, minor); - } - - time = first; - - if (majorTicksEnabled && major && !weekday && !timeOpts.round) { - // Align the first tick on the previous `minor` unit aligned on the `major` unit: - // we first aligned time on the previous `major` unit then add the number of full - // stepSize there is between first and the previous major time. - time = +adapter.startOf(time, major); - time = +adapter.add(time, ~~((first - time) / (interval.size * stepSize)) * stepSize, minor); - } - - for (; time < last; time = +adapter.add(time, stepSize, minor)) { - ticks.push(+time); - } - - ticks.push(+time); - - return ticks; -} - -/** - * Returns the start and end offsets from edges in the form of {start, end} - * where each value is a relative width to the scale and ranges between 0 and 1. - * They add extra margins on the both sides by scaling down the original scale. - * Offsets are added when the `offset` option is true. - */ -function computeOffsets(table, ticks, min, max, options) { - var start = 0; - var end = 0; - var first, last; - - if (options.offset && ticks.length) { - if (!options.time.min) { - first = interpolate$1(table, 'time', ticks[0], 'pos'); - if (ticks.length === 1) { - start = 1 - first; - } else { - start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2; - } - } - if (!options.time.max) { - last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos'); - if (ticks.length === 1) { - end = last; - } else { - end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2; - } - } - } - - return {start: start, end: end}; -} - -function ticksFromTimestamps(scale, values, majorUnit) { - var ticks = []; - var i, ilen, value, major; - - for (i = 0, ilen = values.length; i < ilen; ++i) { - value = values[i]; - major = majorUnit ? value === +scale._adapter.startOf(value, majorUnit) : false; - - ticks.push({ - value: value, - major: major - }); - } - - return ticks; -} - -var defaultConfig$4 = { - position: 'bottom', - - /** - * Data distribution along the scale: - * - 'linear': data are spread according to their time (distances can vary), - * - 'series': data are spread at the same distance from each other. - * @see https://github.com/chartjs/Chart.js/pull/4507 - * @since 2.7.0 - */ - distribution: 'linear', - - /** - * Scale boundary strategy (bypassed by min/max time options) - * - `data`: make sure data are fully visible, ticks outside are removed - * - `ticks`: make sure ticks are fully visible, data outside are truncated - * @see https://github.com/chartjs/Chart.js/pull/4556 - * @since 2.7.0 - */ - bounds: 'data', - - adapters: {}, - time: { - parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment - format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from https://momentjs.com/docs/#/parsing/string-format/ - unit: false, // false == automatic or override with week, month, year, etc. - round: false, // none, or override with week, month, year, etc. - displayFormat: false, // DEPRECATED - isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/ - minUnit: 'millisecond', - displayFormats: {} - }, - ticks: { - autoSkip: false, - - /** - * Ticks generation input values: - * - 'auto': generates "optimal" ticks based on scale size and time options. - * - 'data': generates ticks from data (including labels from data {t|x|y} objects). - * - 'labels': generates ticks from user given `data.labels` values ONLY. - * @see https://github.com/chartjs/Chart.js/pull/4507 - * @since 2.7.0 - */ - source: 'auto', - - major: { - enabled: false - } - } -}; - -var scale_time = core_scale.extend({ - initialize: function() { - this.mergeTicksOptions(); - core_scale.prototype.initialize.call(this); - }, - - update: function() { - var me = this; - var options = me.options; - var time = options.time || (options.time = {}); - var adapter = me._adapter = new core_adapters._date(options.adapters.date); - - // DEPRECATIONS: output a message only one time per update - if (time.format) { - console.warn('options.time.format is deprecated and replaced by options.time.parser.'); - } - - // Backward compatibility: before introducing adapter, `displayFormats` was - // supposed to contain *all* unit/string pairs but this can't be resolved - // when loading the scale (adapters are loaded afterward), so let's populate - // missing formats on update - helpers$1.mergeIf(time.displayFormats, adapter.formats()); - - return core_scale.prototype.update.apply(me, arguments); - }, - - /** - * Allows data to be referenced via 't' attribute - */ - getRightValue: function(rawValue) { - if (rawValue && rawValue.t !== undefined) { - rawValue = rawValue.t; - } - return core_scale.prototype.getRightValue.call(this, rawValue); - }, - - determineDataLimits: function() { - var me = this; - var chart = me.chart; - var adapter = me._adapter; - var timeOpts = me.options.time; - var unit = timeOpts.unit || 'day'; - var min = MAX_INTEGER; - var max = MIN_INTEGER; - var timestamps = []; - var datasets = []; - var labels = []; - var i, j, ilen, jlen, data, timestamp; - var dataLabels = chart.data.labels || []; - - // Convert labels to timestamps - for (i = 0, ilen = dataLabels.length; i < ilen; ++i) { - labels.push(parse(me, dataLabels[i])); - } - - // Convert data to timestamps - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - if (chart.isDatasetVisible(i)) { - data = chart.data.datasets[i].data; - - // Let's consider that all data have the same format. - if (helpers$1.isObject(data[0])) { - datasets[i] = []; - - for (j = 0, jlen = data.length; j < jlen; ++j) { - timestamp = parse(me, data[j]); - timestamps.push(timestamp); - datasets[i][j] = timestamp; - } - } else { - for (j = 0, jlen = labels.length; j < jlen; ++j) { - timestamps.push(labels[j]); - } - datasets[i] = labels.slice(0); - } - } else { - datasets[i] = []; - } - } - - if (labels.length) { - // Sort labels **after** data have been converted - labels = arrayUnique(labels).sort(sorter); - min = Math.min(min, labels[0]); - max = Math.max(max, labels[labels.length - 1]); - } - - if (timestamps.length) { - timestamps = arrayUnique(timestamps).sort(sorter); - min = Math.min(min, timestamps[0]); - max = Math.max(max, timestamps[timestamps.length - 1]); - } - - min = parse(me, timeOpts.min) || min; - max = parse(me, timeOpts.max) || max; - - // In case there is no valid min/max, set limits based on unit time option - min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min; - max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max; - - // Make sure that max is strictly higher than min (required by the lookup table) - me.min = Math.min(min, max); - me.max = Math.max(min + 1, max); - - // PRIVATE - me._horizontal = me.isHorizontal(); - me._table = []; - me._timestamps = { - data: timestamps, - datasets: datasets, - labels: labels - }; - }, - - buildTicks: function() { - var me = this; - var min = me.min; - var max = me.max; - var options = me.options; - var timeOpts = options.time; - var timestamps = []; - var ticks = []; - var i, ilen, timestamp; - - switch (options.ticks.source) { - case 'data': - timestamps = me._timestamps.data; - break; - case 'labels': - timestamps = me._timestamps.labels; - break; - case 'auto': - default: - timestamps = generate(me, min, max, me.getLabelCapacity(min), options); - } - - if (options.bounds === 'ticks' && timestamps.length) { - min = timestamps[0]; - max = timestamps[timestamps.length - 1]; - } - - // Enforce limits with user min/max options - min = parse(me, timeOpts.min) || min; - max = parse(me, timeOpts.max) || max; - - // Remove ticks outside the min/max range - for (i = 0, ilen = timestamps.length; i < ilen; ++i) { - timestamp = timestamps[i]; - if (timestamp >= min && timestamp <= max) { - ticks.push(timestamp); - } - } - - me.min = min; - me.max = max; - - // PRIVATE - me._unit = timeOpts.unit || determineUnitForFormatting(me, ticks, timeOpts.minUnit, me.min, me.max); - me._majorUnit = determineMajorUnit(me._unit); - me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); - me._offsets = computeOffsets(me._table, ticks, min, max, options); - - if (options.ticks.reverse) { - ticks.reverse(); - } - - return ticksFromTimestamps(me, ticks, me._majorUnit); - }, - - getLabelForIndex: function(index, datasetIndex) { - var me = this; - var adapter = me._adapter; - var data = me.chart.data; - var timeOpts = me.options.time; - var label = data.labels && index < data.labels.length ? data.labels[index] : ''; - var value = data.datasets[datasetIndex].data[index]; - - if (helpers$1.isObject(value)) { - label = me.getRightValue(value); - } - if (timeOpts.tooltipFormat) { - return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat); - } - if (typeof label === 'string') { - return label; - } - return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime); - }, - - /** - * Function to format an individual tick mark - * @private - */ - tickFormatFunction: function(time, index, ticks, format) { - var me = this; - var adapter = me._adapter; - var options = me.options; - var formats = options.time.displayFormats; - var minorFormat = formats[me._unit]; - var majorUnit = me._majorUnit; - var majorFormat = formats[majorUnit]; - var majorTime = +adapter.startOf(time, majorUnit); - var majorTickOpts = options.ticks.major; - var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; - var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat); - var tickOpts = major ? majorTickOpts : options.ticks.minor; - var formatter = valueOrDefault$c(tickOpts.callback, tickOpts.userCallback); - - return formatter ? formatter(label, index, ticks) : label; - }, - - convertTicksToLabels: function(ticks) { - var labels = []; - var i, ilen; - - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - labels.push(this.tickFormatFunction(ticks[i].value, i, ticks)); - } - - return labels; - }, - - /** - * @private - */ - getPixelForOffset: function(time) { - var me = this; - var isReverse = me.options.ticks.reverse; - var size = me._horizontal ? me.width : me.height; - var start = me._horizontal ? isReverse ? me.right : me.left : isReverse ? me.bottom : me.top; - var pos = interpolate$1(me._table, 'time', time, 'pos'); - var offset = size * (me._offsets.start + pos) / (me._offsets.start + 1 + me._offsets.end); - - return isReverse ? start - offset : start + offset; - }, - - getPixelForValue: function(value, index, datasetIndex) { - var me = this; - var time = null; - - if (index !== undefined && datasetIndex !== undefined) { - time = me._timestamps.datasets[datasetIndex][index]; - } - - if (time === null) { - time = parse(me, value); - } - - if (time !== null) { - return me.getPixelForOffset(time); - } - }, - - getPixelForTick: function(index) { - var ticks = this.getTicks(); - return index >= 0 && index < ticks.length ? - this.getPixelForOffset(ticks[index].value) : - null; - }, - - getValueForPixel: function(pixel) { - var me = this; - var size = me._horizontal ? me.width : me.height; - var start = me._horizontal ? me.left : me.top; - var pos = (size ? (pixel - start) / size : 0) * (me._offsets.start + 1 + me._offsets.start) - me._offsets.end; - var time = interpolate$1(me._table, 'pos', pos, 'time'); - - // DEPRECATION, we should return time directly - return me._adapter._create(time); - }, - - /** - * Crude approximation of what the label width might be - * @private - */ - getLabelWidth: function(label) { - var me = this; - var ticksOpts = me.options.ticks; - var tickLabelWidth = me.ctx.measureText(label).width; - var angle = helpers$1.toRadians(ticksOpts.maxRotation); - var cosRotation = Math.cos(angle); - var sinRotation = Math.sin(angle); - var tickFontSize = valueOrDefault$c(ticksOpts.fontSize, core_defaults.global.defaultFontSize); - - return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); - }, - - /** - * @private - */ - getLabelCapacity: function(exampleTime) { - var me = this; - - // pick the longest format (milliseconds) for guestimation - var format = me.options.time.displayFormats.millisecond; - var exampleLabel = me.tickFormatFunction(exampleTime, 0, [], format); - var tickLabelWidth = me.getLabelWidth(exampleLabel); - var innerWidth = me.isHorizontal() ? me.width : me.height; - var capacity = Math.floor(innerWidth / tickLabelWidth); - - return capacity > 0 ? capacity : 1; - } -}); - -// INTERNAL: static default options, registered in src/index.js -var _defaults$4 = defaultConfig$4; -scale_time._defaults = _defaults$4; - -var scales = { - category: scale_category, - linear: scale_linear, - logarithmic: scale_logarithmic, - radialLinear: scale_radialLinear, - time: scale_time -}; - -var FORMATS = { - datetime: 'MMM D, YYYY, h:mm:ss a', - millisecond: 'h:mm:ss.SSS a', - second: 'h:mm:ss a', - minute: 'h:mm a', - hour: 'hA', - day: 'MMM D', - week: 'll', - month: 'MMM YYYY', - quarter: '[Q]Q - YYYY', - year: 'YYYY' -}; - -core_adapters._date.override(typeof moment === 'function' ? { - _id: 'moment', // DEBUG ONLY - - formats: function() { - return FORMATS; - }, - - parse: function(value, format) { - if (typeof value === 'string' && typeof format === 'string') { - value = moment(value, format); - } else if (!(value instanceof moment)) { - value = moment(value); - } - return value.isValid() ? value.valueOf() : null; - }, - - format: function(time, format) { - return moment(time).format(format); - }, - - add: function(time, amount, unit) { - return moment(time).add(amount, unit).valueOf(); - }, - - diff: function(max, min, unit) { - return moment.duration(moment(max).diff(moment(min))).as(unit); - }, - - startOf: function(time, unit, weekday) { - time = moment(time); - if (unit === 'isoWeek') { - return time.isoWeekday(weekday).valueOf(); - } - return time.startOf(unit).valueOf(); - }, - - endOf: function(time, unit) { - return moment(time).endOf(unit).valueOf(); - }, - - // DEPRECATIONS - - /** - * Provided for backward compatibility with scale.getValueForPixel(). - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ - _create: function(time) { - return moment(time); - }, -} : {}); - -core_defaults._set('global', { - plugins: { - filler: { - propagate: true - } - } -}); - -var mappers = { - dataset: function(source) { - var index = source.fill; - var chart = source.chart; - var meta = chart.getDatasetMeta(index); - var visible = meta && chart.isDatasetVisible(index); - var points = (visible && meta.dataset._children) || []; - var length = points.length || 0; - - return !length ? null : function(point, i) { - return (i < length && points[i]._view) || null; - }; - }, - - boundary: function(source) { - var boundary = source.boundary; - var x = boundary ? boundary.x : null; - var y = boundary ? boundary.y : null; - - return function(point) { - return { - x: x === null ? point.x : x, - y: y === null ? point.y : y, - }; - }; - } -}; - -// @todo if (fill[0] === '#') -function decodeFill(el, index, count) { - var model = el._model || {}; - var fill = model.fill; - var target; - - if (fill === undefined) { - fill = !!model.backgroundColor; - } - - if (fill === false || fill === null) { - return false; - } - - if (fill === true) { - return 'origin'; - } - - target = parseFloat(fill, 10); - if (isFinite(target) && Math.floor(target) === target) { - if (fill[0] === '-' || fill[0] === '+') { - target = index + target; - } - - if (target === index || target < 0 || target >= count) { - return false; - } - - return target; - } - - switch (fill) { - // compatibility - case 'bottom': - return 'start'; - case 'top': - return 'end'; - case 'zero': - return 'origin'; - // supported boundaries - case 'origin': - case 'start': - case 'end': - return fill; - // invalid fill values - default: - return false; - } -} - -function computeBoundary(source) { - var model = source.el._model || {}; - var scale = source.el._scale || {}; - var fill = source.fill; - var target = null; - var horizontal; - - if (isFinite(fill)) { - return null; - } - - // Backward compatibility: until v3, we still need to support boundary values set on - // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and - // controllers might still use it (e.g. the Smith chart). - - if (fill === 'start') { - target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; - } else if (fill === 'end') { - target = model.scaleTop === undefined ? scale.top : model.scaleTop; - } else if (model.scaleZero !== undefined) { - target = model.scaleZero; - } else if (scale.getBasePosition) { - target = scale.getBasePosition(); - } else if (scale.getBasePixel) { - target = scale.getBasePixel(); - } - - if (target !== undefined && target !== null) { - if (target.x !== undefined && target.y !== undefined) { - return target; - } - - if (helpers$1.isFinite(target)) { - horizontal = scale.isHorizontal(); - return { - x: horizontal ? target : null, - y: horizontal ? null : target - }; - } - } - - return null; -} - -function resolveTarget(sources, index, propagate) { - var source = sources[index]; - var fill = source.fill; - var visited = [index]; - var target; - - if (!propagate) { - return fill; - } - - while (fill !== false && visited.indexOf(fill) === -1) { - if (!isFinite(fill)) { - return fill; - } - - target = sources[fill]; - if (!target) { - return false; - } - - if (target.visible) { - return fill; - } - - visited.push(fill); - fill = target.fill; - } - - return false; -} - -function createMapper(source) { - var fill = source.fill; - var type = 'dataset'; - - if (fill === false) { - return null; - } - - if (!isFinite(fill)) { - type = 'boundary'; - } - - return mappers[type](source); -} - -function isDrawable(point) { - return point && !point.skip; -} - -function drawArea(ctx, curve0, curve1, len0, len1) { - var i; - - if (!len0 || !len1) { - return; - } - - // building first area curve (normal) - ctx.moveTo(curve0[0].x, curve0[0].y); - for (i = 1; i < len0; ++i) { - helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); - } - - // joining the two area curves - ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); - - // building opposite area curve (reverse) - for (i = len1 - 1; i > 0; --i) { - helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); - } -} - -function doFill(ctx, points, mapper, view, color, loop) { - var count = points.length; - var span = view.spanGaps; - var curve0 = []; - var curve1 = []; - var len0 = 0; - var len1 = 0; - var i, ilen, index, p0, p1, d0, d1; - - ctx.beginPath(); - - for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { - index = i % count; - p0 = points[index]._view; - p1 = mapper(p0, index, view); - d0 = isDrawable(p0); - d1 = isDrawable(p1); - - if (d0 && d1) { - len0 = curve0.push(p0); - len1 = curve1.push(p1); - } else if (len0 && len1) { - if (!span) { - drawArea(ctx, curve0, curve1, len0, len1); - len0 = len1 = 0; - curve0 = []; - curve1 = []; - } else { - if (d0) { - curve0.push(p0); - } - if (d1) { - curve1.push(p1); - } - } - } - } - - drawArea(ctx, curve0, curve1, len0, len1); - - ctx.closePath(); - ctx.fillStyle = color; - ctx.fill(); -} - -var plugin_filler = { - id: 'filler', - - afterDatasetsUpdate: function(chart, options) { - var count = (chart.data.datasets || []).length; - var propagate = options.propagate; - var sources = []; - var meta, i, el, source; - - for (i = 0; i < count; ++i) { - meta = chart.getDatasetMeta(i); - el = meta.dataset; - source = null; - - if (el && el._model && el instanceof elements.Line) { - source = { - visible: chart.isDatasetVisible(i), - fill: decodeFill(el, i, count), - chart: chart, - el: el - }; - } - - meta.$filler = source; - sources.push(source); - } - - for (i = 0; i < count; ++i) { - source = sources[i]; - if (!source) { - continue; - } - - source.fill = resolveTarget(sources, i, propagate); - source.boundary = computeBoundary(source); - source.mapper = createMapper(source); - } - }, - - beforeDatasetDraw: function(chart, args) { - var meta = args.meta.$filler; - if (!meta) { - return; - } - - var ctx = chart.ctx; - var el = meta.el; - var view = el._view; - var points = el._children || []; - var mapper = meta.mapper; - var color = view.backgroundColor || core_defaults.global.defaultColor; - - if (mapper && color && points.length) { - helpers$1.canvas.clipArea(ctx, chart.chartArea); - doFill(ctx, points, mapper, view, color, el._loop); - helpers$1.canvas.unclipArea(ctx); - } - } -}; - -var noop$1 = helpers$1.noop; -var valueOrDefault$d = helpers$1.valueOrDefault; - -core_defaults._set('global', { - legend: { - display: true, - position: 'top', - fullWidth: true, - reverse: false, - weight: 1000, - - // a callback that will handle - onClick: function(e, legendItem) { - var index = legendItem.datasetIndex; - var ci = this.chart; - var meta = ci.getDatasetMeta(index); - - // See controller.isDatasetVisible comment - meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; - - // We hid a dataset ... rerender the chart - ci.update(); - }, - - onHover: null, - onLeave: null, - - labels: { - boxWidth: 40, - padding: 10, - // Generates labels shown in the legend - // Valid properties to return: - // text : text to display - // fillStyle : fill of coloured box - // strokeStyle: stroke of coloured box - // hidden : if this legend item refers to a hidden item - // lineCap : cap style for line - // lineDash - // lineDashOffset : - // lineJoin : - // lineWidth : - generateLabels: function(chart) { - var data = chart.data; - return helpers$1.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { - return { - text: dataset.label, - fillStyle: (!helpers$1.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), - hidden: !chart.isDatasetVisible(i), - lineCap: dataset.borderCapStyle, - lineDash: dataset.borderDash, - lineDashOffset: dataset.borderDashOffset, - lineJoin: dataset.borderJoinStyle, - lineWidth: dataset.borderWidth, - strokeStyle: dataset.borderColor, - pointStyle: dataset.pointStyle, - - // Below is extra data used for toggling the datasets - datasetIndex: i - }; - }, this) : []; - } - } - }, - - legendCallback: function(chart) { - var text = []; - text.push('
    '); - for (var i = 0; i < chart.data.datasets.length; i++) { - text.push('
  • '); - if (chart.data.datasets[i].label) { - text.push(chart.data.datasets[i].label); - } - text.push('
  • '); - } - text.push('
'); - return text.join(''); - } -}); - -/** - * Helper function to get the box width based on the usePointStyle option - * @param {object} labelopts - the label options on the legend - * @param {number} fontSize - the label font size - * @return {number} width of the color box area - */ -function getBoxWidth(labelOpts, fontSize) { - return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ? - fontSize : - labelOpts.boxWidth; -} - -/** - * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! - */ -var Legend = core_element.extend({ - - initialize: function(config) { - helpers$1.extend(this, config); - - // Contains hit boxes for each dataset (in dataset order) - this.legendHitBoxes = []; - - /** - * @private - */ - this._hoveredItem = null; - - // Are we in doughnut mode which has a different data type - this.doughnutMode = false; - }, - - // These methods are ordered by lifecycle. Utilities then follow. - // Any function defined here is inherited by all legend types. - // Any function can be extended by the legend type - - beforeUpdate: noop$1, - update: function(maxWidth, maxHeight, margins) { - var me = this; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = margins; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - // Labels - me.beforeBuildLabels(); - me.buildLabels(); - me.afterBuildLabels(); - - // Fit - me.beforeFit(); - me.fit(); - me.afterFit(); - // - me.afterUpdate(); - - return me.minSize; - }, - afterUpdate: noop$1, - - // - - beforeSetDimensions: noop$1, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - - // Reset minSize - me.minSize = { - width: 0, - height: 0 - }; - }, - afterSetDimensions: noop$1, - - // - - beforeBuildLabels: noop$1, - buildLabels: function() { - var me = this; - var labelOpts = me.options.labels || {}; - var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || []; - - if (labelOpts.filter) { - legendItems = legendItems.filter(function(item) { - return labelOpts.filter(item, me.chart.data); - }); - } - - if (me.options.reverse) { - legendItems.reverse(); - } - - me.legendItems = legendItems; - }, - afterBuildLabels: noop$1, - - // - - beforeFit: noop$1, - fit: function() { - var me = this; - var opts = me.options; - var labelOpts = opts.labels; - var display = opts.display; - - var ctx = me.ctx; - - var labelFont = helpers$1.options._parseFont(labelOpts); - var fontSize = labelFont.size; - - // Reset hit boxes - var hitboxes = me.legendHitBoxes = []; - - var minSize = me.minSize; - var isHorizontal = me.isHorizontal(); - - if (isHorizontal) { - minSize.width = me.maxWidth; // fill all the width - minSize.height = display ? 10 : 0; - } else { - minSize.width = display ? 10 : 0; - minSize.height = me.maxHeight; // fill all the height - } - - // Increase sizes here - if (display) { - ctx.font = labelFont.string; - - if (isHorizontal) { - // Labels - - // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one - var lineWidths = me.lineWidths = [0]; - var totalHeight = 0; - - ctx.textAlign = 'left'; - ctx.textBaseline = 'top'; - - helpers$1.each(me.legendItems, function(legendItem, i) { - var boxWidth = getBoxWidth(labelOpts, fontSize); - var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - - if (i === 0 || lineWidths[lineWidths.length - 1] + width + labelOpts.padding > minSize.width) { - totalHeight += fontSize + labelOpts.padding; - lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = labelOpts.padding; - } - - // Store the hitbox width and height here. Final position will be updated in `draw` - hitboxes[i] = { - left: 0, - top: 0, - width: width, - height: fontSize - }; - - lineWidths[lineWidths.length - 1] += width + labelOpts.padding; - }); - - minSize.height += totalHeight; - - } else { - var vPadding = labelOpts.padding; - var columnWidths = me.columnWidths = []; - var totalWidth = labelOpts.padding; - var currentColWidth = 0; - var currentColHeight = 0; - var itemHeight = fontSize + vPadding; - - helpers$1.each(me.legendItems, function(legendItem, i) { - var boxWidth = getBoxWidth(labelOpts, fontSize); - var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - - // If too tall, go to new column - if (i > 0 && currentColHeight + itemHeight > minSize.height - vPadding) { - totalWidth += currentColWidth + labelOpts.padding; - columnWidths.push(currentColWidth); // previous column width - - currentColWidth = 0; - currentColHeight = 0; - } - - // Get max width - currentColWidth = Math.max(currentColWidth, itemWidth); - currentColHeight += itemHeight; - - // Store the hitbox width and height here. Final position will be updated in `draw` - hitboxes[i] = { - left: 0, - top: 0, - width: itemWidth, - height: fontSize - }; - }); - - totalWidth += currentColWidth; - columnWidths.push(currentColWidth); - minSize.width += totalWidth; - } - } - - me.width = minSize.width; - me.height = minSize.height; - }, - afterFit: noop$1, - - // Shared Methods - isHorizontal: function() { - return this.options.position === 'top' || this.options.position === 'bottom'; - }, - - // Actually draw the legend on the canvas - draw: function() { - var me = this; - var opts = me.options; - var labelOpts = opts.labels; - var globalDefaults = core_defaults.global; - var defaultColor = globalDefaults.defaultColor; - var lineDefault = globalDefaults.elements.line; - var legendWidth = me.width; - var lineWidths = me.lineWidths; - - if (opts.display) { - var ctx = me.ctx; - var fontColor = valueOrDefault$d(labelOpts.fontColor, globalDefaults.defaultFontColor); - var labelFont = helpers$1.options._parseFont(labelOpts); - var fontSize = labelFont.size; - var cursor; - - // Canvas setup - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - ctx.lineWidth = 0.5; - ctx.strokeStyle = fontColor; // for strikethrough effect - ctx.fillStyle = fontColor; // render in correct colour - ctx.font = labelFont.string; - - var boxWidth = getBoxWidth(labelOpts, fontSize); - var hitboxes = me.legendHitBoxes; - - // current position - var drawLegendBox = function(x, y, legendItem) { - if (isNaN(boxWidth) || boxWidth <= 0) { - return; - } - - // Set the ctx for the box - ctx.save(); - - var lineWidth = valueOrDefault$d(legendItem.lineWidth, lineDefault.borderWidth); - ctx.fillStyle = valueOrDefault$d(legendItem.fillStyle, defaultColor); - ctx.lineCap = valueOrDefault$d(legendItem.lineCap, lineDefault.borderCapStyle); - ctx.lineDashOffset = valueOrDefault$d(legendItem.lineDashOffset, lineDefault.borderDashOffset); - ctx.lineJoin = valueOrDefault$d(legendItem.lineJoin, lineDefault.borderJoinStyle); - ctx.lineWidth = lineWidth; - ctx.strokeStyle = valueOrDefault$d(legendItem.strokeStyle, defaultColor); - - if (ctx.setLineDash) { - // IE 9 and 10 do not support line dash - ctx.setLineDash(valueOrDefault$d(legendItem.lineDash, lineDefault.borderDash)); - } - - if (opts.labels && opts.labels.usePointStyle) { - // Recalculate x and y for drawPoint() because its expecting - // x and y to be center of figure (instead of top left) - var radius = boxWidth * Math.SQRT2 / 2; - var centerX = x + boxWidth / 2; - var centerY = y + fontSize / 2; - - // Draw pointStyle as legend symbol - helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); - } else { - // Draw box as legend symbol - if (lineWidth !== 0) { - ctx.strokeRect(x, y, boxWidth, fontSize); - } - ctx.fillRect(x, y, boxWidth, fontSize); - } - - ctx.restore(); - }; - var fillText = function(x, y, legendItem, textWidth) { - var halfFontSize = fontSize / 2; - var xLeft = boxWidth + halfFontSize + x; - var yMiddle = y + halfFontSize; - - ctx.fillText(legendItem.text, xLeft, yMiddle); - - if (legendItem.hidden) { - // Strikethrough the text if hidden - ctx.beginPath(); - ctx.lineWidth = 2; - ctx.moveTo(xLeft, yMiddle); - ctx.lineTo(xLeft + textWidth, yMiddle); - ctx.stroke(); - } - }; - - // Horizontal - var isHorizontal = me.isHorizontal(); - if (isHorizontal) { - cursor = { - x: me.left + ((legendWidth - lineWidths[0]) / 2) + labelOpts.padding, - y: me.top + labelOpts.padding, - line: 0 - }; - } else { - cursor = { - x: me.left + labelOpts.padding, - y: me.top + labelOpts.padding, - line: 0 - }; - } - - var itemHeight = fontSize + labelOpts.padding; - helpers$1.each(me.legendItems, function(legendItem, i) { - var textWidth = ctx.measureText(legendItem.text).width; - var width = boxWidth + (fontSize / 2) + textWidth; - var x = cursor.x; - var y = cursor.y; - - // Use (me.left + me.minSize.width) and (me.top + me.minSize.height) - // instead of me.right and me.bottom because me.width and me.height - // may have been changed since me.minSize was calculated - if (isHorizontal) { - if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) { - y = cursor.y += itemHeight; - cursor.line++; - x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2) + labelOpts.padding; - } - } else if (i > 0 && y + itemHeight > me.top + me.minSize.height) { - x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; - y = cursor.y = me.top + labelOpts.padding; - cursor.line++; - } - - drawLegendBox(x, y, legendItem); - - hitboxes[i].left = x; - hitboxes[i].top = y; - - // Fill the actual label - fillText(x, y, legendItem, textWidth); - - if (isHorizontal) { - cursor.x += width + labelOpts.padding; - } else { - cursor.y += itemHeight; - } - - }); - } - }, - - /** - * @private - */ - _getLegendItemAt: function(x, y) { - var me = this; - var i, hitBox, lh; - - if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { - // See if we are touching one of the dataset boxes - lh = me.legendHitBoxes; - for (i = 0; i < lh.length; ++i) { - hitBox = lh[i]; - - if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { - // Touching an element - return me.legendItems[i]; - } - } - } - - return null; - }, - - /** - * Handle an event - * @private - * @param {IEvent} event - The event to handle - */ - handleEvent: function(e) { - var me = this; - var opts = me.options; - var type = e.type === 'mouseup' ? 'click' : e.type; - var hoveredItem; - - if (type === 'mousemove') { - if (!opts.onHover && !opts.onLeave) { - return; - } - } else if (type === 'click') { - if (!opts.onClick) { - return; - } - } else { - return; - } - - // Chart event already has relative position in it - hoveredItem = me._getLegendItemAt(e.x, e.y); - - if (type === 'click') { - if (hoveredItem && opts.onClick) { - // use e.native for backwards compatibility - opts.onClick.call(me, e.native, hoveredItem); - } - } else { - if (opts.onLeave && hoveredItem !== me._hoveredItem) { - if (me._hoveredItem) { - opts.onLeave.call(me, e.native, me._hoveredItem); - } - me._hoveredItem = hoveredItem; - } - - if (opts.onHover && hoveredItem) { - // use e.native for backwards compatibility - opts.onHover.call(me, e.native, hoveredItem); - } - } - } -}); - -function createNewLegendAndAttach(chart, legendOpts) { - var legend = new Legend({ - ctx: chart.ctx, - options: legendOpts, - chart: chart - }); - - core_layouts.configure(chart, legend, legendOpts); - core_layouts.addBox(chart, legend); - chart.legend = legend; -} - -var plugin_legend = { - id: 'legend', - - /** - * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making - * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of - * the plugin, which one will be re-exposed in the chart.js file. - * https://github.com/chartjs/Chart.js/pull/2640 - * @private - */ - _element: Legend, - - beforeInit: function(chart) { - var legendOpts = chart.options.legend; - - if (legendOpts) { - createNewLegendAndAttach(chart, legendOpts); - } - }, - - beforeUpdate: function(chart) { - var legendOpts = chart.options.legend; - var legend = chart.legend; - - if (legendOpts) { - helpers$1.mergeIf(legendOpts, core_defaults.global.legend); - - if (legend) { - core_layouts.configure(chart, legend, legendOpts); - legend.options = legendOpts; - } else { - createNewLegendAndAttach(chart, legendOpts); - } - } else if (legend) { - core_layouts.removeBox(chart, legend); - delete chart.legend; - } - }, - - afterEvent: function(chart, e) { - var legend = chart.legend; - if (legend) { - legend.handleEvent(e); - } - } -}; - -var noop$2 = helpers$1.noop; - -core_defaults._set('global', { - title: { - display: false, - fontStyle: 'bold', - fullWidth: true, - padding: 10, - position: 'top', - text: '', - weight: 2000 // by default greater than legend (1000) to be above - } -}); - -/** - * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! - */ -var Title = core_element.extend({ - initialize: function(config) { - var me = this; - helpers$1.extend(me, config); - - // Contains hit boxes for each dataset (in dataset order) - me.legendHitBoxes = []; - }, - - // These methods are ordered by lifecycle. Utilities then follow. - - beforeUpdate: noop$2, - update: function(maxWidth, maxHeight, margins) { - var me = this; - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - me.beforeUpdate(); - - // Absorb the master measurements - me.maxWidth = maxWidth; - me.maxHeight = maxHeight; - me.margins = margins; - - // Dimensions - me.beforeSetDimensions(); - me.setDimensions(); - me.afterSetDimensions(); - // Labels - me.beforeBuildLabels(); - me.buildLabels(); - me.afterBuildLabels(); - - // Fit - me.beforeFit(); - me.fit(); - me.afterFit(); - // - me.afterUpdate(); - - return me.minSize; - - }, - afterUpdate: noop$2, - - // - - beforeSetDimensions: noop$2, - setDimensions: function() { - var me = this; - // Set the unconstrained dimension before label rotation - if (me.isHorizontal()) { - // Reset position before calculating rotation - me.width = me.maxWidth; - me.left = 0; - me.right = me.width; - } else { - me.height = me.maxHeight; - - // Reset position before calculating rotation - me.top = 0; - me.bottom = me.height; - } - - // Reset padding - me.paddingLeft = 0; - me.paddingTop = 0; - me.paddingRight = 0; - me.paddingBottom = 0; - - // Reset minSize - me.minSize = { - width: 0, - height: 0 - }; - }, - afterSetDimensions: noop$2, - - // - - beforeBuildLabels: noop$2, - buildLabels: noop$2, - afterBuildLabels: noop$2, - - // - - beforeFit: noop$2, - fit: function() { - var me = this; - var opts = me.options; - var display = opts.display; - var minSize = me.minSize; - var lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1; - var fontOpts = helpers$1.options._parseFont(opts); - var textSize = display ? (lineCount * fontOpts.lineHeight) + (opts.padding * 2) : 0; - - if (me.isHorizontal()) { - minSize.width = me.maxWidth; // fill all the width - minSize.height = textSize; - } else { - minSize.width = textSize; - minSize.height = me.maxHeight; // fill all the height - } - - me.width = minSize.width; - me.height = minSize.height; - - }, - afterFit: noop$2, - - // Shared Methods - isHorizontal: function() { - var pos = this.options.position; - return pos === 'top' || pos === 'bottom'; - }, - - // Actually draw the title block on the canvas - draw: function() { - var me = this; - var ctx = me.ctx; - var opts = me.options; - - if (opts.display) { - var fontOpts = helpers$1.options._parseFont(opts); - var lineHeight = fontOpts.lineHeight; - var offset = lineHeight / 2 + opts.padding; - var rotation = 0; - var top = me.top; - var left = me.left; - var bottom = me.bottom; - var right = me.right; - var maxWidth, titleX, titleY; - - ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour - ctx.font = fontOpts.string; - - // Horizontal - if (me.isHorizontal()) { - titleX = left + ((right - left) / 2); // midpoint of the width - titleY = top + offset; - maxWidth = right - left; - } else { - titleX = opts.position === 'left' ? left + offset : right - offset; - titleY = top + ((bottom - top) / 2); - maxWidth = bottom - top; - rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); - } - - ctx.save(); - ctx.translate(titleX, titleY); - ctx.rotate(rotation); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - - var text = opts.text; - if (helpers$1.isArray(text)) { - var y = 0; - for (var i = 0; i < text.length; ++i) { - ctx.fillText(text[i], 0, y, maxWidth); - y += lineHeight; - } - } else { - ctx.fillText(text, 0, 0, maxWidth); - } - - ctx.restore(); - } - } -}); - -function createNewTitleBlockAndAttach(chart, titleOpts) { - var title = new Title({ - ctx: chart.ctx, - options: titleOpts, - chart: chart - }); - - core_layouts.configure(chart, title, titleOpts); - core_layouts.addBox(chart, title); - chart.titleBlock = title; -} - -var plugin_title = { - id: 'title', - - /** - * Backward compatibility: since 2.1.5, the title is registered as a plugin, making - * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of - * the plugin, which one will be re-exposed in the chart.js file. - * https://github.com/chartjs/Chart.js/pull/2640 - * @private - */ - _element: Title, - - beforeInit: function(chart) { - var titleOpts = chart.options.title; - - if (titleOpts) { - createNewTitleBlockAndAttach(chart, titleOpts); - } - }, - - beforeUpdate: function(chart) { - var titleOpts = chart.options.title; - var titleBlock = chart.titleBlock; - - if (titleOpts) { - helpers$1.mergeIf(titleOpts, core_defaults.global.title); - - if (titleBlock) { - core_layouts.configure(chart, titleBlock, titleOpts); - titleBlock.options = titleOpts; - } else { - createNewTitleBlockAndAttach(chart, titleOpts); - } - } else if (titleBlock) { - core_layouts.removeBox(chart, titleBlock); - delete chart.titleBlock; - } - } -}; - -var plugins = {}; -var filler = plugin_filler; -var legend = plugin_legend; -var title = plugin_title; -plugins.filler = filler; -plugins.legend = legend; -plugins.title = title; - -/** - * @namespace Chart - */ - - -core_controller.helpers = helpers$1; - -// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! -core_helpers(core_controller); - -core_controller._adapters = core_adapters; -core_controller.Animation = core_animation; -core_controller.animationService = core_animations; -core_controller.controllers = controllers; -core_controller.DatasetController = core_datasetController; -core_controller.defaults = core_defaults; -core_controller.Element = core_element; -core_controller.elements = elements; -core_controller.Interaction = core_interaction; -core_controller.layouts = core_layouts; -core_controller.platform = platform; -core_controller.plugins = core_plugins; -core_controller.Scale = core_scale; -core_controller.scaleService = core_scaleService; -core_controller.Ticks = core_ticks; -core_controller.Tooltip = core_tooltip; - -// Register built-in scales - -core_controller.helpers.each(scales, function(scale, type) { - core_controller.scaleService.registerScaleType(type, scale, scale._defaults); -}); - -// Load to register built-in adapters (as side effects) - - -// Loading built-in plugins - -for (var k in plugins) { - if (plugins.hasOwnProperty(k)) { - core_controller.plugins.register(plugins[k]); - } -} - -core_controller.platform.initialize(); - -var src = core_controller; -if (typeof window !== 'undefined') { - window.Chart = core_controller; -} - -// DEPRECATIONS - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Chart - * @deprecated since version 2.8.0 - * @todo remove at version 3 - * @private - */ -core_controller.Chart = core_controller; - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Legend - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.Legend = plugins.legend._element; - -/** - * Provided for backward compatibility, not available anymore - * @namespace Chart.Title - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.Title = plugins.title._element; - -/** - * Provided for backward compatibility, use Chart.plugins instead - * @namespace Chart.pluginService - * @deprecated since version 2.1.5 - * @todo remove at version 3 - * @private - */ -core_controller.pluginService = core_controller.plugins; - -/** - * Provided for backward compatibility, inheriting from Chart.PlugingBase has no - * effect, instead simply create/register plugins via plain JavaScript objects. - * @interface Chart.PluginBase - * @deprecated since version 2.5.0 - * @todo remove at version 3 - * @private - */ -core_controller.PluginBase = core_controller.Element.extend({}); - -/** - * Provided for backward compatibility, use Chart.helpers.canvas instead. - * @namespace Chart.canvasHelpers - * @deprecated since version 2.6.0 - * @todo remove at version 3 - * @private - */ -core_controller.canvasHelpers = core_controller.helpers.canvas; - -/** - * Provided for backward compatibility, use Chart.layouts instead. - * @namespace Chart.layoutService - * @deprecated since version 2.7.3 - * @todo remove at version 3 - * @private - */ -core_controller.layoutService = core_controller.layouts; - -/** - * Provided for backward compatibility, not available anymore. - * @namespace Chart.LinearScaleBase - * @deprecated since version 2.8 - * @todo remove at version 3 - * @private - */ -core_controller.LinearScaleBase = scale_linearbase; - -/** - * Provided for backward compatibility, instead we should create a new Chart - * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`). - * @deprecated since version 2.8.0 - * @todo remove at version 3 - */ -core_controller.helpers.each( - [ - 'Bar', - 'Bubble', - 'Doughnut', - 'Line', - 'PolarArea', - 'Radar', - 'Scatter' - ], - function(klass) { - core_controller[klass] = function(ctx, cfg) { - return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, { - type: klass.charAt(0).toLowerCase() + klass.slice(1) - })); - }; - } -); - -return src; - -}))); diff --git a/resources/libs/clipboard/clipboard.js b/resources/libs/clipboard/clipboard.js deleted file mode 100644 index 85e8e2d..0000000 --- a/resources/libs/clipboard/clipboard.js +++ /dev/null @@ -1,742 +0,0 @@ -/*! - * clipboard.js v1.5.12 - * https://zenorocha.github.io/clipboard.js - * - * Licensed MIT © Zeno Rocha - */ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0})},e=function(a,b){var c={},d=new RegExp("^"+b+"([A-Z])(.*)");for(var e in a){var f=e.match(d);if(f){var g=(f[1]+f[2].replace(/([A-Z])/g,"-$1")).toLowerCase();c[g]=a[e]}}return c},f={keyup:"onKeyUp",resize:"onResize"},g=function(c){a.each(b.opened().reverse(),function(){return c.isDefaultPrevented()||!1!==this[f[c.type]](c)?void 0:(c.preventDefault(),c.stopPropagation(),!1)})},h=function(c){if(c!==b._globalHandlerInstalled){b._globalHandlerInstalled=c;var d=a.map(f,function(a,c){return c+"."+b.prototype.namespace}).join(" ");a(window)[c?"on":"off"](d,g)}};b.prototype={constructor:b,namespace:"featherlight",targetAttr:"data-featherlight",variant:null,resetCss:!1,background:null,openTrigger:"click",closeTrigger:"click",filter:null,root:"body",openSpeed:250,closeSpeed:250,closeOnClick:"background",closeOnEsc:!0,closeIcon:"✕",loading:"",otherClose:null,beforeOpen:a.noop,beforeContent:a.noop,beforeClose:a.noop,afterOpen:a.noop,afterContent:a.noop,afterClose:a.noop,onKeyUp:a.noop,onResize:a.noop,type:null,contentFilters:["jquery","image","html","ajax","iframe","text"],setup:function(b,c){"object"!=typeof b||b instanceof a!=!1||c||(c=b,b=void 0);var d=a.extend(this,c,{target:b}),e=d.resetCss?d.namespace+"-reset":d.namespace,f=a(d.background||['
','
','',d.closeIcon,"",'
'+d.loading+"
","
","
"].join("")),g="."+d.namespace+"-close"+(d.otherClose?","+d.otherClose:"");return d.$instance=f.clone().addClass(d.variant),d.$instance.on(d.closeTrigger+"."+d.namespace,function(b){var c=a(b.target);("background"===d.closeOnClick&&c.is("."+d.namespace)||"anywhere"===d.closeOnClick||c.closest(g).length)&&(b.preventDefault(),d.close())}),this},getContent:function(){var b=this,c=this.constructor.contentFilters,d=function(a){return b.$currentTarget&&b.$currentTarget.attr(a)},e=d(b.targetAttr),f=b.target||e||"",g=c[b.type];if(!g&&f in c&&(g=c[f],f=b.target&&e),f=f||d("href")||"",!g)for(var h in c)b[h]&&(g=c[h],f=b[h]);if(!g){var i=f;if(f=null,a.each(b.contentFilters,function(){return g=c[this],g.test&&(f=g.test(i)),!f&&g.regex&&i.match&&i.match(g.regex)&&(f=i),!f}),!f)return"console"in window&&window.console.error("Featherlight: no content filter found "+(i?' for "'+i+'"':" (no target specified)")),!1}return g.process.call(b,f)},setContent:function(b){var c=this;return(b.is("iframe")||a("iframe",b).length>0)&&c.$instance.addClass(c.namespace+"-iframe"),c.$instance.removeClass(c.namespace+"-loading"),c.$instance.find("."+c.namespace+"-inner").slice(1).remove().end().replaceWith(a.contains(c.$instance[0],b[0])?"":b),c.$content=b.addClass(c.namespace+"-inner"),c},open:function(b){var d=this;if(!(b&&b.isDefaultPrevented()||d.beforeOpen(b)===!1)){b&&b.preventDefault();var e=d.getContent();if(e)return c.push(d),h(!0),d.$instance.appendTo(d.root).fadeIn(d.openSpeed),d.beforeContent(b),a.when(e).always(function(c){d.setContent(c),d.afterContent(b),a.when(d.$instance.promise()).done(function(){d.afterOpen(b)})}),d}return!1},close:function(a){var b=this;return b.beforeClose(a)===!1?!1:(0===d(b).length&&h(!1),void b.$instance.fadeOut(b.closeSpeed,function(){b.$instance.detach(),b.afterClose(a)}))},chainCallbacks:function(b){for(var c in b)this[c]=a.proxy(b[c],this,a.proxy(this[c],this))}},a.extend(b,{id:0,autoBind:"[data-featherlight]",defaults:b.prototype,contentFilters:{jquery:{regex:/^[#.]\w/,test:function(b){return b instanceof a&&b},process:function(b){return a(b).clone(!0)}},image:{regex:/\.(png|jpg|jpeg|gif|tiff|bmp)(\?\S*)?$/i,process:function(b){var c=this,d=a.Deferred(),e=new Image,f=a('');return e.onload=function(){f.naturalWidth=e.width,f.naturalHeight=e.height,d.resolve(f)},e.onerror=function(){d.reject(f)},e.src=b,d.promise()}},html:{regex:/^\s*<[\w!][^<]*>/,process:function(b){return a(b)}},ajax:{regex:/./,process:function(b){var c=a.Deferred(),d=a("
").load(b,function(a,b){"error"!==b&&c.resolve(d.contents()),c.fail()});return c.promise()}},iframe:{process:function(b){var c=new a.Deferred,d=a(" + {% if contest_problem and contest_problem.contest.use_clarifications %} +
+

{{ _('Clarifications') }}

+ {% if has_clarifications %} + {% for clarification in clarifications %} +
+
{{ relative_time(clarification.date) }}
+ + {{ clarification.description|markdown('problem', MATH_ENGINE)|reference }} + +
+ {% endfor %} + {% else %} +

{{ _('No clarifications have been made at this time.') }}

+ {% endif %} +
+ {% endif %} + +{% endblock %} + +{% block bodyend %} + {{ super() }} + {% include "comments/math.html" %} {% endblock %} diff --git a/templates/problem/raw.html b/templates/problem/raw.html index a769a24..bca3412 100644 --- a/templates/problem/raw.html +++ b/templates/problem/raw.html @@ -1,98 +1,98 @@ - + - - - - + - -

{{ problem_name }}

-
-
-
- {{ _('Input:') }} {{ fileio_input or _('stdin') }} -
-
- {{ _('Output:') }} {{ fileio_output or _('stdout') }} -
-
+ +

{{ problem_name }}

+
+
+
{{ _('Time Limit:') }} {{ problem.time_limit }}s {% for name, limit in problem.language_time_limit %} -
- {{ name }} - {{ limit }}s -
+
+ {{ name }} + {{ limit }}s +
{% endfor %} -
-
+
+
{{ _('Memory Limit:') }} {{ problem.memory_limit|kbsimpleformat}} {% for name, limit in problem.language_memory_limit %} -
- {{ name }} - {{ limit|kbsimpleformat }} -
+
+ {{ name }} + {{ limit|kbsimpleformat }} +
{% endfor %} -
-
-
- {{ description|markdown|reference|absolutify(url)|str|safe }} -
- - {% include "katex-load.html" %} - +
+
+
+ {{ description|markdown('problem', 'tex')|reference|absolutify(url)|str|safe }} +
+ + + + + + diff --git a/templates/problem/recent-attempt.html b/templates/problem/recent-attempt.html deleted file mode 100644 index 195fe27..0000000 --- a/templates/problem/recent-attempt.html +++ /dev/null @@ -1,21 +0,0 @@ -{% if last_attempted_problems%} - -{% endif %} \ No newline at end of file diff --git a/templates/problem/related_problems.html b/templates/problem/related_problems.html deleted file mode 100644 index a92a75b..0000000 --- a/templates/problem/related_problems.html +++ /dev/null @@ -1,19 +0,0 @@ -{% if request.user.is_authenticated and related_problems %} -
-

{{_('Recommended problems')}}:

-
    - {% for problem in related_problems %} -
  • - {% if problem.id in completed_problem_ids %} - - {% elif problem.id in attempted_problems %} - - {% else %} - - {% endif %} - {{ problem.name }} -
  • - {% endfor %} -
-
-{% endif %} \ No newline at end of file diff --git a/templates/problem/search-form.html b/templates/problem/search-form.html index 73a9288..83ff22f 100644 --- a/templates/problem/search-form.html +++ b/templates/problem/search-form.html @@ -1,100 +1,84 @@ \ No newline at end of file diff --git a/templates/problem/submission-diff.html b/templates/problem/submission-diff.html index 0a6515e..55dbd35 100644 --- a/templates/problem/submission-diff.html +++ b/templates/problem/submission-diff.html @@ -1,99 +1,100 @@ {% extends "common-content.html" %} {% block js_media %} - - + + + {% endblock %} {% block media %} - + .sub-case-status { + text-decoration: underline; + } + {% endblock %} {% block body %} - - - - - - - - - {% for case in range(num_cases) %} - - {% endfor %} - - - - {% for sub in submissions %} +
IDUsernameResultLanguageDate{{ loop.index }}
+ - - - - - - {% for case in sub.test_cases.all() %} - - {% endfor %} + + + + + + {% for case in range(num_cases) %} + + {% endfor %} - {% endfor %} - -
{{ sub.id }}{{ link_user(sub.user) }}{{ sub.result }}{{ sub.language.name }}{{ relative_time(sub.date) }} - {% if case.status == 'SC' %} - --- - {% else %} - - {% if case.status == 'AC' %} - {{ case.time|floatformat(2) }} - {% else %} - {{ case.status }} - {% endif %} - - {% endif %} - IDUsernameResultLanguageDate{{ loop.index }}
- + + + {% for sub in submissions %} + + {{ sub.id }} + {{ link_user(sub.user) }} + {{ sub.result }} + {{ sub.language.name }} + {{ relative_time(sub.date) }} + {% for case in sub.test_cases.all() %} + + {% if case.status == 'SC' %} + --- + {% else %} + + {% if case.status == 'AC' %} + {{ case.time|floatformat(2) }} + {% else %} + {{ case.status }} + {% endif %} + + {% endif %} + + {% endfor %} + + {% endfor %} + + + {% endblock %} \ No newline at end of file diff --git a/templates/problem/submit.html b/templates/problem/submit.html index af3b64b..4d5cd43 100644 --- a/templates/problem/submit.html +++ b/templates/problem/submit.html @@ -1,261 +1,267 @@ {% extends "base.html" %} {% block js_media %} - - {{ form.media.js }} - + {{ form.media.js }} + {% compress js %} + - - {% compress js %} - - {% endcompress %} + + {% endcompress %} {% endblock %} {% block media %} - {{ form.media.css }} + {{ form.media.css }} + {% compress css %} + + {% endcompress %} {% endblock %} {% block body %} -
- {% if not no_judges %} - {% if default_lang not in form.fields.language.queryset %} -
- x - {% trans trimmed default_language=default_lang.name %} - Warning! Your default language, {{ default_language }}, - is unavailable for this problem and has been deselected. - {% endtrans %} -
+
+ {% if not no_judges %} + {% if default_lang not in form.fields.language.queryset %} +
+ x + {% trans trimmed default_language=default_lang.name %} + Warning! Your default language, {{ default_language }}, + is unavailable for this problem and has been deselected. + {% endtrans %} +
+ {% endif %} + + {% if request.in_contest and submission_limit %} + {% if submissions_left > 0 %} +
+ x + {% trans left=submissions_left %} + You have {{ left }} submission left + {% pluralize %} + You have {{ left }} submissions left + {% endtrans %} +
+ {% else %} +
+ x + {{ _('You have 0 submissions left') }} +
+ {% endif %} + {% endif %} {% endif %} - {% if request.in_contest and submission_limit %} - {% if submissions_left > 0 %} -
- x - {% trans left=submissions_left %} - You have {{ left }} submission left - {% pluralize %} - You have {{ left }} submissions left - {% endtrans %} +
+ {% csrf_token %} + {{ form.non_field_errors() }} +
+
+ {{ form.source.errors }} + {{ form.source }} +
+ {% if not no_judges %} +
+ {{ form.language.errors }} +
+ +
+
+ {% endif %}
- {% else %} -
- x - {{ _('You have 0 submissions left') }} -
- {% endif %} - {% endif %} - {% endif %} - - {% csrf_token %} - {{ form.non_field_errors() }} -
-
- {{ form.source.errors }} - {{ form.source }} -
- {% if not no_judges %} -
- {{ form.language.errors }} -
- -
-
- {% endif %} -
+
-
- - {% if no_judges %} - {{ _('No judge is available for this problem.') }} - {% else %} - {{ form.source_file.errors }} - {{ form.source_file }} -
- {{ form.judge }} - - -
- {% endif %} -
+ {% if no_judges %} + {{ _('No judge is available for this problem.') }} + {% else %} +
+ {{ form.judge }} + +
+ {% endif %} + {% endblock %} \ No newline at end of file diff --git a/templates/problem/yaml.html b/templates/problem/yaml.html index 33c703f..8dee21c 100644 --- a/templates/problem/yaml.html +++ b/templates/problem/yaml.html @@ -1,5 +1,20 @@ {% extends "base.html" %} {% block body %} - {{ highlighted_source }} +
+ + + + + +
+
+ {% for line in raw_source.split('\n') %} + +
{{ loop.index }}
+
+ {% endfor %} +
+
{{ highlighted_source }}
+
{% endblock %} \ No newline at end of file diff --git a/templates/profile-table.html b/templates/profile-table.html deleted file mode 100644 index 8394843..0000000 --- a/templates/profile-table.html +++ /dev/null @@ -1,77 +0,0 @@ -{% if request.profile %} - -{% endif %} \ No newline at end of file diff --git a/templates/recent-organization.html b/templates/recent-organization.html deleted file mode 100644 index 9a627fe..0000000 --- a/templates/recent-organization.html +++ /dev/null @@ -1,17 +0,0 @@ -{% if recent_organizations %} - -{% endif %} \ No newline at end of file diff --git a/templates/registration/activate.html b/templates/registration/activate.html index 6d0db5c..8cee082 100644 --- a/templates/registration/activate.html +++ b/templates/registration/activate.html @@ -1,4 +1,4 @@ {% extends "base.html" %} {% block body %} -

{{ _('%(key)s is an invalid activation key.', key=activation_key) }}

+

{{ _('%(key)s is an invalid activation key.', key=activation_key) }}

{% endblock %} \ No newline at end of file diff --git a/templates/registration/activation_complete.html b/templates/registration/activation_complete.html index 6ee4c0d..6140ad7 100644 --- a/templates/registration/activation_complete.html +++ b/templates/registration/activation_complete.html @@ -1,4 +1,4 @@ {% extends "base.html" %} {% block body %} -

{{ _('Your account has been successfully activated.') }}

+

{{ _('Your account has been successfully activated.') }}

{% endblock %} \ No newline at end of file diff --git a/templates/registration/activation_email.html b/templates/registration/activation_email.html index 786bc11..a90b9fc 100644 --- a/templates/registration/activation_email.html +++ b/templates/registration/activation_email.html @@ -1,16 +1,23 @@ -{% set url_path = "/accounts/activate/" + activation_key %} -{% set title = _("Account activation") %} -{% set message = _("Thanks for registering! We're glad to have you. The last step is activating your account. Please activate your account in the next %(expiration_days)d days.", expiration_days=expiration_days) %} -{% set username = user.get_username() %} -{% set button_text = _("Activate") %} -{% set domain = site.domain %} -{% set protocol = "https" if request.is_secure() else "http" %} -{% include "general_email.html" %} -
-{{_("Alternatively, you can reply to this message to activate your account. Your reply must keep the following text intact for this to work:")}} +Thanks for registering on the {{ site.name }}! We're glad to have you. +

+The last step is activating your account. Please activate your {{ SITE_NAME }} account in the next {{ expiration_days }} days. +

+Please click on the following link to activate your account: +

+ http://{{ site.domain }}/accounts/activate/{{ activation_key }} +

+ +Alternatively, you can reply to this message to activate your account. +Your reply must keep the following text intact for this to work:
 {{ activation_key }}
 
-{{_("See you soon!")}} +{% if SITE_ADMIN_EMAIL %} +See you soon! +
+If you have problems activating your account, feel free to send us an email at {{ SITE_ADMIN_EMAIL }}. +{% else %} +See you soon! +{% endif %} diff --git a/templates/registration/activation_email_subject.txt b/templates/registration/activation_email_subject.txt index 5b3bd66..b0888c8 100644 --- a/templates/registration/activation_email_subject.txt +++ b/templates/registration/activation_email_subject.txt @@ -1 +1 @@ -{% trans %}Activate your {{ SITE_NAME }} account{% endtrans %} +Activate your {{ SITE_NAME }} account \ No newline at end of file diff --git a/templates/registration/login.html b/templates/registration/login.html index ce9040c..dafb087 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -1,51 +1,85 @@ {% extends "base.html" %} -{% block body %} -
-
- {% csrf_token %} - {% if form.errors %} -
-

{{ _('Invalid username/email or password.') }}

-
- {% endif %} - - - - - - - - - -
- {{ form.username }} -
- {{ form.password }} -
-
- - -
-
{{ _('Forgot your password?') }} +{% block media %} + +{% endblock %} + +{% block body %} +
+
+ {% csrf_token %} + {% if form.errors %} +
+

{{ _('Invalid username or password.') }}

+
+ {% endif %} + + + + + + + + + +
+ {{ form.username }} +
+ {{ form.password }} +
+
+ + +
+
{{ _('Forgot your password?') }} + + {% if form.has_google_auth or form.has_facebook_auth or form.has_github_auth %} +

{{ _('Or log in with...') }}

+ {% if form.has_google_auth %} + + {% endif %} + {% if form.has_facebook_auth %} + + {% endif %} + {% if form.has_github_auth %} + + {% endif %} + {% endif %} +
{% endblock %} diff --git a/templates/registration/logout.html b/templates/registration/logout.html index 76c6ec2..176a230 100644 --- a/templates/registration/logout.html +++ b/templates/registration/logout.html @@ -1,4 +1,4 @@ {% extends "base.html" %} {% block body %} -

{{ _('See you later!') }}

+

{{ _('See you later!') }}

{% endblock %} \ No newline at end of file diff --git a/templates/registration/password_change_done.html b/templates/registration/password_change_done.html index 3cd3e58..78a146c 100644 --- a/templates/registration/password_change_done.html +++ b/templates/registration/password_change_done.html @@ -1,4 +1,4 @@ {% extends "base.html" %} {% block body %} -

{{ _('Your password was sucessfully changed.') }}

+

{{ _('Your password was sucessfully changed.') }}

{% endblock %} \ No newline at end of file diff --git a/templates/registration/password_change_form.html b/templates/registration/password_change_form.html index 7d0642a..a8f56ae 100644 --- a/templates/registration/password_change_form.html +++ b/templates/registration/password_change_form.html @@ -1,10 +1,10 @@ {% extends "base.html" %} {% block body %} -
- {% csrf_token %} - {{ form.as_table() }}
-
- -
+
+ {% csrf_token %} + {{ form.as_table() }}
+
+ +
{% endblock %} diff --git a/templates/registration/password_reset.html b/templates/registration/password_reset.html index 0685935..4f5d738 100644 --- a/templates/registration/password_reset.html +++ b/templates/registration/password_reset.html @@ -1,9 +1,9 @@ {% extends "base.html" %} {% block body %} -
{% csrf_token %} - {{ form.as_table() }}
-
- -
+
{% csrf_token %} + {{ form.as_table() }}
+
+ +
{% endblock %} \ No newline at end of file diff --git a/templates/registration/password_reset_complete.html b/templates/registration/password_reset_complete.html index a456473..5727282 100644 --- a/templates/registration/password_reset_complete.html +++ b/templates/registration/password_reset_complete.html @@ -1,5 +1,5 @@ {% extends "base.html" %} {% block body %} -

{{ _('Your password has been set. You may go ahead and log in now') }}

- {{ _('Log in') }} +

{{ _('Your password has been set. You may go ahead and log in now') }}

+ {{ _('Log in') }} {% endblock %} \ No newline at end of file diff --git a/templates/registration/password_reset_confirm.html b/templates/registration/password_reset_confirm.html index d2d0d78..6f65215 100644 --- a/templates/registration/password_reset_confirm.html +++ b/templates/registration/password_reset_confirm.html @@ -1,14 +1,14 @@ {% extends "base.html" %} {% block body %} - {% if validlink %} -
- {% csrf_token %} - {{ form.as_table() }}
-
- -
- {% else %} -

Invalid password reset link.

- {% endif %} + {% if validlink %} +
+ {% csrf_token %} + {{ form.as_table() }}
+
+ +
+ {% else %} +

Invalid password reset link.

+ {% endif %} {% endblock %} diff --git a/templates/registration/password_reset_done.html b/templates/registration/password_reset_done.html index 5255864..8e415d1 100644 --- a/templates/registration/password_reset_done.html +++ b/templates/registration/password_reset_done.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% block body %} -

{{ _("We've emailed you instructions for setting your password. You should be receiving them shortly.") }}

-

{{ _("If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder.") }}

+

{{ _("We've emailed you instructions for setting your password. You should be receiving them shortly.") }}

+

{{ _("If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder.") }}

{% endblock %} \ No newline at end of file diff --git a/templates/registration/password_reset_email.html b/templates/registration/password_reset_email.html index 0b30eb3..8aeb454 100644 --- a/templates/registration/password_reset_email.html +++ b/templates/registration/password_reset_email.html @@ -1,6 +1,19 @@ -{% set url_path = url('password_reset_confirm', uidb64=uid, token=token) %} -{% set title = _("Password Reset") %} -{% set message = _("We have received a request to reset your password. Click the button below to reset your password:") %} -{% set username = user.get_username() %} -{% set button_text = _("Reset Password") %} -{% include "general_email.html" %} \ No newline at end of file + +
+

DM::OJ +

+
+
+
+ +Forgot your password on the {{ site_name }}? Don't worry!

+To reset the password for your account "{{ user.get_username() }}", click the below button. +

+Reset password +

+{% if SITE_ADMIN_EMAIL %} +See you soon! If you have problems resetting your email, feel free to shoot us an email at {{ SITE_ADMIN_EMAIL }} +{% else %} +See you soon! +{% endif %} +
diff --git a/templates/registration/profile_creation.html b/templates/registration/profile_creation.html index 60455b2..7641b65 100644 --- a/templates/registration/profile_creation.html +++ b/templates/registration/profile_creation.html @@ -1,60 +1,58 @@ {% extends "base.html" %} {% block media %} - - {{ form.media.css }} - + {% if TIMEZONE_BG %} + .map-wrap { + background: {{TIMEZONE_BG}} + } + {% endif %} + {% endblock %} {% block js_media %}{{ form.media.js }}{% endblock %} {% block body %} -
- {% csrf_token %} - {{ form.as_table() }}
- + {% csrf_token %} + {{ form.as_table() }}
+
-
-
-
-
-
+
+
+
+
+
-
{% endblock %} {% block bodyend %} - - + + {% endblock %} \ No newline at end of file diff --git a/templates/registration/registration_closed.html b/templates/registration/registration_closed.html index fcd1777..7c732b0 100644 --- a/templates/registration/registration_closed.html +++ b/templates/registration/registration_closed.html @@ -1,4 +1,4 @@ {% extends "base.html" %} {% block body %} -

{{ _('Registration is currently closed. Please contact an administrator.') }}

+

{{ _('Registration is currently closed. Please contact an administrator.') }}

{% endblock %} diff --git a/templates/registration/registration_complete.html b/templates/registration/registration_complete.html index b91a32d..25903d7 100644 --- a/templates/registration/registration_complete.html +++ b/templates/registration/registration_complete.html @@ -1,4 +1,4 @@ {% extends "base.html" %} {% block body %} -

{{ _('You have successfully been registered. An email has been sent to the email address you provided to confirm your registration. If you don\'t see it, kindly check your spam folder as well.') }}

+

{{ _('You have successfully been registered. An email has been sent to the email address you provided to confirm your registration.') }}

{% endblock %} \ No newline at end of file diff --git a/templates/registration/registration_form.html b/templates/registration/registration_form.html index cf5bc30..528145b 100644 --- a/templates/registration/registration_form.html +++ b/templates/registration/registration_form.html @@ -1,99 +1,223 @@ {% extends "base.html" %} {% block media %} - {% include "timezone/media-css.html" %} - {{ form.media.css }} - + {% include "timezone/media-css.html" %} + {{ form.media.css }} + {% endblock %} {% block js_media %} - {% include "timezone/media-js.html" %} - {{ form.media.js }} - - {% if form.captcha %} - {{ recaptcha_init(LANGUAGE_CODE) }} - {% endif %} + {% include "timezone/media-js.html" %} + {{ form.media.js }} + + {% if form.captcha %} + {{ recaptcha_init(LANGUAGE_CODE) }} + {% endif %} {% endblock %} {% block body %} -
-
- {% csrf_token %} +
+ + {% csrf_token %} -
{{ _('Username') }}
- {{ form.username }} - {% if form.username.errors %} -
{{ form.username.errors }}
- {% endif %} +
{{ _('Username') }}
+ {{ form.username }} + {% if form.username.errors %} +
{{ form.username.errors }}
+ {% endif %} -
{{ _('Email') }}
- {{ form.email }} - {% if form.email.errors %} -
{{ form.email.errors }}
- {% endif %} +
{{ _('Email') }}
+ {{ form.email }} + {% if form.email.errors %} +
{{ form.email.errors }}
+ {% endif %} -
{{ _('Password') -}} - (?) -
- - {{ form.password1 }} - {% if form.password1.errors %} -
{{ form.password1.errors }}
- {% endif %} -
{{ _('Password') }}2{# -#} - {{ _('(again, for confirmation)') }} -
- {{ form.password2 }} - {% if form.password2.errors %} -
{{ form.password2.errors }}
- {% endif %} +
{{ _('Password') -}} + (?) +
+ + {{ form.password1 }} + {% if form.password1.errors %} +
{{ form.password1.errors }}
+ {% endif %} +
{{ _('Password') }}2{# -#} + {{ _('(again, for confirmation)') }} +
+ {{ form.password2 }} + {% if form.password2.errors %} +
{{ form.password2.errors }}
+ {% endif %} -
{{ _('Timezone') }}{{ _('(select your closest major city)') }}
-
-
{{ form.timezone }} -
- {{ _('or') }} - {{ _('pick from map') }} -
-
-
+
{{ _('Timezone') }}{{ _('(select your closest major city)') }}
+
+
{{ form.timezone }} +
+ {{ _('or') }} + {{ _('pick from map') }} +
+
+
-
{{ _('Default language') }}
- {{ form.language }} +
{{ _('Default language') }}
+ {{ form.language }} - {% if form.captcha %} -
{{ form.captcha }}
- {% if form.captcha.errors %} -
{{ form.captcha.errors }}
- {% endif %} - {% endif %} +
{{ _('Affiliated organizations') }}
+ {{ form.organizations }} + {% if form.organizations.errors %} +
{{ form.organizations.errors }}
+ {% endif %} -
- - -
+ {% if form.newsletter %} +
{{ form.newsletter }} + +
+ {% endif %} -
-
-
-
+ {% if form.captcha %} +
{{ form.captcha }}
+ {% if form.captcha.errors %} +
{{ form.captcha.errors }}
+ {% endif %} + {% endif %} + +
+ {% if tos_url %} + + {{ _('By registering, you agree to our') }} + {{ _('Terms & Conditions') }}. + + {% endif %} + + +
+ +
+
+
+
+
-
{% endblock %} diff --git a/templates/registration/totp_auth.html b/templates/registration/totp_auth.html index 7fc596b..2c6ac4e 100644 --- a/templates/registration/totp_auth.html +++ b/templates/registration/totp_auth.html @@ -1,43 +1,43 @@ {% extends "base.html" %} {% block media %} - + .totp-panel-message { + width: 300px; + } + {% endblock %} {% block body %} -
-
- {% csrf_token %} - {% if form.errors %} -
-

{{ _('Invalid Two Factor Authentication token.') }}

-
- {% endif %} +
+ + {% csrf_token %} + {% if form.errors %} +
+

{{ _('Invalid Two Factor Authentication token.') }}

+
+ {% endif %} -
-
{{ form.totp_token }}
-
- - -

{{ _('If you lost your authentication device, please contact us at %(email)s.', email=SITE_ADMIN_EMAIL)|urlize }}

-
+
+
{{ form.totp_token }}
+
+ + +

{{ _('If you lost your authentication device, please contact us at %(email)s.', email=SITE_ADMIN_EMAIL)|urlize }}

+
{% endblock %} diff --git a/templates/registration/totp_disable.html b/templates/registration/totp_disable.html index 4aab628..9919bb6 100644 --- a/templates/registration/totp_disable.html +++ b/templates/registration/totp_disable.html @@ -1,51 +1,51 @@ {% extends "base.html" %} {% block media %} - + #totp-disable-form { + border: unset; + background: unset; + max-width: 700px; + } + {% endblock %} {% block body %} -
-
- {% csrf_token %} -
{{ _('To protect your account, you must first authenticate before you can disable Two Factor Authentication.') }}
- {% if form.totp_token.errors %} -
- {{ form.totp_token.errors }} -
- {% endif %} -
- - {{ form.totp_token }} -
- -
-
+
+
+ {% csrf_token %} +
{{ _('To protect your account, you must first authenticate before you can disable Two Factor Authentication.') }}
+ {% if form.totp_token.errors %} +
+ {{ form.totp_token.errors }} +
+ {% endif %} +
+ + {{ form.totp_token }} +
+ +
+
{% endblock %} diff --git a/templates/registration/totp_enable.html b/templates/registration/totp_enable.html index 4ade865..c291353 100644 --- a/templates/registration/totp_enable.html +++ b/templates/registration/totp_enable.html @@ -1,89 +1,89 @@ {% extends "base.html" %} {% block media %} - + .totp-qr-code img, .totp-qr-code canvas { + width: 400px; + max-width: 100%; + margin: 0 auto; + display: block; + -ms-interpolation-mode: nearest-neighbor; + image-rendering: -webkit-optimize-contrast; + image-rendering: -webkit-crisp-edges; + image-rendering: -o-crisp-edges; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + } + {% endblock %} {% block js_media %} - + {% endblock %} {% block body %} -
-
- {% csrf_token %} +
+ + {% csrf_token %} -
{{ _('Scan this code with your authenticator app:') }}
-
{{ _('QR code') }}
-
{{ _('Or enter this code manually:') }} - {{ totp_key }} -
-
- {% if form.totp_token.errors %} -
- {{ form.totp_token.errors }} -
- {% endif %} -
- - {{ form.totp_token }} -
- - -
+
{{ _('Scan this code with your authenticator app:') }}
+
{{ _('QR code') }}
+
{{ _('Or enter this code manually:') }} + {{ totp_key }} +
+
+ {% if form.totp_token.errors %} +
+ {{ form.totp_token.errors }} +
+ {% endif %} +
+ + {{ form.totp_token }} +
+ + +
{% endblock %} diff --git a/templates/registration/username_select.html b/templates/registration/username_select.html index 8eb66e4..c3df4d4 100644 --- a/templates/registration/username_select.html +++ b/templates/registration/username_select.html @@ -1,9 +1,9 @@ {% extends "base.html" %} {% block body %} -
- {% csrf_token %} - {{ form.as_table() }}
- -
+
+ {% csrf_token %} + {{ form.as_table() }}
+ +
{% endblock %} diff --git a/templates/resolver/media-js.html b/templates/resolver/media-js.html deleted file mode 100644 index 07e40e2..0000000 --- a/templates/resolver/media-js.html +++ /dev/null @@ -1,427 +0,0 @@ -{% compress js %} - -{% endcompress %} \ No newline at end of file diff --git a/templates/resolver/resolver.html b/templates/resolver/resolver.html deleted file mode 100644 index b0008d8..0000000 --- a/templates/resolver/resolver.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - Contest Resolver - - - - - - - - - - - - - - -
-
- -
-
Rank
-
-
Name
-
-
Score
-
- -
-
- -
-
X
-
Y
-
Z
-
- {% include "resolver/media-js.html" %} - - - \ No newline at end of file diff --git a/templates/runtime-version-fragment.html b/templates/runtime-version-fragment.html index 321ecee..0457b83 100644 --- a/templates/runtime-version-fragment.html +++ b/templates/runtime-version-fragment.html @@ -1,12 +1,12 @@ {% for name, versions in runtime_versions -%} - {%- if versions -%} - {%- if versions[0] != versions|last -%} - {{ name }} {{ versions[0] }} - {{ versions|last }} + {%- if versions -%} + {%- if versions[0] != versions|last -%} + {{ name }} {{ versions[0] }} - {{ versions|last }} + {%- else -%} + {{ name }} {{ versions[0] }} + {%- endif -%} {%- else -%} - {{ name }} {{ versions[0] }} + {{ name }} {%- endif -%} - {%- else -%} - {{ name }} - {%- endif -%} - {%- if not loop.last %}, {% endif -%} + {%- if not loop.last %}, {% endif -%} {%- endfor -%} diff --git a/templates/site-logo-fragment.html b/templates/site-logo-fragment.html index 7b47f7b..9a170ea 100644 --- a/templates/site-logo-fragment.html +++ b/templates/site-logo-fragment.html @@ -1,12 +1,8 @@ {% if request.in_contest_mode and request.participation.contest.logo_override_image %} - {{ SITE_NAME }} -{% elif request.organization and request.organization.organization_image %} - {{ SITE_NAME }} -{% elif organization_image is defined and organization_image %} - {{ SITE_NAME }} + {{ SITE_NAME }} {% elif logo_override_image is defined and logo_override_image %} - {{ SITE_NAME }} + {{ SITE_NAME }} {% else %} - {{ SITE_NAME }} + {{ SITE_NAME }} {% endif %} diff --git a/templates/solution-preview.html b/templates/solution-preview.html index 8dc6f6e..90004b6 100644 --- a/templates/solution-preview.html +++ b/templates/solution-preview.html @@ -1 +1,4 @@ -{{ preview_data|markdown|reference|str|safe }} +{{ preview_data|markdown('solution', MATH_ENGINE)|reference|str|safe }} +{% if REQUIRE_JAX %} +
+{% endif %} \ No newline at end of file diff --git a/templates/stats/base.html b/templates/stats/base.html index 406b6c3..d83ac96 100644 --- a/templates/stats/base.html +++ b/templates/stats/base.html @@ -1,15 +1,18 @@ {% extends "base.html" %} {% block js_media %} - {% compress js %} - {% include "stats/media-js.html" %} - {% endcompress %} + {% compress js %} + {% include "stats/media-js.html" %} + {% endcompress %} {% endblock %} -{% block content_title %}{% endblock %} -{% block title_ruler %}{% endblock %} +{% block content_title %}{{ _('Statistics') }}{% endblock %} {% block body %} - {% include "stats/tab.html" %} - {% block chart_body %}{% endblock %} + + {% block chart_body %}{% endblock %} {% endblock %} diff --git a/templates/stats/language.html b/templates/stats/language.html index aaa9e9d..d142c62 100644 --- a/templates/stats/language.html +++ b/templates/stats/language.html @@ -1,45 +1,55 @@ {% extends "stats/base.html" %} {% block media %} - + {% endblock %} {% block chart_body %} -

{{ _('Submission Statistics') }}

-
- -
+

{{ _('Submission Statistics') }}

+
+ +
-

{{ _('Submissions by Language') }}

-
- -
+

{{ _('Submissions by Language') }}

+
+ +
-

{{ _('AC Submissions by Language') }}

-
- -
+

{{ _('AC Submissions by Language') }}

+
+ +
-

{{ _('Language AC Rate') }}

-
- +

{{ _('Language AC Rate') }}

+
+
{% endblock %} {% block bodyend %} - + {% endblock %} diff --git a/templates/stats/media-js.html b/templates/stats/media-js.html index cba5683..69f9ad8 100644 --- a/templates/stats/media-js.html +++ b/templates/stats/media-js.html @@ -1,110 +1,90 @@ diff --git a/templates/stats/site.html b/templates/stats/site.html deleted file mode 100644 index e622b9d..0000000 --- a/templates/stats/site.html +++ /dev/null @@ -1,65 +0,0 @@ -{% extends "stats/base.html" %} -{% block media %} - -{% endblock %} - -{% block chart_body %} -
-
- -
-

{{ _('Submissions') }}

-
-
-
- -
-

{{ _('Contests') }}

-
-
-
- -
-

{{ _('New users') }}

-
-
-
- -
-

{{ _('Groups') }}

-
-
-
- -
-

{{ _('Comments') }}

-
-
-
- -
-

{{ _('Chat messages') }}

-
-{% endblock %} - -{% block bodyend %} - -{% endblock %} diff --git a/templates/stats/tab.html b/templates/stats/tab.html deleted file mode 100644 index 5dfef09..0000000 --- a/templates/stats/tab.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "tabs-base.html" %} - -{% block tabs %} - {{ make_tab('language', 'fa-list', url('language_stats'), _('Language')) }} - {{ make_tab('site', 'fa-list', url('site_stats'), _('Site')) }} -{% endblock %} \ No newline at end of file diff --git a/templates/status/judge-status-table.html b/templates/status/judge-status-table.html index 7a86514..5968819 100644 --- a/templates/status/judge-status-table.html +++ b/templates/status/judge-status-table.html @@ -1,67 +1,67 @@ - {{ _('Judge') }} - {% if see_all_judges %} - {{ _('Online') }} - {% endif %} - {{ _('Uptime') }} - {{ _('Ping') }} - {{ _('Load') }} - {{ _('Runtimes') }} + {{ _('Judge') }} + {% if see_all_judges %} + {{ _('Online') }} + {% endif %} + {{ _('Uptime') }} + {{ _('Ping') }} + {{ _('Load') }} + {{ _('Runtimes') }} {% for judge in judges %} - - - {% if perms.judge.change_judge %} - {{ judge.name }} - {% else %} - {{ judge.name }} - {% endif %} - - {% if see_all_judges %} - - {% if judge.online %} - - {% else %} - + + + {% if perms.judge.change_judge %} + {{ judge.name }} + {% else %} + {{ judge.name }} + {% endif %} + + {% if see_all_judges %} + + {% if judge.online %} + + {% else %} + + {% endif %} + {% endif %} - - {% endif %} - - {% if judge.online %} - {{ judge.uptime|timedelta("simple") }} - {% else %} - {{ _('N/A') }} - {% endif %} - - - {% if judge.online and judge.ping_ms %} - {{ judge.ping_ms|floatformat(3) }} ms - {% else %} - {{ _('N/A') }} - {% endif %} - - - {% if judge.online %} - {{ judge.load|floatformat(3) }} - {% else %} - {{ _('N/A') }} - {% endif %} - - - {% if judge.online %} - {% for key, info in judge.runtime_versions -%} - {{ info.name }} - {%- if not loop.last %}, {% endif %} - {%- endfor %} - {% else %}{{ _('N/A') }}{% endif %} - - + + {% if judge.online %} + {{ judge.uptime|timedelta("simple") }} + {% else %} + {{ _('N/A') }} + {% endif %} + + + {% if judge.online and judge.ping_ms %} + {{ judge.ping_ms|floatformat(3) }} ms + {% else %} + {{ _('N/A') }} + {% endif %} + + + {% if judge.online %} + {{ judge.load|floatformat(3) }} + {% else %} + {{ _('N/A') }} + {% endif %} + + + {% if judge.online %} + {% for key, info in judge.runtime_versions -%} + {{ info.name }} + {%- if not loop.last %}, {% endif %} + {%- endfor %} + {% else %}{{ _('N/A') }}{% endif %} + + {% else %} - - {{ _('There are no judges available at this time.') }} - - + + {{ _('There are no judges available at this time.') }} + + {% endfor %} diff --git a/templates/status/judge-status.html b/templates/status/judge-status.html index 4e8f87f..dac5cb1 100644 --- a/templates/status/judge-status.html +++ b/templates/status/judge-status.html @@ -1,23 +1,23 @@ {% extends "base.html" %} {% block js_media %} - {% include "status/media-js.html" %} + {% include "status/media-js.html" %} {% endblock %} {% block media %} - {% include "status/media-css.html" %} - {% block content_media %}{% endblock %} + {% include "status/media-css.html" %} + {% block content_media %}{% endblock %} {% endblock %} {% block title_ruler %}{% endblock %} {% block title_row %} - {% set tab = 'judges' %} - {% include "status/status-tabs.html" %} + {% set tab = 'judges' %} + {% include "status/status-tabs.html" %} {% endblock %} {% block body %} - - {% include "status/judge-status-table.html" %} -
+ + {% include "status/judge-status-table.html" %} +
{% endblock %} \ No newline at end of file diff --git a/templates/status/language-list.html b/templates/status/language-list.html index bb08829..298633b 100644 --- a/templates/status/language-list.html +++ b/templates/status/language-list.html @@ -1,58 +1,60 @@ {% extends "base.html" %} {% block media %} - + .table td { + text-align: left; + } + {% endblock %} {% block title_ruler %}{% endblock %} {% block title_row %} - {% set tab = 'runtimes' %} - {% include "status/status-tabs.html" %} + {% set tab = 'runtimes' %} + {% include "status/status-tabs.html" %} {% endblock %} {% block body %} - - - - - - - - - - {% for language in languages %} - {# All online languages have runtime_versions, even if we're not going to display them #} - {% if language.runtime_versions %} - - - - - - {% endif %} - {% endfor %} - -
{{ _('ID') }}{{ _('Name') }}{{ _('Runtime Info') }}
{{ language.short_display_name }}{{ language.name }} - {{ runtime_versions(language.runtime_versions()) }} - {% if language.description %} -
- {{ language.description|markdown|safe }} -
- {% endif %} -
+ + + + + + + + + + {% for language in languages %} + {# All online languages have runtime_versions, even if we're not going to display them #} + {% if language.runtime_versions %} + + + + + + {% endif %} + {% endfor %} + +
{{ _('ID') }}{{ _('Name') }}{{ _('Runtime Info') }}
{{ language.short_display_name }}{{ language.name }} + {{ runtime_versions(language.runtime_versions()) }} + {% if language.description %} +
+ {% cache 86400 'language_html' language.id %} + {{ language.description|markdown('language') }} + {% endcache %} +
+ {% endif %} +
{% endblock %} \ No newline at end of file diff --git a/templates/status/media-css.html b/templates/status/media-css.html index a60fb41..49658bf 100644 --- a/templates/status/media-css.html +++ b/templates/status/media-css.html @@ -1,14 +1,14 @@ \ No newline at end of file diff --git a/templates/status/media-js.html b/templates/status/media-js.html index 6d3df3c..a5b1cb1 100644 --- a/templates/status/media-js.html +++ b/templates/status/media-js.html @@ -1,62 +1,62 @@ \ No newline at end of file diff --git a/templates/status/status-tabs.html b/templates/status/status-tabs.html index 6381810..1dd737e 100644 --- a/templates/status/status-tabs.html +++ b/templates/status/status-tabs.html @@ -1,7 +1,7 @@ {% extends "tabs-base.html" %} {% block tabs %} - {{ make_tab('judges', 'fa-server', url('status_all'), _('Judges')) }} - {{ make_tab('runtimes', 'fa-code', url('runtime_list'), _('Runtimes')) }} - {{ make_tab('matrix', 'fa-table', url('version_matrix'), _('Version Matrix')) }} + {{ make_tab('judges', 'fa-server', url('status_all'), _('Judges')) }} + {{ make_tab('runtimes', 'fa-code', url('runtime_list'), _('Runtimes')) }} + {{ make_tab('matrix', 'fa-table', url('version_matrix'), _('Version Matrix')) }} {% endblock %} diff --git a/templates/status/versions.html b/templates/status/versions.html index 1f56e03..1c0a991 100644 --- a/templates/status/versions.html +++ b/templates/status/versions.html @@ -3,40 +3,40 @@ {% block title_ruler %}{% endblock %} {% block title_row %} - {% set tab = 'matrix' %} - {% include "status/status-tabs.html" %} + {% set tab = 'matrix' %} + {% include "status/status-tabs.html" %} {% endblock %} {% block body %} - - - - {% for judge in judges %} - - {% endfor %} - - {% for language in languages %} - - - {% for judge in judges %} - {% set versions = matrix[judge][language.id] %} - +
{{ judge }}
{{ language.name }} - {%- for version in versions -%} - {{ version.name }}{% if version.version %} {{ version.version }}{% endif %} - {% if not loop.last %}
{% endif %} - {%- else -%} - — - {%- endfor -%} -
+ + + {% for judge in judges %} + + {% endfor %} + + {% for language in languages %} + + + {% for judge in judges %} + {% set versions = matrix[judge][language.id] %} + + {% endfor %} + {% endfor %} - - {% endfor %} -
{{ judge }}
{{ language.name }} + {%- for version in versions -%} + {{ version.name }}{% if version.version %} {{ version.version }}{% endif %} + {% if not loop.last %}
{% endif %} + {%- else -%} + — + {%- endfor -%} +
+ {% endblock %} diff --git a/templates/submission/info-base.html b/templates/submission/info-base.html index d6c8961..0511994 100644 --- a/templates/submission/info-base.html +++ b/templates/submission/info-base.html @@ -1,16 +1,16 @@ {% extends "common-content.html" %} {% block header %} - - {{ submission.date|date(_("N j, Y, g:i a")) }} - {%- if perms.judge.change_submission and submission.judged_on %} - on {{ submission.judged_on.name }} - {% endif %} -
- {{ submission.language }} - {% if perms.judge.change_submission %} - [{{ _('Admin') }}] - {% endif %} + + {{ submission.date|date(_("N j, Y, g:i a")) }} + {%- if perms.judge.change_submission and submission.judged_on %} + on {{ submission.judged_on.name }} + {% endif %} +
+ {{ submission.language }} + {% if perms.judge.change_submission %} + [{{ _('Admin') }}] + {% endif %} +
-
{% endblock %} diff --git a/templates/submission/internal-error-message.html b/templates/submission/internal-error-message.html index f47c50a..2b2e5b0 100644 --- a/templates/submission/internal-error-message.html +++ b/templates/submission/internal-error-message.html @@ -1,18 +1,18 @@ -

- {% if request.user == submission.user.user %} - {% trans trimmed %} - An internal error occurred while grading, and the {{ SITE_NAME }} administrators have been notified.
- In the meantime, try resubmitting in a few seconds. - {% endtrans %} - {% else %} - {{ _('An internal error occurred while grading.') }} - {% endif %} +

+ {% if request.user == submission.user.user %} + {% trans trimmed %} + An internal error occurred while grading, and the {{ SITE_NAME }} administrators have been notified.
+ In the meantime, try resubmitting in a few seconds. + {% endtrans %} + {% else %} + {{ _('An internal error occurred while grading.') }} + {% endif %}

{% if submission.error and request.user.is_authenticated %} - {% if request.profile.id in submission.problem.editor_ids or perms.judge.edit_all_problem %} -

-

{{ _('Error information') }}

- {{ submission.error|highlight('pytb', linenos=True) }} - {% endif %} + {% if request.profile.id in submission.problem.editor_ids or perms.judge.edit_all_problem %} +

+

{{ _('Error information') }}

+
{{ submission.error|highlight('pytb') }}
+ {% endif %} {% endif %} \ No newline at end of file diff --git a/templates/submission/list.html b/templates/submission/list.html index e36576d..e2afa8d 100644 --- a/templates/submission/list.html +++ b/templates/submission/list.html @@ -1,397 +1,377 @@ -{% extends "three-column-content.html" %} - -{% set has_hidden_subtasks = request.in_contest_mode and request.participation.contest.format.has_hidden_subtasks %} - +{% extends "common-content.html" %} {% block js_media %} - - - {% compress js %} - - {% if request.user.is_authenticated and perms.judge.rejudge_submission %} - - {% endif %} - - - {% endcompress %} - {% if dynamic_update and last_msg and not has_hidden_subtasks %} - + {% if request.user.is_authenticated and perms.judge.rejudge_submission %} + + {% endif %} - var table = $('#submissions-table'); - var statistics = $("#statistics-table"); - var doing_ajax = false; - var first = parseInt(table.find('>div:first-child').attr('id')); - - var update_submission = function (message, force) { - if (language_filter.length && 'language' in message && - language_filter.indexOf(message.language) == -1) - return; - if (status_filter.length && 'status' in message && - status_filter.indexOf(message.status) == -1) - return; - var id = message.id; - var row = table.find('div#' + id); - if (row.length < 1) { - if (id < first) - return; - first = id; - row = $('
', {id: id, 'class': 'submission-row'}).hide().prependTo(table); - if (table.find('>div').length >= {{ paginator.per_page }}) - table.find('>div:last-child').hide('slow', function () { - $(this).remove(); - }); - } - if (force || !doing_ajax) { - if (!force) doing_ajax = true; - $.ajax({ - url: '{{ url('submission_single_query') }}', - data: {id: id, show_problem: show_problem} - }).done(function (data) { - var was_shown = row.is(':visible'); - row.html(data); - register_time(row.find('.time-with-rel')); - if (!was_shown) { - row.slideDown('slow'); + + - {% endif %} + + {% endcompress %} + + {% if dynamic_update and last_msg %} + + {% endif %} {% endblock %} {% block title_ruler %}{% endblock %} -{% block title_row %}{% endblock %} +{% block title_row %} + {% include "submission/submission-list-tabs.html" %} +{% endblock %} + +{% block media %} + {% if perms.judge.change_submission and perms.judge.rejudge_submission %} + + {% endif %} -{% block three_col_media %} - {% if perms.judge.change_submission and perms.judge.rejudge_submission %} - {% endif %} - - {% endblock %} -{% block middle_title %} -
-
-

{{content_title}}

-
-
-{% endblock %} - -{% block middle_content %} -
- - -
- {% set profile_id = request.profile.id if request.user.is_authenticated else 0 %} - {% for submission in submissions %} -
- {% with problem_name=show_problem and submission.problem.i18n_name %} - {% include "submission/row.html" %} - {% endwith %} -
- {% endfor %} -
+{% block body %} {% if page_obj.num_pages > 1 %} -
{% include "list-pages.html" %}
+
+ {% include "list-pages.html" %} +
{% endif %} -
-{% endblock %} -{% block right_sidebar %} -