From 783c5a5942fbb2a5377b33dffeba6824052515f8 Mon Sep 17 00:00:00 2001 From: Alice Snow Date: Wed, 25 Feb 2026 16:58:57 +0100 Subject: [PATCH 01/99] update core doc detail & architecture --- README.md | 104 +++++--------- docs/{ => assets}/dashboard-v0.5.png | Bin docs/{ => assets}/media-item-v0.5.png | Bin docs/{ => assets}/media-v0.5.png | Bin docs/{ => assets}/source-v0.5.png | Bin docs/{ => assets}/sources-v0.5.png | Bin docs/dev.md | 198 ++++++++++++++++++++++++++ docs/environment-variables.md | 73 ++++++++++ 8 files changed, 306 insertions(+), 69 deletions(-) rename docs/{ => assets}/dashboard-v0.5.png (100%) rename docs/{ => assets}/media-item-v0.5.png (100%) rename docs/{ => assets}/media-v0.5.png (100%) rename docs/{ => assets}/source-v0.5.png (100%) rename docs/{ => assets}/sources-v0.5.png (100%) create mode 100644 docs/dev.md create mode 100644 docs/environment-variables.md diff --git a/README.md b/README.md index 65f051ea6..09ec1850d 100644 --- a/README.md +++ b/README.md @@ -25,25 +25,30 @@ ghcr.io/meeb/tubesync:latest # Screenshots +
+Click to expand screenshots + ### Dashboard -![TubeSync Dashboard](https://github.com/meeb/tubesync/blob/main/docs/dashboard-v0.5.png?raw=true) +![TubeSync Dashboard](docs/assets/dashboard-v0.5.png) ### Sources overview -![TubeSync sources overview](https://github.com/meeb/tubesync/blob/main/docs/sources-v0.5.png?raw=true) +![TubeSync sources overview](docs/assets/sources-v0.5.png) ### Source details -![TubeSync source details](https://github.com/meeb/tubesync/blob/main/docs/source-v0.5.png?raw=true) +![TubeSync source details](docs/assets/source-v0.5.png) ### Media overview -![TubeSync media overview](https://github.com/meeb/tubesync/blob/main/docs/media-v0.5.png?raw=true) +![TubeSync media overview](docs/assets/media-v0.5.png) ### Media details -![TubeSync media-details](https://github.com/meeb/tubesync/blob/main/docs/media-item-v0.5.png?raw=true) +![TubeSync media-details](docs/assets/media-item-v0.5.png) + +
# Requirements @@ -146,36 +151,9 @@ services: ## Optional authentication -Available in `v1.0` (or `:latest`) and later. If you want to enable a basic username and -password to be required to access the TubeSync dashboard you can set them with the -following environment variables: - -```bash -HTTP_USER -HTTP_PASS -``` - -For example, in the `docker run ...` line add in: - -```bash -... --e HTTP_USER=some-username \ --e HTTP_PASS=some-secure-password \ -... -``` - -Or in your Docker Compose file you would add in: - -```yaml -... - environment: - - HTTP_USER=some-username - - HTTP_PASS=some-secure-password -... -``` - -When BOTH `HTTP_USER` and `HTTP_PASS` are set then basic HTTP authentication will be -enabled. +You can enable basic HTTP authentication by setting the `HTTP_USER` and `HTTP_PASS` +environment variables. See the [environment variables reference](docs/environment-variables.md#authentication) +for details. # Updating @@ -253,19 +231,27 @@ docker logs -t tubesync > TubeSync.logs.txt 2>&1 ``` -# Advanced usage guides +# Documentation + +### Getting started + + * [Development guide](docs/dev.md) - Local setup, running tests, contributing + * [Environment variables](docs/environment-variables.md) - Full configuration reference + +### Usage guides + + * [Using Plex](docs/plex-notes.md) - Plex integration with YouTube-Agent + * [Using cookies](docs/using-cookies.md) - Authenticate with YouTube via cookies + * [Writing custom filters](docs/custom-filters.md) - Filter media with custom Python code + * [YouTube Proof-of-Origin Tokens](docs/youtube-pot.md) - PoT setup for rate-limiting -Once you're happy using TubeSync there are some advanced usage guides for more complex -and less common features: +### Administration - * [Using Plex](https://github.com/meeb/tubesync/blob/main/docs/plex-notes.md) - * [Import existing media into TubeSync](https://github.com/meeb/tubesync/blob/main/docs/import-existing-media.md) - * [Sync or create missing metadata files](https://github.com/meeb/tubesync/blob/main/docs/create-missing-metadata.md) - * [Reset tasks from the command line](https://github.com/meeb/tubesync/blob/main/docs/reset-tasks.md) - * [Using PostgreSQL, MySQL or MariaDB as database backends](https://github.com/meeb/tubesync/blob/main/docs/other-database-backends.md) - * [YouTube Proof-of-Origin Tokens](https://github.com/meeb/tubesync/blob/main/docs/youtube-pot.md) - * [Using cookies](https://github.com/meeb/tubesync/blob/main/docs/using-cookies.md) - * [Reset metadata](https://github.com/meeb/tubesync/blob/main/docs/reset-metadata.md) + * [Import existing media](docs/import-existing-media.md) - Import already-downloaded files + * [Sync or create missing metadata](docs/create-missing-metadata.md) - Re-generate metadata files + * [Reset tasks](docs/reset-tasks.md) - Reset stuck or failed tasks from the CLI + * [Reset metadata](docs/reset-metadata.md) - Reset cached metadata + * [Using PostgreSQL, MySQL or MariaDB](docs/other-database-backends.md) - External database setup # Warnings @@ -384,29 +370,9 @@ Make sure that you have `mysql_config` or `mariadb_config` available, as require # Advanced configuration -There are a number of other environment variables you can set. These are, mostly, -**NOT** required to be set in the default container installation, they are really only -useful if you are manually installing TubeSync in some other environment. These are: - -| Name | What | Example | -| ---------------------------- | ------------------------------------------------------------- |-------------------------------------------------------------------------------| -| DJANGO_SECRET_KEY | Django's SECRET_KEY | YJySXnQLB7UVZw2dXKDWxI5lEZaImK6l | -| DJANGO_URL_PREFIX | Run TubeSync in a sub-URL on the web server | /somepath/ | -| TUBESYNC_DEBUG | Enable debugging | True | -| TUBESYNC_HOSTS | Django's ALLOWED_HOSTS, defaults to `*` | tubesync.example.com,otherhost.com | -| TUBESYNC_RESET_DOWNLOAD_DIR | Toggle resetting `/downloads` permissions, defaults to True | True | -| TUBESYNC_VIDEO_HEIGHT_CUTOFF | Smallest video height in pixels permitted to download | 240 | -| TUBESYNC_RENAME_SOURCES | Rename media files from selected sources | Source1_directory,Source2_directory | -| TUBESYNC_RENAME_ALL_SOURCES | Rename media files from all sources | True | -| TUBESYNC_DIRECTORY_PREFIX | Enable `video` and `audio` directory prefixes in `/downloads` | True | -| TUBESYNC_SHRINK_NEW | Filter unneeded information from newly retrieved metadata | True | -| TUBESYNC_SHRINK_OLD | Filter unneeded information from metadata loaded from the database | True | -| GUNICORN_WORKERS | Number of `gunicorn` (web request) workers to spawn | 3 | -| LISTEN_HOST | IP address for `gunicorn` to listen on | 127.0.0.1 | -| LISTEN_PORT | Port number for `gunicorn` to listen on | 8080 | -| HTTP_USER | Sets the username for HTTP basic authentication | some-username | -| HTTP_PASS | Sets the password for HTTP basic authentication | some-secure-password | -| DATABASE_CONNECTION | Optional external database connection details | postgresql://user:pass@host:port/database | +See the full [environment variables reference](docs/environment-variables.md) for all +available configuration options (database, web server, downloads, metadata, debugging, +etc.). # Manual, non-containerised, installation diff --git a/docs/dashboard-v0.5.png b/docs/assets/dashboard-v0.5.png similarity index 100% rename from docs/dashboard-v0.5.png rename to docs/assets/dashboard-v0.5.png diff --git a/docs/media-item-v0.5.png b/docs/assets/media-item-v0.5.png similarity index 100% rename from docs/media-item-v0.5.png rename to docs/assets/media-item-v0.5.png diff --git a/docs/media-v0.5.png b/docs/assets/media-v0.5.png similarity index 100% rename from docs/media-v0.5.png rename to docs/assets/media-v0.5.png diff --git a/docs/source-v0.5.png b/docs/assets/source-v0.5.png similarity index 100% rename from docs/source-v0.5.png rename to docs/assets/source-v0.5.png diff --git a/docs/sources-v0.5.png b/docs/assets/sources-v0.5.png similarity index 100% rename from docs/sources-v0.5.png rename to docs/assets/sources-v0.5.png diff --git a/docs/dev.md b/docs/dev.md new file mode 100644 index 000000000..33ebbd968 --- /dev/null +++ b/docs/dev.md @@ -0,0 +1,198 @@ +# Development Guide + +This guide covers everything you need to set up a local development environment, run +tests, and contribute to TubeSync. + +## Prerequisites + +- Python 3.10 or later (3.10, 3.11, 3.12, 3.13 are tested in CI) +- [Pipenv](https://pipenv.pypa.io/) for dependency management +- `ffmpeg` installed and available in your `$PATH` +- `mysql_config` or `mariadb_config` if you need the MySQL/MariaDB driver + (Debian/Ubuntu: `libmysqlclient-dev`) + +## Initial setup + +1. Clone the repository: + +```bash +git clone https://github.com/meeb/tubesync.git +cd tubesync +``` + +2. Install Python dependencies: + +```bash +pipenv install --dev +``` + +Or, if you prefer using `uv` + `pip` (same as CI): + +```bash +python -m pip install uv +pipenv lock +pipenv requirements | tee requirements.txt +uv pip install --system --strict --requirements requirements.txt +``` + +3. Copy the local settings file: + +```bash +cp tubesync/tubesync/local_settings.py.example tubesync/tubesync/local_settings.py +``` + +This configures a local SQLite database and sensible defaults for development. + +4. Run database migrations: + +```bash +cd tubesync +python manage.py migrate +``` + +5. Collect static files: + +```bash +python manage.py collectstatic --noinput +``` + +Or use the Makefile shortcut: + +```bash +make build +``` + +## Running the development server + +```bash +make dev +``` + +This starts Django's built-in `runserver` on `http://localhost:8000`. + +You can also run it directly: + +```bash +cd tubesync +python manage.py runserver +``` + +## Running tests + +Run the full test suite: + +```bash +make test +``` + +Or directly with Django: + +```bash +cd tubesync +python manage.py test --verbosity=2 +``` + +For more verbose output with debug logging: + +```bash +cd tubesync +TUBESYNC_DEBUG=True python manage.py test --no-input --buffer --verbosity=2 +``` + +The CI runs the test suite against Python 3.10, 3.11, 3.12, and 3.13. Make sure your +changes pass on at least Python 3.10 before submitting a PR. + +## Linting + +The CI uses [ruff](https://github.com/astral-sh/ruff) for linting. Run it locally: + +```bash +cd tubesync +uvx ruff check --target-version py310 +``` + +Current ignored rules: `E701`, `E722`, `E731`. + +## Project structure + +``` +tubesync/ +├── tubesync/ # Django project root +│ ├── tubesync/ # Django project config (settings, urls, wsgi) +│ ├── common/ # Shared app (base templates, utils, middleware, task queue) +│ └── sync/ # Main app (sources, media, downloads, media servers) +├── docs/ # Documentation +│ └── assets/ # Screenshots and images +├── patches/ # Patches applied to yt-dlp +├── config/ # yt-dlp configuration +├── Dockerfile # Multi-stage container build +├── Makefile # Dev shortcuts (build, test, dev, container) +├── Pipfile # Python dependencies +└── dev.env # Environment variables for local container testing +``` + +### Key files + +| File | Purpose | +|------|---------| +| `sync/models/` | Source, Media, Metadata, MediaServer models | +| `sync/views.py` | All web interface views | +| `sync/tasks.py` | Huey background tasks (indexing, downloading) | +| `sync/signals.py` | Django signals for task triggers | +| `sync/youtube.py` | yt-dlp integration layer | +| `sync/matching.py` | Media format selection logic | +| `sync/choices.py` | Enum choices (schedules, resolutions, codecs) | +| `common/huey.py` | Custom Huey task queue with SQLite storage | +| `common/utils.py` | Shared utilities (env parsing, file ops) | + +## Creating a superuser + +To access the Django admin at `/admin`: + +```bash +cd tubesync +python manage.py createsuperuser +``` + +## Running with Docker locally + +Build and run the container: + +```bash +make container +make runcontainer +``` + +This uses `dev.env` for environment variables and exposes the app on port `4848`. + +## Management commands + +TubeSync ships several management commands for common operations: + +| Command | Purpose | +|---------|---------| +| `import-existing-media` | Import already-downloaded media files | +| `list-sources` | List all configured sources | +| `delete-source` | Delete a source and its media | +| `reset-tasks` | Reset stuck or failed tasks | +| `reset-metadata` | Reset cached metadata | +| `sync-missing-metadata` | Re-fetch missing metadata | +| `create-tvshow-nfo` | Generate NFO files for media | +| `youtube-add-subscriptions` | Bulk-add YouTube subscriptions | + +Run any command with: + +```bash +cd tubesync +python manage.py --help +``` + +## Submitting a pull request + +1. Create a feature branch from `main` +2. Make your changes +3. Run `make test` and ensure all tests pass +4. Run `uvx ruff check --target-version py310` in `tubesync/` and fix any issues +5. Open a PR against `main` + +The CI will automatically run the test suite and a container build/analysis. diff --git a/docs/environment-variables.md b/docs/environment-variables.md new file mode 100644 index 000000000..f1fe84286 --- /dev/null +++ b/docs/environment-variables.md @@ -0,0 +1,73 @@ +# Environment Variables + +All environment variables used to configure TubeSync. None of them are required for +a default container installation -- the defaults work out of the box. + +## Container + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `PUID` | User ID the container runs as | `1000` | `1000` | +| `PGID` | Group ID the container runs as | `1000` | `1000` | +| `TZ` | Timezone ([tz database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)) | `UTC` | `Europe/London` | + +## Authentication + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `HTTP_USER` | Username for HTTP basic authentication | *(disabled)* | `some-username` | +| `HTTP_PASS` | Password for HTTP basic authentication | *(disabled)* | `some-secure-password` | + +Both `HTTP_USER` and `HTTP_PASS` must be set to enable basic authentication. + +## Django + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `DJANGO_SECRET_KEY` | Django's `SECRET_KEY` for cryptographic signing | *(auto-generated)* | `YJySXnQLB7UVZw2dXKDWxI5lEZaImK6l` | +| `DJANGO_URL_PREFIX` | Run TubeSync under a sub-path on the web server | *(none)* | `/somepath/` | + +## Web Server + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `GUNICORN_WORKERS` | Number of gunicorn (web request) workers to spawn | `2` | `3` | +| `LISTEN_HOST` | IP address for gunicorn to listen on | `0.0.0.0` | `127.0.0.1` | +| `LISTEN_PORT` | Port number for gunicorn to listen on | `4848` | `8080` | +| `TUBESYNC_HOSTS` | Django's `ALLOWED_HOSTS`, comma-separated | `*` | `tubesync.example.com,otherhost.com` | + +## Database + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `DATABASE_CONNECTION` | External database connection string | *(SQLite)* | `postgresql://user:pass@host:port/database` | + +See [Using PostgreSQL, MySQL or MariaDB](other-database-backends.md) for details. + +## Downloads + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `TUBESYNC_DIRECTORY_PREFIX` | Enable `video` and `audio` subdirectories in `/downloads` | `True` | `False` | +| `TUBESYNC_RESET_DOWNLOAD_DIR` | Reset `/downloads` directory permissions on startup | `True` | `False` | +| `TUBESYNC_VIDEO_HEIGHT_CUTOFF` | Smallest video height in pixels permitted to download | `240` | `360` | + +## Media Renaming + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `TUBESYNC_RENAME_ALL_SOURCES` | Rename media files from all sources when format changes | `True` | `False` | +| `TUBESYNC_RENAME_SOURCES` | Rename media files only from these source directories (comma-separated) | *(none)* | `Source1_directory,Source2_directory` | + +## Metadata + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `TUBESYNC_SHRINK_NEW` | Filter unneeded information from newly retrieved metadata | `False` | `True` | +| `TUBESYNC_SHRINK_OLD` | Filter unneeded information from metadata loaded from the database | `False` | `True` | + +## Debugging + +| Name | Description | Default | Example | +|------|-------------|---------|---------| +| `TUBESYNC_DEBUG` | Enable debug logging | `False` | `True` | From 8bf044e9c0af0d27b6322eee3f0b9d3797d964d2 Mon Sep 17 00:00:00 2001 From: Alice Snow Date: Wed, 25 Feb 2026 17:25:43 +0100 Subject: [PATCH 02/99] add dev file, scripts and setup --- .gitignore | 1 + Dockerfile | 2 +- .../scss/_animated.scss | 0 .../scss/_bordered-pulled.scss | 0 .../scss/_core.scss | 0 .../scss/_fixed-width.scss | 0 .../scss/_functions.scss | 0 .../scss/_icons.scss | 0 .../scss/_list.scss | 0 .../scss/_mixins.scss | 0 .../scss/_rotated-flipped.scss | 0 .../scss/_screen-reader.scss | 0 .../scss/_shims.scss | 0 .../scss/_sizing.scss | 0 .../scss/_stacked.scss | 0 .../scss/_variables.scss | 0 .../scss/brands.scss | 0 .../scss/fontawesome.scss | 0 .../scss/regular.scss | 0 .../scss/solid.scss | 0 .../scss/v4-shims.scss | 0 .../webfonts/fa-brands-400.ttf | Bin .../webfonts/fa-brands-400.woff2 | Bin .../webfonts/fa-regular-400.ttf | Bin .../webfonts/fa-regular-400.woff2 | Bin .../webfonts/fa-solid-900.ttf | Bin .../webfonts/fa-solid-900.woff2 | Bin .../webfonts/fa-v4compatibility.ttf | Bin .../webfonts/fa-v4compatibility.woff2 | Bin docs/dev.md | 69 ++++++++++++------ requirements-dev.txt | 4 + requirements.txt | 47 ++++++++++++ scripts/setup-dev.sh | 59 +++++++++++++++ tubesync/common/huey.py | 10 ++- .../fonts/fontawesome/fa-brands-400.ttf | 2 +- .../fonts/fontawesome/fa-brands-400.woff2 | 2 +- .../fonts/fontawesome/fa-regular-400.ttf | 2 +- .../fonts/fontawesome/fa-regular-400.woff2 | 2 +- .../static/fonts/fontawesome/fa-solid-900.ttf | 2 +- .../fonts/fontawesome/fa-solid-900.woff2 | 2 +- .../fonts/fontawesome/fa-v4compatibility.ttf | 2 +- .../fontawesome/fa-v4compatibility.woff2 | 2 +- .../static/styles/fontawesome/_animated.scss | 2 +- .../styles/fontawesome/_bordered-pulled.scss | 2 +- .../static/styles/fontawesome/_core.scss | 2 +- .../styles/fontawesome/_fixed-width.scss | 2 +- .../static/styles/fontawesome/_functions.scss | 2 +- .../static/styles/fontawesome/_icons.scss | 2 +- .../static/styles/fontawesome/_list.scss | 2 +- .../static/styles/fontawesome/_mixins.scss | 2 +- .../styles/fontawesome/_rotated-flipped.scss | 2 +- .../styles/fontawesome/_screen-reader.scss | 2 +- .../static/styles/fontawesome/_shims.scss | 2 +- .../static/styles/fontawesome/_sizing.scss | 2 +- .../static/styles/fontawesome/_stacked.scss | 2 +- .../static/styles/fontawesome/brands.scss | 2 +- .../styles/fontawesome/fontawesome.scss | 2 +- .../static/styles/fontawesome/regular.scss | 2 +- .../static/styles/fontawesome/solid.scss | 2 +- .../static/styles/fontawesome/v4-shims.scss | 2 +- tubesync/tubesync/local_settings.py.container | 1 + tubesync/tubesync/local_settings.py.example | 2 + tubesync/tubesync/settings.py | 45 ++++++------ 63 files changed, 220 insertions(+), 72 deletions(-) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_animated.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_bordered-pulled.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_core.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_fixed-width.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_functions.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_icons.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_list.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_mixins.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_rotated-flipped.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_screen-reader.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_shims.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_sizing.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_stacked.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/_variables.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/brands.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/fontawesome.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/regular.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/solid.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/scss/v4-shims.scss (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.ttf (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.woff2 (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.ttf (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.woff2 (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.ttf (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.woff2 (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.ttf (100%) rename {fontawesome-free => assets/fontawesome-free}/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.woff2 (100%) create mode 100644 requirements-dev.txt create mode 100644 requirements.txt create mode 100755 scripts/setup-dev.sh diff --git a/.gitignore b/.gitignore index 11b0fc5bd..49e2f806a 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,4 @@ Pipfile.lock # Compiled CSS /tubesync/common/static/styles/*.css +tubesync/tubesync/local_settings.py diff --git a/Dockerfile b/Dockerfile index 13bb3dee5..308f06bfb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -403,7 +403,7 @@ FROM tubesync-base AS tubesync-prepare-app COPY tubesync /app -RUN --mount=type=bind,source=fontawesome-free,target=/fontawesome-free \ +RUN --mount=type=bind,source=assets/fontawesome-free,target=/fontawesome-free \ set -x && \ # turn any symbolic links into files ( \ diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_animated.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_animated.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_animated.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_animated.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_bordered-pulled.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_bordered-pulled.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_bordered-pulled.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_bordered-pulled.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_core.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_core.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_core.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_core.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_fixed-width.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_fixed-width.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_fixed-width.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_fixed-width.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_functions.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_functions.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_functions.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_functions.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_icons.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_icons.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_icons.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_icons.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_list.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_list.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_list.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_list.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_mixins.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_mixins.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_mixins.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_mixins.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_rotated-flipped.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_rotated-flipped.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_rotated-flipped.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_rotated-flipped.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_screen-reader.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_screen-reader.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_screen-reader.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_screen-reader.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_shims.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_shims.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_shims.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_shims.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_sizing.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_sizing.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_sizing.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_sizing.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_stacked.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_stacked.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_stacked.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_stacked.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_variables.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_variables.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_variables.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_variables.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/brands.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/brands.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/brands.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/brands.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/fontawesome.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/fontawesome.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/fontawesome.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/fontawesome.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/regular.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/regular.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/regular.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/regular.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/solid.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/solid.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/solid.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/solid.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/v4-shims.scss b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/v4-shims.scss similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/scss/v4-shims.scss rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/v4-shims.scss diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.ttf b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.ttf similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.ttf rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.ttf diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.woff2 b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.woff2 similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.woff2 rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.woff2 diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.ttf b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.ttf similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.ttf rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.ttf diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.woff2 b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.woff2 similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.woff2 rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.woff2 diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.ttf b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.ttf similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.ttf rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.ttf diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.woff2 b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.woff2 similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.woff2 rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.woff2 diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.ttf b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.ttf similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.ttf rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.ttf diff --git a/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.woff2 b/assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.woff2 similarity index 100% rename from fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.woff2 rename to assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.woff2 diff --git a/docs/dev.md b/docs/dev.md index 33ebbd968..e8d665ea6 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -11,57 +11,83 @@ tests, and contribute to TubeSync. - `mysql_config` or `mariadb_config` if you need the MySQL/MariaDB driver (Debian/Ubuntu: `libmysqlclient-dev`) -## Initial setup - -1. Clone the repository: +## Quick setup ```bash git clone https://github.com/meeb/tubesync.git cd tubesync +./scripts/setup-dev.sh ``` -2. Install Python dependencies: +This script handles everything: creates a `.venv` virtualenv, installs dependencies +from `requirements.txt`, applies the yt-dlp patches, copies `local_settings.py`, runs +migrations, and collects static files. + +Once done: ```bash -pipenv install --dev +source .venv/bin/activate +cd tubesync && python manage.py runserver ``` -Or, if you prefer using `uv` + `pip` (same as CI): +## Manual setup + +If you prefer doing it step by step: + +1. Clone and create a virtualenv: ```bash -python -m pip install uv -pipenv lock -pipenv requirements | tee requirements.txt -uv pip install --system --strict --requirements requirements.txt +git clone https://github.com/meeb/tubesync.git +cd tubesync +python3 -m venv .venv +source .venv/bin/activate ``` -3. Copy the local settings file: +2. Install dependencies: ```bash -cp tubesync/tubesync/local_settings.py.example tubesync/tubesync/local_settings.py +pip install -r requirements.txt +pip install -r requirements-dev.txt ``` -This configures a local SQLite database and sensible defaults for development. - -4. Run database migrations: +3. Apply yt-dlp patches (required -- TubeSync ships patches that get copied into the + installed `yt_dlp` package): ```bash -cd tubesync -python manage.py migrate +cp -a patches/yt_dlp/* "$(python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')/yt_dlp/" ``` -5. Collect static files: +4. Copy the local settings file: ```bash -python manage.py collectstatic --noinput +cp tubesync/tubesync/local_settings.py.example tubesync/tubesync/local_settings.py ``` -Or use the Makefile shortcut: +5. Run migrations and collect static files: ```bash -make build +cd tubesync +python manage.py migrate +python manage.py collectstatic --noinput ``` +Or use the Makefile shortcut for static files: `make build` + +## Local settings + +`local_settings.py` configures paths so everything stays inside the project directory: + +| Setting | Dev value | Container value | +|---------|-----------|-----------------| +| `CONFIG_BASE_DIR` | `tubesync/` (project dir) | `/config` | +| `DOWNLOADS_BASE_DIR` | `tubesync/` (project dir) | `/downloads` | +| `HUEY_TASKS_DIR` | `tubesync/tasks/` | `/config/tasks` | +| `DOWNLOAD_ROOT` | `tubesync/downloads/` | `/downloads` | +| Database | `tubesync/db.sqlite3` | `/config/db.sqlite3` | + +All data files (database, task queues, downloads) are created inside the project +directory so nothing touches system paths like `/config` or `/downloads`. + ## Running the development server ```bash @@ -121,6 +147,7 @@ tubesync/ │ ├── tubesync/ # Django project config (settings, urls, wsgi) │ ├── common/ # Shared app (base templates, utils, middleware, task queue) │ └── sync/ # Main app (sources, media, downloads, media servers) +├── assets/ # Vendored third-party assets (Font Awesome) ├── docs/ # Documentation │ └── assets/ # Screenshots and images ├── patches/ # Patches applied to yt-dlp diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 000000000..082515736 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,4 @@ +-i https://pypi.org/simple +autopep8==2.3.2; python_version >= '3.9' +pycodestyle==2.14.0; python_version >= '3.9' +libsass>=0.23.0 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..a759b6ee0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,47 @@ +-i https://pypi.org/simple +asgiref==3.11.1; python_version >= '3.9' +babi==1.6.1; python_version >= '3.10' +babi-grammars==0.0.71; python_version >= '3.10' +bgutil-ytdlp-pot-provider==1.2.2; python_version >= '3.8' +brotli==1.2.0 +certifi==2026.2.25; python_version >= '3.7' +cffi==2.0.0; python_version >= '3.9' +charset-normalizer==3.4.4; python_version >= '3.7' +curl-cffi==0.14.0; python_version >= '3.10' +django==5.2.11; python_version >= '3.10' +django-appconf==1.2.0; python_version >= '3.9' +django-basicauth==0.5.3 +django-compressor==4.6.0; python_version >= '3.10' +django-huey==1.3.1; python_version >= '3.8' +django-sass-processor[management-command]==1.4.2 +emoji==2.15.0; python_version >= '3.8' +gunicorn==25.1.0; python_version >= '3.10' +html5lib==1.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +httptools==0.7.1; python_version >= '3.9' +huey==2.6.0 +identify==2.6.16; python_version >= '3.10' +idna==3.11; python_version >= '3.8' +mutagen==1.47.0; python_version >= '3.7' +mysqlclient==2.2.8; python_version >= '3.10' +onigurumacffi==1.5.0; python_version >= '3.10' +packaging==26.0; python_version >= '3.8' +pillow==12.1.1; python_version >= '3.10' +psycopg[binary,pool]==3.3.3; python_version >= '3.10' +psycopg-binary==3.3.3; python_version >= '3.10' +psycopg-pool==3.3.0; python_version >= '3.10' +pycparser==3.0; python_version >= '3.10' +pycryptodomex==3.23.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6' +pysocks==1.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +rcssmin==1.2.2 +requests[socks]==2.32.5; python_version >= '3.9' +rjsmin==1.2.5 +six==1.17.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' +sqlparse==0.5.5; python_version >= '3.8' +typing-extensions==4.15.0; python_version >= '3.9' +urllib3[socks]==2.6.3; python_version >= '3.9' +webencodings==0.5.1 +websockets==16.0; python_version >= '3.10' +whitenoise==6.11.0; python_version >= '3.9' +yt-dlp[curl-cffi,default]==2026.2.21; python_version >= '3.10' +yt-dlp-ejs==0.5.0; python_version >= '3.10' +yt-dlp-remote-cipher==0.0.1; python_version >= '3.10' diff --git a/scripts/setup-dev.sh b/scripts/setup-dev.sh new file mode 100755 index 000000000..134cfa82e --- /dev/null +++ b/scripts/setup-dev.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +set -euo pipefail + +# TubeSync development environment setup script +# Usage: ./scripts/setup-dev.sh + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +PYTHON="${PYTHON:-python3}" + +echo "==> Setting up TubeSync dev environment" +echo " Python: $($PYTHON --version)" +echo " Repo: $REPO_ROOT" +echo + +# 1. Create a virtualenv if not already in one +if [ -z "${VIRTUAL_ENV:-}" ]; then + echo "==> Creating virtualenv in .venv/" + $PYTHON -m venv "$REPO_ROOT/.venv" + source "$REPO_ROOT/.venv/bin/activate" + echo " Activated .venv" +else + echo "==> Using existing virtualenv: $VIRTUAL_ENV" +fi + +# 2. Install dependencies +echo "==> Installing dependencies from requirements.txt" +pip install --upgrade pip -q +pip install -r "$REPO_ROOT/requirements.txt" -q +pip install -r "$REPO_ROOT/requirements-dev.txt" -q +echo " Done" + +# 3. Apply yt-dlp patches +echo "==> Applying yt-dlp patches" +SITE_PACKAGES="$(python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')" +cp -a "$REPO_ROOT/patches/yt_dlp/"* "$SITE_PACKAGES/yt_dlp/" +echo " Patched yt_dlp in $SITE_PACKAGES" + +# 4. Copy local settings if missing +if [ ! -f "$REPO_ROOT/tubesync/tubesync/local_settings.py" ]; then + echo "==> Copying local_settings.py.example" + cp "$REPO_ROOT/tubesync/tubesync/local_settings.py.example" \ + "$REPO_ROOT/tubesync/tubesync/local_settings.py" +else + echo "==> local_settings.py already exists, skipping" +fi + +# 5. Run migrations +echo "==> Running database migrations" +$PYTHON "$REPO_ROOT/tubesync/manage.py" migrate --run-syncdb -v 0 + +# 6. Collect static files +echo "==> Collecting static files" +$PYTHON "$REPO_ROOT/tubesync/manage.py" collectstatic --noinput -v 0 + +echo +echo "==> Setup complete!" +echo " Activate the virtualenv: source .venv/bin/activate" +echo " Run the dev server: cd tubesync && python manage.py runserver" +echo " Run tests: cd tubesync && python manage.py test --verbosity=2" diff --git a/tubesync/common/huey.py b/tubesync/common/huey.py index 85028c19e..f7bfb7c2d 100644 --- a/tubesync/common/huey.py +++ b/tubesync/common/huey.py @@ -1,6 +1,7 @@ import datetime import os import uuid +from pathlib import Path from functools import wraps from huey import ( CancelExecution, Huey as huey_Huey, @@ -214,7 +215,7 @@ def default_maint_func(queue, /, exception=None, status=None): return maint_result -def sqlite_tasks(key, /, prefix=None, thread=None, workers=None): +def sqlite_tasks(key, /, prefix=None, thread=None, workers=None, tasks_dir=None): name_fmt = 'huey_{}' if prefix: name_fmt = f'huey_{prefix}_' + '{}' @@ -226,7 +227,10 @@ def sqlite_tasks(key, /, prefix=None, thread=None, workers=None): workers = 2 finally: if 0 >= workers: - useful_cpus = os.sched_getaffinity(0) + try: + useful_cpus = os.sched_getaffinity(0) + except AttributeError: + useful_cpus = range(os.cpu_count() or 2) workers = max(2, len(useful_cpus) // 2) elif 1 == workers: thread = False @@ -239,7 +243,7 @@ def sqlite_tasks(key, /, prefix=None, thread=None, workers=None): utc=True, compression=True, connection=dict( - filename=f'/config/tasks/{name}.db', + filename=str(Path(tasks_dir or '/config/tasks') / f'{name}.db'), fsync=True, isolation_level='IMMEDIATE', # _create_connection sets this to None strict_fifo=True, diff --git a/tubesync/common/static/fonts/fontawesome/fa-brands-400.ttf b/tubesync/common/static/fonts/fontawesome/fa-brands-400.ttf index 752a19ec3..e346f9b36 120000 --- a/tubesync/common/static/fonts/fontawesome/fa-brands-400.ttf +++ b/tubesync/common/static/fonts/fontawesome/fa-brands-400.ttf @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.ttf \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.ttf \ No newline at end of file diff --git a/tubesync/common/static/fonts/fontawesome/fa-brands-400.woff2 b/tubesync/common/static/fonts/fontawesome/fa-brands-400.woff2 index 642dffee8..949496cd6 120000 --- a/tubesync/common/static/fonts/fontawesome/fa-brands-400.woff2 +++ b/tubesync/common/static/fonts/fontawesome/fa-brands-400.woff2 @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.woff2 \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-brands-400.woff2 \ No newline at end of file diff --git a/tubesync/common/static/fonts/fontawesome/fa-regular-400.ttf b/tubesync/common/static/fonts/fontawesome/fa-regular-400.ttf index 44f1b4415..00d8fc110 120000 --- a/tubesync/common/static/fonts/fontawesome/fa-regular-400.ttf +++ b/tubesync/common/static/fonts/fontawesome/fa-regular-400.ttf @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.ttf \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.ttf \ No newline at end of file diff --git a/tubesync/common/static/fonts/fontawesome/fa-regular-400.woff2 b/tubesync/common/static/fonts/fontawesome/fa-regular-400.woff2 index 1b95c9d37..0c17b2ba6 120000 --- a/tubesync/common/static/fonts/fontawesome/fa-regular-400.woff2 +++ b/tubesync/common/static/fonts/fontawesome/fa-regular-400.woff2 @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.woff2 \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-regular-400.woff2 \ No newline at end of file diff --git a/tubesync/common/static/fonts/fontawesome/fa-solid-900.ttf b/tubesync/common/static/fonts/fontawesome/fa-solid-900.ttf index 03fe77d6b..2d9543445 120000 --- a/tubesync/common/static/fonts/fontawesome/fa-solid-900.ttf +++ b/tubesync/common/static/fonts/fontawesome/fa-solid-900.ttf @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.ttf \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.ttf \ No newline at end of file diff --git a/tubesync/common/static/fonts/fontawesome/fa-solid-900.woff2 b/tubesync/common/static/fonts/fontawesome/fa-solid-900.woff2 index c3d992ba7..decef82f3 120000 --- a/tubesync/common/static/fonts/fontawesome/fa-solid-900.woff2 +++ b/tubesync/common/static/fonts/fontawesome/fa-solid-900.woff2 @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.woff2 \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-solid-900.woff2 \ No newline at end of file diff --git a/tubesync/common/static/fonts/fontawesome/fa-v4compatibility.ttf b/tubesync/common/static/fonts/fontawesome/fa-v4compatibility.ttf index a727603bb..42aeddc4e 120000 --- a/tubesync/common/static/fonts/fontawesome/fa-v4compatibility.ttf +++ b/tubesync/common/static/fonts/fontawesome/fa-v4compatibility.ttf @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.ttf \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.ttf \ No newline at end of file diff --git a/tubesync/common/static/fonts/fontawesome/fa-v4compatibility.woff2 b/tubesync/common/static/fonts/fontawesome/fa-v4compatibility.woff2 index bdd276913..413b37e4d 120000 --- a/tubesync/common/static/fonts/fontawesome/fa-v4compatibility.woff2 +++ b/tubesync/common/static/fonts/fontawesome/fa-v4compatibility.woff2 @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.woff2 \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/webfonts/fa-v4compatibility.woff2 \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_animated.scss b/tubesync/common/static/styles/fontawesome/_animated.scss index baf5bc606..d618e324a 120000 --- a/tubesync/common/static/styles/fontawesome/_animated.scss +++ b/tubesync/common/static/styles/fontawesome/_animated.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_animated.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_animated.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_bordered-pulled.scss b/tubesync/common/static/styles/fontawesome/_bordered-pulled.scss index 44b849fdf..8f0e9507d 120000 --- a/tubesync/common/static/styles/fontawesome/_bordered-pulled.scss +++ b/tubesync/common/static/styles/fontawesome/_bordered-pulled.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_bordered-pulled.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_bordered-pulled.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_core.scss b/tubesync/common/static/styles/fontawesome/_core.scss index 15dec1845..6203b127f 120000 --- a/tubesync/common/static/styles/fontawesome/_core.scss +++ b/tubesync/common/static/styles/fontawesome/_core.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_core.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_core.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_fixed-width.scss b/tubesync/common/static/styles/fontawesome/_fixed-width.scss index bd83f3898..47da1f187 120000 --- a/tubesync/common/static/styles/fontawesome/_fixed-width.scss +++ b/tubesync/common/static/styles/fontawesome/_fixed-width.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_fixed-width.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_fixed-width.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_functions.scss b/tubesync/common/static/styles/fontawesome/_functions.scss index fa7e1371d..a19874389 120000 --- a/tubesync/common/static/styles/fontawesome/_functions.scss +++ b/tubesync/common/static/styles/fontawesome/_functions.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_functions.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_functions.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_icons.scss b/tubesync/common/static/styles/fontawesome/_icons.scss index 724992751..c2556692a 120000 --- a/tubesync/common/static/styles/fontawesome/_icons.scss +++ b/tubesync/common/static/styles/fontawesome/_icons.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_icons.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_icons.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_list.scss b/tubesync/common/static/styles/fontawesome/_list.scss index 671ecbc57..9b2bd03ed 120000 --- a/tubesync/common/static/styles/fontawesome/_list.scss +++ b/tubesync/common/static/styles/fontawesome/_list.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_list.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_list.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_mixins.scss b/tubesync/common/static/styles/fontawesome/_mixins.scss index 0611849c8..d0a3f85f7 120000 --- a/tubesync/common/static/styles/fontawesome/_mixins.scss +++ b/tubesync/common/static/styles/fontawesome/_mixins.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_mixins.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_mixins.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_rotated-flipped.scss b/tubesync/common/static/styles/fontawesome/_rotated-flipped.scss index 77309a702..48470288c 120000 --- a/tubesync/common/static/styles/fontawesome/_rotated-flipped.scss +++ b/tubesync/common/static/styles/fontawesome/_rotated-flipped.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_rotated-flipped.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_rotated-flipped.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_screen-reader.scss b/tubesync/common/static/styles/fontawesome/_screen-reader.scss index 41cc2a085..ca2aea4ed 120000 --- a/tubesync/common/static/styles/fontawesome/_screen-reader.scss +++ b/tubesync/common/static/styles/fontawesome/_screen-reader.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_screen-reader.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_screen-reader.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_shims.scss b/tubesync/common/static/styles/fontawesome/_shims.scss index 0c4222c84..fb6eed2f0 120000 --- a/tubesync/common/static/styles/fontawesome/_shims.scss +++ b/tubesync/common/static/styles/fontawesome/_shims.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_shims.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_shims.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_sizing.scss b/tubesync/common/static/styles/fontawesome/_sizing.scss index 9ae097466..f95224290 120000 --- a/tubesync/common/static/styles/fontawesome/_sizing.scss +++ b/tubesync/common/static/styles/fontawesome/_sizing.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_sizing.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_sizing.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_stacked.scss b/tubesync/common/static/styles/fontawesome/_stacked.scss index 5cc44444d..babdd95b5 120000 --- a/tubesync/common/static/styles/fontawesome/_stacked.scss +++ b/tubesync/common/static/styles/fontawesome/_stacked.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_stacked.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_stacked.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/brands.scss b/tubesync/common/static/styles/fontawesome/brands.scss index 00ae340f4..1c880b911 120000 --- a/tubesync/common/static/styles/fontawesome/brands.scss +++ b/tubesync/common/static/styles/fontawesome/brands.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/brands.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/brands.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/fontawesome.scss b/tubesync/common/static/styles/fontawesome/fontawesome.scss index 033f4251e..0651eff38 120000 --- a/tubesync/common/static/styles/fontawesome/fontawesome.scss +++ b/tubesync/common/static/styles/fontawesome/fontawesome.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/fontawesome.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/fontawesome.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/regular.scss b/tubesync/common/static/styles/fontawesome/regular.scss index eb75776cb..524138620 120000 --- a/tubesync/common/static/styles/fontawesome/regular.scss +++ b/tubesync/common/static/styles/fontawesome/regular.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/regular.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/regular.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/solid.scss b/tubesync/common/static/styles/fontawesome/solid.scss index 325447664..877a13a76 120000 --- a/tubesync/common/static/styles/fontawesome/solid.scss +++ b/tubesync/common/static/styles/fontawesome/solid.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/solid.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/solid.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/v4-shims.scss b/tubesync/common/static/styles/fontawesome/v4-shims.scss index 2b2fcff5a..843942234 120000 --- a/tubesync/common/static/styles/fontawesome/v4-shims.scss +++ b/tubesync/common/static/styles/fontawesome/v4-shims.scss @@ -1 +1 @@ -../../../../../fontawesome-free/6/fontawesome-free-6.7.2-web/scss/v4-shims.scss \ No newline at end of file +../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/v4-shims.scss \ No newline at end of file diff --git a/tubesync/tubesync/local_settings.py.container b/tubesync/tubesync/local_settings.py.container index 0800f055b..edae12771 100644 --- a/tubesync/tubesync/local_settings.py.container +++ b/tubesync/tubesync/local_settings.py.container @@ -72,6 +72,7 @@ else: SQLITE_DELAY_FLOAT = 5 +HUEY_TASKS_DIR = CONFIG_BASE_DIR / 'tasks' MEDIA_ROOT = CONFIG_BASE_DIR / 'media' DOWNLOAD_ROOT = DOWNLOADS_BASE_DIR YOUTUBE_DL_CACHEDIR = CONFIG_BASE_DIR / 'cache/yt-dlp' diff --git a/tubesync/tubesync/local_settings.py.example b/tubesync/tubesync/local_settings.py.example index 08dfabf68..0bbea69f0 100644 --- a/tubesync/tubesync/local_settings.py.example +++ b/tubesync/tubesync/local_settings.py.example @@ -19,3 +19,5 @@ DATABASE_CONNECTION_STR = f'sqlite at "{DATABASES["default"]["NAME"]}"' DOWNLOAD_ROOT = DOWNLOADS_BASE_DIR / 'downloads' + +HUEY_TASKS_DIR = CONFIG_BASE_DIR / 'tasks' diff --git a/tubesync/tubesync/settings.py b/tubesync/tubesync/settings.py index 034ec39a7..6042fe277 100644 --- a/tubesync/tubesync/settings.py +++ b/tubesync/tubesync/settings.py @@ -50,27 +50,7 @@ FORCE_SCRIPT_NAME = None -DJANGO_HUEY = { - 'default': TaskQueue.LIMIT.value, - 'queues': dict(), - 'verbose': None if DEBUG else False, -} -for queue_name in TaskQueue.values: - queues = DJANGO_HUEY['queues'] - if TaskQueue.LIMIT.value == queue_name: - queues[queue_name] = sqlite_tasks(queue_name, prefix='net') - elif TaskQueue.NET.value == queue_name: - queues[queue_name] = sqlite_tasks(queue_name, thread=True, workers=0) - else: - queues[queue_name] = sqlite_tasks(queue_name, thread=True) -for django_huey_queue in DJANGO_HUEY['queues'].values(): - connection = django_huey_queue.get('connection') - if connection: - filepath = Path('/.' + connection.get('filename') or '').resolve(strict=False) - filepath.parent.mkdir(exist_ok=True, parents=True) - consumer = django_huey_queue.get('consumer') - if consumer: - consumer['verbose'] = DJANGO_HUEY.get('verbose', False) +HUEY_TASKS_DIR = None # Set in local_settings.py, defaults to /config/tasks TEMPLATES = [ @@ -255,6 +235,29 @@ sys.exit(1) +DJANGO_HUEY = { + 'default': TaskQueue.LIMIT.value, + 'queues': dict(), + 'verbose': None if DEBUG else False, +} +for queue_name in TaskQueue.values: + queues = DJANGO_HUEY['queues'] + if TaskQueue.LIMIT.value == queue_name: + queues[queue_name] = sqlite_tasks(queue_name, prefix='net', tasks_dir=HUEY_TASKS_DIR) + elif TaskQueue.NET.value == queue_name: + queues[queue_name] = sqlite_tasks(queue_name, thread=True, workers=0, tasks_dir=HUEY_TASKS_DIR) + else: + queues[queue_name] = sqlite_tasks(queue_name, thread=True, tasks_dir=HUEY_TASKS_DIR) +for django_huey_queue in DJANGO_HUEY['queues'].values(): + connection = django_huey_queue.get('connection') + if connection: + filepath = Path('/.' + connection.get('filename') or '').resolve(strict=False) + filepath.parent.mkdir(exist_ok=True, parents=True) + consumer = django_huey_queue.get('consumer') + if consumer: + consumer['verbose'] = DJANGO_HUEY.get('verbose', False) + + try: MAX_RUN_TIME = int(str(MAX_RUN_TIME), base=10) except: From 0475f5688af6d4264af4a0c6e79aee36c7142edf Mon Sep 17 00:00:00 2001 From: Alice Snow Date: Wed, 25 Feb 2026 17:40:53 +0100 Subject: [PATCH 03/99] migrate view.py to clean multi file --- .gitignore | 1 + tubesync/sync/views.py | 1524 --------------------------- tubesync/sync/views/__init__.py | 24 + tubesync/sync/views/dashboard.py | 78 ++ tubesync/sync/views/media.py | 444 ++++++++ tubesync/sync/views/mediaservers.py | 236 +++++ tubesync/sync/views/sources.py | 354 +++++++ tubesync/sync/views/tasks.py | 390 +++++++ tubesync/sync/views/utils.py | 75 ++ 9 files changed, 1602 insertions(+), 1524 deletions(-) delete mode 100644 tubesync/sync/views.py create mode 100644 tubesync/sync/views/__init__.py create mode 100644 tubesync/sync/views/dashboard.py create mode 100644 tubesync/sync/views/media.py create mode 100644 tubesync/sync/views/mediaservers.py create mode 100644 tubesync/sync/views/sources.py create mode 100644 tubesync/sync/views/tasks.py create mode 100644 tubesync/sync/views/utils.py diff --git a/.gitignore b/.gitignore index 49e2f806a..9235e1966 100644 --- a/.gitignore +++ b/.gitignore @@ -149,3 +149,4 @@ Pipfile.lock # Compiled CSS /tubesync/common/static/styles/*.css tubesync/tubesync/local_settings.py +tubesync/tubesync/downloads diff --git a/tubesync/sync/views.py b/tubesync/sync/views.py deleted file mode 100644 index 067a3b77b..000000000 --- a/tubesync/sync/views.py +++ /dev/null @@ -1,1524 +0,0 @@ -import glob -import os -from base64 import b64decode -import pathlib -import sys -import uuid -from django.conf import settings -from django.http import FileResponse, Http404, HttpResponseNotFound, HttpResponseRedirect -from django.views.generic import TemplateView, ListView, DetailView -from django.views.generic.edit import (FormView, FormMixin, CreateView, UpdateView, - DeleteView) -from django.views.generic.detail import SingleObjectMixin -from django.core.exceptions import SuspiciousFileOperation -from django.http import HttpResponse -from django.urls import reverse_lazy -from django.db import connection, transaction, IntegrityError -from django.db.models import F, Q, Count, Sum, When, Case -from django.db.models.expressions import RawSQL -from django.forms import Form, ValidationError -from django.utils.text import slugify -from django.utils._os import safe_join -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ -from common.models import TaskHistory -from common.timestamp import timestamp_to_datetime -from common.utils import append_uri_params, mkdir_p, multi_key_sort -from django_huey import DJANGO_HUEY, get_queue -from common.huey import h_q_reset_tasks -from common.logger import log -from .models import Source, Media, MediaServer, Metadata -from .forms import (ValidateSourceForm, ConfirmDeleteSourceForm, RedownloadMediaForm, - SkipMediaForm, EnableMediaForm, ResetTasksForm, ScheduleTaskForm, - ConfirmDeleteMediaServerForm, SourceForm) -from .utils import delete_file, validate_url -from .tasks import ( - map_task_to_instance, get_error_message, - get_running_tasks, get_media_download_task, get_source_completed_tasks, - check_source_directory_exists, index_source, download_media_image, download_media_file, - refresh_formats, -) -from .choices import (Val, MediaServerType, SourceResolution, IndexSchedule, - YouTube_SourceType, youtube_long_source_types, - youtube_validation_urls) -from . import signals # noqa -from . import youtube - - -def get_histories_from_huey_ids(huey_task_ids): - """ - Robustly matches Huey task.id values to TaskHistory records. - - Optimized for large datasets across SQLite, PostgreSQL, and MariaDB. - Bypasses SQL variable limits by using request-cycle temporary tables. - """ - # 1. Guard clause for empty input - returns a valid, empty QuerySet - if not huey_task_ids: - return TaskHistory.objects.none() - - # 2. Dynamic metadata and unique naming - task_history_table = TaskHistory._meta.db_table - unique_suffix = uuid.uuid4().hex[:8] - input_tmp = f"tmp_huey_ids_{unique_suffix}" - results_tmp = f"tmp_history_pks_{unique_suffix}" - - # 3. Database operations - def validated_id_generator(): - for tid in huey_task_ids: - try: - # Validates format and normalizes to lowercase dashed string - yield (str(uuid.UUID(str(tid))).lower(),) - except (ValueError, TypeError, AttributeError): - # Skip malformed IDs and log for troubleshooting - log.warning(f"Skipping malformed Huey task ID: {tid}") - continue - - with transaction.atomic(): - with connection.cursor() as cursor: - # Stage 1: Store and normalize input IDs - cursor.execute(f"CREATE TEMPORARY TABLE {input_tmp} (tid VARCHAR(40) PRIMARY KEY)") - - # Single iteration: ensures dashed strings and lowercase for case-sensitive DBs - cursor.executemany(f"INSERT INTO {input_tmp} VALUES (%s)", validated_id_generator()) - - # Stage 2: Filter to a PK-only results table - cursor.execute(f"CREATE TEMPORARY TABLE {results_tmp} (id BIGINT)") - cursor.execute(f""" - INSERT INTO {results_tmp} (id) - SELECT id FROM {task_history_table} - WHERE task_id IN (SELECT tid FROM {input_tmp}) - """) - - # Index the result PKs to ensure dashboard pagination and ordering are fast - cursor.execute(f"CREATE INDEX idx_{results_tmp} ON {results_tmp}(id)") - - # Immediate cleanup of the input string table to save memory - cursor.execute(f"DROP TABLE IF EXISTS {input_tmp}") - - # 4. Return a Lazy QuerySet via RawSQL to bypass RawQuerySet.clone() limitations. - # The 'results_tmp' table persists until the connection closes after the request. - return TaskHistory.objects.filter( - id__in=RawSQL(f"SELECT id FROM {results_tmp}", []) - ) - -def get_waiting_tasks(): - huey_queue_names = (DJANGO_HUEY or {}).get('queues', {}) - huey_queues = list(map(get_queue, huey_queue_names)) - huey_task_ids = { - str(t.id) for q in huey_queues for t in set( - q.pending() - ).union( - q.scheduled() - ) - } - return get_histories_from_huey_ids(huey_task_ids) - - -class DashboardView(TemplateView): - ''' - The dashboard shows non-interactive totals and summaries. - ''' - - template_name = 'sync/dashboard.html' - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['now'] = timezone.now() - # Sources - data['num_sources'] = Source.objects.all().count() - data['num_video_sources'] = Source.objects.filter( - ~Q(source_resolution=Val(SourceResolution.AUDIO)) - ).count() - data['num_audio_sources'] = data['num_sources'] - data['num_video_sources'] - data['num_failed_sources'] = Source.objects.filter(has_failed=True).count() - # Media - data['num_media'] = Media.objects.all().count() - data['num_downloaded_media'] = Media.objects.filter(downloaded=True).count() - # Tasks - completed_qs = TaskHistory.objects.filter( - start_at__isnull=False, - end_at__gt=F('start_at'), - ) - waiting_qs = get_waiting_tasks() - data['num_tasks'] = waiting_qs.count() - data['num_completed_tasks'] = completed_qs.count() - # Disk usage - disk_usage = Media.objects.filter( - downloaded=True, downloaded_filesize__isnull=False - ).defer('metadata').aggregate(Sum('downloaded_filesize')) - data['disk_usage_bytes'] = disk_usage['downloaded_filesize__sum'] - if not data['disk_usage_bytes']: - data['disk_usage_bytes'] = 0 - if data['disk_usage_bytes'] and data['num_downloaded_media']: - data['average_bytes_per_media'] = round(data['disk_usage_bytes'] / - data['num_downloaded_media']) - else: - data['average_bytes_per_media'] = 0 - # Latest downloads - data['latest_downloads'] = Media.objects.filter( - downloaded=True, - download_date__isnull=False, - downloaded_filesize__isnull=False, - ).defer('metadata').order_by('-download_date')[:10] - # Largest downloads - data['largest_downloads'] = Media.objects.filter( - downloaded=True, downloaded_filesize__isnull=False - ).defer('metadata').order_by('-downloaded_filesize')[:10] - # UID and GID - data['uid'] = os.getuid() - data['gid'] = os.getgid() - # Config and download locations - data['config_dir'] = str(settings.CONFIG_BASE_DIR) - data['downloads_dir'] = str(settings.DOWNLOAD_ROOT) - data['database_connection'] = settings.DATABASE_CONNECTION_STR - # Add the database filesize when using db.sqlite3 - data['database_filesize'] = None - if 'sqlite' == connection.vendor: - db_name = str(connection.get_connection_params().get('database', '')) - db_path = pathlib.Path(db_name) if '/' == db_name[0] else None - if db_path: - data['database_filesize'] = db_path.stat().st_size - return data - - -class SourcesView(ListView): - ''' - A bare list of the sources which have been created with their states. - ''' - - template_name = 'sync/sources.html' - context_object_name = 'sources' - paginate_by = settings.SOURCES_PER_PAGE - messages = { - 'source-deleted': _('Your selected source has been deleted.'), - 'source-refreshed': _('The source has been scheduled to be synced now.') - } - - def get(self, *args, **kwargs): - if args[0].path.startswith("/source-sync-now/"): - source = Source.objects.get(pk=kwargs["pk"]) - if source is None: - return HttpResponseNotFound() - - TaskHistory.schedule( - index_source, - str(source.pk), - delay=30, - vn_fmt=_('Index media from source "{}" once'), - vn_args=(source.name,), - ) - url = reverse_lazy('sync:sources') - url = append_uri_params(url, {'message': 'source-refreshed'}) - return HttpResponseRedirect(url) - else: - return super().get(self, *args, **kwargs) - - def __init__(self, *args, **kwargs): - self.message = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - message_key = request.GET.get('message', '') - self.message = self.messages.get(message_key, '') - return super().dispatch(request, *args, **kwargs) - - def get_queryset(self): - all_sources = Source.objects.all().order_by('name') - return all_sources.annotate( - media_count=Count('media_source'), - downloaded_count=Count(Case(When(media_source__downloaded=True, then=1))) - ) - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['message'] = self.message - return data - - -class ValidateSourceView(FormView): - ''' - Validate a URL and prepopulate a create source view form with confirmed - accurate data. The aim here is to streamline onboarding of new sources - which otherwise may not be entirely obvious to add, such as the "key" - being just a playlist ID or some other reasonably opaque internals. - ''' - - template_name = 'sync/source-validate.html' - form_class = ValidateSourceForm - errors = { - 'invalid_url': _('That URL does not match any supported formats.'), - } - source_types = youtube_long_source_types - validation_urls = youtube_validation_urls - prepopulate_fields = { - Val(YouTube_SourceType.CHANNEL): ('source_type', 'key', 'name', 'directory'), - Val(YouTube_SourceType.CHANNEL_ID): ('source_type', 'key'), - Val(YouTube_SourceType.PLAYLIST): ('source_type', 'key'), - } - - def __init__(self, *args, **kwargs): - self.source_type_str = '' - self.source_type = None - self.key = '' - super().__init__(*args, **kwargs) - - def form_valid(self, form): - # Perform extra validation on the URL, we need to extract the channel name or - # playlist ID and check they are valid - source_url = form.cleaned_data['source_url'] - for source_type in YouTube_SourceType.values: - validation_url = self.validation_urls.get(source_type) - try: - self.key = validate_url(source_url, validation_url) - self.source_type = source_type - for long_type_str, st_val in youtube_long_source_types.items(): - if st_val == source_type: - self.source_type_str = long_type_str - break - return super().form_valid(form) - except ValidationError: - continue - # Source type wasn't detected - presumably it's not a valid URL - form.add_error('source_url', ValidationError(self.errors['invalid_url'])) - return super().form_invalid(form) - - def get_success_url(self): - url = reverse_lazy('sync:add-source') - fields_to_populate = self.prepopulate_fields.get(self.source_type) - fields = {} - value = self.key - use_channel_id = ( - 'youtube-channel' == self.source_type_str and - '@' == self.key[0] - ) - if use_channel_id: - old_key = self.key - old_source_type = self.source_type - old_source_type_str = self.source_type_str - - self.source_type_str = 'youtube-channel-id' - self.source_type = self.source_types.get(self.source_type_str, None) - index_url = Source.create_index_url(self.source_type, self.key, 'videos') - try: - self.key = youtube.get_channel_id( - index_url.replace('/channel/', '/') - ) - except youtube.YouTubeError: - # It did not work, revert to previous behavior - self.key = old_key - self.source_type = old_source_type - self.source_type_str = old_source_type_str - - for field in fields_to_populate: - if field == 'source_type': - fields[field] = self.source_type - elif field == 'key': - fields[field] = self.key - elif field in ('name', 'directory'): - fields[field] = value - return append_uri_params(url, fields) - - -class EditSourceMixin: - model = Source - form_class = SourceForm - errors = { - 'invalid_media_format': _('Invalid media format, the media format contains ' - 'errors or is empty. Check the table at the end of ' - 'this page for valid media name variables'), - 'dir_outside_dlroot': _('You cannot specify a directory outside of the ' - 'base directory (%BASEDIR%)') - } - - def form_valid(self, form: Form): - # Perform extra validation to make sure the media_format is valid - obj = form.save(commit=False) - # temporarily use media_format from the form - saved_media_format = obj.media_format - obj.media_format = form.cleaned_data['media_format'] - example_media_file = obj.get_example_media_format() - obj.media_format = saved_media_format - - if '' == example_media_file: - form.add_error( - 'media_format', - ValidationError(self.errors['invalid_media_format']) - ) - - # Check for suspicious file path(s) - try: - targetCheck = form.cleaned_data['directory'] + '/.virt' - safe_join(settings.DOWNLOAD_ROOT, targetCheck) - except SuspiciousFileOperation: - form.add_error( - 'directory', - ValidationError( - self.errors['dir_outside_dlroot'].replace( - "%BASEDIR%", str(settings.DOWNLOAD_ROOT) - ) - ), - ) - - if form.errors: - return super().form_invalid(form) - - return super().form_valid(form) - - -class AddSourceView(EditSourceMixin, CreateView): - ''' - Adds a new source, optionally takes some initial data querystring values to - prepopulate some of the more unclear values. - ''' - - template_name = 'sync/source-add.html' - - def __init__(self, *args, **kwargs): - self.prepopulated_data = {} - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - # Inject the adjustable media format default - self.prepopulated_data['media_format'] = getattr( - settings, 'MEDIA_FORMATSTR', settings.MEDIA_FORMATSTR_DEFAULT - ) - source_type = request.GET.get('source_type', '') - if source_type and source_type in YouTube_SourceType.values: - self.prepopulated_data['source_type'] = source_type - key = request.GET.get('key', '') - if key: - self.prepopulated_data['key'] = key.strip() - name = request.GET.get('name', '') - if name: - self.prepopulated_data['name'] = slugify(name) - directory = request.GET.get('directory', '') - if directory: - self.prepopulated_data['directory'] = slugify(directory) - return super().dispatch(request, *args, **kwargs) - - def get_initial(self): - initial = super().get_initial() - initial['target_schedule'] = timezone.now().replace( - second=0, microsecond=0, - ) - for k, v in self.prepopulated_data.items(): - initial[k] = v - return initial - - def get_success_url(self): - url = reverse_lazy('sync:source', kwargs={'pk': self.object.pk}) - return append_uri_params(url, {'message': 'source-created'}) - - -class SourceView(DetailView): - - template_name = 'sync/source.html' - model = Source - messages = { - 'source-created': _('Your new source has been created. If you have added a ' - 'very large source such as a channel with hundreds of ' - 'videos it can take several minutes or up to an hour ' - 'for media to start to appear.'), - 'source-updated': _('Your source has been updated.'), - } - - def __init__(self, *args, **kwargs): - self.message = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - message_key = request.GET.get('message', '') - self.message = self.messages.get(message_key, '') - return super().dispatch(request, *args, **kwargs) - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['message'] = self.message - data['errors'] = [] - for error in get_source_completed_tasks(self.object.pk, only_errors=True): - error_message = get_error_message(error) - setattr(error, 'error_message', error_message) - data['errors'].append(error) - data['media'] = Media.objects.filter(source=self.object).order_by('-published').defer('metadata') - return data - - -class UpdateSourceView(EditSourceMixin, UpdateView): - - template_name = 'sync/source-update.html' - - def get_initial(self): - initial = super().get_initial() - when = getattr(self.object, 'target_schedule') or timezone.now() - initial['target_schedule'] = when.replace(second=0, microsecond=0) - return initial - - def get_success_url(self): - url = reverse_lazy('sync:source', kwargs={'pk': self.object.pk}) - return append_uri_params(url, {'message': 'source-updated'}) - - -class DeleteSourceView(DeleteView, FormMixin): - ''' - Confirm the deletion of a source with an option to delete all the media - associated with the source from disk when the source is deleted. - ''' - - template_name = 'sync/source-delete.html' - model = Source - form_class = ConfirmDeleteSourceForm - context_object_name = 'source' - - def post(self, request, *args, **kwargs): - source = self.get_object() - media_source = dict( - uuid=None, - index_schedule=IndexSchedule.NEVER, - download_media=False, - index_videos=False, - index_streams=False, - filter_text=str(source.pk), - target_schedule=source.target_schedule or timezone.now(), - ) - copy_fields = set(map(lambda f: f.name, source._meta.fields)) - set(media_source.keys()) - for k, v in source.__dict__.items(): - if k in copy_fields: - media_source[k] = v - media_source = Source(**media_source) - delete_media_val = request.POST.get('delete_media', False) - delete_media = True if delete_media_val is not False else False - # overload this boolean for our own use - media_source.delete_removed_media = delete_media - # adjust the directory and key on the source to be deleted - source.directory = source.directory + '/deleted' - source.key = source.key + '/deleted' - source.name = f'[Deleting] {source.name}' - source.save(update_fields={'directory', 'key', 'name'}) - source.refresh_from_db() - # save the new media source now that it is not a duplicate - media_source.uuid = None - media_source.save() - media_source.refresh_from_db() - # switch the media to the new source instance - Media.objects.filter(source=source).update(source=media_source) - if delete_media: - directory_path = pathlib.Path(media_source.directory_path) - mkdir_p(directory_path) - (directory_path / '.to_be_removed').touch(exist_ok=True) - return super().post(request, *args, **kwargs) - - def get_success_url(self): - url = reverse_lazy('sync:sources') - return append_uri_params(url, {'message': 'source-deleted'}) - - -class MediaView(ListView): - ''' - A bare list of media added with their states. - ''' - - template_name = 'sync/media.html' - context_object_name = 'media' - paginate_by = settings.MEDIA_PER_PAGE - messages = { - 'filter': _('Viewing media filtered for source: {name}'), - } - - def __init__(self, *args, **kwargs): - self.filter_source = None - self.show_skipped = False - self.only_skipped = False - self.query = None - self.search_description = False - self.sp = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - def is_active(arg, /): - return str(arg).strip().lower() in ( - 'enable', 'enabled', 'on', 'true', 'yes', '1', - ) - def post_or_get(request, /, key, default=None): - return request.POST.get(key) or request.GET.get(key) or default - - filter_by = post_or_get(request, 'filter', '') - if filter_by: - try: - self.filter_source = Source.objects.get(pk=filter_by) - except Source.DoesNotExist: - self.filter_source = None - show_skipped = post_or_get(request, 'show_skipped', '') - if is_active(show_skipped): - self.show_skipped = True - only_skipped = post_or_get(request, 'only_skipped', '') - if is_active(only_skipped): - self.only_skipped = True - self.query = post_or_get(request, 'query') - self.search_description = is_active(post_or_get(request, 'search_description')) - self.sp = post_or_get(request, 'sp') - if self.sp not in ('combined', 'union', 'or',): - self.sp = 'or' - return super().dispatch(request, *args, **kwargs) - - def get_queryset(self): - q = Media.objects.all() - md_qs = Metadata.objects.all() - needle = self.query - - if self.filter_source: - q = q.filter(source=self.filter_source) - m_q = q - if needle and 'combined' == self.sp: - if self.search_description: - q = q.filter( - Q(new_metadata__value__description__icontains=needle) | - Q(key__contains=needle) | - Q(title__icontains=needle) | - Q(new_metadata__value__fulltitle__icontains=needle) - ) - else: - q = q.filter( - Q(key__contains=needle) | - Q(title__icontains=needle) | - Q(new_metadata__value__fulltitle__icontains=needle) - ) - elif needle: - md_q = md_qs.filter( - Q(media__in=m_q) & - ( - Q(key__contains=needle) | - Q(media__title__icontains=needle) | - Q(value__fulltitle__icontains=needle) - ) - ).only('pk') - if 'union' == self.sp: - if self.search_description: - q = q.union(m_q.filter(new_metadata__value__description__icontains=needle).only('pk')) - # We need to be able to filter again, even after using union - q = m_q.filter(pk__in=q.only('pk')) - else: - q = m_q.filter(new_metadata__in=md_q) - else: - if self.search_description: - q = m_q.filter(Q(new_metadata__value__description__icontains=needle) | Q(new_metadata__in=md_q)) - else: - q = m_q.filter(new_metadata__in=md_q) - if self.only_skipped: - q = q.filter(Q(can_download=False) | Q(skip=True) | Q(manual_skip=True)) - elif not self.show_skipped: - q = q.filter(Q(can_download=True) & Q(skip=False) & Q(manual_skip=False)) - - return q.order_by('-published', '-created') - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['message'] = '' - data['source'] = None - if self.filter_source: - message = str(self.messages.get('filter', '')) - data['message'] = message.format(name=self.filter_source.name) - data['source'] = self.filter_source - data['show_skipped'] = self.show_skipped - data['only_skipped'] = self.only_skipped - data['query'] = self.query or str() - data['search_description'] = self.search_description - return data - - -class MediaThumbView(DetailView): - ''' - Shows a media thumbnail. Whitenoise doesn't support post-start media image - serving and the images here are pretty small so just serve them manually. This - isn't fast, but it's not likely to be a serious bottleneck. - ''' - - model = Media - - def get(self, request, *args, **kwargs): - media = self.get_object() - # Thumbnail media is never updated so we can ask the browser to cache it - # for ages, 604800 = 7 days - max_age = 604800 - if media.thumb_file_exists: - thumb_path = pathlib.Path(media.thumb.path) - thumb = thumb_path.read_bytes() - content_type = 'image/jpeg' - else: - # No thumbnail on disk, return a blank 1x1 gif - thumb = b64decode('R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAA' - 'AAAABAAEAAAICTAEAOw==') - content_type = 'image/gif' - max_age = 600 - response = HttpResponse(thumb, content_type=content_type) - - response['Cache-Control'] = f'public, max-age={max_age}' - return response - - -class MediaItemView(DetailView): - ''' - A single media item overview page. - ''' - - template_name = 'sync/media-item.html' - model = Media - messages = { - 'thumbnail': _('Thumbnail has been scheduled to redownload'), - 'redownloading': _('Media file has been deleted and scheduled to redownload'), - 'skipped': _('Media file has been deleted and marked to never download'), - 'enabled': _('Media has been re-enabled and will be downloaded'), - } - - def __init__(self, *args, **kwargs): - self.message = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - message_key = request.GET.get('message', '') - self.message = self.messages.get(message_key, '') - return super().dispatch(request, *args, **kwargs) - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['message'] = self.message - combined_exact, combined_format = self.object.get_best_combined_format() - audio_exact, audio_format = self.object.get_best_audio_format() - video_exact, video_format = self.object.get_best_video_format() - data['combined_format_dict'] = {'id': str(combined_format)} - data['audio_format_dict'] = {'id': str(audio_format)} - data['video_format_dict'] = {'id': str(video_format)} - context_keys = { k for k in data.keys() if k.endswith('_format_dict') } - for fmt in self.object.iter_formats(): - for k in context_keys: - v = data[k] - if v.get('id') == fmt.get('id'): - data[k] = fmt - task = get_media_download_task(self.object.pk) - data['task'] = task - data['download_state'] = self.object.get_download_state(task) - data['download_state_icon'] = self.object.get_download_state_icon(task) - data['combined_exact'] = combined_exact - data['combined_format'] = combined_format - data['audio_exact'] = audio_exact - data['audio_format'] = audio_format - data['video_exact'] = video_exact - data['video_format'] = video_format - data['youtube_dl_format'] = self.object.get_format_str() - data['filename_path'] = pathlib.Path(self.object.filename) - data['media_file_path'] = pathlib.Path(self.object.media_file.path) if self.object.media_file else None - return data - - def get(self, *args, **kwargs): - if args[0].path.startswith("/media-thumb-redownload/"): - media = Media.objects.get(pk=kwargs["pk"]) - if media is None: - return HttpResponseNotFound() - - TaskHistory.schedule( - download_media_image, - str(media.pk), - media.thumbnail, - priority=1+download_media_image.settings.get('default_priority', 0), - vn_fmt=_('Redownload thumbnail for "{}": {}'), - vn_args=( - media.key, - media.name, - ), - ) - url = reverse_lazy('sync:media-item', kwargs={'pk': media.pk}) - url = append_uri_params(url, {'message': 'thumbnail'}) - return HttpResponseRedirect(url) - else: - return super().get(self, *args, **kwargs) - - -class MediaRedownloadView(FormView, SingleObjectMixin): - ''' - Confirm that the media file should be deleted and redownloaded. - ''' - - template_name = 'sync/media-redownload.html' - form_class = RedownloadMediaForm - model = Media - - def __init__(self, *args, **kwargs): - self.object = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - return super().dispatch(request, *args, **kwargs) - - def form_valid(self, form): - # Try to download manually, when can_download was true - media = self.object - if media.can_download: - attempted_key = '_refresh_formats_attempted' - media.save_to_metadata(attempted_key, 1) - refreshed_key = 'formats_epoch' - media.save_to_metadata(refreshed_key, 1) - TaskHistory.schedule( - refresh_formats, - str(media.pk), - priority=90, - remove_duplicates=False, - retries=1, - retry_delay=300, - vn_fmt=_('Refreshing formats (manually) for "{}"'), - vn_args=(media.key,), - ) - TaskHistory.schedule( - download_media_file, - str(media.pk), - override=True, - priority=90, - remove_duplicates=True, - delay=10, - retries=3, - retry_delay=600, - vn_fmt=_('Downloading media (manually) for "{}"'), - vn_args=(media.name,), - ) - # If the thumbnail file exists on disk, delete it - if self.object.thumb_file_exists: - delete_file(self.object.thumb.path) - self.object.thumb = None - # If the media file exists on disk, delete it - if self.object.media_file_exists: - delete_file(self.object.media_file.path) - self.object.media_file = None - # If the media has an associated thumbnail copied, also delete it - delete_file(self.object.thumbpath) - # If the media has an associated NFO file with it, also delete it - delete_file(self.object.nfopath) - # Reset all download data - self.object.downloaded = False - self.object.downloaded_audio_codec = None - self.object.downloaded_video_codec = None - self.object.downloaded_container = None - self.object.downloaded_fps = None - self.object.downloaded_hdr = False - self.object.downloaded_filesize = None - # Mark it as not skipped - self.object.skip = False - self.object.manual_skip = False - # Saving here will trigger the post_create signals to schedule new tasks - self.object.save() - return super().form_valid(form) - - def get_success_url(self): - url = reverse_lazy('sync:media-item', kwargs={'pk': self.object.pk}) - return append_uri_params(url, {'message': 'redownloading'}) - - -class MediaSkipView(FormView, SingleObjectMixin): - ''' - Confirm that the media file should be deleted and marked to skip. - ''' - - template_name = 'sync/media-skip.html' - form_class = SkipMediaForm - model = Media - - def __init__(self, *args, **kwargs): - self.object = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - return super().dispatch(request, *args, **kwargs) - - def form_valid(self, form): - # If the media file exists on disk, delete it - if self.object.media_file_exists: - # Delete all files which contains filename - filepath = self.object.media_file.path - barefilepath, fileext = os.path.splitext(filepath) - # Get all files that start with the bare file path - all_related_files = glob.glob(f'{barefilepath}.*') - for file in all_related_files: - delete_file(file) - # Reset all download data - self.object.metadata_clear() - self.object.downloaded = False - self.object.downloaded_audio_codec = None - self.object.downloaded_video_codec = None - self.object.downloaded_container = None - self.object.downloaded_fps = None - self.object.downloaded_hdr = False - self.object.downloaded_filesize = None - # Mark it to be skipped - self.object.skip = True - self.object.manual_skip = True - self.object.save() - return super().form_valid(form) - - def get_success_url(self): - url = reverse_lazy('sync:media-item', kwargs={'pk': self.object.pk}) - return append_uri_params(url, {'message': 'skipped'}) - - -class MediaEnableView(FormView, SingleObjectMixin): - ''' - Confirm that the media item should be re-enabled (marked as unskipped). - ''' - - template_name = 'sync/media-enable.html' - form_class = EnableMediaForm - model = Media - - def __init__(self, *args, **kwargs): - self.object = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - return super().dispatch(request, *args, **kwargs) - - def form_valid(self, form): - # Mark it as not skipped - self.object.skip = False - self.object.manual_skip = False - self.object.save() - return super().form_valid(form) - - def get_success_url(self): - url = reverse_lazy('sync:media-item', kwargs={'pk': self.object.pk}) - return append_uri_params(url, {'message': 'enabled'}) - - -class MediaContent(DetailView): - ''' - Redirect to nginx to download the file - ''' - model = Media - - def __init__(self, *args, **kwargs): - self.object = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - # development direct file stream - DO NOT USE PRODUCTIVLY - if settings.DEBUG and 'runserver' in sys.argv: - # get media URL - pth = self.object.media_file.url - # remove "/media-data/" - pth = pth.split("/media-data/",1)[1] - # remove "/" (incase of absolute path) - pth = pth.split(str(settings.DOWNLOAD_ROOT).lstrip("/"),1) - - # if we do not have a "/" at the beginning, it is not a absolute path... - if len(pth) > 1: - pth = pth[1] - else: - pth = pth[0] - - - # build final path - filepth = pathlib.Path(str(settings.DOWNLOAD_ROOT) + pth) - - if filepth.exists(): - # return file - response = FileResponse(open(filepth,'rb')) - return response - else: - return HttpResponseNotFound() - - else: - headers = { - 'Content-Type': self.object.content_type, - 'X-Accel-Redirect': self.object.media_file.url, - } - return HttpResponse(headers=headers) - - -class TasksView(ListView): - ''' - A list of tasks queued to be completed. This is, for example, scraping for new - media or downloading media. - ''' - - template_name = 'sync/tasks.html' - context_object_name = 'tasks' - paginate_by = settings.TASKS_PER_PAGE - messages = { - 'filter': _('Viewing tasks filtered for source: {name}'), - 'reset': _('All tasks have been reset'), - 'revoked': _('Revoked task: {task_id}'), - 'scheduled': _('Scheduled task: {name}'), - } - - def __init__(self, *args, **kwargs): - self.filter_source = None - self.message = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - message_key = request.GET.get('message', '') - self.message = self.messages.get(message_key, '') - filter_by = request.GET.get('filter', '') - if filter_by: - try: - self.filter_source = Source.objects.get(pk=filter_by) - except Source.DoesNotExist: - self.filter_source = None - else: - if not message_key or 'filter' == message_key: - message = self.messages.get('filter', '') - self.message = message.format( - name=self.filter_source.name - ) - - if message_key in ('revoked', 'scheduled'): - fmt_vars = dict( - pk=request.GET.get('pk', 'Unknown'), - task_id=request.GET.get('task_id', 'Unknown'), - ) - try: - task = TaskHistory.objects.get(pk=fmt_vars['pk']) - except TaskHistory.DoesNotExist: - fmt_vars['name'] = fmt_vars['pk'] - else: - fmt_vars['name'] = task.verbose_name or task.task_id or task.pk - fmt_vars['task_id'] = task.task_id - self.message = self.message.format(**fmt_vars) - - return super().dispatch(request, *args, **kwargs) - - def get_queryset(self): - qs = get_waiting_tasks() - if self.filter_source: - params_prefix=f'[["{self.filter_source.pk}"' - qs = qs.filter(task_params__istartswith=params_prefix) - return qs.order_by( - '-priority', - 'scheduled_at', - 'end_at', - ) - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - now_dt = timezone.now() - scheduled_qs = get_waiting_tasks() - # Huey removes running tasks, - # so the waiting tasks will not include them. - running_qs = get_running_tasks(now_dt) - errors_qs = scheduled_qs.filter( - attempts__gt=0 - ).exclude(last_error__exact='') - - # Add to context data from ListView - data['message'] = self.message - data['source'] = self.filter_source - data['running'] = list() - data['errors'] = list() - data['total_errors'] = errors_qs.count() - data['scheduled'] = list() - data['total_scheduled'] = scheduled_qs.count() - data['wait_for_database_queue'] = False - - def add_to_task(task): - setattr(task, 'run_now', task.scheduled_at < now_dt) - obj, url = map_task_to_instance(task) - if obj: - setattr(task, 'instance', obj) - setattr(task, 'url', url) - if task.has_error(): - error_message = get_error_message(task) - setattr(task, 'error_message', error_message) - return 'error' - return True and obj - - for task in running_qs: - if task in data['running']: - continue - add_to_task(task) - data['running'].append(task) - - # show all the errors when they fit on one page - if (data['total_errors'] + len(data['running'])) < self.paginate_by: - for task in errors_qs: - if task in data['running']: - continue - mapped = add_to_task(task) - if 'error' == mapped: - data['errors'].append(task) - elif mapped: - data['scheduled'].append(task) - - for task in data['tasks']: - already_added = ( - task in data['running'] or - task in data['errors'] or - task in data['scheduled'] - ) - if already_added: - continue - mapped = add_to_task(task) - if 'error' == mapped: - data['errors'].append(task) - elif mapped or settings.DEBUG: - data['scheduled'].append(task) - - sort_keys = ( - # key, reverse - ('priority', True), - ('scheduled_at', False), - ('run_now', True), - ) - data['errors'] = multi_key_sort(data['errors'], sort_keys, attr=True) - data['scheduled'] = multi_key_sort(data['scheduled'], sort_keys, attr=True) - - return data - - def paginate_queryset(self, queryset, page_size): - """Paginate the queryset, if needed.""" - paginator = self.get_paginator( - queryset, - page_size, - orphans=self.get_paginate_orphans(), - allow_empty_first_page=self.get_allow_empty(), - ) - 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: - pass - else: - if page_number > paginator.num_pages: - self.kwargs[page_kwarg] = 'last' - return super().paginate_queryset(queryset, page_size) - - def get(self, *args, **kwargs): - path = args[0].path - if path.startswith('/task/') and path.endswith('/cancel'): - try: - task = TaskHistory.objects.get(pk=kwargs["pk"]) - except TaskHistory.DoesNotExist: - return HttpResponseNotFound() - else: - huey_queue_names = (DJANGO_HUEY or {}).get('queues', {}) - huey_queues = { q.name: q for q in map(get_queue, huey_queue_names) } - q = huey_queues.get(task.queue) - if q is None: - msg = f'TasksView: queue not found: {task.pk=} {task.queue=}' - log.warning(msg) - return HttpResponseNotFound() - # revoke the task we want to cancel - q.revoke_by_id(id=task.task_id, revoke_once=True) - vn = task.verbose_name or task.task_id or task.pk - if not vn.startswith('[revoked] '): - task.verbose_name = f'[revoked] {vn}' - task.save() - return HttpResponseRedirect(append_uri_params( - reverse_lazy('sync:tasks'), - dict( - message='revoked', - pk=str(task.pk), - task_id=str(task.task_id), - ), - )) - else: - return super().get(self, *args, **kwargs) - -class CompletedTasksView(ListView): - ''' - List of tasks which have been completed with an optional per-source filter. - ''' - - template_name = 'sync/tasks-completed.html' - context_object_name = 'tasks' - paginate_by = settings.TASKS_PER_PAGE - messages = { - 'filter': _('Viewing tasks filtered for source: {name}'), - } - - def __init__(self, *args, **kwargs): - self.filter_source = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - filter_by = request.GET.get('filter', '') - if filter_by: - try: - self.filter_source = Source.objects.get(pk=filter_by) - except Source.DoesNotExist: - self.filter_source = None - return super().dispatch(request, *args, **kwargs) - - def get_queryset(self): - qs = TaskHistory.objects.filter( - start_at__isnull=False, - end_at__gt=F('start_at'), - ) - if self.filter_source: - params_prefix=f'[["{self.filter_source.pk}"' - qs = qs.filter(task_params__istartswith=params_prefix) - return qs.order_by('-end_at') - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - for task in data['tasks']: - if task.has_error(): - error_message = get_error_message(task) - setattr(task, 'error_message', error_message) - data['message'] = '' - data['source'] = self.filter_source - if self.filter_source: - message = str(self.messages.get('filter', '')) - data['message'] = message.format(name=self.filter_source.name) - return data - - -class ResetTasks(FormView): - ''' - Confirm that all tasks should be reset. As all tasks are triggered from - signals by checking for files existing etc. this can be done by just deleting - all tasks and then calling every Source objects .save() method. - ''' - - template_name = 'sync/tasks-reset.html' - form_class = ResetTasksForm - - def form_valid(self, form): - # Delete all tasks - huey_queue_names = (DJANGO_HUEY or {}).get('queues', {}) - for queue_name in huey_queue_names: - h_q_reset_tasks(queue_name) - # Iter all tasks - for source in Source.objects.all(): - check_source_directory_exists(str(source.pk)) - # This also chains down to call each Media objects .save() as well - source.save() - return super().form_valid(form) - - def get_success_url(self): - url = reverse_lazy('sync:tasks') - return append_uri_params(url, {'message': 'reset'}) - - -class TaskScheduleView(FormView, SingleObjectMixin): - ''' - Confirm that the task should be re-scheduled. - ''' - - template_name = 'sync/task-schedule.html' - form_class = ScheduleTaskForm - model = TaskHistory - context_object_name = 'task' - errors = dict( - invalid_when=_('The type ({}) was incorrect.'), - when_before_now=_('The date and time must be in the future.'), - ) - - def __init__(self, *args, **kwargs): - self.now = timezone.now() - self.object = None - self.timestamp = None - self.when = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - self.now = timezone.now() - self.object = self.get_object() - self.timestamp = kwargs.get('timestamp') - try: - self.when = timestamp_to_datetime(self.timestamp) - except AssertionError: - self.when = None - if self.when is None: - self.when = self.now - # Use the next minute and zero seconds - # The web browser does not select seconds by default - self.when = self.when.replace(second=0) + timezone.timedelta(minutes=1) - return super().dispatch(request, *args, **kwargs) - - def get_initial(self): - initial = super().get_initial() - initial['now'] = self.now - initial['when'] = self.when - return initial - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['now'] = self.now - data['when'] = self.when - return data - - def get_success_url(self): - return append_uri_params( - reverse_lazy('sync:tasks'), - dict( - message='scheduled', - pk=str(self.object.pk), - task_id=str(self.object.task_id), - ), - ) - - def form_valid(self, form): - when = form.cleaned_data.get('when') - - if not isinstance(when, self.now.__class__): - form.add_error( - 'when', - ValidationError( - self.errors['invalid_when'].format( - type(when), - ), - ), - ) - if when < self.now: - form.add_error( - 'when', - ValidationError(self.errors['when_before_now']), - ) - - if form.errors: - return super().form_invalid(form) - - pk = self.object.pk - queue = self.object.queue - task_id = self.object.task_id - huey_queue_names = (DJANGO_HUEY or {}).get('queues', {}) - huey_queues = { q.name: q for q in map(get_queue, huey_queue_names) } - q = huey_queues.get(queue) - if q is None: - msg = f'TaskScheduleView: queue not found: {pk=} {queue=}' - log.warning(msg) - else: - eta = max(self.now, when) - if q.reschedule(task_id, eta): - vn = self.object.verbose_name or task_id or pk - self.object.verbose_name = f'[revoked] {vn}' - self.object.save() - else: - msg = f'TaskScheduleView: task not found: {pk=} {task_id=}' - log.warning(msg) - - return super().form_valid(form) - - -class MediaServersView(ListView): - ''' - List of media servers which have been added. - ''' - - template_name = 'sync/mediaservers.html' - context_object_name = 'mediaservers' - types_object = MediaServerType - messages = { - 'deleted': _('Your selected media server has been deleted.'), - } - - def __init__(self, *args, **kwargs): - self.message = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - message_key = request.GET.get('message', '') - self.message = self.messages.get(message_key, '') - return super().dispatch(request, *args, **kwargs) - - def get_queryset(self): - return MediaServer.objects.all().order_by('host', 'port') - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['message'] = self.message - data['media_server_types'] = self.types_object.members_list() - return data - - -class AddMediaServerView(FormView): - ''' - Adds a new media server. The form is switched out to whatever matches the - server type. - ''' - - template_name = 'sync/mediaserver-add.html' - server_types = MediaServerType.long_types() - server_type_names = dict(MediaServerType.choices) - forms = MediaServerType.forms_dict() - - def __init__(self, *args, **kwargs): - self.server_type = None - self.model_class = None - self.object = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - server_type_str = kwargs.get('server_type', '') - self.server_type = self.server_types.get(server_type_str) - if not self.server_type: - raise Http404 - self.form_class = self.forms.get(self.server_type) - if not self.form_class: - raise Http404 - self.model_class = MediaServer(server_type=self.server_type) - return super().dispatch(request, *args, **kwargs) - - def form_valid(self, form): - # Assign mandatory fields, bundle other fields into options - mediaserver = MediaServer(server_type=self.server_type) - options = dict() - model_fields = [field.name for field in MediaServer._meta.fields] - for field_name, field_value in form.cleaned_data.items(): - if field_name in model_fields: - setattr(mediaserver, field_name, field_value) - else: - options[field_name] = field_value - mediaserver.options = options - # Test the media server details are valid - try: - mediaserver.validate() - except ValidationError as e: - form.add_error(None, e) - # Check if validation detected any errors - if form.errors: - return super().form_invalid(form) - # All good, try to save and return - try: - mediaserver.save() - except IntegrityError: - form.add_error( - None, - (f'A media server already exists with the host and port ' - f'{mediaserver.host}:{mediaserver.port}') - ) - # Check if saving caused any errors - if form.errors: - return super().form_invalid(form) - # All good! - self.object = mediaserver - return super().form_valid(form) - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['server_type'] = self.server_type - data['server_type_long'] = self.server_types.get(self.server_type) - data['server_type_name'] = self.server_type_names.get(self.server_type) - data['server_help'] = self.model_class.get_help_html() - return data - - def get_success_url(self): - url = reverse_lazy('sync:mediaserver', kwargs={'pk': self.object.pk}) - return append_uri_params(url, {'message': 'created'}) - - -class MediaServerView(DetailView): - ''' - A single media server overview page. - ''' - - template_name = 'sync/mediaserver.html' - model = MediaServer - private_options = ('token',) - messages = { - 'created': _('Your media server has been successfully added'), - } - - def __init__(self, *args, **kwargs): - self.message = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - message_key = request.GET.get('message', '') - self.message = self.messages.get(message_key, '') - return super().dispatch(request, *args, **kwargs) - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['message'] = self.message - data['private_options'] = self.private_options - return data - - -class DeleteMediaServerView(DeleteView, FormMixin): - ''' - Confirms deletion and then deletes a media server. - ''' - - template_name = 'sync/mediaserver-delete.html' - model = MediaServer - form_class = ConfirmDeleteMediaServerForm - context_object_name = 'mediaserver' - - def get_success_url(self): - url = reverse_lazy('sync:mediaservers') - return append_uri_params(url, {'message': 'deleted'}) - - -class UpdateMediaServerView(FormView, SingleObjectMixin): - ''' - Adds a new media server. The form is switched out to whatever matches the - server type. - ''' - - template_name = 'sync/mediaserver-update.html' - model = MediaServer - forms = MediaServerType.forms_dict() - - def __init__(self, *args, **kwargs): - self.object = None - super().__init__(*args, **kwargs) - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - self.form_class = self.forms.get(self.object.server_type, None) - if not self.form_class: - raise Http404 - return super().dispatch(request, *args, **kwargs) - - def get_initial(self): - initial = super().get_initial() - for field in self.object._meta.fields: - if field.name in self.form_class.declared_fields: - initial[field.name] = getattr(self.object, field.name) - for option_key, option_val in self.object.options.items(): - if option_key in self.form_class.declared_fields: - initial[option_key] = option_val - return initial - - def form_valid(self, form): - # Assign mandatory fields, bundle other fields into options - options = dict() - model_fields = [field.name for field in MediaServer._meta.fields] - for field_name, field_value in form.cleaned_data.items(): - if field_name in model_fields: - setattr(self.object, field_name, field_value) - else: - options[field_name] = field_value - self.object.options = options - # Test the media server details are valid - try: - self.object.validate() - except ValidationError as e: - form.add_error(None, e) - # Check if validation detected any errors - if form.errors: - return super().form_invalid(form) - # All good, try to save and return - try: - self.object.save() - except IntegrityError: - form.add_error( - None, - (f'A media server already exists with the host and port ' - f'{self.object.host}:{self.object.port}') - ) - # Check if saving caused any errors - if form.errors: - return super().form_invalid(form) - # All good! - return super().form_valid(form) - - def get_context_data(self, *args, **kwargs): - data = super().get_context_data(*args, **kwargs) - data['server_help'] = self.object.get_help_html - return data - - def get_success_url(self): - url = reverse_lazy('sync:mediaserver', kwargs={'pk': self.object.pk}) - return append_uri_params(url, {'message': 'updated'}) diff --git a/tubesync/sync/views/__init__.py b/tubesync/sync/views/__init__.py new file mode 100644 index 000000000..b62e17475 --- /dev/null +++ b/tubesync/sync/views/__init__.py @@ -0,0 +1,24 @@ +from .dashboard import DashboardView +from .sources import (SourcesView, ValidateSourceView, AddSourceView, + SourceView, UpdateSourceView, DeleteSourceView) +from .media import (MediaView, MediaThumbView, MediaItemView, + MediaRedownloadView, MediaSkipView, MediaEnableView, + MediaContent) +from .tasks import (TasksView, CompletedTasksView, ResetTasks, + TaskScheduleView) +from .mediaservers import (MediaServersView, AddMediaServerView, + MediaServerView, DeleteMediaServerView, + UpdateMediaServerView) + +__all__ = [ + 'DashboardView', + 'SourcesView', 'ValidateSourceView', 'AddSourceView', + 'SourceView', 'UpdateSourceView', 'DeleteSourceView', + 'MediaView', 'MediaThumbView', 'MediaItemView', + 'MediaRedownloadView', 'MediaSkipView', 'MediaEnableView', + 'MediaContent', + 'TasksView', 'CompletedTasksView', 'ResetTasks', + 'TaskScheduleView', + 'MediaServersView', 'AddMediaServerView', 'MediaServerView', + 'DeleteMediaServerView', 'UpdateMediaServerView', +] diff --git a/tubesync/sync/views/dashboard.py b/tubesync/sync/views/dashboard.py new file mode 100644 index 000000000..a26cd6fef --- /dev/null +++ b/tubesync/sync/views/dashboard.py @@ -0,0 +1,78 @@ +import os +import pathlib +from django.conf import settings +from django.views.generic import TemplateView +from django.db import connection +from django.db.models import F, Q, Sum +from django.utils import timezone +from common.models import TaskHistory +from .utils import get_waiting_tasks +from ..models import Source, Media +from ..choices import Val, SourceResolution + + +class DashboardView(TemplateView): + ''' + The dashboard shows non-interactive totals and summaries. + ''' + + template_name = 'sync/dashboard.html' + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['now'] = timezone.now() + # Sources + data['num_sources'] = Source.objects.all().count() + data['num_video_sources'] = Source.objects.filter( + ~Q(source_resolution=Val(SourceResolution.AUDIO)) + ).count() + data['num_audio_sources'] = data['num_sources'] - data['num_video_sources'] + data['num_failed_sources'] = Source.objects.filter(has_failed=True).count() + # Media + data['num_media'] = Media.objects.all().count() + data['num_downloaded_media'] = Media.objects.filter(downloaded=True).count() + # Tasks + completed_qs = TaskHistory.objects.filter( + start_at__isnull=False, + end_at__gt=F('start_at'), + ) + waiting_qs = get_waiting_tasks() + data['num_tasks'] = waiting_qs.count() + data['num_completed_tasks'] = completed_qs.count() + # Disk usage + disk_usage = Media.objects.filter( + downloaded=True, downloaded_filesize__isnull=False + ).defer('metadata').aggregate(Sum('downloaded_filesize')) + data['disk_usage_bytes'] = disk_usage['downloaded_filesize__sum'] + if not data['disk_usage_bytes']: + data['disk_usage_bytes'] = 0 + if data['disk_usage_bytes'] and data['num_downloaded_media']: + data['average_bytes_per_media'] = round(data['disk_usage_bytes'] / + data['num_downloaded_media']) + else: + data['average_bytes_per_media'] = 0 + # Latest downloads + data['latest_downloads'] = Media.objects.filter( + downloaded=True, + download_date__isnull=False, + downloaded_filesize__isnull=False, + ).defer('metadata').order_by('-download_date')[:10] + # Largest downloads + data['largest_downloads'] = Media.objects.filter( + downloaded=True, downloaded_filesize__isnull=False + ).defer('metadata').order_by('-downloaded_filesize')[:10] + # UID and GID + data['uid'] = os.getuid() + data['gid'] = os.getgid() + # Config and download locations + data['config_dir'] = str(settings.CONFIG_BASE_DIR) + data['downloads_dir'] = str(settings.DOWNLOAD_ROOT) + data['database_connection'] = settings.DATABASE_CONNECTION_STR + # Add the database filesize when using db.sqlite3 + data['database_filesize'] = None + if 'sqlite' == connection.vendor: + db_name = str(connection.get_connection_params().get('database', '')) + db_path = pathlib.Path(db_name) if '/' == db_name[0] else None + if db_path: + data['database_filesize'] = db_path.stat().st_size + return data diff --git a/tubesync/sync/views/media.py b/tubesync/sync/views/media.py new file mode 100644 index 000000000..4f88d1cb0 --- /dev/null +++ b/tubesync/sync/views/media.py @@ -0,0 +1,444 @@ +import glob +import os +from base64 import b64decode +import pathlib +import sys +from django.conf import settings +from django.http import FileResponse, HttpResponseNotFound, HttpResponseRedirect +from django.views.generic import ListView, DetailView +from django.views.generic.edit import FormView +from django.views.generic.detail import SingleObjectMixin +from django.http import HttpResponse +from django.urls import reverse_lazy +from django.db.models import Q +from django.utils.translation import gettext_lazy as _ +from common.models import TaskHistory +from common.utils import append_uri_params +from ..models import Source, Media, Metadata +from ..forms import RedownloadMediaForm, SkipMediaForm, EnableMediaForm +from ..utils import delete_file +from ..tasks import ( + get_media_download_task, download_media_image, download_media_file, + refresh_formats, +) + + +class MediaView(ListView): + ''' + A bare list of media added with their states. + ''' + + template_name = 'sync/media.html' + context_object_name = 'media' + paginate_by = settings.MEDIA_PER_PAGE + messages = { + 'filter': _('Viewing media filtered for source: {name}'), + } + + def __init__(self, *args, **kwargs): + self.filter_source = None + self.show_skipped = False + self.only_skipped = False + self.query = None + self.search_description = False + self.sp = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + def is_active(arg, /): + return str(arg).strip().lower() in ( + 'enable', 'enabled', 'on', 'true', 'yes', '1', + ) + def post_or_get(request, /, key, default=None): + return request.POST.get(key) or request.GET.get(key) or default + + filter_by = post_or_get(request, 'filter', '') + if filter_by: + try: + self.filter_source = Source.objects.get(pk=filter_by) + except Source.DoesNotExist: + self.filter_source = None + show_skipped = post_or_get(request, 'show_skipped', '') + if is_active(show_skipped): + self.show_skipped = True + only_skipped = post_or_get(request, 'only_skipped', '') + if is_active(only_skipped): + self.only_skipped = True + self.query = post_or_get(request, 'query') + self.search_description = is_active(post_or_get(request, 'search_description')) + self.sp = post_or_get(request, 'sp') + if self.sp not in ('combined', 'union', 'or',): + self.sp = 'or' + return super().dispatch(request, *args, **kwargs) + + def get_queryset(self): + q = Media.objects.all() + md_qs = Metadata.objects.all() + needle = self.query + + if self.filter_source: + q = q.filter(source=self.filter_source) + m_q = q + if needle and 'combined' == self.sp: + if self.search_description: + q = q.filter( + Q(new_metadata__value__description__icontains=needle) | + Q(key__contains=needle) | + Q(title__icontains=needle) | + Q(new_metadata__value__fulltitle__icontains=needle) + ) + else: + q = q.filter( + Q(key__contains=needle) | + Q(title__icontains=needle) | + Q(new_metadata__value__fulltitle__icontains=needle) + ) + elif needle: + md_q = md_qs.filter( + Q(media__in=m_q) & + ( + Q(key__contains=needle) | + Q(media__title__icontains=needle) | + Q(value__fulltitle__icontains=needle) + ) + ).only('pk') + if 'union' == self.sp: + if self.search_description: + q = q.union(m_q.filter(new_metadata__value__description__icontains=needle).only('pk')) + # We need to be able to filter again, even after using union + q = m_q.filter(pk__in=q.only('pk')) + else: + q = m_q.filter(new_metadata__in=md_q) + else: + if self.search_description: + q = m_q.filter(Q(new_metadata__value__description__icontains=needle) | Q(new_metadata__in=md_q)) + else: + q = m_q.filter(new_metadata__in=md_q) + if self.only_skipped: + q = q.filter(Q(can_download=False) | Q(skip=True) | Q(manual_skip=True)) + elif not self.show_skipped: + q = q.filter(Q(can_download=True) & Q(skip=False) & Q(manual_skip=False)) + + return q.order_by('-published', '-created') + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['message'] = '' + data['source'] = None + if self.filter_source: + message = str(self.messages.get('filter', '')) + data['message'] = message.format(name=self.filter_source.name) + data['source'] = self.filter_source + data['show_skipped'] = self.show_skipped + data['only_skipped'] = self.only_skipped + data['query'] = self.query or str() + data['search_description'] = self.search_description + return data + + +class MediaThumbView(DetailView): + ''' + Shows a media thumbnail. Whitenoise doesn't support post-start media image + serving and the images here are pretty small so just serve them manually. This + isn't fast, but it's not likely to be a serious bottleneck. + ''' + + model = Media + + def get(self, request, *args, **kwargs): + media = self.get_object() + # Thumbnail media is never updated so we can ask the browser to cache it + # for ages, 604800 = 7 days + max_age = 604800 + if media.thumb_file_exists: + thumb_path = pathlib.Path(media.thumb.path) + thumb = thumb_path.read_bytes() + content_type = 'image/jpeg' + else: + # No thumbnail on disk, return a blank 1x1 gif + thumb = b64decode('R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAA' + 'AAAABAAEAAAICTAEAOw==') + content_type = 'image/gif' + max_age = 600 + response = HttpResponse(thumb, content_type=content_type) + + response['Cache-Control'] = f'public, max-age={max_age}' + return response + + +class MediaItemView(DetailView): + ''' + A single media item overview page. + ''' + + template_name = 'sync/media-item.html' + model = Media + messages = { + 'thumbnail': _('Thumbnail has been scheduled to redownload'), + 'redownloading': _('Media file has been deleted and scheduled to redownload'), + 'skipped': _('Media file has been deleted and marked to never download'), + 'enabled': _('Media has been re-enabled and will be downloaded'), + } + + def __init__(self, *args, **kwargs): + self.message = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + message_key = request.GET.get('message', '') + self.message = self.messages.get(message_key, '') + return super().dispatch(request, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['message'] = self.message + combined_exact, combined_format = self.object.get_best_combined_format() + audio_exact, audio_format = self.object.get_best_audio_format() + video_exact, video_format = self.object.get_best_video_format() + data['combined_format_dict'] = {'id': str(combined_format)} + data['audio_format_dict'] = {'id': str(audio_format)} + data['video_format_dict'] = {'id': str(video_format)} + context_keys = { k for k in data.keys() if k.endswith('_format_dict') } + for fmt in self.object.iter_formats(): + for k in context_keys: + v = data[k] + if v.get('id') == fmt.get('id'): + data[k] = fmt + task = get_media_download_task(self.object.pk) + data['task'] = task + data['download_state'] = self.object.get_download_state(task) + data['download_state_icon'] = self.object.get_download_state_icon(task) + data['combined_exact'] = combined_exact + data['combined_format'] = combined_format + data['audio_exact'] = audio_exact + data['audio_format'] = audio_format + data['video_exact'] = video_exact + data['video_format'] = video_format + data['youtube_dl_format'] = self.object.get_format_str() + data['filename_path'] = pathlib.Path(self.object.filename) + data['media_file_path'] = pathlib.Path(self.object.media_file.path) if self.object.media_file else None + return data + + def get(self, *args, **kwargs): + if args[0].path.startswith("/media-thumb-redownload/"): + media = Media.objects.get(pk=kwargs["pk"]) + if media is None: + return HttpResponseNotFound() + + TaskHistory.schedule( + download_media_image, + str(media.pk), + media.thumbnail, + priority=1+download_media_image.settings.get('default_priority', 0), + vn_fmt=_('Redownload thumbnail for "{}": {}'), + vn_args=( + media.key, + media.name, + ), + ) + url = reverse_lazy('sync:media-item', kwargs={'pk': media.pk}) + url = append_uri_params(url, {'message': 'thumbnail'}) + return HttpResponseRedirect(url) + else: + return super().get(self, *args, **kwargs) + + +class MediaRedownloadView(FormView, SingleObjectMixin): + ''' + Confirm that the media file should be deleted and redownloaded. + ''' + + template_name = 'sync/media-redownload.html' + form_class = RedownloadMediaForm + model = Media + + def __init__(self, *args, **kwargs): + self.object = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + self.object = self.get_object() + return super().dispatch(request, *args, **kwargs) + + def form_valid(self, form): + # Try to download manually, when can_download was true + media = self.object + if media.can_download: + attempted_key = '_refresh_formats_attempted' + media.save_to_metadata(attempted_key, 1) + refreshed_key = 'formats_epoch' + media.save_to_metadata(refreshed_key, 1) + TaskHistory.schedule( + refresh_formats, + str(media.pk), + priority=90, + remove_duplicates=False, + retries=1, + retry_delay=300, + vn_fmt=_('Refreshing formats (manually) for "{}"'), + vn_args=(media.key,), + ) + TaskHistory.schedule( + download_media_file, + str(media.pk), + override=True, + priority=90, + remove_duplicates=True, + delay=10, + retries=3, + retry_delay=600, + vn_fmt=_('Downloading media (manually) for "{}"'), + vn_args=(media.name,), + ) + # If the thumbnail file exists on disk, delete it + if self.object.thumb_file_exists: + delete_file(self.object.thumb.path) + self.object.thumb = None + # If the media file exists on disk, delete it + if self.object.media_file_exists: + delete_file(self.object.media_file.path) + self.object.media_file = None + # If the media has an associated thumbnail copied, also delete it + delete_file(self.object.thumbpath) + # If the media has an associated NFO file with it, also delete it + delete_file(self.object.nfopath) + # Reset all download data + self.object.downloaded = False + self.object.downloaded_audio_codec = None + self.object.downloaded_video_codec = None + self.object.downloaded_container = None + self.object.downloaded_fps = None + self.object.downloaded_hdr = False + self.object.downloaded_filesize = None + # Mark it as not skipped + self.object.skip = False + self.object.manual_skip = False + # Saving here will trigger the post_create signals to schedule new tasks + self.object.save() + return super().form_valid(form) + + def get_success_url(self): + url = reverse_lazy('sync:media-item', kwargs={'pk': self.object.pk}) + return append_uri_params(url, {'message': 'redownloading'}) + + +class MediaSkipView(FormView, SingleObjectMixin): + ''' + Confirm that the media file should be deleted and marked to skip. + ''' + + template_name = 'sync/media-skip.html' + form_class = SkipMediaForm + model = Media + + def __init__(self, *args, **kwargs): + self.object = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + self.object = self.get_object() + return super().dispatch(request, *args, **kwargs) + + def form_valid(self, form): + # If the media file exists on disk, delete it + if self.object.media_file_exists: + # Delete all files which contains filename + filepath = self.object.media_file.path + barefilepath, fileext = os.path.splitext(filepath) + # Get all files that start with the bare file path + all_related_files = glob.glob(f'{barefilepath}.*') + for file in all_related_files: + delete_file(file) + # Reset all download data + self.object.metadata_clear() + self.object.downloaded = False + self.object.downloaded_audio_codec = None + self.object.downloaded_video_codec = None + self.object.downloaded_container = None + self.object.downloaded_fps = None + self.object.downloaded_hdr = False + self.object.downloaded_filesize = None + # Mark it to be skipped + self.object.skip = True + self.object.manual_skip = True + self.object.save() + return super().form_valid(form) + + def get_success_url(self): + url = reverse_lazy('sync:media-item', kwargs={'pk': self.object.pk}) + return append_uri_params(url, {'message': 'skipped'}) + + +class MediaEnableView(FormView, SingleObjectMixin): + ''' + Confirm that the media item should be re-enabled (marked as unskipped). + ''' + + template_name = 'sync/media-enable.html' + form_class = EnableMediaForm + model = Media + + def __init__(self, *args, **kwargs): + self.object = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + self.object = self.get_object() + return super().dispatch(request, *args, **kwargs) + + def form_valid(self, form): + # Mark it as not skipped + self.object.skip = False + self.object.manual_skip = False + self.object.save() + return super().form_valid(form) + + def get_success_url(self): + url = reverse_lazy('sync:media-item', kwargs={'pk': self.object.pk}) + return append_uri_params(url, {'message': 'enabled'}) + + +class MediaContent(DetailView): + ''' + Redirect to nginx to download the file + ''' + model = Media + + def __init__(self, *args, **kwargs): + self.object = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + self.object = self.get_object() + # development direct file stream - DO NOT USE PRODUCTIVLY + if settings.DEBUG and 'runserver' in sys.argv: + # get media URL + pth = self.object.media_file.url + # remove "/media-data/" + pth = pth.split("/media-data/",1)[1] + # remove "/" (incase of absolute path) + pth = pth.split(str(settings.DOWNLOAD_ROOT).lstrip("/"),1) + + # if we do not have a "/" at the beginning, it is not a absolute path... + if len(pth) > 1: + pth = pth[1] + else: + pth = pth[0] + + + # build final path + filepth = pathlib.Path(str(settings.DOWNLOAD_ROOT) + pth) + + if filepth.exists(): + # return file + response = FileResponse(open(filepth,'rb')) + return response + else: + return HttpResponseNotFound() + + else: + headers = { + 'Content-Type': self.object.content_type, + 'X-Accel-Redirect': self.object.media_file.url, + } + return HttpResponse(headers=headers) diff --git a/tubesync/sync/views/mediaservers.py b/tubesync/sync/views/mediaservers.py new file mode 100644 index 000000000..900d7ec5d --- /dev/null +++ b/tubesync/sync/views/mediaservers.py @@ -0,0 +1,236 @@ +from django.http import Http404 +from django.views.generic import ListView, DetailView +from django.views.generic.edit import FormView, FormMixin, DeleteView +from django.views.generic.detail import SingleObjectMixin +from django.urls import reverse_lazy +from django.db import IntegrityError +from django.forms import ValidationError +from django.utils.translation import gettext_lazy as _ +from common.utils import append_uri_params +from ..models import MediaServer +from ..forms import ConfirmDeleteMediaServerForm +from ..choices import MediaServerType + + +class MediaServersView(ListView): + ''' + List of media servers which have been added. + ''' + + template_name = 'sync/mediaservers.html' + context_object_name = 'mediaservers' + types_object = MediaServerType + messages = { + 'deleted': _('Your selected media server has been deleted.'), + } + + def __init__(self, *args, **kwargs): + self.message = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + message_key = request.GET.get('message', '') + self.message = self.messages.get(message_key, '') + return super().dispatch(request, *args, **kwargs) + + def get_queryset(self): + return MediaServer.objects.all().order_by('host', 'port') + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['message'] = self.message + data['media_server_types'] = self.types_object.members_list() + return data + + +class AddMediaServerView(FormView): + ''' + Adds a new media server. The form is switched out to whatever matches the + server type. + ''' + + template_name = 'sync/mediaserver-add.html' + server_types = MediaServerType.long_types() + server_type_names = dict(MediaServerType.choices) + forms = MediaServerType.forms_dict() + + def __init__(self, *args, **kwargs): + self.server_type = None + self.model_class = None + self.object = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + server_type_str = kwargs.get('server_type', '') + self.server_type = self.server_types.get(server_type_str) + if not self.server_type: + raise Http404 + self.form_class = self.forms.get(self.server_type) + if not self.form_class: + raise Http404 + self.model_class = MediaServer(server_type=self.server_type) + return super().dispatch(request, *args, **kwargs) + + def form_valid(self, form): + # Assign mandatory fields, bundle other fields into options + mediaserver = MediaServer(server_type=self.server_type) + options = dict() + model_fields = [field.name for field in MediaServer._meta.fields] + for field_name, field_value in form.cleaned_data.items(): + if field_name in model_fields: + setattr(mediaserver, field_name, field_value) + else: + options[field_name] = field_value + mediaserver.options = options + # Test the media server details are valid + try: + mediaserver.validate() + except ValidationError as e: + form.add_error(None, e) + # Check if validation detected any errors + if form.errors: + return super().form_invalid(form) + # All good, try to save and return + try: + mediaserver.save() + except IntegrityError: + form.add_error( + None, + (f'A media server already exists with the host and port ' + f'{mediaserver.host}:{mediaserver.port}') + ) + # Check if saving caused any errors + if form.errors: + return super().form_invalid(form) + # All good! + self.object = mediaserver + return super().form_valid(form) + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['server_type'] = self.server_type + data['server_type_long'] = self.server_types.get(self.server_type) + data['server_type_name'] = self.server_type_names.get(self.server_type) + data['server_help'] = self.model_class.get_help_html() + return data + + def get_success_url(self): + url = reverse_lazy('sync:mediaserver', kwargs={'pk': self.object.pk}) + return append_uri_params(url, {'message': 'created'}) + + +class MediaServerView(DetailView): + ''' + A single media server overview page. + ''' + + template_name = 'sync/mediaserver.html' + model = MediaServer + private_options = ('token',) + messages = { + 'created': _('Your media server has been successfully added'), + } + + def __init__(self, *args, **kwargs): + self.message = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + message_key = request.GET.get('message', '') + self.message = self.messages.get(message_key, '') + return super().dispatch(request, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['message'] = self.message + data['private_options'] = self.private_options + return data + + +class DeleteMediaServerView(DeleteView, FormMixin): + ''' + Confirms deletion and then deletes a media server. + ''' + + template_name = 'sync/mediaserver-delete.html' + model = MediaServer + form_class = ConfirmDeleteMediaServerForm + context_object_name = 'mediaserver' + + def get_success_url(self): + url = reverse_lazy('sync:mediaservers') + return append_uri_params(url, {'message': 'deleted'}) + + +class UpdateMediaServerView(FormView, SingleObjectMixin): + ''' + Adds a new media server. The form is switched out to whatever matches the + server type. + ''' + + template_name = 'sync/mediaserver-update.html' + model = MediaServer + forms = MediaServerType.forms_dict() + + def __init__(self, *args, **kwargs): + self.object = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + self.object = self.get_object() + self.form_class = self.forms.get(self.object.server_type, None) + if not self.form_class: + raise Http404 + return super().dispatch(request, *args, **kwargs) + + def get_initial(self): + initial = super().get_initial() + for field in self.object._meta.fields: + if field.name in self.form_class.declared_fields: + initial[field.name] = getattr(self.object, field.name) + for option_key, option_val in self.object.options.items(): + if option_key in self.form_class.declared_fields: + initial[option_key] = option_val + return initial + + def form_valid(self, form): + # Assign mandatory fields, bundle other fields into options + options = dict() + model_fields = [field.name for field in MediaServer._meta.fields] + for field_name, field_value in form.cleaned_data.items(): + if field_name in model_fields: + setattr(self.object, field_name, field_value) + else: + options[field_name] = field_value + self.object.options = options + # Test the media server details are valid + try: + self.object.validate() + except ValidationError as e: + form.add_error(None, e) + # Check if validation detected any errors + if form.errors: + return super().form_invalid(form) + # All good, try to save and return + try: + self.object.save() + except IntegrityError: + form.add_error( + None, + (f'A media server already exists with the host and port ' + f'{self.object.host}:{self.object.port}') + ) + # Check if saving caused any errors + if form.errors: + return super().form_invalid(form) + # All good! + return super().form_valid(form) + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['server_help'] = self.object.get_help_html + return data + + def get_success_url(self): + url = reverse_lazy('sync:mediaserver', kwargs={'pk': self.object.pk}) + return append_uri_params(url, {'message': 'updated'}) diff --git a/tubesync/sync/views/sources.py b/tubesync/sync/views/sources.py new file mode 100644 index 000000000..64023d5cc --- /dev/null +++ b/tubesync/sync/views/sources.py @@ -0,0 +1,354 @@ +import pathlib +from django.conf import settings +from django.http import HttpResponseNotFound, HttpResponseRedirect +from django.views.generic import ListView, DetailView +from django.views.generic.edit import FormView, FormMixin, CreateView, UpdateView, DeleteView +from django.core.exceptions import SuspiciousFileOperation +from django.urls import reverse_lazy +from django.db.models import Count, When, Case +from django.forms import Form, ValidationError +from django.utils.text import slugify +from django.utils._os import safe_join +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from common.models import TaskHistory +from common.utils import append_uri_params, mkdir_p +from ..models import Source, Media +from ..forms import ValidateSourceForm, ConfirmDeleteSourceForm, SourceForm +from ..utils import validate_url +from ..tasks import get_source_completed_tasks, get_error_message, index_source +from ..choices import (Val, IndexSchedule, YouTube_SourceType, + youtube_long_source_types, youtube_validation_urls) +from .. import signals # noqa +from .. import youtube + + +class SourcesView(ListView): + ''' + A bare list of the sources which have been created with their states. + ''' + + template_name = 'sync/sources.html' + context_object_name = 'sources' + paginate_by = settings.SOURCES_PER_PAGE + messages = { + 'source-deleted': _('Your selected source has been deleted.'), + 'source-refreshed': _('The source has been scheduled to be synced now.') + } + + def get(self, *args, **kwargs): + if args[0].path.startswith("/source-sync-now/"): + source = Source.objects.get(pk=kwargs["pk"]) + if source is None: + return HttpResponseNotFound() + + TaskHistory.schedule( + index_source, + str(source.pk), + delay=30, + vn_fmt=_('Index media from source "{}" once'), + vn_args=(source.name,), + ) + url = reverse_lazy('sync:sources') + url = append_uri_params(url, {'message': 'source-refreshed'}) + return HttpResponseRedirect(url) + else: + return super().get(self, *args, **kwargs) + + def __init__(self, *args, **kwargs): + self.message = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + message_key = request.GET.get('message', '') + self.message = self.messages.get(message_key, '') + return super().dispatch(request, *args, **kwargs) + + def get_queryset(self): + all_sources = Source.objects.all().order_by('name') + return all_sources.annotate( + media_count=Count('media_source'), + downloaded_count=Count(Case(When(media_source__downloaded=True, then=1))) + ) + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['message'] = self.message + return data + + +class ValidateSourceView(FormView): + ''' + Validate a URL and prepopulate a create source view form with confirmed + accurate data. The aim here is to streamline onboarding of new sources + which otherwise may not be entirely obvious to add, such as the "key" + being just a playlist ID or some other reasonably opaque internals. + ''' + + template_name = 'sync/source-validate.html' + form_class = ValidateSourceForm + errors = { + 'invalid_url': _('That URL does not match any supported formats.'), + } + source_types = youtube_long_source_types + validation_urls = youtube_validation_urls + prepopulate_fields = { + Val(YouTube_SourceType.CHANNEL): ('source_type', 'key', 'name', 'directory'), + Val(YouTube_SourceType.CHANNEL_ID): ('source_type', 'key'), + Val(YouTube_SourceType.PLAYLIST): ('source_type', 'key'), + } + + def __init__(self, *args, **kwargs): + self.source_type_str = '' + self.source_type = None + self.key = '' + super().__init__(*args, **kwargs) + + def form_valid(self, form): + # Perform extra validation on the URL, we need to extract the channel name or + # playlist ID and check they are valid + source_url = form.cleaned_data['source_url'] + for source_type in YouTube_SourceType.values: + validation_url = self.validation_urls.get(source_type) + try: + self.key = validate_url(source_url, validation_url) + self.source_type = source_type + for long_type_str, st_val in youtube_long_source_types.items(): + if st_val == source_type: + self.source_type_str = long_type_str + break + return super().form_valid(form) + except ValidationError: + continue + # Source type wasn't detected - presumably it's not a valid URL + form.add_error('source_url', ValidationError(self.errors['invalid_url'])) + return super().form_invalid(form) + + def get_success_url(self): + url = reverse_lazy('sync:add-source') + fields_to_populate = self.prepopulate_fields.get(self.source_type) + fields = {} + value = self.key + use_channel_id = ( + 'youtube-channel' == self.source_type_str and + '@' == self.key[0] + ) + if use_channel_id: + old_key = self.key + old_source_type = self.source_type + old_source_type_str = self.source_type_str + + self.source_type_str = 'youtube-channel-id' + self.source_type = self.source_types.get(self.source_type_str, None) + index_url = Source.create_index_url(self.source_type, self.key, 'videos') + try: + self.key = youtube.get_channel_id( + index_url.replace('/channel/', '/') + ) + except youtube.YouTubeError: + # It did not work, revert to previous behavior + self.key = old_key + self.source_type = old_source_type + self.source_type_str = old_source_type_str + + for field in fields_to_populate: + if field == 'source_type': + fields[field] = self.source_type + elif field == 'key': + fields[field] = self.key + elif field in ('name', 'directory'): + fields[field] = value + return append_uri_params(url, fields) + + +class EditSourceMixin: + model = Source + form_class = SourceForm + errors = { + 'invalid_media_format': _('Invalid media format, the media format contains ' + 'errors or is empty. Check the table at the end of ' + 'this page for valid media name variables'), + 'dir_outside_dlroot': _('You cannot specify a directory outside of the ' + 'base directory (%BASEDIR%)') + } + + def form_valid(self, form: Form): + # Perform extra validation to make sure the media_format is valid + obj = form.save(commit=False) + # temporarily use media_format from the form + saved_media_format = obj.media_format + obj.media_format = form.cleaned_data['media_format'] + example_media_file = obj.get_example_media_format() + obj.media_format = saved_media_format + + if '' == example_media_file: + form.add_error( + 'media_format', + ValidationError(self.errors['invalid_media_format']) + ) + + # Check for suspicious file path(s) + try: + targetCheck = form.cleaned_data['directory'] + '/.virt' + safe_join(settings.DOWNLOAD_ROOT, targetCheck) + except SuspiciousFileOperation: + form.add_error( + 'directory', + ValidationError( + self.errors['dir_outside_dlroot'].replace( + "%BASEDIR%", str(settings.DOWNLOAD_ROOT) + ) + ), + ) + + if form.errors: + return super().form_invalid(form) + + return super().form_valid(form) + + +class AddSourceView(EditSourceMixin, CreateView): + ''' + Adds a new source, optionally takes some initial data querystring values to + prepopulate some of the more unclear values. + ''' + + template_name = 'sync/source-add.html' + + def __init__(self, *args, **kwargs): + self.prepopulated_data = {} + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + # Inject the adjustable media format default + self.prepopulated_data['media_format'] = getattr( + settings, 'MEDIA_FORMATSTR', settings.MEDIA_FORMATSTR_DEFAULT + ) + source_type = request.GET.get('source_type', '') + if source_type and source_type in YouTube_SourceType.values: + self.prepopulated_data['source_type'] = source_type + key = request.GET.get('key', '') + if key: + self.prepopulated_data['key'] = key.strip() + name = request.GET.get('name', '') + if name: + self.prepopulated_data['name'] = slugify(name) + directory = request.GET.get('directory', '') + if directory: + self.prepopulated_data['directory'] = slugify(directory) + return super().dispatch(request, *args, **kwargs) + + def get_initial(self): + initial = super().get_initial() + initial['target_schedule'] = timezone.now().replace( + second=0, microsecond=0, + ) + for k, v in self.prepopulated_data.items(): + initial[k] = v + return initial + + def get_success_url(self): + url = reverse_lazy('sync:source', kwargs={'pk': self.object.pk}) + return append_uri_params(url, {'message': 'source-created'}) + + +class SourceView(DetailView): + + template_name = 'sync/source.html' + model = Source + messages = { + 'source-created': _('Your new source has been created. If you have added a ' + 'very large source such as a channel with hundreds of ' + 'videos it can take several minutes or up to an hour ' + 'for media to start to appear.'), + 'source-updated': _('Your source has been updated.'), + } + + def __init__(self, *args, **kwargs): + self.message = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + message_key = request.GET.get('message', '') + self.message = self.messages.get(message_key, '') + return super().dispatch(request, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['message'] = self.message + data['errors'] = [] + for error in get_source_completed_tasks(self.object.pk, only_errors=True): + error_message = get_error_message(error) + setattr(error, 'error_message', error_message) + data['errors'].append(error) + data['media'] = Media.objects.filter(source=self.object).order_by('-published').defer('metadata') + return data + + +class UpdateSourceView(EditSourceMixin, UpdateView): + + template_name = 'sync/source-update.html' + + def get_initial(self): + initial = super().get_initial() + when = getattr(self.object, 'target_schedule') or timezone.now() + initial['target_schedule'] = when.replace(second=0, microsecond=0) + return initial + + def get_success_url(self): + url = reverse_lazy('sync:source', kwargs={'pk': self.object.pk}) + return append_uri_params(url, {'message': 'source-updated'}) + + +class DeleteSourceView(DeleteView, FormMixin): + ''' + Confirm the deletion of a source with an option to delete all the media + associated with the source from disk when the source is deleted. + ''' + + template_name = 'sync/source-delete.html' + model = Source + form_class = ConfirmDeleteSourceForm + context_object_name = 'source' + + def post(self, request, *args, **kwargs): + source = self.get_object() + media_source = dict( + uuid=None, + index_schedule=IndexSchedule.NEVER, + download_media=False, + index_videos=False, + index_streams=False, + filter_text=str(source.pk), + target_schedule=source.target_schedule or timezone.now(), + ) + copy_fields = set(map(lambda f: f.name, source._meta.fields)) - set(media_source.keys()) + for k, v in source.__dict__.items(): + if k in copy_fields: + media_source[k] = v + media_source = Source(**media_source) + delete_media_val = request.POST.get('delete_media', False) + delete_media = True if delete_media_val is not False else False + # overload this boolean for our own use + media_source.delete_removed_media = delete_media + # adjust the directory and key on the source to be deleted + source.directory = source.directory + '/deleted' + source.key = source.key + '/deleted' + source.name = f'[Deleting] {source.name}' + source.save(update_fields={'directory', 'key', 'name'}) + source.refresh_from_db() + # save the new media source now that it is not a duplicate + media_source.uuid = None + media_source.save() + media_source.refresh_from_db() + # switch the media to the new source instance + Media.objects.filter(source=source).update(source=media_source) + if delete_media: + directory_path = pathlib.Path(media_source.directory_path) + mkdir_p(directory_path) + (directory_path / '.to_be_removed').touch(exist_ok=True) + return super().post(request, *args, **kwargs) + + def get_success_url(self): + url = reverse_lazy('sync:sources') + return append_uri_params(url, {'message': 'source-deleted'}) diff --git a/tubesync/sync/views/tasks.py b/tubesync/sync/views/tasks.py new file mode 100644 index 000000000..c437d87a4 --- /dev/null +++ b/tubesync/sync/views/tasks.py @@ -0,0 +1,390 @@ +from django.conf import settings +from django.http import HttpResponseNotFound, HttpResponseRedirect +from django.views.generic import ListView +from django.views.generic.edit import FormView +from django.views.generic.detail import SingleObjectMixin +from django.urls import reverse_lazy +from django.db.models import F +from django.forms import ValidationError +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from common.models import TaskHistory +from common.timestamp import timestamp_to_datetime +from common.utils import append_uri_params, multi_key_sort +from common.huey import h_q_reset_tasks +from common.logger import log +from django_huey import DJANGO_HUEY, get_queue +from .utils import get_waiting_tasks +from ..models import Source +from ..forms import ResetTasksForm, ScheduleTaskForm +from ..tasks import ( + map_task_to_instance, get_error_message, + get_running_tasks, check_source_directory_exists, +) + + +class TasksView(ListView): + ''' + A list of tasks queued to be completed. This is, for example, scraping for new + media or downloading media. + ''' + + template_name = 'sync/tasks.html' + context_object_name = 'tasks' + paginate_by = settings.TASKS_PER_PAGE + messages = { + 'filter': _('Viewing tasks filtered for source: {name}'), + 'reset': _('All tasks have been reset'), + 'revoked': _('Revoked task: {task_id}'), + 'scheduled': _('Scheduled task: {name}'), + } + + def __init__(self, *args, **kwargs): + self.filter_source = None + self.message = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + message_key = request.GET.get('message', '') + self.message = self.messages.get(message_key, '') + filter_by = request.GET.get('filter', '') + if filter_by: + try: + self.filter_source = Source.objects.get(pk=filter_by) + except Source.DoesNotExist: + self.filter_source = None + else: + if not message_key or 'filter' == message_key: + message = self.messages.get('filter', '') + self.message = message.format( + name=self.filter_source.name + ) + + if message_key in ('revoked', 'scheduled'): + fmt_vars = dict( + pk=request.GET.get('pk', 'Unknown'), + task_id=request.GET.get('task_id', 'Unknown'), + ) + try: + task = TaskHistory.objects.get(pk=fmt_vars['pk']) + except TaskHistory.DoesNotExist: + fmt_vars['name'] = fmt_vars['pk'] + else: + fmt_vars['name'] = task.verbose_name or task.task_id or task.pk + fmt_vars['task_id'] = task.task_id + self.message = self.message.format(**fmt_vars) + + return super().dispatch(request, *args, **kwargs) + + def get_queryset(self): + qs = get_waiting_tasks() + if self.filter_source: + params_prefix=f'[["{self.filter_source.pk}"' + qs = qs.filter(task_params__istartswith=params_prefix) + return qs.order_by( + '-priority', + 'scheduled_at', + 'end_at', + ) + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + now_dt = timezone.now() + scheduled_qs = get_waiting_tasks() + # Huey removes running tasks, + # so the waiting tasks will not include them. + running_qs = get_running_tasks(now_dt) + errors_qs = scheduled_qs.filter( + attempts__gt=0 + ).exclude(last_error__exact='') + + # Add to context data from ListView + data['message'] = self.message + data['source'] = self.filter_source + data['running'] = list() + data['errors'] = list() + data['total_errors'] = errors_qs.count() + data['scheduled'] = list() + data['total_scheduled'] = scheduled_qs.count() + data['wait_for_database_queue'] = False + + def add_to_task(task): + setattr(task, 'run_now', task.scheduled_at < now_dt) + obj, url = map_task_to_instance(task) + if obj: + setattr(task, 'instance', obj) + setattr(task, 'url', url) + if task.has_error(): + error_message = get_error_message(task) + setattr(task, 'error_message', error_message) + return 'error' + return True and obj + + for task in running_qs: + if task in data['running']: + continue + add_to_task(task) + data['running'].append(task) + + # show all the errors when they fit on one page + if (data['total_errors'] + len(data['running'])) < self.paginate_by: + for task in errors_qs: + if task in data['running']: + continue + mapped = add_to_task(task) + if 'error' == mapped: + data['errors'].append(task) + elif mapped: + data['scheduled'].append(task) + + for task in data['tasks']: + already_added = ( + task in data['running'] or + task in data['errors'] or + task in data['scheduled'] + ) + if already_added: + continue + mapped = add_to_task(task) + if 'error' == mapped: + data['errors'].append(task) + elif mapped or settings.DEBUG: + data['scheduled'].append(task) + + sort_keys = ( + # key, reverse + ('priority', True), + ('scheduled_at', False), + ('run_now', True), + ) + data['errors'] = multi_key_sort(data['errors'], sort_keys, attr=True) + data['scheduled'] = multi_key_sort(data['scheduled'], sort_keys, attr=True) + + return data + + def paginate_queryset(self, queryset, page_size): + """Paginate the queryset, if needed.""" + paginator = self.get_paginator( + queryset, + page_size, + orphans=self.get_paginate_orphans(), + allow_empty_first_page=self.get_allow_empty(), + ) + 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: + pass + else: + if page_number > paginator.num_pages: + self.kwargs[page_kwarg] = 'last' + return super().paginate_queryset(queryset, page_size) + + def get(self, *args, **kwargs): + path = args[0].path + if path.startswith('/task/') and path.endswith('/cancel'): + try: + task = TaskHistory.objects.get(pk=kwargs["pk"]) + except TaskHistory.DoesNotExist: + return HttpResponseNotFound() + else: + huey_queue_names = (DJANGO_HUEY or {}).get('queues', {}) + huey_queues = { q.name: q for q in map(get_queue, huey_queue_names) } + q = huey_queues.get(task.queue) + if q is None: + msg = f'TasksView: queue not found: {task.pk=} {task.queue=}' + log.warning(msg) + return HttpResponseNotFound() + # revoke the task we want to cancel + q.revoke_by_id(id=task.task_id, revoke_once=True) + vn = task.verbose_name or task.task_id or task.pk + if not vn.startswith('[revoked] '): + task.verbose_name = f'[revoked] {vn}' + task.save() + return HttpResponseRedirect(append_uri_params( + reverse_lazy('sync:tasks'), + dict( + message='revoked', + pk=str(task.pk), + task_id=str(task.task_id), + ), + )) + else: + return super().get(self, *args, **kwargs) + +class CompletedTasksView(ListView): + ''' + List of tasks which have been completed with an optional per-source filter. + ''' + + template_name = 'sync/tasks-completed.html' + context_object_name = 'tasks' + paginate_by = settings.TASKS_PER_PAGE + messages = { + 'filter': _('Viewing tasks filtered for source: {name}'), + } + + def __init__(self, *args, **kwargs): + self.filter_source = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + filter_by = request.GET.get('filter', '') + if filter_by: + try: + self.filter_source = Source.objects.get(pk=filter_by) + except Source.DoesNotExist: + self.filter_source = None + return super().dispatch(request, *args, **kwargs) + + def get_queryset(self): + qs = TaskHistory.objects.filter( + start_at__isnull=False, + end_at__gt=F('start_at'), + ) + if self.filter_source: + params_prefix=f'[["{self.filter_source.pk}"' + qs = qs.filter(task_params__istartswith=params_prefix) + return qs.order_by('-end_at') + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + for task in data['tasks']: + if task.has_error(): + error_message = get_error_message(task) + setattr(task, 'error_message', error_message) + data['message'] = '' + data['source'] = self.filter_source + if self.filter_source: + message = str(self.messages.get('filter', '')) + data['message'] = message.format(name=self.filter_source.name) + return data + + +class ResetTasks(FormView): + ''' + Confirm that all tasks should be reset. As all tasks are triggered from + signals by checking for files existing etc. this can be done by just deleting + all tasks and then calling every Source objects .save() method. + ''' + + template_name = 'sync/tasks-reset.html' + form_class = ResetTasksForm + + def form_valid(self, form): + # Delete all tasks + huey_queue_names = (DJANGO_HUEY or {}).get('queues', {}) + for queue_name in huey_queue_names: + h_q_reset_tasks(queue_name) + # Iter all tasks + for source in Source.objects.all(): + check_source_directory_exists(str(source.pk)) + # This also chains down to call each Media objects .save() as well + source.save() + return super().form_valid(form) + + def get_success_url(self): + url = reverse_lazy('sync:tasks') + return append_uri_params(url, {'message': 'reset'}) + + +class TaskScheduleView(FormView, SingleObjectMixin): + ''' + Confirm that the task should be re-scheduled. + ''' + + template_name = 'sync/task-schedule.html' + form_class = ScheduleTaskForm + model = TaskHistory + context_object_name = 'task' + errors = dict( + invalid_when=_('The type ({}) was incorrect.'), + when_before_now=_('The date and time must be in the future.'), + ) + + def __init__(self, *args, **kwargs): + self.now = timezone.now() + self.object = None + self.timestamp = None + self.when = None + super().__init__(*args, **kwargs) + + def dispatch(self, request, *args, **kwargs): + self.now = timezone.now() + self.object = self.get_object() + self.timestamp = kwargs.get('timestamp') + try: + self.when = timestamp_to_datetime(self.timestamp) + except AssertionError: + self.when = None + if self.when is None: + self.when = self.now + # Use the next minute and zero seconds + # The web browser does not select seconds by default + self.when = self.when.replace(second=0) + timezone.timedelta(minutes=1) + return super().dispatch(request, *args, **kwargs) + + def get_initial(self): + initial = super().get_initial() + initial['now'] = self.now + initial['when'] = self.when + return initial + + def get_context_data(self, *args, **kwargs): + data = super().get_context_data(*args, **kwargs) + data['now'] = self.now + data['when'] = self.when + return data + + def get_success_url(self): + return append_uri_params( + reverse_lazy('sync:tasks'), + dict( + message='scheduled', + pk=str(self.object.pk), + task_id=str(self.object.task_id), + ), + ) + + def form_valid(self, form): + when = form.cleaned_data.get('when') + + if not isinstance(when, self.now.__class__): + form.add_error( + 'when', + ValidationError( + self.errors['invalid_when'].format( + type(when), + ), + ), + ) + if when < self.now: + form.add_error( + 'when', + ValidationError(self.errors['when_before_now']), + ) + + if form.errors: + return super().form_invalid(form) + + pk = self.object.pk + queue = self.object.queue + task_id = self.object.task_id + huey_queue_names = (DJANGO_HUEY or {}).get('queues', {}) + huey_queues = { q.name: q for q in map(get_queue, huey_queue_names) } + q = huey_queues.get(queue) + if q is None: + msg = f'TaskScheduleView: queue not found: {pk=} {queue=}' + log.warning(msg) + else: + eta = max(self.now, when) + if q.reschedule(task_id, eta): + vn = self.object.verbose_name or task_id or pk + self.object.verbose_name = f'[revoked] {vn}' + self.object.save() + else: + msg = f'TaskScheduleView: task not found: {pk=} {task_id=}' + log.warning(msg) + + return super().form_valid(form) diff --git a/tubesync/sync/views/utils.py b/tubesync/sync/views/utils.py new file mode 100644 index 000000000..54da14b20 --- /dev/null +++ b/tubesync/sync/views/utils.py @@ -0,0 +1,75 @@ +import uuid +from django.db import connection, transaction +from django.db.models.expressions import RawSQL +from common.models import TaskHistory +from common.logger import log +from django_huey import DJANGO_HUEY, get_queue + + +def get_histories_from_huey_ids(huey_task_ids): + """ + Robustly matches Huey task.id values to TaskHistory records. + + Optimized for large datasets across SQLite, PostgreSQL, and MariaDB. + Bypasses SQL variable limits by using request-cycle temporary tables. + """ + # 1. Guard clause for empty input - returns a valid, empty QuerySet + if not huey_task_ids: + return TaskHistory.objects.none() + + # 2. Dynamic metadata and unique naming + task_history_table = TaskHistory._meta.db_table + unique_suffix = uuid.uuid4().hex[:8] + input_tmp = f"tmp_huey_ids_{unique_suffix}" + results_tmp = f"tmp_history_pks_{unique_suffix}" + + # 3. Database operations + def validated_id_generator(): + for tid in huey_task_ids: + try: + # Validates format and normalizes to lowercase dashed string + yield (str(uuid.UUID(str(tid))).lower(),) + except (ValueError, TypeError, AttributeError): + # Skip malformed IDs and log for troubleshooting + log.warning(f"Skipping malformed Huey task ID: {tid}") + continue + + with transaction.atomic(): + with connection.cursor() as cursor: + # Stage 1: Store and normalize input IDs + cursor.execute(f"CREATE TEMPORARY TABLE {input_tmp} (tid VARCHAR(40) PRIMARY KEY)") + + # Single iteration: ensures dashed strings and lowercase for case-sensitive DBs + cursor.executemany(f"INSERT INTO {input_tmp} VALUES (%s)", validated_id_generator()) + + # Stage 2: Filter to a PK-only results table + cursor.execute(f"CREATE TEMPORARY TABLE {results_tmp} (id BIGINT)") + cursor.execute(f""" + INSERT INTO {results_tmp} (id) + SELECT id FROM {task_history_table} + WHERE task_id IN (SELECT tid FROM {input_tmp}) + """) + + # Index the result PKs to ensure dashboard pagination and ordering are fast + cursor.execute(f"CREATE INDEX idx_{results_tmp} ON {results_tmp}(id)") + + # Immediate cleanup of the input string table to save memory + cursor.execute(f"DROP TABLE IF EXISTS {input_tmp}") + + # 4. Return a Lazy QuerySet via RawSQL to bypass RawQuerySet.clone() limitations. + # The 'results_tmp' table persists until the connection closes after the request. + return TaskHistory.objects.filter( + id__in=RawSQL(f"SELECT id FROM {results_tmp}", []) + ) + +def get_waiting_tasks(): + huey_queue_names = (DJANGO_HUEY or {}).get('queues', {}) + huey_queues = list(map(get_queue, huey_queue_names)) + huey_task_ids = { + str(t.id) for q in huey_queues for t in set( + q.pending() + ).union( + q.scheduled() + ) + } + return get_histories_from_huey_ids(huey_task_ids) From 3c8dfcabf85f65b290f58b70fdc4aef1a23c8ed4 Mon Sep 17 00:00:00 2001 From: Alice Snow Date: Wed, 25 Feb 2026 20:46:34 +0100 Subject: [PATCH 04/99] migrate from Materialize CSS to TailwindCSS --- .gitignore | 7 +- Dockerfile | 2 - Makefile | 8 +- requirements-dev.txt | 1 - requirements.txt | 1 - scripts/setup-dev.sh | 29 +- tubesync/common/middleware.py | 21 - tubesync/common/static/styles/_colours.scss | 71 - tubesync/common/static/styles/_forms.scss | 52 - tubesync/common/static/styles/_helpers.scss | 40 - tubesync/common/static/styles/_template.scss | 250 - tubesync/common/static/styles/_variables.scss | 2 - .../common/static/styles/fontawesome.min.css | 9 + .../static/styles/fontawesome/_animated.scss | 1 - .../styles/fontawesome/_bordered-pulled.scss | 1 - .../static/styles/fontawesome/_core.scss | 1 - .../styles/fontawesome/_fixed-width.scss | 1 - .../static/styles/fontawesome/_functions.scss | 1 - .../static/styles/fontawesome/_icons.scss | 1 - .../static/styles/fontawesome/_list.scss | 1 - .../static/styles/fontawesome/_mixins.scss | 1 - .../styles/fontawesome/_rotated-flipped.scss | 1 - .../styles/fontawesome/_screen-reader.scss | 1 - .../static/styles/fontawesome/_shims.scss | 1 - .../static/styles/fontawesome/_sizing.scss | 1 - .../static/styles/fontawesome/_stacked.scss | 1 - .../static/styles/fontawesome/_variables.scss | 5044 ----------------- .../static/styles/fontawesome/brands.scss | 1 - .../styles/fontawesome/fontawesome.scss | 1 - .../static/styles/fontawesome/regular.scss | 1 - .../static/styles/fontawesome/solid.scss | 1 - .../static/styles/fontawesome/v4-shims.scss | 1 - .../static/styles/{_fonts.scss => fonts.css} | 15 +- .../materializecss/components/_badges.scss | 55 - .../materializecss/components/_buttons.scss | 322 -- .../materializecss/components/_cards.scss | 195 - .../materializecss/components/_carousel.scss | 90 - .../materializecss/components/_chips.scss | 90 - .../components/_collapsible.scss | 91 - .../components/_color-classes.scss | 32 - .../components/_color-variables.scss | 370 -- .../components/_datepicker.scss | 191 - .../materializecss/components/_dropdown.scss | 85 - .../materializecss/components/_global.scss | 769 --- .../materializecss/components/_grid.scss | 156 - .../components/_icons-material-design.scss | 5 - .../components/_materialbox.scss | 43 - .../materializecss/components/_modal.scss | 94 - .../materializecss/components/_navbar.scss | 208 - .../materializecss/components/_normalize.scss | 447 -- .../materializecss/components/_preloader.scss | 334 -- .../materializecss/components/_pulse.scss | 34 - .../materializecss/components/_sidenav.scss | 216 - .../materializecss/components/_slider.scss | 92 - .../components/_table_of_contents.scss | 33 - .../materializecss/components/_tabs.scss | 99 - .../materializecss/components/_tapTarget.scss | 103 - .../components/_timepicker.scss | 183 - .../materializecss/components/_toast.scss | 58 - .../materializecss/components/_tooltip.scss | 32 - .../components/_transitions.scss | 13 - .../components/_typography.scss | 60 - .../materializecss/components/_variables.scss | 349 -- .../materializecss/components/_waves.scss | 114 - .../components/forms/_checkboxes.scss | 200 - .../components/forms/_file-input.scss | 44 - .../components/forms/_forms.scss | 22 - .../components/forms/_input-fields.scss | 354 -- .../components/forms/_radio-buttons.scss | 115 - .../components/forms/_range.scss | 161 - .../components/forms/_select.scss | 180 - .../components/forms/_switches.scss | 89 - .../styles/materializecss/materialize.scss | 41 - tubesync/common/static/styles/tubesync.css | 137 + tubesync/common/static/styles/tubesync.scss | 37 - tubesync/common/templates/base.html | 91 +- tubesync/common/templates/errorbox.html | 10 +- tubesync/common/templates/infobox.html | 10 +- tubesync/common/templates/pagination.html | 12 +- tubesync/common/templates/simpleform.html | 34 +- .../sync/templates/sync/_mediaformatvars.html | 162 +- tubesync/sync/templates/sync/dashboard.html | 201 +- .../sync/templates/sync/media-enable.html | 31 +- tubesync/sync/templates/sync/media-item.html | 338 +- .../sync/templates/sync/media-redownload.html | 37 +- tubesync/sync/templates/sync/media-skip.html | 33 +- tubesync/sync/templates/sync/media.html | 120 +- .../sync/templates/sync/mediaserver-add.html | 33 +- .../templates/sync/mediaserver-delete.html | 33 +- .../templates/sync/mediaserver-update.html | 33 +- tubesync/sync/templates/sync/mediaserver.html | 71 +- .../sync/templates/sync/mediaservers.html | 51 +- tubesync/sync/templates/sync/source-add.html | 38 +- .../sync/templates/sync/source-delete.html | 35 +- .../sync/templates/sync/source-update.html | 42 +- .../sync/templates/sync/source-validate.html | 41 +- tubesync/sync/templates/sync/source.html | 426 +- tubesync/sync/templates/sync/sources.html | 54 +- .../sync/templates/sync/task-schedule.html | 47 +- .../sync/templates/sync/tasks-completed.html | 42 +- tubesync/sync/templates/sync/tasks-reset.html | 41 +- tubesync/sync/templates/sync/tasks.html | 199 +- .../templates/widgets/checkbox_option.html | 7 +- tubesync/tubesync/settings.py | 6 - 104 files changed, 1172 insertions(+), 13019 deletions(-) delete mode 100644 tubesync/common/static/styles/_colours.scss delete mode 100644 tubesync/common/static/styles/_forms.scss delete mode 100644 tubesync/common/static/styles/_helpers.scss delete mode 100644 tubesync/common/static/styles/_template.scss delete mode 100644 tubesync/common/static/styles/_variables.scss create mode 100644 tubesync/common/static/styles/fontawesome.min.css delete mode 120000 tubesync/common/static/styles/fontawesome/_animated.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_bordered-pulled.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_core.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_fixed-width.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_functions.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_icons.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_list.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_mixins.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_rotated-flipped.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_screen-reader.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_shims.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_sizing.scss delete mode 120000 tubesync/common/static/styles/fontawesome/_stacked.scss delete mode 100644 tubesync/common/static/styles/fontawesome/_variables.scss delete mode 120000 tubesync/common/static/styles/fontawesome/brands.scss delete mode 120000 tubesync/common/static/styles/fontawesome/fontawesome.scss delete mode 120000 tubesync/common/static/styles/fontawesome/regular.scss delete mode 120000 tubesync/common/static/styles/fontawesome/solid.scss delete mode 120000 tubesync/common/static/styles/fontawesome/v4-shims.scss rename tubesync/common/static/styles/{_fonts.scss => fonts.css} (59%) delete mode 100644 tubesync/common/static/styles/materializecss/components/_badges.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_buttons.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_cards.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_carousel.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_chips.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_collapsible.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_color-classes.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_color-variables.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_datepicker.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_dropdown.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_global.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_grid.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_icons-material-design.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_materialbox.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_modal.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_navbar.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_normalize.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_preloader.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_pulse.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_sidenav.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_slider.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_table_of_contents.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_tabs.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_tapTarget.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_timepicker.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_toast.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_tooltip.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_transitions.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_typography.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_variables.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/_waves.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/forms/_checkboxes.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/forms/_file-input.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/forms/_forms.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/forms/_input-fields.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/forms/_radio-buttons.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/forms/_range.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/forms/_select.scss delete mode 100644 tubesync/common/static/styles/materializecss/components/forms/_switches.scss delete mode 100644 tubesync/common/static/styles/materializecss/materialize.scss create mode 100644 tubesync/common/static/styles/tubesync.css delete mode 100644 tubesync/common/static/styles/tubesync.scss diff --git a/.gitignore b/.gitignore index 9235e1966..1d47c025f 100644 --- a/.gitignore +++ b/.gitignore @@ -146,7 +146,10 @@ Pipfile.lock /tubesync/tasks/*.db-shm /tubesync/tasks/*.db-wal -# Compiled CSS -/tubesync/common/static/styles/*.css +# Tailwind CLI binary (downloaded per platform) +/tailwindcss + +# Compiled CSS output (generated by Tailwind CLI) +/tubesync/common/static/styles/output.css tubesync/tubesync/local_settings.py tubesync/tubesync/downloads diff --git a/Dockerfile b/Dockerfile index 308f06bfb..c8b130293 100644 --- a/Dockerfile +++ b/Dockerfile @@ -486,7 +486,6 @@ RUN --mount=type=cache,id=apt-lib-cache-${TARGETARCH},sharing=private,target=/va libonig5 \ pkgconf \ python3 \ - python3-libsass \ python3-pip-whl \ python3-socks \ curl \ @@ -679,7 +678,6 @@ RUN set -x && \ # Make absolutely sure we didn't accidentally bundle a SQLite dev database test '!' -e /app/db.sqlite3 && \ # Run any required app commands - /usr/bin/python3 -B /app/manage.py compilescss && \ /usr/bin/python3 -B /app/manage.py collectstatic --no-input --link && \ rm -rf /config /downloads /run/app && \ # Create config, downloads and run dirs diff --git a/Makefile b/Makefile index 638f61ef1..9f16490cf 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,13 @@ dev: $(python) tubesync/manage.py runserver -build: +css: + ./tailwindcss --input tubesync/common/static/styles/tubesync.css --output tubesync/common/static/styles/output.css + +css-watch: + ./tailwindcss --input tubesync/common/static/styles/tubesync.css --output tubesync/common/static/styles/output.css --watch + +build: css mkdir -p tubesync/media mkdir -p tubesync/static $(python) tubesync/manage.py collectstatic --noinput diff --git a/requirements-dev.txt b/requirements-dev.txt index 082515736..eef902249 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,3 @@ -i https://pypi.org/simple autopep8==2.3.2; python_version >= '3.9' pycodestyle==2.14.0; python_version >= '3.9' -libsass>=0.23.0 diff --git a/requirements.txt b/requirements.txt index a759b6ee0..038872218 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,6 @@ django-appconf==1.2.0; python_version >= '3.9' django-basicauth==0.5.3 django-compressor==4.6.0; python_version >= '3.10' django-huey==1.3.1; python_version >= '3.8' -django-sass-processor[management-command]==1.4.2 emoji==2.15.0; python_version >= '3.8' gunicorn==25.1.0; python_version >= '3.10' html5lib==1.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' diff --git a/scripts/setup-dev.sh b/scripts/setup-dev.sh index 134cfa82e..ff30ea5cb 100755 --- a/scripts/setup-dev.sh +++ b/scripts/setup-dev.sh @@ -44,11 +44,36 @@ else echo "==> local_settings.py already exists, skipping" fi -# 5. Run migrations +# 5. Download Tailwind CSS CLI if not present +if [ ! -x "$REPO_ROOT/tailwindcss" ]; then + echo "==> Downloading Tailwind CSS CLI" + ARCH="$(uname -m)" + OS="$(uname -s | tr '[:upper:]' '[:lower:]')" + case "$OS-$ARCH" in + linux-x86_64) TW_BIN="tailwindcss-linux-x64" ;; + linux-aarch64) TW_BIN="tailwindcss-linux-arm64" ;; + darwin-arm64) TW_BIN="tailwindcss-macos-arm64" ;; + darwin-x86_64) TW_BIN="tailwindcss-macos-x64" ;; + *) echo " Unsupported platform: $OS-$ARCH"; TW_BIN="" ;; + esac + if [ -n "$TW_BIN" ]; then + curl -sL "https://github.com/tailwindlabs/tailwindcss/releases/latest/download/$TW_BIN" -o "$REPO_ROOT/tailwindcss" + chmod +x "$REPO_ROOT/tailwindcss" + echo " Downloaded $TW_BIN" + fi +else + echo "==> Tailwind CSS CLI already present" +fi + +# 6. Compile Tailwind CSS +echo "==> Compiling Tailwind CSS" +"$REPO_ROOT/tailwindcss" --input "$REPO_ROOT/tubesync/common/static/styles/tubesync.css" --output "$REPO_ROOT/tubesync/common/static/styles/output.css" + +# 7. Run migrations echo "==> Running database migrations" $PYTHON "$REPO_ROOT/tubesync/manage.py" migrate --run-syncdb -v 0 -# 6. Collect static files +# 8. Collect static files echo "==> Collecting static files" $PYTHON "$REPO_ROOT/tubesync/manage.py" collectstatic --noinput -v 0 diff --git a/tubesync/common/middleware.py b/tubesync/common/middleware.py index 3f46c4817..4d547fb38 100644 --- a/tubesync/common/middleware.py +++ b/tubesync/common/middleware.py @@ -1,28 +1,7 @@ from django.conf import settings -from django.forms import BaseForm from basicauth.middleware import BasicAuthMiddleware as BaseBasicAuthMiddleware -class MaterializeDefaultFieldsMiddleware: - ''' - Adds 'browser-default' CSS attribute class to all form fields. - ''' - - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - response = self.get_response(request) - return response - - def process_template_response(self, request, response): - for _, v in getattr(response, 'context_data', {}).items(): - if isinstance(v, BaseForm): - for _, field in v.fields.items(): - field.widget.attrs.update({'class':'browser-default'}) - return response - - class BasicAuthMiddleware(BaseBasicAuthMiddleware): def process_request(self, request): diff --git a/tubesync/common/static/styles/_colours.scss b/tubesync/common/static/styles/_colours.scss deleted file mode 100644 index fe50c2f8e..000000000 --- a/tubesync/common/static/styles/_colours.scss +++ /dev/null @@ -1,71 +0,0 @@ -$colour-white: #ffffff; -$colour-black: #000000; -$colour-near-black: #011627; -$colour-near-white: #fdfffc; -$colour-light-blue: #1e5c83; -$colour-red: #e71d36; -$colour-orange: #ff9c00; - -$background-colour: $colour-near-white; -$text-colour: $colour-near-black; - -$header-background-colour: $colour-red; -$header-text-colour: $colour-near-white; - -$nav-background-colour: $colour-near-black; -$nav-text-colour: $colour-near-white; -$nav-link-background-hover-colour: $colour-orange; - -$main-button-background-colour: $colour-light-blue; -$main-button-background-hover-colour: $colour-orange; -$main-button-text-colour: $colour-near-white; -$main-link-colour: $colour-light-blue; -$main-link-hover-colour: $colour-orange; - -$footer-background-colour: $colour-red; -$footer-text-colour: $colour-near-white; -$footer-link-colour: $colour-near-black; -$footer-link-hover-colour: $colour-orange; - -$dash-desc-border: $colour-near-black; - -$form-label-text-colour: $colour-near-black; -$form-input-border-colour: $colour-light-blue; -$form-input-border-active-colour: $colour-orange; -$form-select-border-colour: $colour-light-blue; -$form-error-background-colour: $colour-red; -$form-error-text-colour: $colour-near-white; -$form-help-text-colour: $colour-light-blue; -$form-delete-button-background-colour: $colour-red; - -$collection-no-items-text-colour: $colour-near-black; -$collection-text-colour: $colour-light-blue; -$collection-background-hover-colour: $colour-orange; -$collection-text-hover-colour: $colour-near-white; - -$mediacard-title-background-colour: $colour-near-black; -$mediacard-title-text-colour: $colour-near-white; -$mediacard-border-colour: $colour-near-white; -$mediacard-border-hover-colour: $colour-orange; - -$box-error-background-colour: $colour-red; -$box-error-text-colour: $colour-near-white; - -$infobox-background-colour: $colour-near-black; -$infobox-text-colour: $colour-near-white; - -$errorbox-background-colour: $colour-red; -$errorbox-text-colour: $colour-near-white; -$error-text-colour: $colour-red; - -$pagination-background-colour: $colour-near-white; -$pagination-text-colour: $colour-near-black; -$pagination-border-colour: $colour-light-blue; -$pagination-background-hover-colour: $colour-light-blue; -$pagination-text-hover-colour: $colour-near-white; -$pagination-border-hover-colour: $colour-light-blue; -$pagination-current-background-colour: $colour-orange; -$pagination-current-text-colour: $colour-near-white; -$pagination-current-border-colour: $colour-orange; - -$error-text-colour: $colour-red; diff --git a/tubesync/common/static/styles/_forms.scss b/tubesync/common/static/styles/_forms.scss deleted file mode 100644 index 7ec08ce1b..000000000 --- a/tubesync/common/static/styles/_forms.scss +++ /dev/null @@ -1,52 +0,0 @@ -.simpleform { - .row { - margin-bottom: 0; - } - .help-text { - color: $form-help-text-colour; - padding-bottom: 1rem; - } - label { - text-transform: uppercase; - display: block; - font-size: 0.9rem; - position: relative; - transition: none; - top: initial; - left: initial !important; - transform: none; - color: $form-label-text-colour; - } - input { - width: 100%; - padding: 5px 8px 5px 8px; - font-size: 1.1rem; - border: 2px $form-input-border-colour solid; - border-radius: 2px; - outline: none; - &:focus { - outline: none; - border: 2px $form-input-border-active-colour solid; - } - } - textarea { - min-height: 150px; - } -} - -select { - display: initial !important; - border: 2px $form-select-border-colour solid; - height: initial !important; -} - -.delete-button { - background-color: $form-delete-button-background-colour !important; - &:hover { - background-color: $main-button-background-hover-colour !important; - } -} - -.no-text-transform { - text-transform: none; -} diff --git a/tubesync/common/static/styles/_helpers.scss b/tubesync/common/static/styles/_helpers.scss deleted file mode 100644 index 0f1f1893b..000000000 --- a/tubesync/common/static/styles/_helpers.scss +++ /dev/null @@ -1,40 +0,0 @@ -strong { - font-weight: bold; -} - -.nowrap { - white-space: nowrap; -} - -.no-para-margin-top { - margin-block-start: 0; -} - -.no-margin-bottom { - margin-bottom: 0 !important; -} - -.margin-bottom { - margin-bottom: 20px !important; -} - -.padding-top { - padding-top: 20px; -} - -.error-text { - color: $error-text-colour; -} - -.errors { - background-color: $box-error-background-colour; - border-radius: 2px; - padding: 10px 0 5px 0; -} - -.errorlist { - li { - color: $box-error-text-colour; - padding: 0 10px 5px 10px; - } -} diff --git a/tubesync/common/static/styles/_template.scss b/tubesync/common/static/styles/_template.scss deleted file mode 100644 index 04bbb4b59..000000000 --- a/tubesync/common/static/styles/_template.scss +++ /dev/null @@ -1,250 +0,0 @@ -html { - font-family: $font-family; - font-size: $font-size; - background-color: $background-colour; - color: $text-colour; -} - -body { - display: flex; - min-height: 100vh; - flex-direction: column; - justify-content: space-between; -} - -header { - - background-color: $header-background-colour; - color: $header-text-colour; - padding: 1.3rem 0 1.5rem 0; - - a { - text-decoration: none; - color: $header-text-colour; - &:hover { - text-decoration: none; - } - } - - svg.tubesync-logo { - margin-right: 1rem; - float: left; - .logo-icon { - fill: $colour-near-white !important; - } - } - - h1 { - margin: 0; - font-size: 3rem; - line-height: 2.7rem; - } -} - -nav { - - background-color: $nav-background-colour; - color: $nav-text-colour; - height: 3rem; - - a { - font-size: 1.1rem !important; - color: $nav-text-colour; - text-decoration: none; - line-height: 3rem; - height: 3rem; - i { - font-size: 1.1rem !important; - } - &:hover { - background-color: $nav-link-background-hover-colour !important; - } - } - - -} - -main { - - padding: 2rem 0 2rem 0; - - a { - color: $main-link-colour; - text-decoration: none; - &:hover { - color: $main-link-hover-colour; - } - } - - h1 { - margin: 0; - padding: 0 0 0.5rem 0; - font-size: 2rem; - } - - h2 { - margin: 0; - padding: 1.5rem 0 1rem 0; - font-size: 1.5rem; - } - - .btn { - width: 100%; - background-color: $main-button-background-colour; - color: $main-button-text-colour !important; - i { - font-size: 0.9rem; - } - &:hover { - background-color: $main-button-background-hover-colour; - } - } - - .collection { - margin: 0.5rem 0 0 0 !important; - .collection-item { - transition: initial !important; - display: block; - } - a.collection-item { - color: $collection-text-colour; - text-decoration: none; - &:hover { - background-color: $collection-background-hover-colour !important; - color: $collection-text-hover-colour !important; - } - } - .no-items { - color: $collection-no-items-text-colour; - } - } - - .mediacard { - a { - border-radius: 2px 2px 0 0; - display: block; - border: 3px $mediacard-border-colour solid; - &:hover { - border: 3px $mediacard-border-hover-colour solid; - } - } - .card-title { - padding: 3px 10px 3px 5px !important; - margin: 5px !important; - line-height: 1.4rem; - font-size: 1.3rem; - left: initial !important; - right: 0 !important; - max-width: 80% !important; - background-color: $mediacard-title-background-colour !important; - color: $mediacard-title-text-colour !important; - span { - font-size: 1rem; - line-height: 1rem; - } - } - .card-image { - img { - border-radius: 0 !important; - } - } - } - - .dashcard { - text-align: center; - h3 { - font-size: 2.5rem; - padding: 0; - margin: 0; - } - h4 { - text-align: left; - font-size: 1.5rem; - padding: 0 0 0.5rem 0; - margin: 0; - } - .desc { - border-bottom: 1px $dash-desc-border solid; - padding: 0 0 0.5rem 0; - margin: 0 0 0.5rem 0; - } - .collection-item { - text-align: left; - } - } - - .pagination { - width: 100%; - padding-top: 1rem; - text-align: center; - a { - display: inline-block; - font-weight: bold; - text-decoration: none; - padding: 5px 8px 4px 8px; - margin: 0 3px 6px 3px; - min-width: 40px; - min-height: 40px; - background-color: $pagination-background-colour; - color: $pagination-text-colour; - border: 2px $pagination-border-colour solid; - &:hover { - background-color: $pagination-background-hover-colour; - color: $pagination-text-hover-colour; - border: 2px $pagination-border-hover-colour solid; - } - } - .currentpage { - background-color: $pagination-current-background-colour; - color: $pagination-current-text-colour; - border: 2px $pagination-current-border-colour solid; - } - } - - .infobox { - background-color: $infobox-background-colour; - color: $infobox-text-colour; - } - - .errorbox { - background-color: $errorbox-background-colour; - color: $errorbox-text-colour; - } - - .error-text { - color: $error-text-colour !important; - } - -} - -footer { - - background-color: $footer-background-colour; - color: $footer-text-colour; - padding-top: 1.5rem; - - p { - margin: 0; - padding-bottom: 1.5rem; - } - - svg { - path { - fill: $footer-link-colour !important; - } - } - - i { - font-size: 0.85rem !important; - } - - a { - color: $footer-link-colour; - text-decoration: none; - &:hover { - color: $footer-link-hover-colour; - text-decoration: none; - } - } - -} diff --git a/tubesync/common/static/styles/_variables.scss b/tubesync/common/static/styles/_variables.scss deleted file mode 100644 index 2854a81c4..000000000 --- a/tubesync/common/static/styles/_variables.scss +++ /dev/null @@ -1,2 +0,0 @@ -$font-family: 'roboto', Arial, Helvetica, sans-serif; -$font-size: 1.05rem; diff --git a/tubesync/common/static/styles/fontawesome.min.css b/tubesync/common/static/styles/fontawesome.min.css new file mode 100644 index 000000000..de5452f90 --- /dev/null +++ b/tubesync/common/static/styles/fontawesome.min.css @@ -0,0 +1,9 @@ +/*! + * Font Awesome Free 6.7.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-regular,.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-brands:before,.fa-regular:before,.fa-solid:before,.fa:before,.fab:before,.far:before,.fas:before{content:var(--fa)}.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{animation-name:fa-beat;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{animation-name:fa-bounce;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{animation-name:fa-fade;animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{animation-name:fa-beat-fade;animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{animation-name:fa-flip;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{animation-name:fa-shake;animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{animation-name:fa-spin;animation-duration:var(--fa-animation-duration,2s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{animation-name:fa-spin;animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);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{animation-delay:-1ms;animation-duration:1ms;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@keyframes fa-beat{0%,90%{transform:scale(1)}45%{transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-bounce{0%{transform:scale(1) translateY(0)}10%{transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{transform:scale(1) translateY(0)}to{transform:scale(1) translateY(0)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);transform:scale(1)}50%{opacity:1;transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-flip{50%{transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-shake{0%{transform:rotate(-15deg)}4%{transform:rotate(15deg)}8%,24%{transform:rotate(-18deg)}12%,28%{transform:rotate(18deg)}16%{transform:rotate(-22deg)}20%{transform:rotate(22deg)}32%{transform:rotate(-12deg)}36%{transform:rotate(12deg)}40%,to{transform:rotate(0deg)}}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{transform:rotate(90deg)}.fa-rotate-180{transform:rotate(180deg)}.fa-rotate-270{transform:rotate(270deg)}.fa-flip-horizontal{transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}.fa-rotate-by{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{--fa:"\30"}.fa-1{--fa:"\31"}.fa-2{--fa:"\32"}.fa-3{--fa:"\33"}.fa-4{--fa:"\34"}.fa-5{--fa:"\35"}.fa-6{--fa:"\36"}.fa-7{--fa:"\37"}.fa-8{--fa:"\38"}.fa-9{--fa:"\39"}.fa-fill-drip{--fa:"\f576"}.fa-arrows-to-circle{--fa:"\e4bd"}.fa-chevron-circle-right,.fa-circle-chevron-right{--fa:"\f138"}.fa-at{--fa:"\40"}.fa-trash-alt,.fa-trash-can{--fa:"\f2ed"}.fa-text-height{--fa:"\f034"}.fa-user-times,.fa-user-xmark{--fa:"\f235"}.fa-stethoscope{--fa:"\f0f1"}.fa-comment-alt,.fa-message{--fa:"\f27a"}.fa-info{--fa:"\f129"}.fa-compress-alt,.fa-down-left-and-up-right-to-center{--fa:"\f422"}.fa-explosion{--fa:"\e4e9"}.fa-file-alt,.fa-file-lines,.fa-file-text{--fa:"\f15c"}.fa-wave-square{--fa:"\f83e"}.fa-ring{--fa:"\f70b"}.fa-building-un{--fa:"\e4d9"}.fa-dice-three{--fa:"\f527"}.fa-calendar-alt,.fa-calendar-days{--fa:"\f073"}.fa-anchor-circle-check{--fa:"\e4aa"}.fa-building-circle-arrow-right{--fa:"\e4d1"}.fa-volleyball,.fa-volleyball-ball{--fa:"\f45f"}.fa-arrows-up-to-line{--fa:"\e4c2"}.fa-sort-desc,.fa-sort-down{--fa:"\f0dd"}.fa-circle-minus,.fa-minus-circle{--fa:"\f056"}.fa-door-open{--fa:"\f52b"}.fa-right-from-bracket,.fa-sign-out-alt{--fa:"\f2f5"}.fa-atom{--fa:"\f5d2"}.fa-soap{--fa:"\e06e"}.fa-heart-music-camera-bolt,.fa-icons{--fa:"\f86d"}.fa-microphone-alt-slash,.fa-microphone-lines-slash{--fa:"\f539"}.fa-bridge-circle-check{--fa:"\e4c9"}.fa-pump-medical{--fa:"\e06a"}.fa-fingerprint{--fa:"\f577"}.fa-hand-point-right{--fa:"\f0a4"}.fa-magnifying-glass-location,.fa-search-location{--fa:"\f689"}.fa-forward-step,.fa-step-forward{--fa:"\f051"}.fa-face-smile-beam,.fa-smile-beam{--fa:"\f5b8"}.fa-flag-checkered{--fa:"\f11e"}.fa-football,.fa-football-ball{--fa:"\f44e"}.fa-school-circle-exclamation{--fa:"\e56c"}.fa-crop{--fa:"\f125"}.fa-angle-double-down,.fa-angles-down{--fa:"\f103"}.fa-users-rectangle{--fa:"\e594"}.fa-people-roof{--fa:"\e537"}.fa-people-line{--fa:"\e534"}.fa-beer,.fa-beer-mug-empty{--fa:"\f0fc"}.fa-diagram-predecessor{--fa:"\e477"}.fa-arrow-up-long,.fa-long-arrow-up{--fa:"\f176"}.fa-burn,.fa-fire-flame-simple{--fa:"\f46a"}.fa-male,.fa-person{--fa:"\f183"}.fa-laptop{--fa:"\f109"}.fa-file-csv{--fa:"\f6dd"}.fa-menorah{--fa:"\f676"}.fa-truck-plane{--fa:"\e58f"}.fa-record-vinyl{--fa:"\f8d9"}.fa-face-grin-stars,.fa-grin-stars{--fa:"\f587"}.fa-bong{--fa:"\f55c"}.fa-pastafarianism,.fa-spaghetti-monster-flying{--fa:"\f67b"}.fa-arrow-down-up-across-line{--fa:"\e4af"}.fa-spoon,.fa-utensil-spoon{--fa:"\f2e5"}.fa-jar-wheat{--fa:"\e517"}.fa-envelopes-bulk,.fa-mail-bulk{--fa:"\f674"}.fa-file-circle-exclamation{--fa:"\e4eb"}.fa-circle-h,.fa-hospital-symbol{--fa:"\f47e"}.fa-pager{--fa:"\f815"}.fa-address-book,.fa-contact-book{--fa:"\f2b9"}.fa-strikethrough{--fa:"\f0cc"}.fa-k{--fa:"\4b"}.fa-landmark-flag{--fa:"\e51c"}.fa-pencil,.fa-pencil-alt{--fa:"\f303"}.fa-backward{--fa:"\f04a"}.fa-caret-right{--fa:"\f0da"}.fa-comments{--fa:"\f086"}.fa-file-clipboard,.fa-paste{--fa:"\f0ea"}.fa-code-pull-request{--fa:"\e13c"}.fa-clipboard-list{--fa:"\f46d"}.fa-truck-loading,.fa-truck-ramp-box{--fa:"\f4de"}.fa-user-check{--fa:"\f4fc"}.fa-vial-virus{--fa:"\e597"}.fa-sheet-plastic{--fa:"\e571"}.fa-blog{--fa:"\f781"}.fa-user-ninja{--fa:"\f504"}.fa-person-arrow-up-from-line{--fa:"\e539"}.fa-scroll-torah,.fa-torah{--fa:"\f6a0"}.fa-broom-ball,.fa-quidditch,.fa-quidditch-broom-ball{--fa:"\f458"}.fa-toggle-off{--fa:"\f204"}.fa-archive,.fa-box-archive{--fa:"\f187"}.fa-person-drowning{--fa:"\e545"}.fa-arrow-down-9-1,.fa-sort-numeric-desc,.fa-sort-numeric-down-alt{--fa:"\f886"}.fa-face-grin-tongue-squint,.fa-grin-tongue-squint{--fa:"\f58a"}.fa-spray-can{--fa:"\f5bd"}.fa-truck-monster{--fa:"\f63b"}.fa-w{--fa:"\57"}.fa-earth-africa,.fa-globe-africa{--fa:"\f57c"}.fa-rainbow{--fa:"\f75b"}.fa-circle-notch{--fa:"\f1ce"}.fa-tablet-alt,.fa-tablet-screen-button{--fa:"\f3fa"}.fa-paw{--fa:"\f1b0"}.fa-cloud{--fa:"\f0c2"}.fa-trowel-bricks{--fa:"\e58a"}.fa-face-flushed,.fa-flushed{--fa:"\f579"}.fa-hospital-user{--fa:"\f80d"}.fa-tent-arrow-left-right{--fa:"\e57f"}.fa-gavel,.fa-legal{--fa:"\f0e3"}.fa-binoculars{--fa:"\f1e5"}.fa-microphone-slash{--fa:"\f131"}.fa-box-tissue{--fa:"\e05b"}.fa-motorcycle{--fa:"\f21c"}.fa-bell-concierge,.fa-concierge-bell{--fa:"\f562"}.fa-pen-ruler,.fa-pencil-ruler{--fa:"\f5ae"}.fa-people-arrows,.fa-people-arrows-left-right{--fa:"\e068"}.fa-mars-and-venus-burst{--fa:"\e523"}.fa-caret-square-right,.fa-square-caret-right{--fa:"\f152"}.fa-cut,.fa-scissors{--fa:"\f0c4"}.fa-sun-plant-wilt{--fa:"\e57a"}.fa-toilets-portable{--fa:"\e584"}.fa-hockey-puck{--fa:"\f453"}.fa-table{--fa:"\f0ce"}.fa-magnifying-glass-arrow-right{--fa:"\e521"}.fa-digital-tachograph,.fa-tachograph-digital{--fa:"\f566"}.fa-users-slash{--fa:"\e073"}.fa-clover{--fa:"\e139"}.fa-mail-reply,.fa-reply{--fa:"\f3e5"}.fa-star-and-crescent{--fa:"\f699"}.fa-house-fire{--fa:"\e50c"}.fa-minus-square,.fa-square-minus{--fa:"\f146"}.fa-helicopter{--fa:"\f533"}.fa-compass{--fa:"\f14e"}.fa-caret-square-down,.fa-square-caret-down{--fa:"\f150"}.fa-file-circle-question{--fa:"\e4ef"}.fa-laptop-code{--fa:"\f5fc"}.fa-swatchbook{--fa:"\f5c3"}.fa-prescription-bottle{--fa:"\f485"}.fa-bars,.fa-navicon{--fa:"\f0c9"}.fa-people-group{--fa:"\e533"}.fa-hourglass-3,.fa-hourglass-end{--fa:"\f253"}.fa-heart-broken,.fa-heart-crack{--fa:"\f7a9"}.fa-external-link-square-alt,.fa-square-up-right{--fa:"\f360"}.fa-face-kiss-beam,.fa-kiss-beam{--fa:"\f597"}.fa-film{--fa:"\f008"}.fa-ruler-horizontal{--fa:"\f547"}.fa-people-robbery{--fa:"\e536"}.fa-lightbulb{--fa:"\f0eb"}.fa-caret-left{--fa:"\f0d9"}.fa-circle-exclamation,.fa-exclamation-circle{--fa:"\f06a"}.fa-school-circle-xmark{--fa:"\e56d"}.fa-arrow-right-from-bracket,.fa-sign-out{--fa:"\f08b"}.fa-chevron-circle-down,.fa-circle-chevron-down{--fa:"\f13a"}.fa-unlock-alt,.fa-unlock-keyhole{--fa:"\f13e"}.fa-cloud-showers-heavy{--fa:"\f740"}.fa-headphones-alt,.fa-headphones-simple{--fa:"\f58f"}.fa-sitemap{--fa:"\f0e8"}.fa-circle-dollar-to-slot,.fa-donate{--fa:"\f4b9"}.fa-memory{--fa:"\f538"}.fa-road-spikes{--fa:"\e568"}.fa-fire-burner{--fa:"\e4f1"}.fa-flag{--fa:"\f024"}.fa-hanukiah{--fa:"\f6e6"}.fa-feather{--fa:"\f52d"}.fa-volume-down,.fa-volume-low{--fa:"\f027"}.fa-comment-slash{--fa:"\f4b3"}.fa-cloud-sun-rain{--fa:"\f743"}.fa-compress{--fa:"\f066"}.fa-wheat-alt,.fa-wheat-awn{--fa:"\e2cd"}.fa-ankh{--fa:"\f644"}.fa-hands-holding-child{--fa:"\e4fa"}.fa-asterisk{--fa:"\2a"}.fa-check-square,.fa-square-check{--fa:"\f14a"}.fa-peseta-sign{--fa:"\e221"}.fa-header,.fa-heading{--fa:"\f1dc"}.fa-ghost{--fa:"\f6e2"}.fa-list,.fa-list-squares{--fa:"\f03a"}.fa-phone-square-alt,.fa-square-phone-flip{--fa:"\f87b"}.fa-cart-plus{--fa:"\f217"}.fa-gamepad{--fa:"\f11b"}.fa-circle-dot,.fa-dot-circle{--fa:"\f192"}.fa-dizzy,.fa-face-dizzy{--fa:"\f567"}.fa-egg{--fa:"\f7fb"}.fa-house-medical-circle-xmark{--fa:"\e513"}.fa-campground{--fa:"\f6bb"}.fa-folder-plus{--fa:"\f65e"}.fa-futbol,.fa-futbol-ball,.fa-soccer-ball{--fa:"\f1e3"}.fa-paint-brush,.fa-paintbrush{--fa:"\f1fc"}.fa-lock{--fa:"\f023"}.fa-gas-pump{--fa:"\f52f"}.fa-hot-tub,.fa-hot-tub-person{--fa:"\f593"}.fa-map-location,.fa-map-marked{--fa:"\f59f"}.fa-house-flood-water{--fa:"\e50e"}.fa-tree{--fa:"\f1bb"}.fa-bridge-lock{--fa:"\e4cc"}.fa-sack-dollar{--fa:"\f81d"}.fa-edit,.fa-pen-to-square{--fa:"\f044"}.fa-car-side{--fa:"\f5e4"}.fa-share-alt,.fa-share-nodes{--fa:"\f1e0"}.fa-heart-circle-minus{--fa:"\e4ff"}.fa-hourglass-2,.fa-hourglass-half{--fa:"\f252"}.fa-microscope{--fa:"\f610"}.fa-sink{--fa:"\e06d"}.fa-bag-shopping,.fa-shopping-bag{--fa:"\f290"}.fa-arrow-down-z-a,.fa-sort-alpha-desc,.fa-sort-alpha-down-alt{--fa:"\f881"}.fa-mitten{--fa:"\f7b5"}.fa-person-rays{--fa:"\e54d"}.fa-users{--fa:"\f0c0"}.fa-eye-slash{--fa:"\f070"}.fa-flask-vial{--fa:"\e4f3"}.fa-hand,.fa-hand-paper{--fa:"\f256"}.fa-om{--fa:"\f679"}.fa-worm{--fa:"\e599"}.fa-house-circle-xmark{--fa:"\e50b"}.fa-plug{--fa:"\f1e6"}.fa-chevron-up{--fa:"\f077"}.fa-hand-spock{--fa:"\f259"}.fa-stopwatch{--fa:"\f2f2"}.fa-face-kiss,.fa-kiss{--fa:"\f596"}.fa-bridge-circle-xmark{--fa:"\e4cb"}.fa-face-grin-tongue,.fa-grin-tongue{--fa:"\f589"}.fa-chess-bishop{--fa:"\f43a"}.fa-face-grin-wink,.fa-grin-wink{--fa:"\f58c"}.fa-deaf,.fa-deafness,.fa-ear-deaf,.fa-hard-of-hearing{--fa:"\f2a4"}.fa-road-circle-check{--fa:"\e564"}.fa-dice-five{--fa:"\f523"}.fa-rss-square,.fa-square-rss{--fa:"\f143"}.fa-land-mine-on{--fa:"\e51b"}.fa-i-cursor{--fa:"\f246"}.fa-stamp{--fa:"\f5bf"}.fa-stairs{--fa:"\e289"}.fa-i{--fa:"\49"}.fa-hryvnia,.fa-hryvnia-sign{--fa:"\f6f2"}.fa-pills{--fa:"\f484"}.fa-face-grin-wide,.fa-grin-alt{--fa:"\f581"}.fa-tooth{--fa:"\f5c9"}.fa-v{--fa:"\56"}.fa-bangladeshi-taka-sign{--fa:"\e2e6"}.fa-bicycle{--fa:"\f206"}.fa-rod-asclepius,.fa-rod-snake,.fa-staff-aesculapius,.fa-staff-snake{--fa:"\e579"}.fa-head-side-cough-slash{--fa:"\e062"}.fa-ambulance,.fa-truck-medical{--fa:"\f0f9"}.fa-wheat-awn-circle-exclamation{--fa:"\e598"}.fa-snowman{--fa:"\f7d0"}.fa-mortar-pestle{--fa:"\f5a7"}.fa-road-barrier{--fa:"\e562"}.fa-school{--fa:"\f549"}.fa-igloo{--fa:"\f7ae"}.fa-joint{--fa:"\f595"}.fa-angle-right{--fa:"\f105"}.fa-horse{--fa:"\f6f0"}.fa-q{--fa:"\51"}.fa-g{--fa:"\47"}.fa-notes-medical{--fa:"\f481"}.fa-temperature-2,.fa-temperature-half,.fa-thermometer-2,.fa-thermometer-half{--fa:"\f2c9"}.fa-dong-sign{--fa:"\e169"}.fa-capsules{--fa:"\f46b"}.fa-poo-bolt,.fa-poo-storm{--fa:"\f75a"}.fa-face-frown-open,.fa-frown-open{--fa:"\f57a"}.fa-hand-point-up{--fa:"\f0a6"}.fa-money-bill{--fa:"\f0d6"}.fa-bookmark{--fa:"\f02e"}.fa-align-justify{--fa:"\f039"}.fa-umbrella-beach{--fa:"\f5ca"}.fa-helmet-un{--fa:"\e503"}.fa-bullseye{--fa:"\f140"}.fa-bacon{--fa:"\f7e5"}.fa-hand-point-down{--fa:"\f0a7"}.fa-arrow-up-from-bracket{--fa:"\e09a"}.fa-folder,.fa-folder-blank{--fa:"\f07b"}.fa-file-medical-alt,.fa-file-waveform{--fa:"\f478"}.fa-radiation{--fa:"\f7b9"}.fa-chart-simple{--fa:"\e473"}.fa-mars-stroke{--fa:"\f229"}.fa-vial{--fa:"\f492"}.fa-dashboard,.fa-gauge,.fa-gauge-med,.fa-tachometer-alt-average{--fa:"\f624"}.fa-magic-wand-sparkles,.fa-wand-magic-sparkles{--fa:"\e2ca"}.fa-e{--fa:"\45"}.fa-pen-alt,.fa-pen-clip{--fa:"\f305"}.fa-bridge-circle-exclamation{--fa:"\e4ca"}.fa-user{--fa:"\f007"}.fa-school-circle-check{--fa:"\e56b"}.fa-dumpster{--fa:"\f793"}.fa-shuttle-van,.fa-van-shuttle{--fa:"\f5b6"}.fa-building-user{--fa:"\e4da"}.fa-caret-square-left,.fa-square-caret-left{--fa:"\f191"}.fa-highlighter{--fa:"\f591"}.fa-key{--fa:"\f084"}.fa-bullhorn{--fa:"\f0a1"}.fa-globe{--fa:"\f0ac"}.fa-synagogue{--fa:"\f69b"}.fa-person-half-dress{--fa:"\e548"}.fa-road-bridge{--fa:"\e563"}.fa-location-arrow{--fa:"\f124"}.fa-c{--fa:"\43"}.fa-tablet-button{--fa:"\f10a"}.fa-building-lock{--fa:"\e4d6"}.fa-pizza-slice{--fa:"\f818"}.fa-money-bill-wave{--fa:"\f53a"}.fa-area-chart,.fa-chart-area{--fa:"\f1fe"}.fa-house-flag{--fa:"\e50d"}.fa-person-circle-minus{--fa:"\e540"}.fa-ban,.fa-cancel{--fa:"\f05e"}.fa-camera-rotate{--fa:"\e0d8"}.fa-air-freshener,.fa-spray-can-sparkles{--fa:"\f5d0"}.fa-star{--fa:"\f005"}.fa-repeat{--fa:"\f363"}.fa-cross{--fa:"\f654"}.fa-box{--fa:"\f466"}.fa-venus-mars{--fa:"\f228"}.fa-arrow-pointer,.fa-mouse-pointer{--fa:"\f245"}.fa-expand-arrows-alt,.fa-maximize{--fa:"\f31e"}.fa-charging-station{--fa:"\f5e7"}.fa-shapes,.fa-triangle-circle-square{--fa:"\f61f"}.fa-random,.fa-shuffle{--fa:"\f074"}.fa-person-running,.fa-running{--fa:"\f70c"}.fa-mobile-retro{--fa:"\e527"}.fa-grip-lines-vertical{--fa:"\f7a5"}.fa-spider{--fa:"\f717"}.fa-hands-bound{--fa:"\e4f9"}.fa-file-invoice-dollar{--fa:"\f571"}.fa-plane-circle-exclamation{--fa:"\e556"}.fa-x-ray{--fa:"\f497"}.fa-spell-check{--fa:"\f891"}.fa-slash{--fa:"\f715"}.fa-computer-mouse,.fa-mouse{--fa:"\f8cc"}.fa-arrow-right-to-bracket,.fa-sign-in{--fa:"\f090"}.fa-shop-slash,.fa-store-alt-slash{--fa:"\e070"}.fa-server{--fa:"\f233"}.fa-virus-covid-slash{--fa:"\e4a9"}.fa-shop-lock{--fa:"\e4a5"}.fa-hourglass-1,.fa-hourglass-start{--fa:"\f251"}.fa-blender-phone{--fa:"\f6b6"}.fa-building-wheat{--fa:"\e4db"}.fa-person-breastfeeding{--fa:"\e53a"}.fa-right-to-bracket,.fa-sign-in-alt{--fa:"\f2f6"}.fa-venus{--fa:"\f221"}.fa-passport{--fa:"\f5ab"}.fa-thumb-tack-slash,.fa-thumbtack-slash{--fa:"\e68f"}.fa-heart-pulse,.fa-heartbeat{--fa:"\f21e"}.fa-people-carry,.fa-people-carry-box{--fa:"\f4ce"}.fa-temperature-high{--fa:"\f769"}.fa-microchip{--fa:"\f2db"}.fa-crown{--fa:"\f521"}.fa-weight-hanging{--fa:"\f5cd"}.fa-xmarks-lines{--fa:"\e59a"}.fa-file-prescription{--fa:"\f572"}.fa-weight,.fa-weight-scale{--fa:"\f496"}.fa-user-friends,.fa-user-group{--fa:"\f500"}.fa-arrow-up-a-z,.fa-sort-alpha-up{--fa:"\f15e"}.fa-chess-knight{--fa:"\f441"}.fa-face-laugh-squint,.fa-laugh-squint{--fa:"\f59b"}.fa-wheelchair{--fa:"\f193"}.fa-arrow-circle-up,.fa-circle-arrow-up{--fa:"\f0aa"}.fa-toggle-on{--fa:"\f205"}.fa-person-walking,.fa-walking{--fa:"\f554"}.fa-l{--fa:"\4c"}.fa-fire{--fa:"\f06d"}.fa-bed-pulse,.fa-procedures{--fa:"\f487"}.fa-shuttle-space,.fa-space-shuttle{--fa:"\f197"}.fa-face-laugh,.fa-laugh{--fa:"\f599"}.fa-folder-open{--fa:"\f07c"}.fa-heart-circle-plus{--fa:"\e500"}.fa-code-fork{--fa:"\e13b"}.fa-city{--fa:"\f64f"}.fa-microphone-alt,.fa-microphone-lines{--fa:"\f3c9"}.fa-pepper-hot{--fa:"\f816"}.fa-unlock{--fa:"\f09c"}.fa-colon-sign{--fa:"\e140"}.fa-headset{--fa:"\f590"}.fa-store-slash{--fa:"\e071"}.fa-road-circle-xmark{--fa:"\e566"}.fa-user-minus{--fa:"\f503"}.fa-mars-stroke-up,.fa-mars-stroke-v{--fa:"\f22a"}.fa-champagne-glasses,.fa-glass-cheers{--fa:"\f79f"}.fa-clipboard{--fa:"\f328"}.fa-house-circle-exclamation{--fa:"\e50a"}.fa-file-arrow-up,.fa-file-upload{--fa:"\f574"}.fa-wifi,.fa-wifi-3,.fa-wifi-strong{--fa:"\f1eb"}.fa-bath,.fa-bathtub{--fa:"\f2cd"}.fa-underline{--fa:"\f0cd"}.fa-user-edit,.fa-user-pen{--fa:"\f4ff"}.fa-signature{--fa:"\f5b7"}.fa-stroopwafel{--fa:"\f551"}.fa-bold{--fa:"\f032"}.fa-anchor-lock{--fa:"\e4ad"}.fa-building-ngo{--fa:"\e4d7"}.fa-manat-sign{--fa:"\e1d5"}.fa-not-equal{--fa:"\f53e"}.fa-border-style,.fa-border-top-left{--fa:"\f853"}.fa-map-location-dot,.fa-map-marked-alt{--fa:"\f5a0"}.fa-jedi{--fa:"\f669"}.fa-poll,.fa-square-poll-vertical{--fa:"\f681"}.fa-mug-hot{--fa:"\f7b6"}.fa-battery-car,.fa-car-battery{--fa:"\f5df"}.fa-gift{--fa:"\f06b"}.fa-dice-two{--fa:"\f528"}.fa-chess-queen{--fa:"\f445"}.fa-glasses{--fa:"\f530"}.fa-chess-board{--fa:"\f43c"}.fa-building-circle-check{--fa:"\e4d2"}.fa-person-chalkboard{--fa:"\e53d"}.fa-mars-stroke-h,.fa-mars-stroke-right{--fa:"\f22b"}.fa-hand-back-fist,.fa-hand-rock{--fa:"\f255"}.fa-caret-square-up,.fa-square-caret-up{--fa:"\f151"}.fa-cloud-showers-water{--fa:"\e4e4"}.fa-bar-chart,.fa-chart-bar{--fa:"\f080"}.fa-hands-bubbles,.fa-hands-wash{--fa:"\e05e"}.fa-less-than-equal{--fa:"\f537"}.fa-train{--fa:"\f238"}.fa-eye-low-vision,.fa-low-vision{--fa:"\f2a8"}.fa-crow{--fa:"\f520"}.fa-sailboat{--fa:"\e445"}.fa-window-restore{--fa:"\f2d2"}.fa-plus-square,.fa-square-plus{--fa:"\f0fe"}.fa-torii-gate{--fa:"\f6a1"}.fa-frog{--fa:"\f52e"}.fa-bucket{--fa:"\e4cf"}.fa-image{--fa:"\f03e"}.fa-microphone{--fa:"\f130"}.fa-cow{--fa:"\f6c8"}.fa-caret-up{--fa:"\f0d8"}.fa-screwdriver{--fa:"\f54a"}.fa-folder-closed{--fa:"\e185"}.fa-house-tsunami{--fa:"\e515"}.fa-square-nfi{--fa:"\e576"}.fa-arrow-up-from-ground-water{--fa:"\e4b5"}.fa-glass-martini-alt,.fa-martini-glass{--fa:"\f57b"}.fa-square-binary{--fa:"\e69b"}.fa-rotate-back,.fa-rotate-backward,.fa-rotate-left,.fa-undo-alt{--fa:"\f2ea"}.fa-columns,.fa-table-columns{--fa:"\f0db"}.fa-lemon{--fa:"\f094"}.fa-head-side-mask{--fa:"\e063"}.fa-handshake{--fa:"\f2b5"}.fa-gem{--fa:"\f3a5"}.fa-dolly,.fa-dolly-box{--fa:"\f472"}.fa-smoking{--fa:"\f48d"}.fa-compress-arrows-alt,.fa-minimize{--fa:"\f78c"}.fa-monument{--fa:"\f5a6"}.fa-snowplow{--fa:"\f7d2"}.fa-angle-double-right,.fa-angles-right{--fa:"\f101"}.fa-cannabis{--fa:"\f55f"}.fa-circle-play,.fa-play-circle{--fa:"\f144"}.fa-tablets{--fa:"\f490"}.fa-ethernet{--fa:"\f796"}.fa-eur,.fa-euro,.fa-euro-sign{--fa:"\f153"}.fa-chair{--fa:"\f6c0"}.fa-check-circle,.fa-circle-check{--fa:"\f058"}.fa-circle-stop,.fa-stop-circle{--fa:"\f28d"}.fa-compass-drafting,.fa-drafting-compass{--fa:"\f568"}.fa-plate-wheat{--fa:"\e55a"}.fa-icicles{--fa:"\f7ad"}.fa-person-shelter{--fa:"\e54f"}.fa-neuter{--fa:"\f22c"}.fa-id-badge{--fa:"\f2c1"}.fa-marker{--fa:"\f5a1"}.fa-face-laugh-beam,.fa-laugh-beam{--fa:"\f59a"}.fa-helicopter-symbol{--fa:"\e502"}.fa-universal-access{--fa:"\f29a"}.fa-chevron-circle-up,.fa-circle-chevron-up{--fa:"\f139"}.fa-lari-sign{--fa:"\e1c8"}.fa-volcano{--fa:"\f770"}.fa-person-walking-dashed-line-arrow-right{--fa:"\e553"}.fa-gbp,.fa-pound-sign,.fa-sterling-sign{--fa:"\f154"}.fa-viruses{--fa:"\e076"}.fa-square-person-confined{--fa:"\e577"}.fa-user-tie{--fa:"\f508"}.fa-arrow-down-long,.fa-long-arrow-down{--fa:"\f175"}.fa-tent-arrow-down-to-line{--fa:"\e57e"}.fa-certificate{--fa:"\f0a3"}.fa-mail-reply-all,.fa-reply-all{--fa:"\f122"}.fa-suitcase{--fa:"\f0f2"}.fa-person-skating,.fa-skating{--fa:"\f7c5"}.fa-filter-circle-dollar,.fa-funnel-dollar{--fa:"\f662"}.fa-camera-retro{--fa:"\f083"}.fa-arrow-circle-down,.fa-circle-arrow-down{--fa:"\f0ab"}.fa-arrow-right-to-file,.fa-file-import{--fa:"\f56f"}.fa-external-link-square,.fa-square-arrow-up-right{--fa:"\f14c"}.fa-box-open{--fa:"\f49e"}.fa-scroll{--fa:"\f70e"}.fa-spa{--fa:"\f5bb"}.fa-location-pin-lock{--fa:"\e51f"}.fa-pause{--fa:"\f04c"}.fa-hill-avalanche{--fa:"\e507"}.fa-temperature-0,.fa-temperature-empty,.fa-thermometer-0,.fa-thermometer-empty{--fa:"\f2cb"}.fa-bomb{--fa:"\f1e2"}.fa-registered{--fa:"\f25d"}.fa-address-card,.fa-contact-card,.fa-vcard{--fa:"\f2bb"}.fa-balance-scale-right,.fa-scale-unbalanced-flip{--fa:"\f516"}.fa-subscript{--fa:"\f12c"}.fa-diamond-turn-right,.fa-directions{--fa:"\f5eb"}.fa-burst{--fa:"\e4dc"}.fa-house-laptop,.fa-laptop-house{--fa:"\e066"}.fa-face-tired,.fa-tired{--fa:"\f5c8"}.fa-money-bills{--fa:"\e1f3"}.fa-smog{--fa:"\f75f"}.fa-crutch{--fa:"\f7f7"}.fa-cloud-arrow-up,.fa-cloud-upload,.fa-cloud-upload-alt{--fa:"\f0ee"}.fa-palette{--fa:"\f53f"}.fa-arrows-turn-right{--fa:"\e4c0"}.fa-vest{--fa:"\e085"}.fa-ferry{--fa:"\e4ea"}.fa-arrows-down-to-people{--fa:"\e4b9"}.fa-seedling,.fa-sprout{--fa:"\f4d8"}.fa-arrows-alt-h,.fa-left-right{--fa:"\f337"}.fa-boxes-packing{--fa:"\e4c7"}.fa-arrow-circle-left,.fa-circle-arrow-left{--fa:"\f0a8"}.fa-group-arrows-rotate{--fa:"\e4f6"}.fa-bowl-food{--fa:"\e4c6"}.fa-candy-cane{--fa:"\f786"}.fa-arrow-down-wide-short,.fa-sort-amount-asc,.fa-sort-amount-down{--fa:"\f160"}.fa-cloud-bolt,.fa-thunderstorm{--fa:"\f76c"}.fa-remove-format,.fa-text-slash{--fa:"\f87d"}.fa-face-smile-wink,.fa-smile-wink{--fa:"\f4da"}.fa-file-word{--fa:"\f1c2"}.fa-file-powerpoint{--fa:"\f1c4"}.fa-arrows-h,.fa-arrows-left-right{--fa:"\f07e"}.fa-house-lock{--fa:"\e510"}.fa-cloud-arrow-down,.fa-cloud-download,.fa-cloud-download-alt{--fa:"\f0ed"}.fa-children{--fa:"\e4e1"}.fa-blackboard,.fa-chalkboard{--fa:"\f51b"}.fa-user-alt-slash,.fa-user-large-slash{--fa:"\f4fa"}.fa-envelope-open{--fa:"\f2b6"}.fa-handshake-alt-slash,.fa-handshake-simple-slash{--fa:"\e05f"}.fa-mattress-pillow{--fa:"\e525"}.fa-guarani-sign{--fa:"\e19a"}.fa-arrows-rotate,.fa-refresh,.fa-sync{--fa:"\f021"}.fa-fire-extinguisher{--fa:"\f134"}.fa-cruzeiro-sign{--fa:"\e152"}.fa-greater-than-equal{--fa:"\f532"}.fa-shield-alt,.fa-shield-halved{--fa:"\f3ed"}.fa-atlas,.fa-book-atlas{--fa:"\f558"}.fa-virus{--fa:"\e074"}.fa-envelope-circle-check{--fa:"\e4e8"}.fa-layer-group{--fa:"\f5fd"}.fa-arrows-to-dot{--fa:"\e4be"}.fa-archway{--fa:"\f557"}.fa-heart-circle-check{--fa:"\e4fd"}.fa-house-chimney-crack,.fa-house-damage{--fa:"\f6f1"}.fa-file-archive,.fa-file-zipper{--fa:"\f1c6"}.fa-square{--fa:"\f0c8"}.fa-glass-martini,.fa-martini-glass-empty{--fa:"\f000"}.fa-couch{--fa:"\f4b8"}.fa-cedi-sign{--fa:"\e0df"}.fa-italic{--fa:"\f033"}.fa-table-cells-column-lock{--fa:"\e678"}.fa-church{--fa:"\f51d"}.fa-comments-dollar{--fa:"\f653"}.fa-democrat{--fa:"\f747"}.fa-z{--fa:"\5a"}.fa-person-skiing,.fa-skiing{--fa:"\f7c9"}.fa-road-lock{--fa:"\e567"}.fa-a{--fa:"\41"}.fa-temperature-arrow-down,.fa-temperature-down{--fa:"\e03f"}.fa-feather-alt,.fa-feather-pointed{--fa:"\f56b"}.fa-p{--fa:"\50"}.fa-snowflake{--fa:"\f2dc"}.fa-newspaper{--fa:"\f1ea"}.fa-ad,.fa-rectangle-ad{--fa:"\f641"}.fa-arrow-circle-right,.fa-circle-arrow-right{--fa:"\f0a9"}.fa-filter-circle-xmark{--fa:"\e17b"}.fa-locust{--fa:"\e520"}.fa-sort,.fa-unsorted{--fa:"\f0dc"}.fa-list-1-2,.fa-list-numeric,.fa-list-ol{--fa:"\f0cb"}.fa-person-dress-burst{--fa:"\e544"}.fa-money-check-alt,.fa-money-check-dollar{--fa:"\f53d"}.fa-vector-square{--fa:"\f5cb"}.fa-bread-slice{--fa:"\f7ec"}.fa-language{--fa:"\f1ab"}.fa-face-kiss-wink-heart,.fa-kiss-wink-heart{--fa:"\f598"}.fa-filter{--fa:"\f0b0"}.fa-question{--fa:"\3f"}.fa-file-signature{--fa:"\f573"}.fa-arrows-alt,.fa-up-down-left-right{--fa:"\f0b2"}.fa-house-chimney-user{--fa:"\e065"}.fa-hand-holding-heart{--fa:"\f4be"}.fa-puzzle-piece{--fa:"\f12e"}.fa-money-check{--fa:"\f53c"}.fa-star-half-alt,.fa-star-half-stroke{--fa:"\f5c0"}.fa-code{--fa:"\f121"}.fa-glass-whiskey,.fa-whiskey-glass{--fa:"\f7a0"}.fa-building-circle-exclamation{--fa:"\e4d3"}.fa-magnifying-glass-chart{--fa:"\e522"}.fa-arrow-up-right-from-square,.fa-external-link{--fa:"\f08e"}.fa-cubes-stacked{--fa:"\e4e6"}.fa-krw,.fa-won,.fa-won-sign{--fa:"\f159"}.fa-virus-covid{--fa:"\e4a8"}.fa-austral-sign{--fa:"\e0a9"}.fa-f{--fa:"\46"}.fa-leaf{--fa:"\f06c"}.fa-road{--fa:"\f018"}.fa-cab,.fa-taxi{--fa:"\f1ba"}.fa-person-circle-plus{--fa:"\e541"}.fa-chart-pie,.fa-pie-chart{--fa:"\f200"}.fa-bolt-lightning{--fa:"\e0b7"}.fa-sack-xmark{--fa:"\e56a"}.fa-file-excel{--fa:"\f1c3"}.fa-file-contract{--fa:"\f56c"}.fa-fish-fins{--fa:"\e4f2"}.fa-building-flag{--fa:"\e4d5"}.fa-face-grin-beam,.fa-grin-beam{--fa:"\f582"}.fa-object-ungroup{--fa:"\f248"}.fa-poop{--fa:"\f619"}.fa-location-pin,.fa-map-marker{--fa:"\f041"}.fa-kaaba{--fa:"\f66b"}.fa-toilet-paper{--fa:"\f71e"}.fa-hard-hat,.fa-hat-hard,.fa-helmet-safety{--fa:"\f807"}.fa-eject{--fa:"\f052"}.fa-arrow-alt-circle-right,.fa-circle-right{--fa:"\f35a"}.fa-plane-circle-check{--fa:"\e555"}.fa-face-rolling-eyes,.fa-meh-rolling-eyes{--fa:"\f5a5"}.fa-object-group{--fa:"\f247"}.fa-chart-line,.fa-line-chart{--fa:"\f201"}.fa-mask-ventilator{--fa:"\e524"}.fa-arrow-right{--fa:"\f061"}.fa-map-signs,.fa-signs-post{--fa:"\f277"}.fa-cash-register{--fa:"\f788"}.fa-person-circle-question{--fa:"\e542"}.fa-h{--fa:"\48"}.fa-tarp{--fa:"\e57b"}.fa-screwdriver-wrench,.fa-tools{--fa:"\f7d9"}.fa-arrows-to-eye{--fa:"\e4bf"}.fa-plug-circle-bolt{--fa:"\e55b"}.fa-heart{--fa:"\f004"}.fa-mars-and-venus{--fa:"\f224"}.fa-home-user,.fa-house-user{--fa:"\e1b0"}.fa-dumpster-fire{--fa:"\f794"}.fa-house-crack{--fa:"\e3b1"}.fa-cocktail,.fa-martini-glass-citrus{--fa:"\f561"}.fa-face-surprise,.fa-surprise{--fa:"\f5c2"}.fa-bottle-water{--fa:"\e4c5"}.fa-circle-pause,.fa-pause-circle{--fa:"\f28b"}.fa-toilet-paper-slash{--fa:"\e072"}.fa-apple-alt,.fa-apple-whole{--fa:"\f5d1"}.fa-kitchen-set{--fa:"\e51a"}.fa-r{--fa:"\52"}.fa-temperature-1,.fa-temperature-quarter,.fa-thermometer-1,.fa-thermometer-quarter{--fa:"\f2ca"}.fa-cube{--fa:"\f1b2"}.fa-bitcoin-sign{--fa:"\e0b4"}.fa-shield-dog{--fa:"\e573"}.fa-solar-panel{--fa:"\f5ba"}.fa-lock-open{--fa:"\f3c1"}.fa-elevator{--fa:"\e16d"}.fa-money-bill-transfer{--fa:"\e528"}.fa-money-bill-trend-up{--fa:"\e529"}.fa-house-flood-water-circle-arrow-right{--fa:"\e50f"}.fa-poll-h,.fa-square-poll-horizontal{--fa:"\f682"}.fa-circle{--fa:"\f111"}.fa-backward-fast,.fa-fast-backward{--fa:"\f049"}.fa-recycle{--fa:"\f1b8"}.fa-user-astronaut{--fa:"\f4fb"}.fa-plane-slash{--fa:"\e069"}.fa-trademark{--fa:"\f25c"}.fa-basketball,.fa-basketball-ball{--fa:"\f434"}.fa-satellite-dish{--fa:"\f7c0"}.fa-arrow-alt-circle-up,.fa-circle-up{--fa:"\f35b"}.fa-mobile-alt,.fa-mobile-screen-button{--fa:"\f3cd"}.fa-volume-high,.fa-volume-up{--fa:"\f028"}.fa-users-rays{--fa:"\e593"}.fa-wallet{--fa:"\f555"}.fa-clipboard-check{--fa:"\f46c"}.fa-file-audio{--fa:"\f1c7"}.fa-burger,.fa-hamburger{--fa:"\f805"}.fa-wrench{--fa:"\f0ad"}.fa-bugs{--fa:"\e4d0"}.fa-rupee,.fa-rupee-sign{--fa:"\f156"}.fa-file-image{--fa:"\f1c5"}.fa-circle-question,.fa-question-circle{--fa:"\f059"}.fa-plane-departure{--fa:"\f5b0"}.fa-handshake-slash{--fa:"\e060"}.fa-book-bookmark{--fa:"\e0bb"}.fa-code-branch{--fa:"\f126"}.fa-hat-cowboy{--fa:"\f8c0"}.fa-bridge{--fa:"\e4c8"}.fa-phone-alt,.fa-phone-flip{--fa:"\f879"}.fa-truck-front{--fa:"\e2b7"}.fa-cat{--fa:"\f6be"}.fa-anchor-circle-exclamation{--fa:"\e4ab"}.fa-truck-field{--fa:"\e58d"}.fa-route{--fa:"\f4d7"}.fa-clipboard-question{--fa:"\e4e3"}.fa-panorama{--fa:"\e209"}.fa-comment-medical{--fa:"\f7f5"}.fa-teeth-open{--fa:"\f62f"}.fa-file-circle-minus{--fa:"\e4ed"}.fa-tags{--fa:"\f02c"}.fa-wine-glass{--fa:"\f4e3"}.fa-fast-forward,.fa-forward-fast{--fa:"\f050"}.fa-face-meh-blank,.fa-meh-blank{--fa:"\f5a4"}.fa-parking,.fa-square-parking{--fa:"\f540"}.fa-house-signal{--fa:"\e012"}.fa-bars-progress,.fa-tasks-alt{--fa:"\f828"}.fa-faucet-drip{--fa:"\e006"}.fa-cart-flatbed,.fa-dolly-flatbed{--fa:"\f474"}.fa-ban-smoking,.fa-smoking-ban{--fa:"\f54d"}.fa-terminal{--fa:"\f120"}.fa-mobile-button{--fa:"\f10b"}.fa-house-medical-flag{--fa:"\e514"}.fa-basket-shopping,.fa-shopping-basket{--fa:"\f291"}.fa-tape{--fa:"\f4db"}.fa-bus-alt,.fa-bus-simple{--fa:"\f55e"}.fa-eye{--fa:"\f06e"}.fa-face-sad-cry,.fa-sad-cry{--fa:"\f5b3"}.fa-audio-description{--fa:"\f29e"}.fa-person-military-to-person{--fa:"\e54c"}.fa-file-shield{--fa:"\e4f0"}.fa-user-slash{--fa:"\f506"}.fa-pen{--fa:"\f304"}.fa-tower-observation{--fa:"\e586"}.fa-file-code{--fa:"\f1c9"}.fa-signal,.fa-signal-5,.fa-signal-perfect{--fa:"\f012"}.fa-bus{--fa:"\f207"}.fa-heart-circle-xmark{--fa:"\e501"}.fa-home-lg,.fa-house-chimney{--fa:"\e3af"}.fa-window-maximize{--fa:"\f2d0"}.fa-face-frown,.fa-frown{--fa:"\f119"}.fa-prescription{--fa:"\f5b1"}.fa-shop,.fa-store-alt{--fa:"\f54f"}.fa-floppy-disk,.fa-save{--fa:"\f0c7"}.fa-vihara{--fa:"\f6a7"}.fa-balance-scale-left,.fa-scale-unbalanced{--fa:"\f515"}.fa-sort-asc,.fa-sort-up{--fa:"\f0de"}.fa-comment-dots,.fa-commenting{--fa:"\f4ad"}.fa-plant-wilt{--fa:"\e5aa"}.fa-diamond{--fa:"\f219"}.fa-face-grin-squint,.fa-grin-squint{--fa:"\f585"}.fa-hand-holding-dollar,.fa-hand-holding-usd{--fa:"\f4c0"}.fa-chart-diagram{--fa:"\e695"}.fa-bacterium{--fa:"\e05a"}.fa-hand-pointer{--fa:"\f25a"}.fa-drum-steelpan{--fa:"\f56a"}.fa-hand-scissors{--fa:"\f257"}.fa-hands-praying,.fa-praying-hands{--fa:"\f684"}.fa-arrow-right-rotate,.fa-arrow-rotate-forward,.fa-arrow-rotate-right,.fa-redo{--fa:"\f01e"}.fa-biohazard{--fa:"\f780"}.fa-location,.fa-location-crosshairs{--fa:"\f601"}.fa-mars-double{--fa:"\f227"}.fa-child-dress{--fa:"\e59c"}.fa-users-between-lines{--fa:"\e591"}.fa-lungs-virus{--fa:"\e067"}.fa-face-grin-tears,.fa-grin-tears{--fa:"\f588"}.fa-phone{--fa:"\f095"}.fa-calendar-times,.fa-calendar-xmark{--fa:"\f273"}.fa-child-reaching{--fa:"\e59d"}.fa-head-side-virus{--fa:"\e064"}.fa-user-cog,.fa-user-gear{--fa:"\f4fe"}.fa-arrow-up-1-9,.fa-sort-numeric-up{--fa:"\f163"}.fa-door-closed{--fa:"\f52a"}.fa-shield-virus{--fa:"\e06c"}.fa-dice-six{--fa:"\f526"}.fa-mosquito-net{--fa:"\e52c"}.fa-file-fragment{--fa:"\e697"}.fa-bridge-water{--fa:"\e4ce"}.fa-person-booth{--fa:"\f756"}.fa-text-width{--fa:"\f035"}.fa-hat-wizard{--fa:"\f6e8"}.fa-pen-fancy{--fa:"\f5ac"}.fa-digging,.fa-person-digging{--fa:"\f85e"}.fa-trash{--fa:"\f1f8"}.fa-gauge-simple,.fa-gauge-simple-med,.fa-tachometer-average{--fa:"\f629"}.fa-book-medical{--fa:"\f7e6"}.fa-poo{--fa:"\f2fe"}.fa-quote-right,.fa-quote-right-alt{--fa:"\f10e"}.fa-shirt,.fa-t-shirt,.fa-tshirt{--fa:"\f553"}.fa-cubes{--fa:"\f1b3"}.fa-divide{--fa:"\f529"}.fa-tenge,.fa-tenge-sign{--fa:"\f7d7"}.fa-headphones{--fa:"\f025"}.fa-hands-holding{--fa:"\f4c2"}.fa-hands-clapping{--fa:"\e1a8"}.fa-republican{--fa:"\f75e"}.fa-arrow-left{--fa:"\f060"}.fa-person-circle-xmark{--fa:"\e543"}.fa-ruler{--fa:"\f545"}.fa-align-left{--fa:"\f036"}.fa-dice-d6{--fa:"\f6d1"}.fa-restroom{--fa:"\f7bd"}.fa-j{--fa:"\4a"}.fa-users-viewfinder{--fa:"\e595"}.fa-file-video{--fa:"\f1c8"}.fa-external-link-alt,.fa-up-right-from-square{--fa:"\f35d"}.fa-table-cells,.fa-th{--fa:"\f00a"}.fa-file-pdf{--fa:"\f1c1"}.fa-bible,.fa-book-bible{--fa:"\f647"}.fa-o{--fa:"\4f"}.fa-medkit,.fa-suitcase-medical{--fa:"\f0fa"}.fa-user-secret{--fa:"\f21b"}.fa-otter{--fa:"\f700"}.fa-female,.fa-person-dress{--fa:"\f182"}.fa-comment-dollar{--fa:"\f651"}.fa-briefcase-clock,.fa-business-time{--fa:"\f64a"}.fa-table-cells-large,.fa-th-large{--fa:"\f009"}.fa-book-tanakh,.fa-tanakh{--fa:"\f827"}.fa-phone-volume,.fa-volume-control-phone{--fa:"\f2a0"}.fa-hat-cowboy-side{--fa:"\f8c1"}.fa-clipboard-user{--fa:"\f7f3"}.fa-child{--fa:"\f1ae"}.fa-lira-sign{--fa:"\f195"}.fa-satellite{--fa:"\f7bf"}.fa-plane-lock{--fa:"\e558"}.fa-tag{--fa:"\f02b"}.fa-comment{--fa:"\f075"}.fa-birthday-cake,.fa-cake,.fa-cake-candles{--fa:"\f1fd"}.fa-envelope{--fa:"\f0e0"}.fa-angle-double-up,.fa-angles-up{--fa:"\f102"}.fa-paperclip{--fa:"\f0c6"}.fa-arrow-right-to-city{--fa:"\e4b3"}.fa-ribbon{--fa:"\f4d6"}.fa-lungs{--fa:"\f604"}.fa-arrow-up-9-1,.fa-sort-numeric-up-alt{--fa:"\f887"}.fa-litecoin-sign{--fa:"\e1d3"}.fa-border-none{--fa:"\f850"}.fa-circle-nodes{--fa:"\e4e2"}.fa-parachute-box{--fa:"\f4cd"}.fa-indent{--fa:"\f03c"}.fa-truck-field-un{--fa:"\e58e"}.fa-hourglass,.fa-hourglass-empty{--fa:"\f254"}.fa-mountain{--fa:"\f6fc"}.fa-user-doctor,.fa-user-md{--fa:"\f0f0"}.fa-circle-info,.fa-info-circle{--fa:"\f05a"}.fa-cloud-meatball{--fa:"\f73b"}.fa-camera,.fa-camera-alt{--fa:"\f030"}.fa-square-virus{--fa:"\e578"}.fa-meteor{--fa:"\f753"}.fa-car-on{--fa:"\e4dd"}.fa-sleigh{--fa:"\f7cc"}.fa-arrow-down-1-9,.fa-sort-numeric-asc,.fa-sort-numeric-down{--fa:"\f162"}.fa-hand-holding-droplet,.fa-hand-holding-water{--fa:"\f4c1"}.fa-water{--fa:"\f773"}.fa-calendar-check{--fa:"\f274"}.fa-braille{--fa:"\f2a1"}.fa-prescription-bottle-alt,.fa-prescription-bottle-medical{--fa:"\f486"}.fa-landmark{--fa:"\f66f"}.fa-truck{--fa:"\f0d1"}.fa-crosshairs{--fa:"\f05b"}.fa-person-cane{--fa:"\e53c"}.fa-tent{--fa:"\e57d"}.fa-vest-patches{--fa:"\e086"}.fa-check-double{--fa:"\f560"}.fa-arrow-down-a-z,.fa-sort-alpha-asc,.fa-sort-alpha-down{--fa:"\f15d"}.fa-money-bill-wheat{--fa:"\e52a"}.fa-cookie{--fa:"\f563"}.fa-arrow-left-rotate,.fa-arrow-rotate-back,.fa-arrow-rotate-backward,.fa-arrow-rotate-left,.fa-undo{--fa:"\f0e2"}.fa-hard-drive,.fa-hdd{--fa:"\f0a0"}.fa-face-grin-squint-tears,.fa-grin-squint-tears{--fa:"\f586"}.fa-dumbbell{--fa:"\f44b"}.fa-list-alt,.fa-rectangle-list{--fa:"\f022"}.fa-tarp-droplet{--fa:"\e57c"}.fa-house-medical-circle-check{--fa:"\e511"}.fa-person-skiing-nordic,.fa-skiing-nordic{--fa:"\f7ca"}.fa-calendar-plus{--fa:"\f271"}.fa-plane-arrival{--fa:"\f5af"}.fa-arrow-alt-circle-left,.fa-circle-left{--fa:"\f359"}.fa-subway,.fa-train-subway{--fa:"\f239"}.fa-chart-gantt{--fa:"\e0e4"}.fa-indian-rupee,.fa-indian-rupee-sign,.fa-inr{--fa:"\e1bc"}.fa-crop-alt,.fa-crop-simple{--fa:"\f565"}.fa-money-bill-1,.fa-money-bill-alt{--fa:"\f3d1"}.fa-left-long,.fa-long-arrow-alt-left{--fa:"\f30a"}.fa-dna{--fa:"\f471"}.fa-virus-slash{--fa:"\e075"}.fa-minus,.fa-subtract{--fa:"\f068"}.fa-chess{--fa:"\f439"}.fa-arrow-left-long,.fa-long-arrow-left{--fa:"\f177"}.fa-plug-circle-check{--fa:"\e55c"}.fa-street-view{--fa:"\f21d"}.fa-franc-sign{--fa:"\e18f"}.fa-volume-off{--fa:"\f026"}.fa-american-sign-language-interpreting,.fa-asl-interpreting,.fa-hands-american-sign-language-interpreting,.fa-hands-asl-interpreting{--fa:"\f2a3"}.fa-cog,.fa-gear{--fa:"\f013"}.fa-droplet-slash,.fa-tint-slash{--fa:"\f5c7"}.fa-mosque{--fa:"\f678"}.fa-mosquito{--fa:"\e52b"}.fa-star-of-david{--fa:"\f69a"}.fa-person-military-rifle{--fa:"\e54b"}.fa-cart-shopping,.fa-shopping-cart{--fa:"\f07a"}.fa-vials{--fa:"\f493"}.fa-plug-circle-plus{--fa:"\e55f"}.fa-place-of-worship{--fa:"\f67f"}.fa-grip-vertical{--fa:"\f58e"}.fa-hexagon-nodes{--fa:"\e699"}.fa-arrow-turn-up,.fa-level-up{--fa:"\f148"}.fa-u{--fa:"\55"}.fa-square-root-alt,.fa-square-root-variable{--fa:"\f698"}.fa-clock,.fa-clock-four{--fa:"\f017"}.fa-backward-step,.fa-step-backward{--fa:"\f048"}.fa-pallet{--fa:"\f482"}.fa-faucet{--fa:"\e005"}.fa-baseball-bat-ball{--fa:"\f432"}.fa-s{--fa:"\53"}.fa-timeline{--fa:"\e29c"}.fa-keyboard{--fa:"\f11c"}.fa-caret-down{--fa:"\f0d7"}.fa-clinic-medical,.fa-house-chimney-medical{--fa:"\f7f2"}.fa-temperature-3,.fa-temperature-three-quarters,.fa-thermometer-3,.fa-thermometer-three-quarters{--fa:"\f2c8"}.fa-mobile-android-alt,.fa-mobile-screen{--fa:"\f3cf"}.fa-plane-up{--fa:"\e22d"}.fa-piggy-bank{--fa:"\f4d3"}.fa-battery-3,.fa-battery-half{--fa:"\f242"}.fa-mountain-city{--fa:"\e52e"}.fa-coins{--fa:"\f51e"}.fa-khanda{--fa:"\f66d"}.fa-sliders,.fa-sliders-h{--fa:"\f1de"}.fa-folder-tree{--fa:"\f802"}.fa-network-wired{--fa:"\f6ff"}.fa-map-pin{--fa:"\f276"}.fa-hamsa{--fa:"\f665"}.fa-cent-sign{--fa:"\e3f5"}.fa-flask{--fa:"\f0c3"}.fa-person-pregnant{--fa:"\e31e"}.fa-wand-sparkles{--fa:"\f72b"}.fa-ellipsis-v,.fa-ellipsis-vertical{--fa:"\f142"}.fa-ticket{--fa:"\f145"}.fa-power-off{--fa:"\f011"}.fa-long-arrow-alt-right,.fa-right-long{--fa:"\f30b"}.fa-flag-usa{--fa:"\f74d"}.fa-laptop-file{--fa:"\e51d"}.fa-teletype,.fa-tty{--fa:"\f1e4"}.fa-diagram-next{--fa:"\e476"}.fa-person-rifle{--fa:"\e54e"}.fa-house-medical-circle-exclamation{--fa:"\e512"}.fa-closed-captioning{--fa:"\f20a"}.fa-hiking,.fa-person-hiking{--fa:"\f6ec"}.fa-venus-double{--fa:"\f226"}.fa-images{--fa:"\f302"}.fa-calculator{--fa:"\f1ec"}.fa-people-pulling{--fa:"\e535"}.fa-n{--fa:"\4e"}.fa-cable-car,.fa-tram{--fa:"\f7da"}.fa-cloud-rain{--fa:"\f73d"}.fa-building-circle-xmark{--fa:"\e4d4"}.fa-ship{--fa:"\f21a"}.fa-arrows-down-to-line{--fa:"\e4b8"}.fa-download{--fa:"\f019"}.fa-face-grin,.fa-grin{--fa:"\f580"}.fa-backspace,.fa-delete-left{--fa:"\f55a"}.fa-eye-dropper,.fa-eye-dropper-empty,.fa-eyedropper{--fa:"\f1fb"}.fa-file-circle-check{--fa:"\e5a0"}.fa-forward{--fa:"\f04e"}.fa-mobile,.fa-mobile-android,.fa-mobile-phone{--fa:"\f3ce"}.fa-face-meh,.fa-meh{--fa:"\f11a"}.fa-align-center{--fa:"\f037"}.fa-book-dead,.fa-book-skull{--fa:"\f6b7"}.fa-drivers-license,.fa-id-card{--fa:"\f2c2"}.fa-dedent,.fa-outdent{--fa:"\f03b"}.fa-heart-circle-exclamation{--fa:"\e4fe"}.fa-home,.fa-home-alt,.fa-home-lg-alt,.fa-house{--fa:"\f015"}.fa-calendar-week{--fa:"\f784"}.fa-laptop-medical{--fa:"\f812"}.fa-b{--fa:"\42"}.fa-file-medical{--fa:"\f477"}.fa-dice-one{--fa:"\f525"}.fa-kiwi-bird{--fa:"\f535"}.fa-arrow-right-arrow-left,.fa-exchange{--fa:"\f0ec"}.fa-redo-alt,.fa-rotate-forward,.fa-rotate-right{--fa:"\f2f9"}.fa-cutlery,.fa-utensils{--fa:"\f2e7"}.fa-arrow-up-wide-short,.fa-sort-amount-up{--fa:"\f161"}.fa-mill-sign{--fa:"\e1ed"}.fa-bowl-rice{--fa:"\e2eb"}.fa-skull{--fa:"\f54c"}.fa-broadcast-tower,.fa-tower-broadcast{--fa:"\f519"}.fa-truck-pickup{--fa:"\f63c"}.fa-long-arrow-alt-up,.fa-up-long{--fa:"\f30c"}.fa-stop{--fa:"\f04d"}.fa-code-merge{--fa:"\f387"}.fa-upload{--fa:"\f093"}.fa-hurricane{--fa:"\f751"}.fa-mound{--fa:"\e52d"}.fa-toilet-portable{--fa:"\e583"}.fa-compact-disc{--fa:"\f51f"}.fa-file-arrow-down,.fa-file-download{--fa:"\f56d"}.fa-caravan{--fa:"\f8ff"}.fa-shield-cat{--fa:"\e572"}.fa-bolt,.fa-zap{--fa:"\f0e7"}.fa-glass-water{--fa:"\e4f4"}.fa-oil-well{--fa:"\e532"}.fa-vault{--fa:"\e2c5"}.fa-mars{--fa:"\f222"}.fa-toilet{--fa:"\f7d8"}.fa-plane-circle-xmark{--fa:"\e557"}.fa-cny,.fa-jpy,.fa-rmb,.fa-yen,.fa-yen-sign{--fa:"\f157"}.fa-rouble,.fa-rub,.fa-ruble,.fa-ruble-sign{--fa:"\f158"}.fa-sun{--fa:"\f185"}.fa-guitar{--fa:"\f7a6"}.fa-face-laugh-wink,.fa-laugh-wink{--fa:"\f59c"}.fa-horse-head{--fa:"\f7ab"}.fa-bore-hole{--fa:"\e4c3"}.fa-industry{--fa:"\f275"}.fa-arrow-alt-circle-down,.fa-circle-down{--fa:"\f358"}.fa-arrows-turn-to-dots{--fa:"\e4c1"}.fa-florin-sign{--fa:"\e184"}.fa-arrow-down-short-wide,.fa-sort-amount-desc,.fa-sort-amount-down-alt{--fa:"\f884"}.fa-less-than{--fa:"\3c"}.fa-angle-down{--fa:"\f107"}.fa-car-tunnel{--fa:"\e4de"}.fa-head-side-cough{--fa:"\e061"}.fa-grip-lines{--fa:"\f7a4"}.fa-thumbs-down{--fa:"\f165"}.fa-user-lock{--fa:"\f502"}.fa-arrow-right-long,.fa-long-arrow-right{--fa:"\f178"}.fa-anchor-circle-xmark{--fa:"\e4ac"}.fa-ellipsis,.fa-ellipsis-h{--fa:"\f141"}.fa-chess-pawn{--fa:"\f443"}.fa-first-aid,.fa-kit-medical{--fa:"\f479"}.fa-person-through-window{--fa:"\e5a9"}.fa-toolbox{--fa:"\f552"}.fa-hands-holding-circle{--fa:"\e4fb"}.fa-bug{--fa:"\f188"}.fa-credit-card,.fa-credit-card-alt{--fa:"\f09d"}.fa-automobile,.fa-car{--fa:"\f1b9"}.fa-hand-holding-hand{--fa:"\e4f7"}.fa-book-open-reader,.fa-book-reader{--fa:"\f5da"}.fa-mountain-sun{--fa:"\e52f"}.fa-arrows-left-right-to-line{--fa:"\e4ba"}.fa-dice-d20{--fa:"\f6cf"}.fa-truck-droplet{--fa:"\e58c"}.fa-file-circle-xmark{--fa:"\e5a1"}.fa-temperature-arrow-up,.fa-temperature-up{--fa:"\e040"}.fa-medal{--fa:"\f5a2"}.fa-bed{--fa:"\f236"}.fa-h-square,.fa-square-h{--fa:"\f0fd"}.fa-podcast{--fa:"\f2ce"}.fa-temperature-4,.fa-temperature-full,.fa-thermometer-4,.fa-thermometer-full{--fa:"\f2c7"}.fa-bell{--fa:"\f0f3"}.fa-superscript{--fa:"\f12b"}.fa-plug-circle-xmark{--fa:"\e560"}.fa-star-of-life{--fa:"\f621"}.fa-phone-slash{--fa:"\f3dd"}.fa-paint-roller{--fa:"\f5aa"}.fa-hands-helping,.fa-handshake-angle{--fa:"\f4c4"}.fa-location-dot,.fa-map-marker-alt{--fa:"\f3c5"}.fa-file{--fa:"\f15b"}.fa-greater-than{--fa:"\3e"}.fa-person-swimming,.fa-swimmer{--fa:"\f5c4"}.fa-arrow-down{--fa:"\f063"}.fa-droplet,.fa-tint{--fa:"\f043"}.fa-eraser{--fa:"\f12d"}.fa-earth,.fa-earth-america,.fa-earth-americas,.fa-globe-americas{--fa:"\f57d"}.fa-person-burst{--fa:"\e53b"}.fa-dove{--fa:"\f4ba"}.fa-battery-0,.fa-battery-empty{--fa:"\f244"}.fa-socks{--fa:"\f696"}.fa-inbox{--fa:"\f01c"}.fa-section{--fa:"\e447"}.fa-gauge-high,.fa-tachometer-alt,.fa-tachometer-alt-fast{--fa:"\f625"}.fa-envelope-open-text{--fa:"\f658"}.fa-hospital,.fa-hospital-alt,.fa-hospital-wide{--fa:"\f0f8"}.fa-wine-bottle{--fa:"\f72f"}.fa-chess-rook{--fa:"\f447"}.fa-bars-staggered,.fa-reorder,.fa-stream{--fa:"\f550"}.fa-dharmachakra{--fa:"\f655"}.fa-hotdog{--fa:"\f80f"}.fa-blind,.fa-person-walking-with-cane{--fa:"\f29d"}.fa-drum{--fa:"\f569"}.fa-ice-cream{--fa:"\f810"}.fa-heart-circle-bolt{--fa:"\e4fc"}.fa-fax{--fa:"\f1ac"}.fa-paragraph{--fa:"\f1dd"}.fa-check-to-slot,.fa-vote-yea{--fa:"\f772"}.fa-star-half{--fa:"\f089"}.fa-boxes,.fa-boxes-alt,.fa-boxes-stacked{--fa:"\f468"}.fa-chain,.fa-link{--fa:"\f0c1"}.fa-assistive-listening-systems,.fa-ear-listen{--fa:"\f2a2"}.fa-tree-city{--fa:"\e587"}.fa-play{--fa:"\f04b"}.fa-font{--fa:"\f031"}.fa-table-cells-row-lock{--fa:"\e67a"}.fa-rupiah-sign{--fa:"\e23d"}.fa-magnifying-glass,.fa-search{--fa:"\f002"}.fa-ping-pong-paddle-ball,.fa-table-tennis,.fa-table-tennis-paddle-ball{--fa:"\f45d"}.fa-diagnoses,.fa-person-dots-from-line{--fa:"\f470"}.fa-trash-can-arrow-up,.fa-trash-restore-alt{--fa:"\f82a"}.fa-naira-sign{--fa:"\e1f6"}.fa-cart-arrow-down{--fa:"\f218"}.fa-walkie-talkie{--fa:"\f8ef"}.fa-file-edit,.fa-file-pen{--fa:"\f31c"}.fa-receipt{--fa:"\f543"}.fa-pen-square,.fa-pencil-square,.fa-square-pen{--fa:"\f14b"}.fa-suitcase-rolling{--fa:"\f5c1"}.fa-person-circle-exclamation{--fa:"\e53f"}.fa-chevron-down{--fa:"\f078"}.fa-battery,.fa-battery-5,.fa-battery-full{--fa:"\f240"}.fa-skull-crossbones{--fa:"\f714"}.fa-code-compare{--fa:"\e13a"}.fa-list-dots,.fa-list-ul{--fa:"\f0ca"}.fa-school-lock{--fa:"\e56f"}.fa-tower-cell{--fa:"\e585"}.fa-down-long,.fa-long-arrow-alt-down{--fa:"\f309"}.fa-ranking-star{--fa:"\e561"}.fa-chess-king{--fa:"\f43f"}.fa-person-harassing{--fa:"\e549"}.fa-brazilian-real-sign{--fa:"\e46c"}.fa-landmark-alt,.fa-landmark-dome{--fa:"\f752"}.fa-arrow-up{--fa:"\f062"}.fa-television,.fa-tv,.fa-tv-alt{--fa:"\f26c"}.fa-shrimp{--fa:"\e448"}.fa-list-check,.fa-tasks{--fa:"\f0ae"}.fa-jug-detergent{--fa:"\e519"}.fa-circle-user,.fa-user-circle{--fa:"\f2bd"}.fa-user-shield{--fa:"\f505"}.fa-wind{--fa:"\f72e"}.fa-car-burst,.fa-car-crash{--fa:"\f5e1"}.fa-y{--fa:"\59"}.fa-person-snowboarding,.fa-snowboarding{--fa:"\f7ce"}.fa-shipping-fast,.fa-truck-fast{--fa:"\f48b"}.fa-fish{--fa:"\f578"}.fa-user-graduate{--fa:"\f501"}.fa-adjust,.fa-circle-half-stroke{--fa:"\f042"}.fa-clapperboard{--fa:"\e131"}.fa-circle-radiation,.fa-radiation-alt{--fa:"\f7ba"}.fa-baseball,.fa-baseball-ball{--fa:"\f433"}.fa-jet-fighter-up{--fa:"\e518"}.fa-diagram-project,.fa-project-diagram{--fa:"\f542"}.fa-copy{--fa:"\f0c5"}.fa-volume-mute,.fa-volume-times,.fa-volume-xmark{--fa:"\f6a9"}.fa-hand-sparkles{--fa:"\e05d"}.fa-grip,.fa-grip-horizontal{--fa:"\f58d"}.fa-share-from-square,.fa-share-square{--fa:"\f14d"}.fa-child-combatant,.fa-child-rifle{--fa:"\e4e0"}.fa-gun{--fa:"\e19b"}.fa-phone-square,.fa-square-phone{--fa:"\f098"}.fa-add,.fa-plus{--fa:"\2b"}.fa-expand{--fa:"\f065"}.fa-computer{--fa:"\e4e5"}.fa-close,.fa-multiply,.fa-remove,.fa-times,.fa-xmark{--fa:"\f00d"}.fa-arrows,.fa-arrows-up-down-left-right{--fa:"\f047"}.fa-chalkboard-teacher,.fa-chalkboard-user{--fa:"\f51c"}.fa-peso-sign{--fa:"\e222"}.fa-building-shield{--fa:"\e4d8"}.fa-baby{--fa:"\f77c"}.fa-users-line{--fa:"\e592"}.fa-quote-left,.fa-quote-left-alt{--fa:"\f10d"}.fa-tractor{--fa:"\f722"}.fa-trash-arrow-up,.fa-trash-restore{--fa:"\f829"}.fa-arrow-down-up-lock{--fa:"\e4b0"}.fa-lines-leaning{--fa:"\e51e"}.fa-ruler-combined{--fa:"\f546"}.fa-copyright{--fa:"\f1f9"}.fa-equals{--fa:"\3d"}.fa-blender{--fa:"\f517"}.fa-teeth{--fa:"\f62e"}.fa-ils,.fa-shekel,.fa-shekel-sign,.fa-sheqel,.fa-sheqel-sign{--fa:"\f20b"}.fa-map{--fa:"\f279"}.fa-rocket{--fa:"\f135"}.fa-photo-film,.fa-photo-video{--fa:"\f87c"}.fa-folder-minus{--fa:"\f65d"}.fa-hexagon-nodes-bolt{--fa:"\e69a"}.fa-store{--fa:"\f54e"}.fa-arrow-trend-up{--fa:"\e098"}.fa-plug-circle-minus{--fa:"\e55e"}.fa-sign,.fa-sign-hanging{--fa:"\f4d9"}.fa-bezier-curve{--fa:"\f55b"}.fa-bell-slash{--fa:"\f1f6"}.fa-tablet,.fa-tablet-android{--fa:"\f3fb"}.fa-school-flag{--fa:"\e56e"}.fa-fill{--fa:"\f575"}.fa-angle-up{--fa:"\f106"}.fa-drumstick-bite{--fa:"\f6d7"}.fa-holly-berry{--fa:"\f7aa"}.fa-chevron-left{--fa:"\f053"}.fa-bacteria{--fa:"\e059"}.fa-hand-lizard{--fa:"\f258"}.fa-notdef{--fa:"\e1fe"}.fa-disease{--fa:"\f7fa"}.fa-briefcase-medical{--fa:"\f469"}.fa-genderless{--fa:"\f22d"}.fa-chevron-right{--fa:"\f054"}.fa-retweet{--fa:"\f079"}.fa-car-alt,.fa-car-rear{--fa:"\f5de"}.fa-pump-soap{--fa:"\e06b"}.fa-video-slash{--fa:"\f4e2"}.fa-battery-2,.fa-battery-quarter{--fa:"\f243"}.fa-radio{--fa:"\f8d7"}.fa-baby-carriage,.fa-carriage-baby{--fa:"\f77d"}.fa-traffic-light{--fa:"\f637"}.fa-thermometer{--fa:"\f491"}.fa-vr-cardboard{--fa:"\f729"}.fa-hand-middle-finger{--fa:"\f806"}.fa-percent,.fa-percentage{--fa:"\25"}.fa-truck-moving{--fa:"\f4df"}.fa-glass-water-droplet{--fa:"\e4f5"}.fa-display{--fa:"\e163"}.fa-face-smile,.fa-smile{--fa:"\f118"}.fa-thumb-tack,.fa-thumbtack{--fa:"\f08d"}.fa-trophy{--fa:"\f091"}.fa-person-praying,.fa-pray{--fa:"\f683"}.fa-hammer{--fa:"\f6e3"}.fa-hand-peace{--fa:"\f25b"}.fa-rotate,.fa-sync-alt{--fa:"\f2f1"}.fa-spinner{--fa:"\f110"}.fa-robot{--fa:"\f544"}.fa-peace{--fa:"\f67c"}.fa-cogs,.fa-gears{--fa:"\f085"}.fa-warehouse{--fa:"\f494"}.fa-arrow-up-right-dots{--fa:"\e4b7"}.fa-splotch{--fa:"\f5bc"}.fa-face-grin-hearts,.fa-grin-hearts{--fa:"\f584"}.fa-dice-four{--fa:"\f524"}.fa-sim-card{--fa:"\f7c4"}.fa-transgender,.fa-transgender-alt{--fa:"\f225"}.fa-mercury{--fa:"\f223"}.fa-arrow-turn-down,.fa-level-down{--fa:"\f149"}.fa-person-falling-burst{--fa:"\e547"}.fa-award{--fa:"\f559"}.fa-ticket-alt,.fa-ticket-simple{--fa:"\f3ff"}.fa-building{--fa:"\f1ad"}.fa-angle-double-left,.fa-angles-left{--fa:"\f100"}.fa-qrcode{--fa:"\f029"}.fa-clock-rotate-left,.fa-history{--fa:"\f1da"}.fa-face-grin-beam-sweat,.fa-grin-beam-sweat{--fa:"\f583"}.fa-arrow-right-from-file,.fa-file-export{--fa:"\f56e"}.fa-shield,.fa-shield-blank{--fa:"\f132"}.fa-arrow-up-short-wide,.fa-sort-amount-up-alt{--fa:"\f885"}.fa-comment-nodes{--fa:"\e696"}.fa-house-medical{--fa:"\e3b2"}.fa-golf-ball,.fa-golf-ball-tee{--fa:"\f450"}.fa-chevron-circle-left,.fa-circle-chevron-left{--fa:"\f137"}.fa-house-chimney-window{--fa:"\e00d"}.fa-pen-nib{--fa:"\f5ad"}.fa-tent-arrow-turn-left{--fa:"\e580"}.fa-tents{--fa:"\e582"}.fa-magic,.fa-wand-magic{--fa:"\f0d0"}.fa-dog{--fa:"\f6d3"}.fa-carrot{--fa:"\f787"}.fa-moon{--fa:"\f186"}.fa-wine-glass-alt,.fa-wine-glass-empty{--fa:"\f5ce"}.fa-cheese{--fa:"\f7ef"}.fa-yin-yang{--fa:"\f6ad"}.fa-music{--fa:"\f001"}.fa-code-commit{--fa:"\f386"}.fa-temperature-low{--fa:"\f76b"}.fa-biking,.fa-person-biking{--fa:"\f84a"}.fa-broom{--fa:"\f51a"}.fa-shield-heart{--fa:"\e574"}.fa-gopuram{--fa:"\f664"}.fa-earth-oceania,.fa-globe-oceania{--fa:"\e47b"}.fa-square-xmark,.fa-times-square,.fa-xmark-square{--fa:"\f2d3"}.fa-hashtag{--fa:"\23"}.fa-expand-alt,.fa-up-right-and-down-left-from-center{--fa:"\f424"}.fa-oil-can{--fa:"\f613"}.fa-t{--fa:"\54"}.fa-hippo{--fa:"\f6ed"}.fa-chart-column{--fa:"\e0e3"}.fa-infinity{--fa:"\f534"}.fa-vial-circle-check{--fa:"\e596"}.fa-person-arrow-down-to-line{--fa:"\e538"}.fa-voicemail{--fa:"\f897"}.fa-fan{--fa:"\f863"}.fa-person-walking-luggage{--fa:"\e554"}.fa-arrows-alt-v,.fa-up-down{--fa:"\f338"}.fa-cloud-moon-rain{--fa:"\f73c"}.fa-calendar{--fa:"\f133"}.fa-trailer{--fa:"\e041"}.fa-bahai,.fa-haykal{--fa:"\f666"}.fa-sd-card{--fa:"\f7c2"}.fa-dragon{--fa:"\f6d5"}.fa-shoe-prints{--fa:"\f54b"}.fa-circle-plus,.fa-plus-circle{--fa:"\f055"}.fa-face-grin-tongue-wink,.fa-grin-tongue-wink{--fa:"\f58b"}.fa-hand-holding{--fa:"\f4bd"}.fa-plug-circle-exclamation{--fa:"\e55d"}.fa-chain-broken,.fa-chain-slash,.fa-link-slash,.fa-unlink{--fa:"\f127"}.fa-clone{--fa:"\f24d"}.fa-person-walking-arrow-loop-left{--fa:"\e551"}.fa-arrow-up-z-a,.fa-sort-alpha-up-alt{--fa:"\f882"}.fa-fire-alt,.fa-fire-flame-curved{--fa:"\f7e4"}.fa-tornado{--fa:"\f76f"}.fa-file-circle-plus{--fa:"\e494"}.fa-book-quran,.fa-quran{--fa:"\f687"}.fa-anchor{--fa:"\f13d"}.fa-border-all{--fa:"\f84c"}.fa-angry,.fa-face-angry{--fa:"\f556"}.fa-cookie-bite{--fa:"\f564"}.fa-arrow-trend-down{--fa:"\e097"}.fa-feed,.fa-rss{--fa:"\f09e"}.fa-draw-polygon{--fa:"\f5ee"}.fa-balance-scale,.fa-scale-balanced{--fa:"\f24e"}.fa-gauge-simple-high,.fa-tachometer,.fa-tachometer-fast{--fa:"\f62a"}.fa-shower{--fa:"\f2cc"}.fa-desktop,.fa-desktop-alt{--fa:"\f390"}.fa-m{--fa:"\4d"}.fa-table-list,.fa-th-list{--fa:"\f00b"}.fa-comment-sms,.fa-sms{--fa:"\f7cd"}.fa-book{--fa:"\f02d"}.fa-user-plus{--fa:"\f234"}.fa-check{--fa:"\f00c"}.fa-battery-4,.fa-battery-three-quarters{--fa:"\f241"}.fa-house-circle-check{--fa:"\e509"}.fa-angle-left{--fa:"\f104"}.fa-diagram-successor{--fa:"\e47a"}.fa-truck-arrow-right{--fa:"\e58b"}.fa-arrows-split-up-and-left{--fa:"\e4bc"}.fa-fist-raised,.fa-hand-fist{--fa:"\f6de"}.fa-cloud-moon{--fa:"\f6c3"}.fa-briefcase{--fa:"\f0b1"}.fa-person-falling{--fa:"\e546"}.fa-image-portrait,.fa-portrait{--fa:"\f3e0"}.fa-user-tag{--fa:"\f507"}.fa-rug{--fa:"\e569"}.fa-earth-europe,.fa-globe-europe{--fa:"\f7a2"}.fa-cart-flatbed-suitcase,.fa-luggage-cart{--fa:"\f59d"}.fa-rectangle-times,.fa-rectangle-xmark,.fa-times-rectangle,.fa-window-close{--fa:"\f410"}.fa-baht-sign{--fa:"\e0ac"}.fa-book-open{--fa:"\f518"}.fa-book-journal-whills,.fa-journal-whills{--fa:"\f66a"}.fa-handcuffs{--fa:"\e4f8"}.fa-exclamation-triangle,.fa-triangle-exclamation,.fa-warning{--fa:"\f071"}.fa-database{--fa:"\f1c0"}.fa-mail-forward,.fa-share{--fa:"\f064"}.fa-bottle-droplet{--fa:"\e4c4"}.fa-mask-face{--fa:"\e1d7"}.fa-hill-rockslide{--fa:"\e508"}.fa-exchange-alt,.fa-right-left{--fa:"\f362"}.fa-paper-plane{--fa:"\f1d8"}.fa-road-circle-exclamation{--fa:"\e565"}.fa-dungeon{--fa:"\f6d9"}.fa-align-right{--fa:"\f038"}.fa-money-bill-1-wave,.fa-money-bill-wave-alt{--fa:"\f53b"}.fa-life-ring{--fa:"\f1cd"}.fa-hands,.fa-sign-language,.fa-signing{--fa:"\f2a7"}.fa-calendar-day{--fa:"\f783"}.fa-ladder-water,.fa-swimming-pool,.fa-water-ladder{--fa:"\f5c5"}.fa-arrows-up-down,.fa-arrows-v{--fa:"\f07d"}.fa-face-grimace,.fa-grimace{--fa:"\f57f"}.fa-wheelchair-alt,.fa-wheelchair-move{--fa:"\e2ce"}.fa-level-down-alt,.fa-turn-down{--fa:"\f3be"}.fa-person-walking-arrow-right{--fa:"\e552"}.fa-envelope-square,.fa-square-envelope{--fa:"\f199"}.fa-dice{--fa:"\f522"}.fa-bowling-ball{--fa:"\f436"}.fa-brain{--fa:"\f5dc"}.fa-band-aid,.fa-bandage{--fa:"\f462"}.fa-calendar-minus{--fa:"\f272"}.fa-circle-xmark,.fa-times-circle,.fa-xmark-circle{--fa:"\f057"}.fa-gifts{--fa:"\f79c"}.fa-hotel{--fa:"\f594"}.fa-earth-asia,.fa-globe-asia{--fa:"\f57e"}.fa-id-card-alt,.fa-id-card-clip{--fa:"\f47f"}.fa-magnifying-glass-plus,.fa-search-plus{--fa:"\f00e"}.fa-thumbs-up{--fa:"\f164"}.fa-user-clock{--fa:"\f4fd"}.fa-allergies,.fa-hand-dots{--fa:"\f461"}.fa-file-invoice{--fa:"\f570"}.fa-window-minimize{--fa:"\f2d1"}.fa-coffee,.fa-mug-saucer{--fa:"\f0f4"}.fa-brush{--fa:"\f55d"}.fa-file-half-dashed{--fa:"\e698"}.fa-mask{--fa:"\f6fa"}.fa-magnifying-glass-minus,.fa-search-minus{--fa:"\f010"}.fa-ruler-vertical{--fa:"\f548"}.fa-user-alt,.fa-user-large{--fa:"\f406"}.fa-train-tram{--fa:"\e5b4"}.fa-user-nurse{--fa:"\f82f"}.fa-syringe{--fa:"\f48e"}.fa-cloud-sun{--fa:"\f6c4"}.fa-stopwatch-20{--fa:"\e06f"}.fa-square-full{--fa:"\f45c"}.fa-magnet{--fa:"\f076"}.fa-jar{--fa:"\e516"}.fa-note-sticky,.fa-sticky-note{--fa:"\f249"}.fa-bug-slash{--fa:"\e490"}.fa-arrow-up-from-water-pump{--fa:"\e4b6"}.fa-bone{--fa:"\f5d7"}.fa-table-cells-row-unlock{--fa:"\e691"}.fa-user-injured{--fa:"\f728"}.fa-face-sad-tear,.fa-sad-tear{--fa:"\f5b4"}.fa-plane{--fa:"\f072"}.fa-tent-arrows-down{--fa:"\e581"}.fa-exclamation{--fa:"\21"}.fa-arrows-spin{--fa:"\e4bb"}.fa-print{--fa:"\f02f"}.fa-try,.fa-turkish-lira,.fa-turkish-lira-sign{--fa:"\e2bb"}.fa-dollar,.fa-dollar-sign,.fa-usd{--fa:"\24"}.fa-x{--fa:"\58"}.fa-magnifying-glass-dollar,.fa-search-dollar{--fa:"\f688"}.fa-users-cog,.fa-users-gear{--fa:"\f509"}.fa-person-military-pointing{--fa:"\e54a"}.fa-bank,.fa-building-columns,.fa-institution,.fa-museum,.fa-university{--fa:"\f19c"}.fa-umbrella{--fa:"\f0e9"}.fa-trowel{--fa:"\e589"}.fa-d{--fa:"\44"}.fa-stapler{--fa:"\e5af"}.fa-masks-theater,.fa-theater-masks{--fa:"\f630"}.fa-kip-sign{--fa:"\e1c4"}.fa-hand-point-left{--fa:"\f0a5"}.fa-handshake-alt,.fa-handshake-simple{--fa:"\f4c6"}.fa-fighter-jet,.fa-jet-fighter{--fa:"\f0fb"}.fa-share-alt-square,.fa-square-share-nodes{--fa:"\f1e1"}.fa-barcode{--fa:"\f02a"}.fa-plus-minus{--fa:"\e43c"}.fa-video,.fa-video-camera{--fa:"\f03d"}.fa-graduation-cap,.fa-mortar-board{--fa:"\f19d"}.fa-hand-holding-medical{--fa:"\e05c"}.fa-person-circle-check{--fa:"\e53e"}.fa-level-up-alt,.fa-turn-up{--fa:"\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(../fonts/fontawesome/fa-brands-400.woff2) format("woff2"),url(../fonts/fontawesome/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-weight:400}.fa-monero{--fa:"\f3d0"}.fa-hooli{--fa:"\f427"}.fa-yelp{--fa:"\f1e9"}.fa-cc-visa{--fa:"\f1f0"}.fa-lastfm{--fa:"\f202"}.fa-shopware{--fa:"\f5b5"}.fa-creative-commons-nc{--fa:"\f4e8"}.fa-aws{--fa:"\f375"}.fa-redhat{--fa:"\f7bc"}.fa-yoast{--fa:"\f2b1"}.fa-cloudflare{--fa:"\e07d"}.fa-ups{--fa:"\f7e0"}.fa-pixiv{--fa:"\e640"}.fa-wpexplorer{--fa:"\f2de"}.fa-dyalog{--fa:"\f399"}.fa-bity{--fa:"\f37a"}.fa-stackpath{--fa:"\f842"}.fa-buysellads{--fa:"\f20d"}.fa-first-order{--fa:"\f2b0"}.fa-modx{--fa:"\f285"}.fa-guilded{--fa:"\e07e"}.fa-vnv{--fa:"\f40b"}.fa-js-square,.fa-square-js{--fa:"\f3b9"}.fa-microsoft{--fa:"\f3ca"}.fa-qq{--fa:"\f1d6"}.fa-orcid{--fa:"\f8d2"}.fa-java{--fa:"\f4e4"}.fa-invision{--fa:"\f7b0"}.fa-creative-commons-pd-alt{--fa:"\f4ed"}.fa-centercode{--fa:"\f380"}.fa-glide-g{--fa:"\f2a6"}.fa-drupal{--fa:"\f1a9"}.fa-jxl{--fa:"\e67b"}.fa-dart-lang{--fa:"\e693"}.fa-hire-a-helper{--fa:"\f3b0"}.fa-creative-commons-by{--fa:"\f4e7"}.fa-unity{--fa:"\e049"}.fa-whmcs{--fa:"\f40d"}.fa-rocketchat{--fa:"\f3e8"}.fa-vk{--fa:"\f189"}.fa-untappd{--fa:"\f405"}.fa-mailchimp{--fa:"\f59e"}.fa-css3-alt{--fa:"\f38b"}.fa-reddit-square,.fa-square-reddit{--fa:"\f1a2"}.fa-vimeo-v{--fa:"\f27d"}.fa-contao{--fa:"\f26d"}.fa-square-font-awesome{--fa:"\e5ad"}.fa-deskpro{--fa:"\f38f"}.fa-brave{--fa:"\e63c"}.fa-sistrix{--fa:"\f3ee"}.fa-instagram-square,.fa-square-instagram{--fa:"\e055"}.fa-battle-net{--fa:"\f835"}.fa-the-red-yeti{--fa:"\f69d"}.fa-hacker-news-square,.fa-square-hacker-news{--fa:"\f3af"}.fa-edge{--fa:"\f282"}.fa-threads{--fa:"\e618"}.fa-napster{--fa:"\f3d2"}.fa-snapchat-square,.fa-square-snapchat{--fa:"\f2ad"}.fa-google-plus-g{--fa:"\f0d5"}.fa-artstation{--fa:"\f77a"}.fa-markdown{--fa:"\f60f"}.fa-sourcetree{--fa:"\f7d3"}.fa-google-plus{--fa:"\f2b3"}.fa-diaspora{--fa:"\f791"}.fa-foursquare{--fa:"\f180"}.fa-stack-overflow{--fa:"\f16c"}.fa-github-alt{--fa:"\f113"}.fa-phoenix-squadron{--fa:"\f511"}.fa-pagelines{--fa:"\f18c"}.fa-algolia{--fa:"\f36c"}.fa-red-river{--fa:"\f3e3"}.fa-creative-commons-sa{--fa:"\f4ef"}.fa-safari{--fa:"\f267"}.fa-google{--fa:"\f1a0"}.fa-font-awesome-alt,.fa-square-font-awesome-stroke{--fa:"\f35c"}.fa-atlassian{--fa:"\f77b"}.fa-linkedin-in{--fa:"\f0e1"}.fa-digital-ocean{--fa:"\f391"}.fa-nimblr{--fa:"\f5a8"}.fa-chromecast{--fa:"\f838"}.fa-evernote{--fa:"\f839"}.fa-hacker-news{--fa:"\f1d4"}.fa-creative-commons-sampling{--fa:"\f4f0"}.fa-adversal{--fa:"\f36a"}.fa-creative-commons{--fa:"\f25e"}.fa-watchman-monitoring{--fa:"\e087"}.fa-fonticons{--fa:"\f280"}.fa-weixin{--fa:"\f1d7"}.fa-shirtsinbulk{--fa:"\f214"}.fa-codepen{--fa:"\f1cb"}.fa-git-alt{--fa:"\f841"}.fa-lyft{--fa:"\f3c3"}.fa-rev{--fa:"\f5b2"}.fa-windows{--fa:"\f17a"}.fa-wizards-of-the-coast{--fa:"\f730"}.fa-square-viadeo,.fa-viadeo-square{--fa:"\f2aa"}.fa-meetup{--fa:"\f2e0"}.fa-centos{--fa:"\f789"}.fa-adn{--fa:"\f170"}.fa-cloudsmith{--fa:"\f384"}.fa-opensuse{--fa:"\e62b"}.fa-pied-piper-alt{--fa:"\f1a8"}.fa-dribbble-square,.fa-square-dribbble{--fa:"\f397"}.fa-codiepie{--fa:"\f284"}.fa-node{--fa:"\f419"}.fa-mix{--fa:"\f3cb"}.fa-steam{--fa:"\f1b6"}.fa-cc-apple-pay{--fa:"\f416"}.fa-scribd{--fa:"\f28a"}.fa-debian{--fa:"\e60b"}.fa-openid{--fa:"\f19b"}.fa-instalod{--fa:"\e081"}.fa-files-pinwheel{--fa:"\e69f"}.fa-expeditedssl{--fa:"\f23e"}.fa-sellcast{--fa:"\f2da"}.fa-square-twitter,.fa-twitter-square{--fa:"\f081"}.fa-r-project{--fa:"\f4f7"}.fa-delicious{--fa:"\f1a5"}.fa-freebsd{--fa:"\f3a4"}.fa-vuejs{--fa:"\f41f"}.fa-accusoft{--fa:"\f369"}.fa-ioxhost{--fa:"\f208"}.fa-fonticons-fi{--fa:"\f3a2"}.fa-app-store{--fa:"\f36f"}.fa-cc-mastercard{--fa:"\f1f1"}.fa-itunes-note{--fa:"\f3b5"}.fa-golang{--fa:"\e40f"}.fa-kickstarter,.fa-square-kickstarter{--fa:"\f3bb"}.fa-grav{--fa:"\f2d6"}.fa-weibo{--fa:"\f18a"}.fa-uncharted{--fa:"\e084"}.fa-firstdraft{--fa:"\f3a1"}.fa-square-youtube,.fa-youtube-square{--fa:"\f431"}.fa-wikipedia-w{--fa:"\f266"}.fa-rendact,.fa-wpressr{--fa:"\f3e4"}.fa-angellist{--fa:"\f209"}.fa-galactic-republic{--fa:"\f50c"}.fa-nfc-directional{--fa:"\e530"}.fa-skype{--fa:"\f17e"}.fa-joget{--fa:"\f3b7"}.fa-fedora{--fa:"\f798"}.fa-stripe-s{--fa:"\f42a"}.fa-meta{--fa:"\e49b"}.fa-laravel{--fa:"\f3bd"}.fa-hotjar{--fa:"\f3b1"}.fa-bluetooth-b{--fa:"\f294"}.fa-square-letterboxd{--fa:"\e62e"}.fa-sticker-mule{--fa:"\f3f7"}.fa-creative-commons-zero{--fa:"\f4f3"}.fa-hips{--fa:"\f452"}.fa-css{--fa:"\e6a2"}.fa-behance{--fa:"\f1b4"}.fa-reddit{--fa:"\f1a1"}.fa-discord{--fa:"\f392"}.fa-chrome{--fa:"\f268"}.fa-app-store-ios{--fa:"\f370"}.fa-cc-discover{--fa:"\f1f2"}.fa-wpbeginner{--fa:"\f297"}.fa-confluence{--fa:"\f78d"}.fa-shoelace{--fa:"\e60c"}.fa-mdb{--fa:"\f8ca"}.fa-dochub{--fa:"\f394"}.fa-accessible-icon{--fa:"\f368"}.fa-ebay{--fa:"\f4f4"}.fa-amazon{--fa:"\f270"}.fa-unsplash{--fa:"\e07c"}.fa-yarn{--fa:"\f7e3"}.fa-square-steam,.fa-steam-square{--fa:"\f1b7"}.fa-500px{--fa:"\f26e"}.fa-square-vimeo,.fa-vimeo-square{--fa:"\f194"}.fa-asymmetrik{--fa:"\f372"}.fa-font-awesome,.fa-font-awesome-flag,.fa-font-awesome-logo-full{--fa:"\f2b4"}.fa-gratipay{--fa:"\f184"}.fa-apple{--fa:"\f179"}.fa-hive{--fa:"\e07f"}.fa-gitkraken{--fa:"\f3a6"}.fa-keybase{--fa:"\f4f5"}.fa-apple-pay{--fa:"\f415"}.fa-padlet{--fa:"\e4a0"}.fa-amazon-pay{--fa:"\f42c"}.fa-github-square,.fa-square-github{--fa:"\f092"}.fa-stumbleupon{--fa:"\f1a4"}.fa-fedex{--fa:"\f797"}.fa-phoenix-framework{--fa:"\f3dc"}.fa-shopify{--fa:"\e057"}.fa-neos{--fa:"\f612"}.fa-square-threads{--fa:"\e619"}.fa-hackerrank{--fa:"\f5f7"}.fa-researchgate{--fa:"\f4f8"}.fa-swift{--fa:"\f8e1"}.fa-angular{--fa:"\f420"}.fa-speakap{--fa:"\f3f3"}.fa-angrycreative{--fa:"\f36e"}.fa-y-combinator{--fa:"\f23b"}.fa-empire{--fa:"\f1d1"}.fa-envira{--fa:"\f299"}.fa-google-scholar{--fa:"\e63b"}.fa-gitlab-square,.fa-square-gitlab{--fa:"\e5ae"}.fa-studiovinari{--fa:"\f3f8"}.fa-pied-piper{--fa:"\f2ae"}.fa-wordpress{--fa:"\f19a"}.fa-product-hunt{--fa:"\f288"}.fa-firefox{--fa:"\f269"}.fa-linode{--fa:"\f2b8"}.fa-goodreads{--fa:"\f3a8"}.fa-odnoklassniki-square,.fa-square-odnoklassniki{--fa:"\f264"}.fa-jsfiddle{--fa:"\f1cc"}.fa-sith{--fa:"\f512"}.fa-themeisle{--fa:"\f2b2"}.fa-page4{--fa:"\f3d7"}.fa-hashnode{--fa:"\e499"}.fa-react{--fa:"\f41b"}.fa-cc-paypal{--fa:"\f1f4"}.fa-squarespace{--fa:"\f5be"}.fa-cc-stripe{--fa:"\f1f5"}.fa-creative-commons-share{--fa:"\f4f2"}.fa-bitcoin{--fa:"\f379"}.fa-keycdn{--fa:"\f3ba"}.fa-opera{--fa:"\f26a"}.fa-itch-io{--fa:"\f83a"}.fa-umbraco{--fa:"\f8e8"}.fa-galactic-senate{--fa:"\f50d"}.fa-ubuntu{--fa:"\f7df"}.fa-draft2digital{--fa:"\f396"}.fa-stripe{--fa:"\f429"}.fa-houzz{--fa:"\f27c"}.fa-gg{--fa:"\f260"}.fa-dhl{--fa:"\f790"}.fa-pinterest-square,.fa-square-pinterest{--fa:"\f0d3"}.fa-xing{--fa:"\f168"}.fa-blackberry{--fa:"\f37b"}.fa-creative-commons-pd{--fa:"\f4ec"}.fa-playstation{--fa:"\f3df"}.fa-quinscape{--fa:"\f459"}.fa-less{--fa:"\f41d"}.fa-blogger-b{--fa:"\f37d"}.fa-opencart{--fa:"\f23d"}.fa-vine{--fa:"\f1ca"}.fa-signal-messenger{--fa:"\e663"}.fa-paypal{--fa:"\f1ed"}.fa-gitlab{--fa:"\f296"}.fa-typo3{--fa:"\f42b"}.fa-reddit-alien{--fa:"\f281"}.fa-yahoo{--fa:"\f19e"}.fa-dailymotion{--fa:"\e052"}.fa-affiliatetheme{--fa:"\f36b"}.fa-pied-piper-pp{--fa:"\f1a7"}.fa-bootstrap{--fa:"\f836"}.fa-odnoklassniki{--fa:"\f263"}.fa-nfc-symbol{--fa:"\e531"}.fa-mintbit{--fa:"\e62f"}.fa-ethereum{--fa:"\f42e"}.fa-speaker-deck{--fa:"\f83c"}.fa-creative-commons-nc-eu{--fa:"\f4e9"}.fa-patreon{--fa:"\f3d9"}.fa-avianex{--fa:"\f374"}.fa-ello{--fa:"\f5f1"}.fa-gofore{--fa:"\f3a7"}.fa-bimobject{--fa:"\f378"}.fa-brave-reverse{--fa:"\e63d"}.fa-facebook-f{--fa:"\f39e"}.fa-google-plus-square,.fa-square-google-plus{--fa:"\f0d4"}.fa-web-awesome{--fa:"\e682"}.fa-mandalorian{--fa:"\f50f"}.fa-first-order-alt{--fa:"\f50a"}.fa-osi{--fa:"\f41a"}.fa-google-wallet{--fa:"\f1ee"}.fa-d-and-d-beyond{--fa:"\f6ca"}.fa-periscope{--fa:"\f3da"}.fa-fulcrum{--fa:"\f50b"}.fa-cloudscale{--fa:"\f383"}.fa-forumbee{--fa:"\f211"}.fa-mizuni{--fa:"\f3cc"}.fa-schlix{--fa:"\f3ea"}.fa-square-xing,.fa-xing-square{--fa:"\f169"}.fa-bandcamp{--fa:"\f2d5"}.fa-wpforms{--fa:"\f298"}.fa-cloudversify{--fa:"\f385"}.fa-usps{--fa:"\f7e1"}.fa-megaport{--fa:"\f5a3"}.fa-magento{--fa:"\f3c4"}.fa-spotify{--fa:"\f1bc"}.fa-optin-monster{--fa:"\f23c"}.fa-fly{--fa:"\f417"}.fa-square-bluesky{--fa:"\e6a3"}.fa-aviato{--fa:"\f421"}.fa-itunes{--fa:"\f3b4"}.fa-cuttlefish{--fa:"\f38c"}.fa-blogger{--fa:"\f37c"}.fa-flickr{--fa:"\f16e"}.fa-viber{--fa:"\f409"}.fa-soundcloud{--fa:"\f1be"}.fa-digg{--fa:"\f1a6"}.fa-tencent-weibo{--fa:"\f1d5"}.fa-letterboxd{--fa:"\e62d"}.fa-symfony{--fa:"\f83d"}.fa-maxcdn{--fa:"\f136"}.fa-etsy{--fa:"\f2d7"}.fa-facebook-messenger{--fa:"\f39f"}.fa-audible{--fa:"\f373"}.fa-think-peaks{--fa:"\f731"}.fa-bilibili{--fa:"\e3d9"}.fa-erlang{--fa:"\f39d"}.fa-x-twitter{--fa:"\e61b"}.fa-cotton-bureau{--fa:"\f89e"}.fa-dashcube{--fa:"\f210"}.fa-42-group,.fa-innosoft{--fa:"\e080"}.fa-stack-exchange{--fa:"\f18d"}.fa-elementor{--fa:"\f430"}.fa-pied-piper-square,.fa-square-pied-piper{--fa:"\e01e"}.fa-creative-commons-nd{--fa:"\f4eb"}.fa-palfed{--fa:"\f3d8"}.fa-superpowers{--fa:"\f2dd"}.fa-resolving{--fa:"\f3e7"}.fa-xbox{--fa:"\f412"}.fa-square-web-awesome-stroke{--fa:"\e684"}.fa-searchengin{--fa:"\f3eb"}.fa-tiktok{--fa:"\e07b"}.fa-facebook-square,.fa-square-facebook{--fa:"\f082"}.fa-renren{--fa:"\f18b"}.fa-linux{--fa:"\f17c"}.fa-glide{--fa:"\f2a5"}.fa-linkedin{--fa:"\f08c"}.fa-hubspot{--fa:"\f3b2"}.fa-deploydog{--fa:"\f38e"}.fa-twitch{--fa:"\f1e8"}.fa-flutter{--fa:"\e694"}.fa-ravelry{--fa:"\f2d9"}.fa-mixer{--fa:"\e056"}.fa-lastfm-square,.fa-square-lastfm{--fa:"\f203"}.fa-vimeo{--fa:"\f40a"}.fa-mendeley{--fa:"\f7b3"}.fa-uniregistry{--fa:"\f404"}.fa-figma{--fa:"\f799"}.fa-creative-commons-remix{--fa:"\f4ee"}.fa-cc-amazon-pay{--fa:"\f42d"}.fa-dropbox{--fa:"\f16b"}.fa-instagram{--fa:"\f16d"}.fa-cmplid{--fa:"\e360"}.fa-upwork{--fa:"\e641"}.fa-facebook{--fa:"\f09a"}.fa-gripfire{--fa:"\f3ac"}.fa-jedi-order{--fa:"\f50e"}.fa-uikit{--fa:"\f403"}.fa-fort-awesome-alt{--fa:"\f3a3"}.fa-phabricator{--fa:"\f3db"}.fa-ussunnah{--fa:"\f407"}.fa-earlybirds{--fa:"\f39a"}.fa-trade-federation{--fa:"\f513"}.fa-autoprefixer{--fa:"\f41c"}.fa-whatsapp{--fa:"\f232"}.fa-square-upwork{--fa:"\e67c"}.fa-slideshare{--fa:"\f1e7"}.fa-google-play{--fa:"\f3ab"}.fa-viadeo{--fa:"\f2a9"}.fa-line{--fa:"\f3c0"}.fa-google-drive{--fa:"\f3aa"}.fa-servicestack{--fa:"\f3ec"}.fa-simplybuilt{--fa:"\f215"}.fa-bitbucket{--fa:"\f171"}.fa-imdb{--fa:"\f2d8"}.fa-deezer{--fa:"\e077"}.fa-raspberry-pi{--fa:"\f7bb"}.fa-jira{--fa:"\f7b1"}.fa-docker{--fa:"\f395"}.fa-screenpal{--fa:"\e570"}.fa-bluetooth{--fa:"\f293"}.fa-gitter{--fa:"\f426"}.fa-d-and-d{--fa:"\f38d"}.fa-microblog{--fa:"\e01a"}.fa-cc-diners-club{--fa:"\f24c"}.fa-gg-circle{--fa:"\f261"}.fa-pied-piper-hat{--fa:"\f4e5"}.fa-kickstarter-k{--fa:"\f3bc"}.fa-yandex{--fa:"\f413"}.fa-readme{--fa:"\f4d5"}.fa-html5{--fa:"\f13b"}.fa-sellsy{--fa:"\f213"}.fa-square-web-awesome{--fa:"\e683"}.fa-sass{--fa:"\f41e"}.fa-wirsindhandwerk,.fa-wsh{--fa:"\e2d0"}.fa-buromobelexperte{--fa:"\f37f"}.fa-salesforce{--fa:"\f83b"}.fa-octopus-deploy{--fa:"\e082"}.fa-medapps{--fa:"\f3c6"}.fa-ns8{--fa:"\f3d5"}.fa-pinterest-p{--fa:"\f231"}.fa-apper{--fa:"\f371"}.fa-fort-awesome{--fa:"\f286"}.fa-waze{--fa:"\f83f"}.fa-bluesky{--fa:"\e671"}.fa-cc-jcb{--fa:"\f24b"}.fa-snapchat,.fa-snapchat-ghost{--fa:"\f2ab"}.fa-fantasy-flight-games{--fa:"\f6dc"}.fa-rust{--fa:"\e07a"}.fa-wix{--fa:"\f5cf"}.fa-behance-square,.fa-square-behance{--fa:"\f1b5"}.fa-supple{--fa:"\f3f9"}.fa-webflow{--fa:"\e65c"}.fa-rebel{--fa:"\f1d0"}.fa-css3{--fa:"\f13c"}.fa-staylinked{--fa:"\f3f5"}.fa-kaggle{--fa:"\f5fa"}.fa-space-awesome{--fa:"\e5ac"}.fa-deviantart{--fa:"\f1bd"}.fa-cpanel{--fa:"\f388"}.fa-goodreads-g{--fa:"\f3a9"}.fa-git-square,.fa-square-git{--fa:"\f1d2"}.fa-square-tumblr,.fa-tumblr-square{--fa:"\f174"}.fa-trello{--fa:"\f181"}.fa-creative-commons-nc-jp{--fa:"\f4ea"}.fa-get-pocket{--fa:"\f265"}.fa-perbyte{--fa:"\e083"}.fa-grunt{--fa:"\f3ad"}.fa-weebly{--fa:"\f5cc"}.fa-connectdevelop{--fa:"\f20e"}.fa-leanpub{--fa:"\f212"}.fa-black-tie{--fa:"\f27e"}.fa-themeco{--fa:"\f5c6"}.fa-python{--fa:"\f3e2"}.fa-android{--fa:"\f17b"}.fa-bots{--fa:"\e340"}.fa-free-code-camp{--fa:"\f2c5"}.fa-hornbill{--fa:"\f592"}.fa-js{--fa:"\f3b8"}.fa-ideal{--fa:"\e013"}.fa-git{--fa:"\f1d3"}.fa-dev{--fa:"\f6cc"}.fa-sketch{--fa:"\f7c6"}.fa-yandex-international{--fa:"\f414"}.fa-cc-amex{--fa:"\f1f3"}.fa-uber{--fa:"\f402"}.fa-github{--fa:"\f09b"}.fa-php{--fa:"\f457"}.fa-alipay{--fa:"\f642"}.fa-youtube{--fa:"\f167"}.fa-skyatlas{--fa:"\f216"}.fa-firefox-browser{--fa:"\e007"}.fa-replyd{--fa:"\f3e6"}.fa-suse{--fa:"\f7d6"}.fa-jenkins{--fa:"\f3b6"}.fa-twitter{--fa:"\f099"}.fa-rockrms{--fa:"\f3e9"}.fa-pinterest{--fa:"\f0d2"}.fa-buffer{--fa:"\f837"}.fa-npm{--fa:"\f3d4"}.fa-yammer{--fa:"\f840"}.fa-btc{--fa:"\f15a"}.fa-dribbble{--fa:"\f17d"}.fa-stumbleupon-circle{--fa:"\f1a3"}.fa-internet-explorer{--fa:"\f26b"}.fa-stubber{--fa:"\e5c7"}.fa-telegram,.fa-telegram-plane{--fa:"\f2c6"}.fa-old-republic{--fa:"\f510"}.fa-odysee{--fa:"\e5c6"}.fa-square-whatsapp,.fa-whatsapp-square{--fa:"\f40c"}.fa-node-js{--fa:"\f3d3"}.fa-edge-legacy{--fa:"\e078"}.fa-slack,.fa-slack-hash{--fa:"\f198"}.fa-medrt{--fa:"\f3c8"}.fa-usb{--fa:"\f287"}.fa-tumblr{--fa:"\f173"}.fa-vaadin{--fa:"\f408"}.fa-quora{--fa:"\f2c4"}.fa-square-x-twitter{--fa:"\e61a"}.fa-reacteurope{--fa:"\f75d"}.fa-medium,.fa-medium-m{--fa:"\f23a"}.fa-amilia{--fa:"\f36d"}.fa-mixcloud{--fa:"\f289"}.fa-flipboard{--fa:"\f44d"}.fa-viacoin{--fa:"\f237"}.fa-critical-role{--fa:"\f6c9"}.fa-sitrox{--fa:"\e44a"}.fa-discourse{--fa:"\f393"}.fa-joomla{--fa:"\f1aa"}.fa-mastodon{--fa:"\f4f6"}.fa-airbnb{--fa:"\f834"}.fa-wolf-pack-battalion{--fa:"\f514"}.fa-buy-n-large{--fa:"\f8a6"}.fa-gulp{--fa:"\f3ae"}.fa-creative-commons-sampling-plus{--fa:"\f4f1"}.fa-strava{--fa:"\f428"}.fa-ember{--fa:"\f423"}.fa-canadian-maple-leaf{--fa:"\f785"}.fa-teamspeak{--fa:"\f4f9"}.fa-pushed{--fa:"\f3e1"}.fa-wordpress-simple{--fa:"\f411"}.fa-nutritionix{--fa:"\f3d6"}.fa-wodu{--fa:"\e088"}.fa-google-pay{--fa:"\e079"}.fa-intercom{--fa:"\f7af"}.fa-zhihu{--fa:"\f63f"}.fa-korvue{--fa:"\f42f"}.fa-pix{--fa:"\e43a"}.fa-steam-symbol{--fa:"\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(../fonts/fontawesome/fa-regular-400.woff2) format("woff2"),url(../fonts/fontawesome/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(../fonts/fontawesome/fa-solid-900.woff2) format("woff2"),url(../fonts/fontawesome/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(../fonts/fontawesome/fa-brands-400.woff2) format("woff2"),url(../fonts/fontawesome/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../fonts/fontawesome/fa-solid-900.woff2) format("woff2"),url(../fonts/fontawesome/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../fonts/fontawesome/fa-regular-400.woff2) format("woff2"),url(../fonts/fontawesome/fa-regular-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../fonts/fontawesome/fa-solid-900.woff2) format("woff2"),url(../fonts/fontawesome/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../fonts/fontawesome/fa-brands-400.woff2) format("woff2"),url(../fonts/fontawesome/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../fonts/fontawesome/fa-regular-400.woff2) format("woff2"),url(../fonts/fontawesome/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(../fonts/fontawesome/fa-v4compatibility.woff2) format("woff2"),url(../fonts/fontawesome/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/tubesync/common/static/styles/fontawesome/_animated.scss b/tubesync/common/static/styles/fontawesome/_animated.scss deleted file mode 120000 index d618e324a..000000000 --- a/tubesync/common/static/styles/fontawesome/_animated.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_animated.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_bordered-pulled.scss b/tubesync/common/static/styles/fontawesome/_bordered-pulled.scss deleted file mode 120000 index 8f0e9507d..000000000 --- a/tubesync/common/static/styles/fontawesome/_bordered-pulled.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_bordered-pulled.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_core.scss b/tubesync/common/static/styles/fontawesome/_core.scss deleted file mode 120000 index 6203b127f..000000000 --- a/tubesync/common/static/styles/fontawesome/_core.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_core.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_fixed-width.scss b/tubesync/common/static/styles/fontawesome/_fixed-width.scss deleted file mode 120000 index 47da1f187..000000000 --- a/tubesync/common/static/styles/fontawesome/_fixed-width.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_fixed-width.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_functions.scss b/tubesync/common/static/styles/fontawesome/_functions.scss deleted file mode 120000 index a19874389..000000000 --- a/tubesync/common/static/styles/fontawesome/_functions.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_functions.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_icons.scss b/tubesync/common/static/styles/fontawesome/_icons.scss deleted file mode 120000 index c2556692a..000000000 --- a/tubesync/common/static/styles/fontawesome/_icons.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_icons.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_list.scss b/tubesync/common/static/styles/fontawesome/_list.scss deleted file mode 120000 index 9b2bd03ed..000000000 --- a/tubesync/common/static/styles/fontawesome/_list.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_list.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_mixins.scss b/tubesync/common/static/styles/fontawesome/_mixins.scss deleted file mode 120000 index d0a3f85f7..000000000 --- a/tubesync/common/static/styles/fontawesome/_mixins.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_mixins.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_rotated-flipped.scss b/tubesync/common/static/styles/fontawesome/_rotated-flipped.scss deleted file mode 120000 index 48470288c..000000000 --- a/tubesync/common/static/styles/fontawesome/_rotated-flipped.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_rotated-flipped.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_screen-reader.scss b/tubesync/common/static/styles/fontawesome/_screen-reader.scss deleted file mode 120000 index ca2aea4ed..000000000 --- a/tubesync/common/static/styles/fontawesome/_screen-reader.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_screen-reader.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_shims.scss b/tubesync/common/static/styles/fontawesome/_shims.scss deleted file mode 120000 index fb6eed2f0..000000000 --- a/tubesync/common/static/styles/fontawesome/_shims.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_shims.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_sizing.scss b/tubesync/common/static/styles/fontawesome/_sizing.scss deleted file mode 120000 index f95224290..000000000 --- a/tubesync/common/static/styles/fontawesome/_sizing.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_sizing.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_stacked.scss b/tubesync/common/static/styles/fontawesome/_stacked.scss deleted file mode 120000 index babdd95b5..000000000 --- a/tubesync/common/static/styles/fontawesome/_stacked.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/_stacked.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/_variables.scss b/tubesync/common/static/styles/fontawesome/_variables.scss deleted file mode 100644 index a156fd67e..000000000 --- a/tubesync/common/static/styles/fontawesome/_variables.scss +++ /dev/null @@ -1,5044 +0,0 @@ -// variables -// -------------------------- - -$fa-css-prefix : fa !default; -$fa-style : 900 !default; -$fa-style-family : "Font Awesome 6 Free" !default; - -$fa-icon-property : --fa; -$fa-duotone-icon-property : --fa--fa; - -$fa-display : inline-block !default; - -$fa-fw-width : fa-divide(20em, 16) !default; -$fa-inverse : #fff !default; - -$fa-border-color : #eee !default; -$fa-border-padding : .2em .25em .15em !default; -$fa-border-radius : .1em !default; -$fa-border-style : solid !default; -$fa-border-width : .08em !default; - -$fa-size-scale-2xs : 10 !default; -$fa-size-scale-xs : 12 !default; -$fa-size-scale-sm : 14 !default; -$fa-size-scale-base : 16 !default; -$fa-size-scale-lg : 20 !default; -$fa-size-scale-xl : 24 !default; -$fa-size-scale-2xl : 32 !default; - -$fa-sizes: ( - "2xs" : $fa-size-scale-2xs, - "xs" : $fa-size-scale-xs, - "sm" : $fa-size-scale-sm, - "lg" : $fa-size-scale-lg, - "xl" : $fa-size-scale-xl, - "2xl" : $fa-size-scale-2xl -) !default; - -$fa-li-width : 2em !default; -$fa-li-margin : $fa-li-width * fa-divide(5, 4) !default; - -$fa-pull-margin : .3em !default; - -$fa-primary-opacity : 1 !default; -$fa-secondary-opacity : .4 !default; - -$fa-stack-vertical-align : middle !default; -$fa-stack-width : ($fa-fw-width * 2) !default; -$fa-stack-z-index : auto !default; - -$fa-font-display : block !default; -$fa-font-path : "../fonts/fontawesome" !default; - -$fa-var-0: \30; -$fa-var-1: \31; -$fa-var-2: \32; -$fa-var-3: \33; -$fa-var-4: \34; -$fa-var-5: \35; -$fa-var-6: \36; -$fa-var-7: \37; -$fa-var-8: \38; -$fa-var-9: \39; -$fa-var-fill-drip: \f576; -$fa-var-arrows-to-circle: \e4bd; -$fa-var-circle-chevron-right: \f138; -$fa-var-chevron-circle-right: \f138; -$fa-var-at: \40; -$fa-var-trash-can: \f2ed; -$fa-var-trash-alt: \f2ed; -$fa-var-text-height: \f034; -$fa-var-user-xmark: \f235; -$fa-var-user-times: \f235; -$fa-var-stethoscope: \f0f1; -$fa-var-message: \f27a; -$fa-var-comment-alt: \f27a; -$fa-var-info: \f129; -$fa-var-down-left-and-up-right-to-center: \f422; -$fa-var-compress-alt: \f422; -$fa-var-explosion: \e4e9; -$fa-var-file-lines: \f15c; -$fa-var-file-alt: \f15c; -$fa-var-file-text: \f15c; -$fa-var-wave-square: \f83e; -$fa-var-ring: \f70b; -$fa-var-building-un: \e4d9; -$fa-var-dice-three: \f527; -$fa-var-calendar-days: \f073; -$fa-var-calendar-alt: \f073; -$fa-var-anchor-circle-check: \e4aa; -$fa-var-building-circle-arrow-right: \e4d1; -$fa-var-volleyball: \f45f; -$fa-var-volleyball-ball: \f45f; -$fa-var-arrows-up-to-line: \e4c2; -$fa-var-sort-down: \f0dd; -$fa-var-sort-desc: \f0dd; -$fa-var-circle-minus: \f056; -$fa-var-minus-circle: \f056; -$fa-var-door-open: \f52b; -$fa-var-right-from-bracket: \f2f5; -$fa-var-sign-out-alt: \f2f5; -$fa-var-atom: \f5d2; -$fa-var-soap: \e06e; -$fa-var-icons: \f86d; -$fa-var-heart-music-camera-bolt: \f86d; -$fa-var-microphone-lines-slash: \f539; -$fa-var-microphone-alt-slash: \f539; -$fa-var-bridge-circle-check: \e4c9; -$fa-var-pump-medical: \e06a; -$fa-var-fingerprint: \f577; -$fa-var-hand-point-right: \f0a4; -$fa-var-magnifying-glass-location: \f689; -$fa-var-search-location: \f689; -$fa-var-forward-step: \f051; -$fa-var-step-forward: \f051; -$fa-var-face-smile-beam: \f5b8; -$fa-var-smile-beam: \f5b8; -$fa-var-flag-checkered: \f11e; -$fa-var-football: \f44e; -$fa-var-football-ball: \f44e; -$fa-var-school-circle-exclamation: \e56c; -$fa-var-crop: \f125; -$fa-var-angles-down: \f103; -$fa-var-angle-double-down: \f103; -$fa-var-users-rectangle: \e594; -$fa-var-people-roof: \e537; -$fa-var-people-line: \e534; -$fa-var-beer-mug-empty: \f0fc; -$fa-var-beer: \f0fc; -$fa-var-diagram-predecessor: \e477; -$fa-var-arrow-up-long: \f176; -$fa-var-long-arrow-up: \f176; -$fa-var-fire-flame-simple: \f46a; -$fa-var-burn: \f46a; -$fa-var-person: \f183; -$fa-var-male: \f183; -$fa-var-laptop: \f109; -$fa-var-file-csv: \f6dd; -$fa-var-menorah: \f676; -$fa-var-truck-plane: \e58f; -$fa-var-record-vinyl: \f8d9; -$fa-var-face-grin-stars: \f587; -$fa-var-grin-stars: \f587; -$fa-var-bong: \f55c; -$fa-var-spaghetti-monster-flying: \f67b; -$fa-var-pastafarianism: \f67b; -$fa-var-arrow-down-up-across-line: \e4af; -$fa-var-spoon: \f2e5; -$fa-var-utensil-spoon: \f2e5; -$fa-var-jar-wheat: \e517; -$fa-var-envelopes-bulk: \f674; -$fa-var-mail-bulk: \f674; -$fa-var-file-circle-exclamation: \e4eb; -$fa-var-circle-h: \f47e; -$fa-var-hospital-symbol: \f47e; -$fa-var-pager: \f815; -$fa-var-address-book: \f2b9; -$fa-var-contact-book: \f2b9; -$fa-var-strikethrough: \f0cc; -$fa-var-k: \4b; -$fa-var-landmark-flag: \e51c; -$fa-var-pencil: \f303; -$fa-var-pencil-alt: \f303; -$fa-var-backward: \f04a; -$fa-var-caret-right: \f0da; -$fa-var-comments: \f086; -$fa-var-paste: \f0ea; -$fa-var-file-clipboard: \f0ea; -$fa-var-code-pull-request: \e13c; -$fa-var-clipboard-list: \f46d; -$fa-var-truck-ramp-box: \f4de; -$fa-var-truck-loading: \f4de; -$fa-var-user-check: \f4fc; -$fa-var-vial-virus: \e597; -$fa-var-sheet-plastic: \e571; -$fa-var-blog: \f781; -$fa-var-user-ninja: \f504; -$fa-var-person-arrow-up-from-line: \e539; -$fa-var-scroll-torah: \f6a0; -$fa-var-torah: \f6a0; -$fa-var-broom-ball: \f458; -$fa-var-quidditch: \f458; -$fa-var-quidditch-broom-ball: \f458; -$fa-var-toggle-off: \f204; -$fa-var-box-archive: \f187; -$fa-var-archive: \f187; -$fa-var-person-drowning: \e545; -$fa-var-arrow-down-9-1: \f886; -$fa-var-sort-numeric-desc: \f886; -$fa-var-sort-numeric-down-alt: \f886; -$fa-var-face-grin-tongue-squint: \f58a; -$fa-var-grin-tongue-squint: \f58a; -$fa-var-spray-can: \f5bd; -$fa-var-truck-monster: \f63b; -$fa-var-w: \57; -$fa-var-earth-africa: \f57c; -$fa-var-globe-africa: \f57c; -$fa-var-rainbow: \f75b; -$fa-var-circle-notch: \f1ce; -$fa-var-tablet-screen-button: \f3fa; -$fa-var-tablet-alt: \f3fa; -$fa-var-paw: \f1b0; -$fa-var-cloud: \f0c2; -$fa-var-trowel-bricks: \e58a; -$fa-var-face-flushed: \f579; -$fa-var-flushed: \f579; -$fa-var-hospital-user: \f80d; -$fa-var-tent-arrow-left-right: \e57f; -$fa-var-gavel: \f0e3; -$fa-var-legal: \f0e3; -$fa-var-binoculars: \f1e5; -$fa-var-microphone-slash: \f131; -$fa-var-box-tissue: \e05b; -$fa-var-motorcycle: \f21c; -$fa-var-bell-concierge: \f562; -$fa-var-concierge-bell: \f562; -$fa-var-pen-ruler: \f5ae; -$fa-var-pencil-ruler: \f5ae; -$fa-var-people-arrows: \e068; -$fa-var-people-arrows-left-right: \e068; -$fa-var-mars-and-venus-burst: \e523; -$fa-var-square-caret-right: \f152; -$fa-var-caret-square-right: \f152; -$fa-var-scissors: \f0c4; -$fa-var-cut: \f0c4; -$fa-var-sun-plant-wilt: \e57a; -$fa-var-toilets-portable: \e584; -$fa-var-hockey-puck: \f453; -$fa-var-table: \f0ce; -$fa-var-magnifying-glass-arrow-right: \e521; -$fa-var-tachograph-digital: \f566; -$fa-var-digital-tachograph: \f566; -$fa-var-users-slash: \e073; -$fa-var-clover: \e139; -$fa-var-reply: \f3e5; -$fa-var-mail-reply: \f3e5; -$fa-var-star-and-crescent: \f699; -$fa-var-house-fire: \e50c; -$fa-var-square-minus: \f146; -$fa-var-minus-square: \f146; -$fa-var-helicopter: \f533; -$fa-var-compass: \f14e; -$fa-var-square-caret-down: \f150; -$fa-var-caret-square-down: \f150; -$fa-var-file-circle-question: \e4ef; -$fa-var-laptop-code: \f5fc; -$fa-var-swatchbook: \f5c3; -$fa-var-prescription-bottle: \f485; -$fa-var-bars: \f0c9; -$fa-var-navicon: \f0c9; -$fa-var-people-group: \e533; -$fa-var-hourglass-end: \f253; -$fa-var-hourglass-3: \f253; -$fa-var-heart-crack: \f7a9; -$fa-var-heart-broken: \f7a9; -$fa-var-square-up-right: \f360; -$fa-var-external-link-square-alt: \f360; -$fa-var-face-kiss-beam: \f597; -$fa-var-kiss-beam: \f597; -$fa-var-film: \f008; -$fa-var-ruler-horizontal: \f547; -$fa-var-people-robbery: \e536; -$fa-var-lightbulb: \f0eb; -$fa-var-caret-left: \f0d9; -$fa-var-circle-exclamation: \f06a; -$fa-var-exclamation-circle: \f06a; -$fa-var-school-circle-xmark: \e56d; -$fa-var-arrow-right-from-bracket: \f08b; -$fa-var-sign-out: \f08b; -$fa-var-circle-chevron-down: \f13a; -$fa-var-chevron-circle-down: \f13a; -$fa-var-unlock-keyhole: \f13e; -$fa-var-unlock-alt: \f13e; -$fa-var-cloud-showers-heavy: \f740; -$fa-var-headphones-simple: \f58f; -$fa-var-headphones-alt: \f58f; -$fa-var-sitemap: \f0e8; -$fa-var-circle-dollar-to-slot: \f4b9; -$fa-var-donate: \f4b9; -$fa-var-memory: \f538; -$fa-var-road-spikes: \e568; -$fa-var-fire-burner: \e4f1; -$fa-var-flag: \f024; -$fa-var-hanukiah: \f6e6; -$fa-var-feather: \f52d; -$fa-var-volume-low: \f027; -$fa-var-volume-down: \f027; -$fa-var-comment-slash: \f4b3; -$fa-var-cloud-sun-rain: \f743; -$fa-var-compress: \f066; -$fa-var-wheat-awn: \e2cd; -$fa-var-wheat-alt: \e2cd; -$fa-var-ankh: \f644; -$fa-var-hands-holding-child: \e4fa; -$fa-var-asterisk: \2a; -$fa-var-square-check: \f14a; -$fa-var-check-square: \f14a; -$fa-var-peseta-sign: \e221; -$fa-var-heading: \f1dc; -$fa-var-header: \f1dc; -$fa-var-ghost: \f6e2; -$fa-var-list: \f03a; -$fa-var-list-squares: \f03a; -$fa-var-square-phone-flip: \f87b; -$fa-var-phone-square-alt: \f87b; -$fa-var-cart-plus: \f217; -$fa-var-gamepad: \f11b; -$fa-var-circle-dot: \f192; -$fa-var-dot-circle: \f192; -$fa-var-face-dizzy: \f567; -$fa-var-dizzy: \f567; -$fa-var-egg: \f7fb; -$fa-var-house-medical-circle-xmark: \e513; -$fa-var-campground: \f6bb; -$fa-var-folder-plus: \f65e; -$fa-var-futbol: \f1e3; -$fa-var-futbol-ball: \f1e3; -$fa-var-soccer-ball: \f1e3; -$fa-var-paintbrush: \f1fc; -$fa-var-paint-brush: \f1fc; -$fa-var-lock: \f023; -$fa-var-gas-pump: \f52f; -$fa-var-hot-tub-person: \f593; -$fa-var-hot-tub: \f593; -$fa-var-map-location: \f59f; -$fa-var-map-marked: \f59f; -$fa-var-house-flood-water: \e50e; -$fa-var-tree: \f1bb; -$fa-var-bridge-lock: \e4cc; -$fa-var-sack-dollar: \f81d; -$fa-var-pen-to-square: \f044; -$fa-var-edit: \f044; -$fa-var-car-side: \f5e4; -$fa-var-share-nodes: \f1e0; -$fa-var-share-alt: \f1e0; -$fa-var-heart-circle-minus: \e4ff; -$fa-var-hourglass-half: \f252; -$fa-var-hourglass-2: \f252; -$fa-var-microscope: \f610; -$fa-var-sink: \e06d; -$fa-var-bag-shopping: \f290; -$fa-var-shopping-bag: \f290; -$fa-var-arrow-down-z-a: \f881; -$fa-var-sort-alpha-desc: \f881; -$fa-var-sort-alpha-down-alt: \f881; -$fa-var-mitten: \f7b5; -$fa-var-person-rays: \e54d; -$fa-var-users: \f0c0; -$fa-var-eye-slash: \f070; -$fa-var-flask-vial: \e4f3; -$fa-var-hand: \f256; -$fa-var-hand-paper: \f256; -$fa-var-om: \f679; -$fa-var-worm: \e599; -$fa-var-house-circle-xmark: \e50b; -$fa-var-plug: \f1e6; -$fa-var-chevron-up: \f077; -$fa-var-hand-spock: \f259; -$fa-var-stopwatch: \f2f2; -$fa-var-face-kiss: \f596; -$fa-var-kiss: \f596; -$fa-var-bridge-circle-xmark: \e4cb; -$fa-var-face-grin-tongue: \f589; -$fa-var-grin-tongue: \f589; -$fa-var-chess-bishop: \f43a; -$fa-var-face-grin-wink: \f58c; -$fa-var-grin-wink: \f58c; -$fa-var-ear-deaf: \f2a4; -$fa-var-deaf: \f2a4; -$fa-var-deafness: \f2a4; -$fa-var-hard-of-hearing: \f2a4; -$fa-var-road-circle-check: \e564; -$fa-var-dice-five: \f523; -$fa-var-square-rss: \f143; -$fa-var-rss-square: \f143; -$fa-var-land-mine-on: \e51b; -$fa-var-i-cursor: \f246; -$fa-var-stamp: \f5bf; -$fa-var-stairs: \e289; -$fa-var-i: \49; -$fa-var-hryvnia-sign: \f6f2; -$fa-var-hryvnia: \f6f2; -$fa-var-pills: \f484; -$fa-var-face-grin-wide: \f581; -$fa-var-grin-alt: \f581; -$fa-var-tooth: \f5c9; -$fa-var-v: \56; -$fa-var-bangladeshi-taka-sign: \e2e6; -$fa-var-bicycle: \f206; -$fa-var-staff-snake: \e579; -$fa-var-rod-asclepius: \e579; -$fa-var-rod-snake: \e579; -$fa-var-staff-aesculapius: \e579; -$fa-var-head-side-cough-slash: \e062; -$fa-var-truck-medical: \f0f9; -$fa-var-ambulance: \f0f9; -$fa-var-wheat-awn-circle-exclamation: \e598; -$fa-var-snowman: \f7d0; -$fa-var-mortar-pestle: \f5a7; -$fa-var-road-barrier: \e562; -$fa-var-school: \f549; -$fa-var-igloo: \f7ae; -$fa-var-joint: \f595; -$fa-var-angle-right: \f105; -$fa-var-horse: \f6f0; -$fa-var-q: \51; -$fa-var-g: \47; -$fa-var-notes-medical: \f481; -$fa-var-temperature-half: \f2c9; -$fa-var-temperature-2: \f2c9; -$fa-var-thermometer-2: \f2c9; -$fa-var-thermometer-half: \f2c9; -$fa-var-dong-sign: \e169; -$fa-var-capsules: \f46b; -$fa-var-poo-storm: \f75a; -$fa-var-poo-bolt: \f75a; -$fa-var-face-frown-open: \f57a; -$fa-var-frown-open: \f57a; -$fa-var-hand-point-up: \f0a6; -$fa-var-money-bill: \f0d6; -$fa-var-bookmark: \f02e; -$fa-var-align-justify: \f039; -$fa-var-umbrella-beach: \f5ca; -$fa-var-helmet-un: \e503; -$fa-var-bullseye: \f140; -$fa-var-bacon: \f7e5; -$fa-var-hand-point-down: \f0a7; -$fa-var-arrow-up-from-bracket: \e09a; -$fa-var-folder: \f07b; -$fa-var-folder-blank: \f07b; -$fa-var-file-waveform: \f478; -$fa-var-file-medical-alt: \f478; -$fa-var-radiation: \f7b9; -$fa-var-chart-simple: \e473; -$fa-var-mars-stroke: \f229; -$fa-var-vial: \f492; -$fa-var-gauge: \f624; -$fa-var-dashboard: \f624; -$fa-var-gauge-med: \f624; -$fa-var-tachometer-alt-average: \f624; -$fa-var-wand-magic-sparkles: \e2ca; -$fa-var-magic-wand-sparkles: \e2ca; -$fa-var-e: \45; -$fa-var-pen-clip: \f305; -$fa-var-pen-alt: \f305; -$fa-var-bridge-circle-exclamation: \e4ca; -$fa-var-user: \f007; -$fa-var-school-circle-check: \e56b; -$fa-var-dumpster: \f793; -$fa-var-van-shuttle: \f5b6; -$fa-var-shuttle-van: \f5b6; -$fa-var-building-user: \e4da; -$fa-var-square-caret-left: \f191; -$fa-var-caret-square-left: \f191; -$fa-var-highlighter: \f591; -$fa-var-key: \f084; -$fa-var-bullhorn: \f0a1; -$fa-var-globe: \f0ac; -$fa-var-synagogue: \f69b; -$fa-var-person-half-dress: \e548; -$fa-var-road-bridge: \e563; -$fa-var-location-arrow: \f124; -$fa-var-c: \43; -$fa-var-tablet-button: \f10a; -$fa-var-building-lock: \e4d6; -$fa-var-pizza-slice: \f818; -$fa-var-money-bill-wave: \f53a; -$fa-var-chart-area: \f1fe; -$fa-var-area-chart: \f1fe; -$fa-var-house-flag: \e50d; -$fa-var-person-circle-minus: \e540; -$fa-var-ban: \f05e; -$fa-var-cancel: \f05e; -$fa-var-camera-rotate: \e0d8; -$fa-var-spray-can-sparkles: \f5d0; -$fa-var-air-freshener: \f5d0; -$fa-var-star: \f005; -$fa-var-repeat: \f363; -$fa-var-cross: \f654; -$fa-var-box: \f466; -$fa-var-venus-mars: \f228; -$fa-var-arrow-pointer: \f245; -$fa-var-mouse-pointer: \f245; -$fa-var-maximize: \f31e; -$fa-var-expand-arrows-alt: \f31e; -$fa-var-charging-station: \f5e7; -$fa-var-shapes: \f61f; -$fa-var-triangle-circle-square: \f61f; -$fa-var-shuffle: \f074; -$fa-var-random: \f074; -$fa-var-person-running: \f70c; -$fa-var-running: \f70c; -$fa-var-mobile-retro: \e527; -$fa-var-grip-lines-vertical: \f7a5; -$fa-var-spider: \f717; -$fa-var-hands-bound: \e4f9; -$fa-var-file-invoice-dollar: \f571; -$fa-var-plane-circle-exclamation: \e556; -$fa-var-x-ray: \f497; -$fa-var-spell-check: \f891; -$fa-var-slash: \f715; -$fa-var-computer-mouse: \f8cc; -$fa-var-mouse: \f8cc; -$fa-var-arrow-right-to-bracket: \f090; -$fa-var-sign-in: \f090; -$fa-var-shop-slash: \e070; -$fa-var-store-alt-slash: \e070; -$fa-var-server: \f233; -$fa-var-virus-covid-slash: \e4a9; -$fa-var-shop-lock: \e4a5; -$fa-var-hourglass-start: \f251; -$fa-var-hourglass-1: \f251; -$fa-var-blender-phone: \f6b6; -$fa-var-building-wheat: \e4db; -$fa-var-person-breastfeeding: \e53a; -$fa-var-right-to-bracket: \f2f6; -$fa-var-sign-in-alt: \f2f6; -$fa-var-venus: \f221; -$fa-var-passport: \f5ab; -$fa-var-thumbtack-slash: \e68f; -$fa-var-thumb-tack-slash: \e68f; -$fa-var-heart-pulse: \f21e; -$fa-var-heartbeat: \f21e; -$fa-var-people-carry-box: \f4ce; -$fa-var-people-carry: \f4ce; -$fa-var-temperature-high: \f769; -$fa-var-microchip: \f2db; -$fa-var-crown: \f521; -$fa-var-weight-hanging: \f5cd; -$fa-var-xmarks-lines: \e59a; -$fa-var-file-prescription: \f572; -$fa-var-weight-scale: \f496; -$fa-var-weight: \f496; -$fa-var-user-group: \f500; -$fa-var-user-friends: \f500; -$fa-var-arrow-up-a-z: \f15e; -$fa-var-sort-alpha-up: \f15e; -$fa-var-chess-knight: \f441; -$fa-var-face-laugh-squint: \f59b; -$fa-var-laugh-squint: \f59b; -$fa-var-wheelchair: \f193; -$fa-var-circle-arrow-up: \f0aa; -$fa-var-arrow-circle-up: \f0aa; -$fa-var-toggle-on: \f205; -$fa-var-person-walking: \f554; -$fa-var-walking: \f554; -$fa-var-l: \4c; -$fa-var-fire: \f06d; -$fa-var-bed-pulse: \f487; -$fa-var-procedures: \f487; -$fa-var-shuttle-space: \f197; -$fa-var-space-shuttle: \f197; -$fa-var-face-laugh: \f599; -$fa-var-laugh: \f599; -$fa-var-folder-open: \f07c; -$fa-var-heart-circle-plus: \e500; -$fa-var-code-fork: \e13b; -$fa-var-city: \f64f; -$fa-var-microphone-lines: \f3c9; -$fa-var-microphone-alt: \f3c9; -$fa-var-pepper-hot: \f816; -$fa-var-unlock: \f09c; -$fa-var-colon-sign: \e140; -$fa-var-headset: \f590; -$fa-var-store-slash: \e071; -$fa-var-road-circle-xmark: \e566; -$fa-var-user-minus: \f503; -$fa-var-mars-stroke-up: \f22a; -$fa-var-mars-stroke-v: \f22a; -$fa-var-champagne-glasses: \f79f; -$fa-var-glass-cheers: \f79f; -$fa-var-clipboard: \f328; -$fa-var-house-circle-exclamation: \e50a; -$fa-var-file-arrow-up: \f574; -$fa-var-file-upload: \f574; -$fa-var-wifi: \f1eb; -$fa-var-wifi-3: \f1eb; -$fa-var-wifi-strong: \f1eb; -$fa-var-bath: \f2cd; -$fa-var-bathtub: \f2cd; -$fa-var-underline: \f0cd; -$fa-var-user-pen: \f4ff; -$fa-var-user-edit: \f4ff; -$fa-var-signature: \f5b7; -$fa-var-stroopwafel: \f551; -$fa-var-bold: \f032; -$fa-var-anchor-lock: \e4ad; -$fa-var-building-ngo: \e4d7; -$fa-var-manat-sign: \e1d5; -$fa-var-not-equal: \f53e; -$fa-var-border-top-left: \f853; -$fa-var-border-style: \f853; -$fa-var-map-location-dot: \f5a0; -$fa-var-map-marked-alt: \f5a0; -$fa-var-jedi: \f669; -$fa-var-square-poll-vertical: \f681; -$fa-var-poll: \f681; -$fa-var-mug-hot: \f7b6; -$fa-var-car-battery: \f5df; -$fa-var-battery-car: \f5df; -$fa-var-gift: \f06b; -$fa-var-dice-two: \f528; -$fa-var-chess-queen: \f445; -$fa-var-glasses: \f530; -$fa-var-chess-board: \f43c; -$fa-var-building-circle-check: \e4d2; -$fa-var-person-chalkboard: \e53d; -$fa-var-mars-stroke-right: \f22b; -$fa-var-mars-stroke-h: \f22b; -$fa-var-hand-back-fist: \f255; -$fa-var-hand-rock: \f255; -$fa-var-square-caret-up: \f151; -$fa-var-caret-square-up: \f151; -$fa-var-cloud-showers-water: \e4e4; -$fa-var-chart-bar: \f080; -$fa-var-bar-chart: \f080; -$fa-var-hands-bubbles: \e05e; -$fa-var-hands-wash: \e05e; -$fa-var-less-than-equal: \f537; -$fa-var-train: \f238; -$fa-var-eye-low-vision: \f2a8; -$fa-var-low-vision: \f2a8; -$fa-var-crow: \f520; -$fa-var-sailboat: \e445; -$fa-var-window-restore: \f2d2; -$fa-var-square-plus: \f0fe; -$fa-var-plus-square: \f0fe; -$fa-var-torii-gate: \f6a1; -$fa-var-frog: \f52e; -$fa-var-bucket: \e4cf; -$fa-var-image: \f03e; -$fa-var-microphone: \f130; -$fa-var-cow: \f6c8; -$fa-var-caret-up: \f0d8; -$fa-var-screwdriver: \f54a; -$fa-var-folder-closed: \e185; -$fa-var-house-tsunami: \e515; -$fa-var-square-nfi: \e576; -$fa-var-arrow-up-from-ground-water: \e4b5; -$fa-var-martini-glass: \f57b; -$fa-var-glass-martini-alt: \f57b; -$fa-var-square-binary: \e69b; -$fa-var-rotate-left: \f2ea; -$fa-var-rotate-back: \f2ea; -$fa-var-rotate-backward: \f2ea; -$fa-var-undo-alt: \f2ea; -$fa-var-table-columns: \f0db; -$fa-var-columns: \f0db; -$fa-var-lemon: \f094; -$fa-var-head-side-mask: \e063; -$fa-var-handshake: \f2b5; -$fa-var-gem: \f3a5; -$fa-var-dolly: \f472; -$fa-var-dolly-box: \f472; -$fa-var-smoking: \f48d; -$fa-var-minimize: \f78c; -$fa-var-compress-arrows-alt: \f78c; -$fa-var-monument: \f5a6; -$fa-var-snowplow: \f7d2; -$fa-var-angles-right: \f101; -$fa-var-angle-double-right: \f101; -$fa-var-cannabis: \f55f; -$fa-var-circle-play: \f144; -$fa-var-play-circle: \f144; -$fa-var-tablets: \f490; -$fa-var-ethernet: \f796; -$fa-var-euro-sign: \f153; -$fa-var-eur: \f153; -$fa-var-euro: \f153; -$fa-var-chair: \f6c0; -$fa-var-circle-check: \f058; -$fa-var-check-circle: \f058; -$fa-var-circle-stop: \f28d; -$fa-var-stop-circle: \f28d; -$fa-var-compass-drafting: \f568; -$fa-var-drafting-compass: \f568; -$fa-var-plate-wheat: \e55a; -$fa-var-icicles: \f7ad; -$fa-var-person-shelter: \e54f; -$fa-var-neuter: \f22c; -$fa-var-id-badge: \f2c1; -$fa-var-marker: \f5a1; -$fa-var-face-laugh-beam: \f59a; -$fa-var-laugh-beam: \f59a; -$fa-var-helicopter-symbol: \e502; -$fa-var-universal-access: \f29a; -$fa-var-circle-chevron-up: \f139; -$fa-var-chevron-circle-up: \f139; -$fa-var-lari-sign: \e1c8; -$fa-var-volcano: \f770; -$fa-var-person-walking-dashed-line-arrow-right: \e553; -$fa-var-sterling-sign: \f154; -$fa-var-gbp: \f154; -$fa-var-pound-sign: \f154; -$fa-var-viruses: \e076; -$fa-var-square-person-confined: \e577; -$fa-var-user-tie: \f508; -$fa-var-arrow-down-long: \f175; -$fa-var-long-arrow-down: \f175; -$fa-var-tent-arrow-down-to-line: \e57e; -$fa-var-certificate: \f0a3; -$fa-var-reply-all: \f122; -$fa-var-mail-reply-all: \f122; -$fa-var-suitcase: \f0f2; -$fa-var-person-skating: \f7c5; -$fa-var-skating: \f7c5; -$fa-var-filter-circle-dollar: \f662; -$fa-var-funnel-dollar: \f662; -$fa-var-camera-retro: \f083; -$fa-var-circle-arrow-down: \f0ab; -$fa-var-arrow-circle-down: \f0ab; -$fa-var-file-import: \f56f; -$fa-var-arrow-right-to-file: \f56f; -$fa-var-square-arrow-up-right: \f14c; -$fa-var-external-link-square: \f14c; -$fa-var-box-open: \f49e; -$fa-var-scroll: \f70e; -$fa-var-spa: \f5bb; -$fa-var-location-pin-lock: \e51f; -$fa-var-pause: \f04c; -$fa-var-hill-avalanche: \e507; -$fa-var-temperature-empty: \f2cb; -$fa-var-temperature-0: \f2cb; -$fa-var-thermometer-0: \f2cb; -$fa-var-thermometer-empty: \f2cb; -$fa-var-bomb: \f1e2; -$fa-var-registered: \f25d; -$fa-var-address-card: \f2bb; -$fa-var-contact-card: \f2bb; -$fa-var-vcard: \f2bb; -$fa-var-scale-unbalanced-flip: \f516; -$fa-var-balance-scale-right: \f516; -$fa-var-subscript: \f12c; -$fa-var-diamond-turn-right: \f5eb; -$fa-var-directions: \f5eb; -$fa-var-burst: \e4dc; -$fa-var-house-laptop: \e066; -$fa-var-laptop-house: \e066; -$fa-var-face-tired: \f5c8; -$fa-var-tired: \f5c8; -$fa-var-money-bills: \e1f3; -$fa-var-smog: \f75f; -$fa-var-crutch: \f7f7; -$fa-var-cloud-arrow-up: \f0ee; -$fa-var-cloud-upload: \f0ee; -$fa-var-cloud-upload-alt: \f0ee; -$fa-var-palette: \f53f; -$fa-var-arrows-turn-right: \e4c0; -$fa-var-vest: \e085; -$fa-var-ferry: \e4ea; -$fa-var-arrows-down-to-people: \e4b9; -$fa-var-seedling: \f4d8; -$fa-var-sprout: \f4d8; -$fa-var-left-right: \f337; -$fa-var-arrows-alt-h: \f337; -$fa-var-boxes-packing: \e4c7; -$fa-var-circle-arrow-left: \f0a8; -$fa-var-arrow-circle-left: \f0a8; -$fa-var-group-arrows-rotate: \e4f6; -$fa-var-bowl-food: \e4c6; -$fa-var-candy-cane: \f786; -$fa-var-arrow-down-wide-short: \f160; -$fa-var-sort-amount-asc: \f160; -$fa-var-sort-amount-down: \f160; -$fa-var-cloud-bolt: \f76c; -$fa-var-thunderstorm: \f76c; -$fa-var-text-slash: \f87d; -$fa-var-remove-format: \f87d; -$fa-var-face-smile-wink: \f4da; -$fa-var-smile-wink: \f4da; -$fa-var-file-word: \f1c2; -$fa-var-file-powerpoint: \f1c4; -$fa-var-arrows-left-right: \f07e; -$fa-var-arrows-h: \f07e; -$fa-var-house-lock: \e510; -$fa-var-cloud-arrow-down: \f0ed; -$fa-var-cloud-download: \f0ed; -$fa-var-cloud-download-alt: \f0ed; -$fa-var-children: \e4e1; -$fa-var-chalkboard: \f51b; -$fa-var-blackboard: \f51b; -$fa-var-user-large-slash: \f4fa; -$fa-var-user-alt-slash: \f4fa; -$fa-var-envelope-open: \f2b6; -$fa-var-handshake-simple-slash: \e05f; -$fa-var-handshake-alt-slash: \e05f; -$fa-var-mattress-pillow: \e525; -$fa-var-guarani-sign: \e19a; -$fa-var-arrows-rotate: \f021; -$fa-var-refresh: \f021; -$fa-var-sync: \f021; -$fa-var-fire-extinguisher: \f134; -$fa-var-cruzeiro-sign: \e152; -$fa-var-greater-than-equal: \f532; -$fa-var-shield-halved: \f3ed; -$fa-var-shield-alt: \f3ed; -$fa-var-book-atlas: \f558; -$fa-var-atlas: \f558; -$fa-var-virus: \e074; -$fa-var-envelope-circle-check: \e4e8; -$fa-var-layer-group: \f5fd; -$fa-var-arrows-to-dot: \e4be; -$fa-var-archway: \f557; -$fa-var-heart-circle-check: \e4fd; -$fa-var-house-chimney-crack: \f6f1; -$fa-var-house-damage: \f6f1; -$fa-var-file-zipper: \f1c6; -$fa-var-file-archive: \f1c6; -$fa-var-square: \f0c8; -$fa-var-martini-glass-empty: \f000; -$fa-var-glass-martini: \f000; -$fa-var-couch: \f4b8; -$fa-var-cedi-sign: \e0df; -$fa-var-italic: \f033; -$fa-var-table-cells-column-lock: \e678; -$fa-var-church: \f51d; -$fa-var-comments-dollar: \f653; -$fa-var-democrat: \f747; -$fa-var-z: \5a; -$fa-var-person-skiing: \f7c9; -$fa-var-skiing: \f7c9; -$fa-var-road-lock: \e567; -$fa-var-a: \41; -$fa-var-temperature-arrow-down: \e03f; -$fa-var-temperature-down: \e03f; -$fa-var-feather-pointed: \f56b; -$fa-var-feather-alt: \f56b; -$fa-var-p: \50; -$fa-var-snowflake: \f2dc; -$fa-var-newspaper: \f1ea; -$fa-var-rectangle-ad: \f641; -$fa-var-ad: \f641; -$fa-var-circle-arrow-right: \f0a9; -$fa-var-arrow-circle-right: \f0a9; -$fa-var-filter-circle-xmark: \e17b; -$fa-var-locust: \e520; -$fa-var-sort: \f0dc; -$fa-var-unsorted: \f0dc; -$fa-var-list-ol: \f0cb; -$fa-var-list-1-2: \f0cb; -$fa-var-list-numeric: \f0cb; -$fa-var-person-dress-burst: \e544; -$fa-var-money-check-dollar: \f53d; -$fa-var-money-check-alt: \f53d; -$fa-var-vector-square: \f5cb; -$fa-var-bread-slice: \f7ec; -$fa-var-language: \f1ab; -$fa-var-face-kiss-wink-heart: \f598; -$fa-var-kiss-wink-heart: \f598; -$fa-var-filter: \f0b0; -$fa-var-question: \3f; -$fa-var-file-signature: \f573; -$fa-var-up-down-left-right: \f0b2; -$fa-var-arrows-alt: \f0b2; -$fa-var-house-chimney-user: \e065; -$fa-var-hand-holding-heart: \f4be; -$fa-var-puzzle-piece: \f12e; -$fa-var-money-check: \f53c; -$fa-var-star-half-stroke: \f5c0; -$fa-var-star-half-alt: \f5c0; -$fa-var-code: \f121; -$fa-var-whiskey-glass: \f7a0; -$fa-var-glass-whiskey: \f7a0; -$fa-var-building-circle-exclamation: \e4d3; -$fa-var-magnifying-glass-chart: \e522; -$fa-var-arrow-up-right-from-square: \f08e; -$fa-var-external-link: \f08e; -$fa-var-cubes-stacked: \e4e6; -$fa-var-won-sign: \f159; -$fa-var-krw: \f159; -$fa-var-won: \f159; -$fa-var-virus-covid: \e4a8; -$fa-var-austral-sign: \e0a9; -$fa-var-f: \46; -$fa-var-leaf: \f06c; -$fa-var-road: \f018; -$fa-var-taxi: \f1ba; -$fa-var-cab: \f1ba; -$fa-var-person-circle-plus: \e541; -$fa-var-chart-pie: \f200; -$fa-var-pie-chart: \f200; -$fa-var-bolt-lightning: \e0b7; -$fa-var-sack-xmark: \e56a; -$fa-var-file-excel: \f1c3; -$fa-var-file-contract: \f56c; -$fa-var-fish-fins: \e4f2; -$fa-var-building-flag: \e4d5; -$fa-var-face-grin-beam: \f582; -$fa-var-grin-beam: \f582; -$fa-var-object-ungroup: \f248; -$fa-var-poop: \f619; -$fa-var-location-pin: \f041; -$fa-var-map-marker: \f041; -$fa-var-kaaba: \f66b; -$fa-var-toilet-paper: \f71e; -$fa-var-helmet-safety: \f807; -$fa-var-hard-hat: \f807; -$fa-var-hat-hard: \f807; -$fa-var-eject: \f052; -$fa-var-circle-right: \f35a; -$fa-var-arrow-alt-circle-right: \f35a; -$fa-var-plane-circle-check: \e555; -$fa-var-face-rolling-eyes: \f5a5; -$fa-var-meh-rolling-eyes: \f5a5; -$fa-var-object-group: \f247; -$fa-var-chart-line: \f201; -$fa-var-line-chart: \f201; -$fa-var-mask-ventilator: \e524; -$fa-var-arrow-right: \f061; -$fa-var-signs-post: \f277; -$fa-var-map-signs: \f277; -$fa-var-cash-register: \f788; -$fa-var-person-circle-question: \e542; -$fa-var-h: \48; -$fa-var-tarp: \e57b; -$fa-var-screwdriver-wrench: \f7d9; -$fa-var-tools: \f7d9; -$fa-var-arrows-to-eye: \e4bf; -$fa-var-plug-circle-bolt: \e55b; -$fa-var-heart: \f004; -$fa-var-mars-and-venus: \f224; -$fa-var-house-user: \e1b0; -$fa-var-home-user: \e1b0; -$fa-var-dumpster-fire: \f794; -$fa-var-house-crack: \e3b1; -$fa-var-martini-glass-citrus: \f561; -$fa-var-cocktail: \f561; -$fa-var-face-surprise: \f5c2; -$fa-var-surprise: \f5c2; -$fa-var-bottle-water: \e4c5; -$fa-var-circle-pause: \f28b; -$fa-var-pause-circle: \f28b; -$fa-var-toilet-paper-slash: \e072; -$fa-var-apple-whole: \f5d1; -$fa-var-apple-alt: \f5d1; -$fa-var-kitchen-set: \e51a; -$fa-var-r: \52; -$fa-var-temperature-quarter: \f2ca; -$fa-var-temperature-1: \f2ca; -$fa-var-thermometer-1: \f2ca; -$fa-var-thermometer-quarter: \f2ca; -$fa-var-cube: \f1b2; -$fa-var-bitcoin-sign: \e0b4; -$fa-var-shield-dog: \e573; -$fa-var-solar-panel: \f5ba; -$fa-var-lock-open: \f3c1; -$fa-var-elevator: \e16d; -$fa-var-money-bill-transfer: \e528; -$fa-var-money-bill-trend-up: \e529; -$fa-var-house-flood-water-circle-arrow-right: \e50f; -$fa-var-square-poll-horizontal: \f682; -$fa-var-poll-h: \f682; -$fa-var-circle: \f111; -$fa-var-backward-fast: \f049; -$fa-var-fast-backward: \f049; -$fa-var-recycle: \f1b8; -$fa-var-user-astronaut: \f4fb; -$fa-var-plane-slash: \e069; -$fa-var-trademark: \f25c; -$fa-var-basketball: \f434; -$fa-var-basketball-ball: \f434; -$fa-var-satellite-dish: \f7c0; -$fa-var-circle-up: \f35b; -$fa-var-arrow-alt-circle-up: \f35b; -$fa-var-mobile-screen-button: \f3cd; -$fa-var-mobile-alt: \f3cd; -$fa-var-volume-high: \f028; -$fa-var-volume-up: \f028; -$fa-var-users-rays: \e593; -$fa-var-wallet: \f555; -$fa-var-clipboard-check: \f46c; -$fa-var-file-audio: \f1c7; -$fa-var-burger: \f805; -$fa-var-hamburger: \f805; -$fa-var-wrench: \f0ad; -$fa-var-bugs: \e4d0; -$fa-var-rupee-sign: \f156; -$fa-var-rupee: \f156; -$fa-var-file-image: \f1c5; -$fa-var-circle-question: \f059; -$fa-var-question-circle: \f059; -$fa-var-plane-departure: \f5b0; -$fa-var-handshake-slash: \e060; -$fa-var-book-bookmark: \e0bb; -$fa-var-code-branch: \f126; -$fa-var-hat-cowboy: \f8c0; -$fa-var-bridge: \e4c8; -$fa-var-phone-flip: \f879; -$fa-var-phone-alt: \f879; -$fa-var-truck-front: \e2b7; -$fa-var-cat: \f6be; -$fa-var-anchor-circle-exclamation: \e4ab; -$fa-var-truck-field: \e58d; -$fa-var-route: \f4d7; -$fa-var-clipboard-question: \e4e3; -$fa-var-panorama: \e209; -$fa-var-comment-medical: \f7f5; -$fa-var-teeth-open: \f62f; -$fa-var-file-circle-minus: \e4ed; -$fa-var-tags: \f02c; -$fa-var-wine-glass: \f4e3; -$fa-var-forward-fast: \f050; -$fa-var-fast-forward: \f050; -$fa-var-face-meh-blank: \f5a4; -$fa-var-meh-blank: \f5a4; -$fa-var-square-parking: \f540; -$fa-var-parking: \f540; -$fa-var-house-signal: \e012; -$fa-var-bars-progress: \f828; -$fa-var-tasks-alt: \f828; -$fa-var-faucet-drip: \e006; -$fa-var-cart-flatbed: \f474; -$fa-var-dolly-flatbed: \f474; -$fa-var-ban-smoking: \f54d; -$fa-var-smoking-ban: \f54d; -$fa-var-terminal: \f120; -$fa-var-mobile-button: \f10b; -$fa-var-house-medical-flag: \e514; -$fa-var-basket-shopping: \f291; -$fa-var-shopping-basket: \f291; -$fa-var-tape: \f4db; -$fa-var-bus-simple: \f55e; -$fa-var-bus-alt: \f55e; -$fa-var-eye: \f06e; -$fa-var-face-sad-cry: \f5b3; -$fa-var-sad-cry: \f5b3; -$fa-var-audio-description: \f29e; -$fa-var-person-military-to-person: \e54c; -$fa-var-file-shield: \e4f0; -$fa-var-user-slash: \f506; -$fa-var-pen: \f304; -$fa-var-tower-observation: \e586; -$fa-var-file-code: \f1c9; -$fa-var-signal: \f012; -$fa-var-signal-5: \f012; -$fa-var-signal-perfect: \f012; -$fa-var-bus: \f207; -$fa-var-heart-circle-xmark: \e501; -$fa-var-house-chimney: \e3af; -$fa-var-home-lg: \e3af; -$fa-var-window-maximize: \f2d0; -$fa-var-face-frown: \f119; -$fa-var-frown: \f119; -$fa-var-prescription: \f5b1; -$fa-var-shop: \f54f; -$fa-var-store-alt: \f54f; -$fa-var-floppy-disk: \f0c7; -$fa-var-save: \f0c7; -$fa-var-vihara: \f6a7; -$fa-var-scale-unbalanced: \f515; -$fa-var-balance-scale-left: \f515; -$fa-var-sort-up: \f0de; -$fa-var-sort-asc: \f0de; -$fa-var-comment-dots: \f4ad; -$fa-var-commenting: \f4ad; -$fa-var-plant-wilt: \e5aa; -$fa-var-diamond: \f219; -$fa-var-face-grin-squint: \f585; -$fa-var-grin-squint: \f585; -$fa-var-hand-holding-dollar: \f4c0; -$fa-var-hand-holding-usd: \f4c0; -$fa-var-chart-diagram: \e695; -$fa-var-bacterium: \e05a; -$fa-var-hand-pointer: \f25a; -$fa-var-drum-steelpan: \f56a; -$fa-var-hand-scissors: \f257; -$fa-var-hands-praying: \f684; -$fa-var-praying-hands: \f684; -$fa-var-arrow-rotate-right: \f01e; -$fa-var-arrow-right-rotate: \f01e; -$fa-var-arrow-rotate-forward: \f01e; -$fa-var-redo: \f01e; -$fa-var-biohazard: \f780; -$fa-var-location-crosshairs: \f601; -$fa-var-location: \f601; -$fa-var-mars-double: \f227; -$fa-var-child-dress: \e59c; -$fa-var-users-between-lines: \e591; -$fa-var-lungs-virus: \e067; -$fa-var-face-grin-tears: \f588; -$fa-var-grin-tears: \f588; -$fa-var-phone: \f095; -$fa-var-calendar-xmark: \f273; -$fa-var-calendar-times: \f273; -$fa-var-child-reaching: \e59d; -$fa-var-head-side-virus: \e064; -$fa-var-user-gear: \f4fe; -$fa-var-user-cog: \f4fe; -$fa-var-arrow-up-1-9: \f163; -$fa-var-sort-numeric-up: \f163; -$fa-var-door-closed: \f52a; -$fa-var-shield-virus: \e06c; -$fa-var-dice-six: \f526; -$fa-var-mosquito-net: \e52c; -$fa-var-file-fragment: \e697; -$fa-var-bridge-water: \e4ce; -$fa-var-person-booth: \f756; -$fa-var-text-width: \f035; -$fa-var-hat-wizard: \f6e8; -$fa-var-pen-fancy: \f5ac; -$fa-var-person-digging: \f85e; -$fa-var-digging: \f85e; -$fa-var-trash: \f1f8; -$fa-var-gauge-simple: \f629; -$fa-var-gauge-simple-med: \f629; -$fa-var-tachometer-average: \f629; -$fa-var-book-medical: \f7e6; -$fa-var-poo: \f2fe; -$fa-var-quote-right: \f10e; -$fa-var-quote-right-alt: \f10e; -$fa-var-shirt: \f553; -$fa-var-t-shirt: \f553; -$fa-var-tshirt: \f553; -$fa-var-cubes: \f1b3; -$fa-var-divide: \f529; -$fa-var-tenge-sign: \f7d7; -$fa-var-tenge: \f7d7; -$fa-var-headphones: \f025; -$fa-var-hands-holding: \f4c2; -$fa-var-hands-clapping: \e1a8; -$fa-var-republican: \f75e; -$fa-var-arrow-left: \f060; -$fa-var-person-circle-xmark: \e543; -$fa-var-ruler: \f545; -$fa-var-align-left: \f036; -$fa-var-dice-d6: \f6d1; -$fa-var-restroom: \f7bd; -$fa-var-j: \4a; -$fa-var-users-viewfinder: \e595; -$fa-var-file-video: \f1c8; -$fa-var-up-right-from-square: \f35d; -$fa-var-external-link-alt: \f35d; -$fa-var-table-cells: \f00a; -$fa-var-th: \f00a; -$fa-var-file-pdf: \f1c1; -$fa-var-book-bible: \f647; -$fa-var-bible: \f647; -$fa-var-o: \4f; -$fa-var-suitcase-medical: \f0fa; -$fa-var-medkit: \f0fa; -$fa-var-user-secret: \f21b; -$fa-var-otter: \f700; -$fa-var-person-dress: \f182; -$fa-var-female: \f182; -$fa-var-comment-dollar: \f651; -$fa-var-business-time: \f64a; -$fa-var-briefcase-clock: \f64a; -$fa-var-table-cells-large: \f009; -$fa-var-th-large: \f009; -$fa-var-book-tanakh: \f827; -$fa-var-tanakh: \f827; -$fa-var-phone-volume: \f2a0; -$fa-var-volume-control-phone: \f2a0; -$fa-var-hat-cowboy-side: \f8c1; -$fa-var-clipboard-user: \f7f3; -$fa-var-child: \f1ae; -$fa-var-lira-sign: \f195; -$fa-var-satellite: \f7bf; -$fa-var-plane-lock: \e558; -$fa-var-tag: \f02b; -$fa-var-comment: \f075; -$fa-var-cake-candles: \f1fd; -$fa-var-birthday-cake: \f1fd; -$fa-var-cake: \f1fd; -$fa-var-envelope: \f0e0; -$fa-var-angles-up: \f102; -$fa-var-angle-double-up: \f102; -$fa-var-paperclip: \f0c6; -$fa-var-arrow-right-to-city: \e4b3; -$fa-var-ribbon: \f4d6; -$fa-var-lungs: \f604; -$fa-var-arrow-up-9-1: \f887; -$fa-var-sort-numeric-up-alt: \f887; -$fa-var-litecoin-sign: \e1d3; -$fa-var-border-none: \f850; -$fa-var-circle-nodes: \e4e2; -$fa-var-parachute-box: \f4cd; -$fa-var-indent: \f03c; -$fa-var-truck-field-un: \e58e; -$fa-var-hourglass: \f254; -$fa-var-hourglass-empty: \f254; -$fa-var-mountain: \f6fc; -$fa-var-user-doctor: \f0f0; -$fa-var-user-md: \f0f0; -$fa-var-circle-info: \f05a; -$fa-var-info-circle: \f05a; -$fa-var-cloud-meatball: \f73b; -$fa-var-camera: \f030; -$fa-var-camera-alt: \f030; -$fa-var-square-virus: \e578; -$fa-var-meteor: \f753; -$fa-var-car-on: \e4dd; -$fa-var-sleigh: \f7cc; -$fa-var-arrow-down-1-9: \f162; -$fa-var-sort-numeric-asc: \f162; -$fa-var-sort-numeric-down: \f162; -$fa-var-hand-holding-droplet: \f4c1; -$fa-var-hand-holding-water: \f4c1; -$fa-var-water: \f773; -$fa-var-calendar-check: \f274; -$fa-var-braille: \f2a1; -$fa-var-prescription-bottle-medical: \f486; -$fa-var-prescription-bottle-alt: \f486; -$fa-var-landmark: \f66f; -$fa-var-truck: \f0d1; -$fa-var-crosshairs: \f05b; -$fa-var-person-cane: \e53c; -$fa-var-tent: \e57d; -$fa-var-vest-patches: \e086; -$fa-var-check-double: \f560; -$fa-var-arrow-down-a-z: \f15d; -$fa-var-sort-alpha-asc: \f15d; -$fa-var-sort-alpha-down: \f15d; -$fa-var-money-bill-wheat: \e52a; -$fa-var-cookie: \f563; -$fa-var-arrow-rotate-left: \f0e2; -$fa-var-arrow-left-rotate: \f0e2; -$fa-var-arrow-rotate-back: \f0e2; -$fa-var-arrow-rotate-backward: \f0e2; -$fa-var-undo: \f0e2; -$fa-var-hard-drive: \f0a0; -$fa-var-hdd: \f0a0; -$fa-var-face-grin-squint-tears: \f586; -$fa-var-grin-squint-tears: \f586; -$fa-var-dumbbell: \f44b; -$fa-var-rectangle-list: \f022; -$fa-var-list-alt: \f022; -$fa-var-tarp-droplet: \e57c; -$fa-var-house-medical-circle-check: \e511; -$fa-var-person-skiing-nordic: \f7ca; -$fa-var-skiing-nordic: \f7ca; -$fa-var-calendar-plus: \f271; -$fa-var-plane-arrival: \f5af; -$fa-var-circle-left: \f359; -$fa-var-arrow-alt-circle-left: \f359; -$fa-var-train-subway: \f239; -$fa-var-subway: \f239; -$fa-var-chart-gantt: \e0e4; -$fa-var-indian-rupee-sign: \e1bc; -$fa-var-indian-rupee: \e1bc; -$fa-var-inr: \e1bc; -$fa-var-crop-simple: \f565; -$fa-var-crop-alt: \f565; -$fa-var-money-bill-1: \f3d1; -$fa-var-money-bill-alt: \f3d1; -$fa-var-left-long: \f30a; -$fa-var-long-arrow-alt-left: \f30a; -$fa-var-dna: \f471; -$fa-var-virus-slash: \e075; -$fa-var-minus: \f068; -$fa-var-subtract: \f068; -$fa-var-chess: \f439; -$fa-var-arrow-left-long: \f177; -$fa-var-long-arrow-left: \f177; -$fa-var-plug-circle-check: \e55c; -$fa-var-street-view: \f21d; -$fa-var-franc-sign: \e18f; -$fa-var-volume-off: \f026; -$fa-var-hands-asl-interpreting: \f2a3; -$fa-var-american-sign-language-interpreting: \f2a3; -$fa-var-asl-interpreting: \f2a3; -$fa-var-hands-american-sign-language-interpreting: \f2a3; -$fa-var-gear: \f013; -$fa-var-cog: \f013; -$fa-var-droplet-slash: \f5c7; -$fa-var-tint-slash: \f5c7; -$fa-var-mosque: \f678; -$fa-var-mosquito: \e52b; -$fa-var-star-of-david: \f69a; -$fa-var-person-military-rifle: \e54b; -$fa-var-cart-shopping: \f07a; -$fa-var-shopping-cart: \f07a; -$fa-var-vials: \f493; -$fa-var-plug-circle-plus: \e55f; -$fa-var-place-of-worship: \f67f; -$fa-var-grip-vertical: \f58e; -$fa-var-hexagon-nodes: \e699; -$fa-var-arrow-turn-up: \f148; -$fa-var-level-up: \f148; -$fa-var-u: \55; -$fa-var-square-root-variable: \f698; -$fa-var-square-root-alt: \f698; -$fa-var-clock: \f017; -$fa-var-clock-four: \f017; -$fa-var-backward-step: \f048; -$fa-var-step-backward: \f048; -$fa-var-pallet: \f482; -$fa-var-faucet: \e005; -$fa-var-baseball-bat-ball: \f432; -$fa-var-s: \53; -$fa-var-timeline: \e29c; -$fa-var-keyboard: \f11c; -$fa-var-caret-down: \f0d7; -$fa-var-house-chimney-medical: \f7f2; -$fa-var-clinic-medical: \f7f2; -$fa-var-temperature-three-quarters: \f2c8; -$fa-var-temperature-3: \f2c8; -$fa-var-thermometer-3: \f2c8; -$fa-var-thermometer-three-quarters: \f2c8; -$fa-var-mobile-screen: \f3cf; -$fa-var-mobile-android-alt: \f3cf; -$fa-var-plane-up: \e22d; -$fa-var-piggy-bank: \f4d3; -$fa-var-battery-half: \f242; -$fa-var-battery-3: \f242; -$fa-var-mountain-city: \e52e; -$fa-var-coins: \f51e; -$fa-var-khanda: \f66d; -$fa-var-sliders: \f1de; -$fa-var-sliders-h: \f1de; -$fa-var-folder-tree: \f802; -$fa-var-network-wired: \f6ff; -$fa-var-map-pin: \f276; -$fa-var-hamsa: \f665; -$fa-var-cent-sign: \e3f5; -$fa-var-flask: \f0c3; -$fa-var-person-pregnant: \e31e; -$fa-var-wand-sparkles: \f72b; -$fa-var-ellipsis-vertical: \f142; -$fa-var-ellipsis-v: \f142; -$fa-var-ticket: \f145; -$fa-var-power-off: \f011; -$fa-var-right-long: \f30b; -$fa-var-long-arrow-alt-right: \f30b; -$fa-var-flag-usa: \f74d; -$fa-var-laptop-file: \e51d; -$fa-var-tty: \f1e4; -$fa-var-teletype: \f1e4; -$fa-var-diagram-next: \e476; -$fa-var-person-rifle: \e54e; -$fa-var-house-medical-circle-exclamation: \e512; -$fa-var-closed-captioning: \f20a; -$fa-var-person-hiking: \f6ec; -$fa-var-hiking: \f6ec; -$fa-var-venus-double: \f226; -$fa-var-images: \f302; -$fa-var-calculator: \f1ec; -$fa-var-people-pulling: \e535; -$fa-var-n: \4e; -$fa-var-cable-car: \f7da; -$fa-var-tram: \f7da; -$fa-var-cloud-rain: \f73d; -$fa-var-building-circle-xmark: \e4d4; -$fa-var-ship: \f21a; -$fa-var-arrows-down-to-line: \e4b8; -$fa-var-download: \f019; -$fa-var-face-grin: \f580; -$fa-var-grin: \f580; -$fa-var-delete-left: \f55a; -$fa-var-backspace: \f55a; -$fa-var-eye-dropper: \f1fb; -$fa-var-eye-dropper-empty: \f1fb; -$fa-var-eyedropper: \f1fb; -$fa-var-file-circle-check: \e5a0; -$fa-var-forward: \f04e; -$fa-var-mobile: \f3ce; -$fa-var-mobile-android: \f3ce; -$fa-var-mobile-phone: \f3ce; -$fa-var-face-meh: \f11a; -$fa-var-meh: \f11a; -$fa-var-align-center: \f037; -$fa-var-book-skull: \f6b7; -$fa-var-book-dead: \f6b7; -$fa-var-id-card: \f2c2; -$fa-var-drivers-license: \f2c2; -$fa-var-outdent: \f03b; -$fa-var-dedent: \f03b; -$fa-var-heart-circle-exclamation: \e4fe; -$fa-var-house: \f015; -$fa-var-home: \f015; -$fa-var-home-alt: \f015; -$fa-var-home-lg-alt: \f015; -$fa-var-calendar-week: \f784; -$fa-var-laptop-medical: \f812; -$fa-var-b: \42; -$fa-var-file-medical: \f477; -$fa-var-dice-one: \f525; -$fa-var-kiwi-bird: \f535; -$fa-var-arrow-right-arrow-left: \f0ec; -$fa-var-exchange: \f0ec; -$fa-var-rotate-right: \f2f9; -$fa-var-redo-alt: \f2f9; -$fa-var-rotate-forward: \f2f9; -$fa-var-utensils: \f2e7; -$fa-var-cutlery: \f2e7; -$fa-var-arrow-up-wide-short: \f161; -$fa-var-sort-amount-up: \f161; -$fa-var-mill-sign: \e1ed; -$fa-var-bowl-rice: \e2eb; -$fa-var-skull: \f54c; -$fa-var-tower-broadcast: \f519; -$fa-var-broadcast-tower: \f519; -$fa-var-truck-pickup: \f63c; -$fa-var-up-long: \f30c; -$fa-var-long-arrow-alt-up: \f30c; -$fa-var-stop: \f04d; -$fa-var-code-merge: \f387; -$fa-var-upload: \f093; -$fa-var-hurricane: \f751; -$fa-var-mound: \e52d; -$fa-var-toilet-portable: \e583; -$fa-var-compact-disc: \f51f; -$fa-var-file-arrow-down: \f56d; -$fa-var-file-download: \f56d; -$fa-var-caravan: \f8ff; -$fa-var-shield-cat: \e572; -$fa-var-bolt: \f0e7; -$fa-var-zap: \f0e7; -$fa-var-glass-water: \e4f4; -$fa-var-oil-well: \e532; -$fa-var-vault: \e2c5; -$fa-var-mars: \f222; -$fa-var-toilet: \f7d8; -$fa-var-plane-circle-xmark: \e557; -$fa-var-yen-sign: \f157; -$fa-var-cny: \f157; -$fa-var-jpy: \f157; -$fa-var-rmb: \f157; -$fa-var-yen: \f157; -$fa-var-ruble-sign: \f158; -$fa-var-rouble: \f158; -$fa-var-rub: \f158; -$fa-var-ruble: \f158; -$fa-var-sun: \f185; -$fa-var-guitar: \f7a6; -$fa-var-face-laugh-wink: \f59c; -$fa-var-laugh-wink: \f59c; -$fa-var-horse-head: \f7ab; -$fa-var-bore-hole: \e4c3; -$fa-var-industry: \f275; -$fa-var-circle-down: \f358; -$fa-var-arrow-alt-circle-down: \f358; -$fa-var-arrows-turn-to-dots: \e4c1; -$fa-var-florin-sign: \e184; -$fa-var-arrow-down-short-wide: \f884; -$fa-var-sort-amount-desc: \f884; -$fa-var-sort-amount-down-alt: \f884; -$fa-var-less-than: \3c; -$fa-var-angle-down: \f107; -$fa-var-car-tunnel: \e4de; -$fa-var-head-side-cough: \e061; -$fa-var-grip-lines: \f7a4; -$fa-var-thumbs-down: \f165; -$fa-var-user-lock: \f502; -$fa-var-arrow-right-long: \f178; -$fa-var-long-arrow-right: \f178; -$fa-var-anchor-circle-xmark: \e4ac; -$fa-var-ellipsis: \f141; -$fa-var-ellipsis-h: \f141; -$fa-var-chess-pawn: \f443; -$fa-var-kit-medical: \f479; -$fa-var-first-aid: \f479; -$fa-var-person-through-window: \e5a9; -$fa-var-toolbox: \f552; -$fa-var-hands-holding-circle: \e4fb; -$fa-var-bug: \f188; -$fa-var-credit-card: \f09d; -$fa-var-credit-card-alt: \f09d; -$fa-var-car: \f1b9; -$fa-var-automobile: \f1b9; -$fa-var-hand-holding-hand: \e4f7; -$fa-var-book-open-reader: \f5da; -$fa-var-book-reader: \f5da; -$fa-var-mountain-sun: \e52f; -$fa-var-arrows-left-right-to-line: \e4ba; -$fa-var-dice-d20: \f6cf; -$fa-var-truck-droplet: \e58c; -$fa-var-file-circle-xmark: \e5a1; -$fa-var-temperature-arrow-up: \e040; -$fa-var-temperature-up: \e040; -$fa-var-medal: \f5a2; -$fa-var-bed: \f236; -$fa-var-square-h: \f0fd; -$fa-var-h-square: \f0fd; -$fa-var-podcast: \f2ce; -$fa-var-temperature-full: \f2c7; -$fa-var-temperature-4: \f2c7; -$fa-var-thermometer-4: \f2c7; -$fa-var-thermometer-full: \f2c7; -$fa-var-bell: \f0f3; -$fa-var-superscript: \f12b; -$fa-var-plug-circle-xmark: \e560; -$fa-var-star-of-life: \f621; -$fa-var-phone-slash: \f3dd; -$fa-var-paint-roller: \f5aa; -$fa-var-handshake-angle: \f4c4; -$fa-var-hands-helping: \f4c4; -$fa-var-location-dot: \f3c5; -$fa-var-map-marker-alt: \f3c5; -$fa-var-file: \f15b; -$fa-var-greater-than: \3e; -$fa-var-person-swimming: \f5c4; -$fa-var-swimmer: \f5c4; -$fa-var-arrow-down: \f063; -$fa-var-droplet: \f043; -$fa-var-tint: \f043; -$fa-var-eraser: \f12d; -$fa-var-earth-americas: \f57d; -$fa-var-earth: \f57d; -$fa-var-earth-america: \f57d; -$fa-var-globe-americas: \f57d; -$fa-var-person-burst: \e53b; -$fa-var-dove: \f4ba; -$fa-var-battery-empty: \f244; -$fa-var-battery-0: \f244; -$fa-var-socks: \f696; -$fa-var-inbox: \f01c; -$fa-var-section: \e447; -$fa-var-gauge-high: \f625; -$fa-var-tachometer-alt: \f625; -$fa-var-tachometer-alt-fast: \f625; -$fa-var-envelope-open-text: \f658; -$fa-var-hospital: \f0f8; -$fa-var-hospital-alt: \f0f8; -$fa-var-hospital-wide: \f0f8; -$fa-var-wine-bottle: \f72f; -$fa-var-chess-rook: \f447; -$fa-var-bars-staggered: \f550; -$fa-var-reorder: \f550; -$fa-var-stream: \f550; -$fa-var-dharmachakra: \f655; -$fa-var-hotdog: \f80f; -$fa-var-person-walking-with-cane: \f29d; -$fa-var-blind: \f29d; -$fa-var-drum: \f569; -$fa-var-ice-cream: \f810; -$fa-var-heart-circle-bolt: \e4fc; -$fa-var-fax: \f1ac; -$fa-var-paragraph: \f1dd; -$fa-var-check-to-slot: \f772; -$fa-var-vote-yea: \f772; -$fa-var-star-half: \f089; -$fa-var-boxes-stacked: \f468; -$fa-var-boxes: \f468; -$fa-var-boxes-alt: \f468; -$fa-var-link: \f0c1; -$fa-var-chain: \f0c1; -$fa-var-ear-listen: \f2a2; -$fa-var-assistive-listening-systems: \f2a2; -$fa-var-tree-city: \e587; -$fa-var-play: \f04b; -$fa-var-font: \f031; -$fa-var-table-cells-row-lock: \e67a; -$fa-var-rupiah-sign: \e23d; -$fa-var-magnifying-glass: \f002; -$fa-var-search: \f002; -$fa-var-table-tennis-paddle-ball: \f45d; -$fa-var-ping-pong-paddle-ball: \f45d; -$fa-var-table-tennis: \f45d; -$fa-var-person-dots-from-line: \f470; -$fa-var-diagnoses: \f470; -$fa-var-trash-can-arrow-up: \f82a; -$fa-var-trash-restore-alt: \f82a; -$fa-var-naira-sign: \e1f6; -$fa-var-cart-arrow-down: \f218; -$fa-var-walkie-talkie: \f8ef; -$fa-var-file-pen: \f31c; -$fa-var-file-edit: \f31c; -$fa-var-receipt: \f543; -$fa-var-square-pen: \f14b; -$fa-var-pen-square: \f14b; -$fa-var-pencil-square: \f14b; -$fa-var-suitcase-rolling: \f5c1; -$fa-var-person-circle-exclamation: \e53f; -$fa-var-chevron-down: \f078; -$fa-var-battery-full: \f240; -$fa-var-battery: \f240; -$fa-var-battery-5: \f240; -$fa-var-skull-crossbones: \f714; -$fa-var-code-compare: \e13a; -$fa-var-list-ul: \f0ca; -$fa-var-list-dots: \f0ca; -$fa-var-school-lock: \e56f; -$fa-var-tower-cell: \e585; -$fa-var-down-long: \f309; -$fa-var-long-arrow-alt-down: \f309; -$fa-var-ranking-star: \e561; -$fa-var-chess-king: \f43f; -$fa-var-person-harassing: \e549; -$fa-var-brazilian-real-sign: \e46c; -$fa-var-landmark-dome: \f752; -$fa-var-landmark-alt: \f752; -$fa-var-arrow-up: \f062; -$fa-var-tv: \f26c; -$fa-var-television: \f26c; -$fa-var-tv-alt: \f26c; -$fa-var-shrimp: \e448; -$fa-var-list-check: \f0ae; -$fa-var-tasks: \f0ae; -$fa-var-jug-detergent: \e519; -$fa-var-circle-user: \f2bd; -$fa-var-user-circle: \f2bd; -$fa-var-user-shield: \f505; -$fa-var-wind: \f72e; -$fa-var-car-burst: \f5e1; -$fa-var-car-crash: \f5e1; -$fa-var-y: \59; -$fa-var-person-snowboarding: \f7ce; -$fa-var-snowboarding: \f7ce; -$fa-var-truck-fast: \f48b; -$fa-var-shipping-fast: \f48b; -$fa-var-fish: \f578; -$fa-var-user-graduate: \f501; -$fa-var-circle-half-stroke: \f042; -$fa-var-adjust: \f042; -$fa-var-clapperboard: \e131; -$fa-var-circle-radiation: \f7ba; -$fa-var-radiation-alt: \f7ba; -$fa-var-baseball: \f433; -$fa-var-baseball-ball: \f433; -$fa-var-jet-fighter-up: \e518; -$fa-var-diagram-project: \f542; -$fa-var-project-diagram: \f542; -$fa-var-copy: \f0c5; -$fa-var-volume-xmark: \f6a9; -$fa-var-volume-mute: \f6a9; -$fa-var-volume-times: \f6a9; -$fa-var-hand-sparkles: \e05d; -$fa-var-grip: \f58d; -$fa-var-grip-horizontal: \f58d; -$fa-var-share-from-square: \f14d; -$fa-var-share-square: \f14d; -$fa-var-child-combatant: \e4e0; -$fa-var-child-rifle: \e4e0; -$fa-var-gun: \e19b; -$fa-var-square-phone: \f098; -$fa-var-phone-square: \f098; -$fa-var-plus: \2b; -$fa-var-add: \2b; -$fa-var-expand: \f065; -$fa-var-computer: \e4e5; -$fa-var-xmark: \f00d; -$fa-var-close: \f00d; -$fa-var-multiply: \f00d; -$fa-var-remove: \f00d; -$fa-var-times: \f00d; -$fa-var-arrows-up-down-left-right: \f047; -$fa-var-arrows: \f047; -$fa-var-chalkboard-user: \f51c; -$fa-var-chalkboard-teacher: \f51c; -$fa-var-peso-sign: \e222; -$fa-var-building-shield: \e4d8; -$fa-var-baby: \f77c; -$fa-var-users-line: \e592; -$fa-var-quote-left: \f10d; -$fa-var-quote-left-alt: \f10d; -$fa-var-tractor: \f722; -$fa-var-trash-arrow-up: \f829; -$fa-var-trash-restore: \f829; -$fa-var-arrow-down-up-lock: \e4b0; -$fa-var-lines-leaning: \e51e; -$fa-var-ruler-combined: \f546; -$fa-var-copyright: \f1f9; -$fa-var-equals: \3d; -$fa-var-blender: \f517; -$fa-var-teeth: \f62e; -$fa-var-shekel-sign: \f20b; -$fa-var-ils: \f20b; -$fa-var-shekel: \f20b; -$fa-var-sheqel: \f20b; -$fa-var-sheqel-sign: \f20b; -$fa-var-map: \f279; -$fa-var-rocket: \f135; -$fa-var-photo-film: \f87c; -$fa-var-photo-video: \f87c; -$fa-var-folder-minus: \f65d; -$fa-var-hexagon-nodes-bolt: \e69a; -$fa-var-store: \f54e; -$fa-var-arrow-trend-up: \e098; -$fa-var-plug-circle-minus: \e55e; -$fa-var-sign-hanging: \f4d9; -$fa-var-sign: \f4d9; -$fa-var-bezier-curve: \f55b; -$fa-var-bell-slash: \f1f6; -$fa-var-tablet: \f3fb; -$fa-var-tablet-android: \f3fb; -$fa-var-school-flag: \e56e; -$fa-var-fill: \f575; -$fa-var-angle-up: \f106; -$fa-var-drumstick-bite: \f6d7; -$fa-var-holly-berry: \f7aa; -$fa-var-chevron-left: \f053; -$fa-var-bacteria: \e059; -$fa-var-hand-lizard: \f258; -$fa-var-notdef: \e1fe; -$fa-var-disease: \f7fa; -$fa-var-briefcase-medical: \f469; -$fa-var-genderless: \f22d; -$fa-var-chevron-right: \f054; -$fa-var-retweet: \f079; -$fa-var-car-rear: \f5de; -$fa-var-car-alt: \f5de; -$fa-var-pump-soap: \e06b; -$fa-var-video-slash: \f4e2; -$fa-var-battery-quarter: \f243; -$fa-var-battery-2: \f243; -$fa-var-radio: \f8d7; -$fa-var-baby-carriage: \f77d; -$fa-var-carriage-baby: \f77d; -$fa-var-traffic-light: \f637; -$fa-var-thermometer: \f491; -$fa-var-vr-cardboard: \f729; -$fa-var-hand-middle-finger: \f806; -$fa-var-percent: \25; -$fa-var-percentage: \25; -$fa-var-truck-moving: \f4df; -$fa-var-glass-water-droplet: \e4f5; -$fa-var-display: \e163; -$fa-var-face-smile: \f118; -$fa-var-smile: \f118; -$fa-var-thumbtack: \f08d; -$fa-var-thumb-tack: \f08d; -$fa-var-trophy: \f091; -$fa-var-person-praying: \f683; -$fa-var-pray: \f683; -$fa-var-hammer: \f6e3; -$fa-var-hand-peace: \f25b; -$fa-var-rotate: \f2f1; -$fa-var-sync-alt: \f2f1; -$fa-var-spinner: \f110; -$fa-var-robot: \f544; -$fa-var-peace: \f67c; -$fa-var-gears: \f085; -$fa-var-cogs: \f085; -$fa-var-warehouse: \f494; -$fa-var-arrow-up-right-dots: \e4b7; -$fa-var-splotch: \f5bc; -$fa-var-face-grin-hearts: \f584; -$fa-var-grin-hearts: \f584; -$fa-var-dice-four: \f524; -$fa-var-sim-card: \f7c4; -$fa-var-transgender: \f225; -$fa-var-transgender-alt: \f225; -$fa-var-mercury: \f223; -$fa-var-arrow-turn-down: \f149; -$fa-var-level-down: \f149; -$fa-var-person-falling-burst: \e547; -$fa-var-award: \f559; -$fa-var-ticket-simple: \f3ff; -$fa-var-ticket-alt: \f3ff; -$fa-var-building: \f1ad; -$fa-var-angles-left: \f100; -$fa-var-angle-double-left: \f100; -$fa-var-qrcode: \f029; -$fa-var-clock-rotate-left: \f1da; -$fa-var-history: \f1da; -$fa-var-face-grin-beam-sweat: \f583; -$fa-var-grin-beam-sweat: \f583; -$fa-var-file-export: \f56e; -$fa-var-arrow-right-from-file: \f56e; -$fa-var-shield: \f132; -$fa-var-shield-blank: \f132; -$fa-var-arrow-up-short-wide: \f885; -$fa-var-sort-amount-up-alt: \f885; -$fa-var-comment-nodes: \e696; -$fa-var-house-medical: \e3b2; -$fa-var-golf-ball-tee: \f450; -$fa-var-golf-ball: \f450; -$fa-var-circle-chevron-left: \f137; -$fa-var-chevron-circle-left: \f137; -$fa-var-house-chimney-window: \e00d; -$fa-var-pen-nib: \f5ad; -$fa-var-tent-arrow-turn-left: \e580; -$fa-var-tents: \e582; -$fa-var-wand-magic: \f0d0; -$fa-var-magic: \f0d0; -$fa-var-dog: \f6d3; -$fa-var-carrot: \f787; -$fa-var-moon: \f186; -$fa-var-wine-glass-empty: \f5ce; -$fa-var-wine-glass-alt: \f5ce; -$fa-var-cheese: \f7ef; -$fa-var-yin-yang: \f6ad; -$fa-var-music: \f001; -$fa-var-code-commit: \f386; -$fa-var-temperature-low: \f76b; -$fa-var-person-biking: \f84a; -$fa-var-biking: \f84a; -$fa-var-broom: \f51a; -$fa-var-shield-heart: \e574; -$fa-var-gopuram: \f664; -$fa-var-earth-oceania: \e47b; -$fa-var-globe-oceania: \e47b; -$fa-var-square-xmark: \f2d3; -$fa-var-times-square: \f2d3; -$fa-var-xmark-square: \f2d3; -$fa-var-hashtag: \23; -$fa-var-up-right-and-down-left-from-center: \f424; -$fa-var-expand-alt: \f424; -$fa-var-oil-can: \f613; -$fa-var-t: \54; -$fa-var-hippo: \f6ed; -$fa-var-chart-column: \e0e3; -$fa-var-infinity: \f534; -$fa-var-vial-circle-check: \e596; -$fa-var-person-arrow-down-to-line: \e538; -$fa-var-voicemail: \f897; -$fa-var-fan: \f863; -$fa-var-person-walking-luggage: \e554; -$fa-var-up-down: \f338; -$fa-var-arrows-alt-v: \f338; -$fa-var-cloud-moon-rain: \f73c; -$fa-var-calendar: \f133; -$fa-var-trailer: \e041; -$fa-var-bahai: \f666; -$fa-var-haykal: \f666; -$fa-var-sd-card: \f7c2; -$fa-var-dragon: \f6d5; -$fa-var-shoe-prints: \f54b; -$fa-var-circle-plus: \f055; -$fa-var-plus-circle: \f055; -$fa-var-face-grin-tongue-wink: \f58b; -$fa-var-grin-tongue-wink: \f58b; -$fa-var-hand-holding: \f4bd; -$fa-var-plug-circle-exclamation: \e55d; -$fa-var-link-slash: \f127; -$fa-var-chain-broken: \f127; -$fa-var-chain-slash: \f127; -$fa-var-unlink: \f127; -$fa-var-clone: \f24d; -$fa-var-person-walking-arrow-loop-left: \e551; -$fa-var-arrow-up-z-a: \f882; -$fa-var-sort-alpha-up-alt: \f882; -$fa-var-fire-flame-curved: \f7e4; -$fa-var-fire-alt: \f7e4; -$fa-var-tornado: \f76f; -$fa-var-file-circle-plus: \e494; -$fa-var-book-quran: \f687; -$fa-var-quran: \f687; -$fa-var-anchor: \f13d; -$fa-var-border-all: \f84c; -$fa-var-face-angry: \f556; -$fa-var-angry: \f556; -$fa-var-cookie-bite: \f564; -$fa-var-arrow-trend-down: \e097; -$fa-var-rss: \f09e; -$fa-var-feed: \f09e; -$fa-var-draw-polygon: \f5ee; -$fa-var-scale-balanced: \f24e; -$fa-var-balance-scale: \f24e; -$fa-var-gauge-simple-high: \f62a; -$fa-var-tachometer: \f62a; -$fa-var-tachometer-fast: \f62a; -$fa-var-shower: \f2cc; -$fa-var-desktop: \f390; -$fa-var-desktop-alt: \f390; -$fa-var-m: \4d; -$fa-var-table-list: \f00b; -$fa-var-th-list: \f00b; -$fa-var-comment-sms: \f7cd; -$fa-var-sms: \f7cd; -$fa-var-book: \f02d; -$fa-var-user-plus: \f234; -$fa-var-check: \f00c; -$fa-var-battery-three-quarters: \f241; -$fa-var-battery-4: \f241; -$fa-var-house-circle-check: \e509; -$fa-var-angle-left: \f104; -$fa-var-diagram-successor: \e47a; -$fa-var-truck-arrow-right: \e58b; -$fa-var-arrows-split-up-and-left: \e4bc; -$fa-var-hand-fist: \f6de; -$fa-var-fist-raised: \f6de; -$fa-var-cloud-moon: \f6c3; -$fa-var-briefcase: \f0b1; -$fa-var-person-falling: \e546; -$fa-var-image-portrait: \f3e0; -$fa-var-portrait: \f3e0; -$fa-var-user-tag: \f507; -$fa-var-rug: \e569; -$fa-var-earth-europe: \f7a2; -$fa-var-globe-europe: \f7a2; -$fa-var-cart-flatbed-suitcase: \f59d; -$fa-var-luggage-cart: \f59d; -$fa-var-rectangle-xmark: \f410; -$fa-var-rectangle-times: \f410; -$fa-var-times-rectangle: \f410; -$fa-var-window-close: \f410; -$fa-var-baht-sign: \e0ac; -$fa-var-book-open: \f518; -$fa-var-book-journal-whills: \f66a; -$fa-var-journal-whills: \f66a; -$fa-var-handcuffs: \e4f8; -$fa-var-triangle-exclamation: \f071; -$fa-var-exclamation-triangle: \f071; -$fa-var-warning: \f071; -$fa-var-database: \f1c0; -$fa-var-share: \f064; -$fa-var-mail-forward: \f064; -$fa-var-bottle-droplet: \e4c4; -$fa-var-mask-face: \e1d7; -$fa-var-hill-rockslide: \e508; -$fa-var-right-left: \f362; -$fa-var-exchange-alt: \f362; -$fa-var-paper-plane: \f1d8; -$fa-var-road-circle-exclamation: \e565; -$fa-var-dungeon: \f6d9; -$fa-var-align-right: \f038; -$fa-var-money-bill-1-wave: \f53b; -$fa-var-money-bill-wave-alt: \f53b; -$fa-var-life-ring: \f1cd; -$fa-var-hands: \f2a7; -$fa-var-sign-language: \f2a7; -$fa-var-signing: \f2a7; -$fa-var-calendar-day: \f783; -$fa-var-water-ladder: \f5c5; -$fa-var-ladder-water: \f5c5; -$fa-var-swimming-pool: \f5c5; -$fa-var-arrows-up-down: \f07d; -$fa-var-arrows-v: \f07d; -$fa-var-face-grimace: \f57f; -$fa-var-grimace: \f57f; -$fa-var-wheelchair-move: \e2ce; -$fa-var-wheelchair-alt: \e2ce; -$fa-var-turn-down: \f3be; -$fa-var-level-down-alt: \f3be; -$fa-var-person-walking-arrow-right: \e552; -$fa-var-square-envelope: \f199; -$fa-var-envelope-square: \f199; -$fa-var-dice: \f522; -$fa-var-bowling-ball: \f436; -$fa-var-brain: \f5dc; -$fa-var-bandage: \f462; -$fa-var-band-aid: \f462; -$fa-var-calendar-minus: \f272; -$fa-var-circle-xmark: \f057; -$fa-var-times-circle: \f057; -$fa-var-xmark-circle: \f057; -$fa-var-gifts: \f79c; -$fa-var-hotel: \f594; -$fa-var-earth-asia: \f57e; -$fa-var-globe-asia: \f57e; -$fa-var-id-card-clip: \f47f; -$fa-var-id-card-alt: \f47f; -$fa-var-magnifying-glass-plus: \f00e; -$fa-var-search-plus: \f00e; -$fa-var-thumbs-up: \f164; -$fa-var-user-clock: \f4fd; -$fa-var-hand-dots: \f461; -$fa-var-allergies: \f461; -$fa-var-file-invoice: \f570; -$fa-var-window-minimize: \f2d1; -$fa-var-mug-saucer: \f0f4; -$fa-var-coffee: \f0f4; -$fa-var-brush: \f55d; -$fa-var-file-half-dashed: \e698; -$fa-var-mask: \f6fa; -$fa-var-magnifying-glass-minus: \f010; -$fa-var-search-minus: \f010; -$fa-var-ruler-vertical: \f548; -$fa-var-user-large: \f406; -$fa-var-user-alt: \f406; -$fa-var-train-tram: \e5b4; -$fa-var-user-nurse: \f82f; -$fa-var-syringe: \f48e; -$fa-var-cloud-sun: \f6c4; -$fa-var-stopwatch-20: \e06f; -$fa-var-square-full: \f45c; -$fa-var-magnet: \f076; -$fa-var-jar: \e516; -$fa-var-note-sticky: \f249; -$fa-var-sticky-note: \f249; -$fa-var-bug-slash: \e490; -$fa-var-arrow-up-from-water-pump: \e4b6; -$fa-var-bone: \f5d7; -$fa-var-table-cells-row-unlock: \e691; -$fa-var-user-injured: \f728; -$fa-var-face-sad-tear: \f5b4; -$fa-var-sad-tear: \f5b4; -$fa-var-plane: \f072; -$fa-var-tent-arrows-down: \e581; -$fa-var-exclamation: \21; -$fa-var-arrows-spin: \e4bb; -$fa-var-print: \f02f; -$fa-var-turkish-lira-sign: \e2bb; -$fa-var-try: \e2bb; -$fa-var-turkish-lira: \e2bb; -$fa-var-dollar-sign: \24; -$fa-var-dollar: \24; -$fa-var-usd: \24; -$fa-var-x: \58; -$fa-var-magnifying-glass-dollar: \f688; -$fa-var-search-dollar: \f688; -$fa-var-users-gear: \f509; -$fa-var-users-cog: \f509; -$fa-var-person-military-pointing: \e54a; -$fa-var-building-columns: \f19c; -$fa-var-bank: \f19c; -$fa-var-institution: \f19c; -$fa-var-museum: \f19c; -$fa-var-university: \f19c; -$fa-var-umbrella: \f0e9; -$fa-var-trowel: \e589; -$fa-var-d: \44; -$fa-var-stapler: \e5af; -$fa-var-masks-theater: \f630; -$fa-var-theater-masks: \f630; -$fa-var-kip-sign: \e1c4; -$fa-var-hand-point-left: \f0a5; -$fa-var-handshake-simple: \f4c6; -$fa-var-handshake-alt: \f4c6; -$fa-var-jet-fighter: \f0fb; -$fa-var-fighter-jet: \f0fb; -$fa-var-square-share-nodes: \f1e1; -$fa-var-share-alt-square: \f1e1; -$fa-var-barcode: \f02a; -$fa-var-plus-minus: \e43c; -$fa-var-video: \f03d; -$fa-var-video-camera: \f03d; -$fa-var-graduation-cap: \f19d; -$fa-var-mortar-board: \f19d; -$fa-var-hand-holding-medical: \e05c; -$fa-var-person-circle-check: \e53e; -$fa-var-turn-up: \f3bf; -$fa-var-level-up-alt: \f3bf; - -$fa-var-monero: \f3d0; -$fa-var-hooli: \f427; -$fa-var-yelp: \f1e9; -$fa-var-cc-visa: \f1f0; -$fa-var-lastfm: \f202; -$fa-var-shopware: \f5b5; -$fa-var-creative-commons-nc: \f4e8; -$fa-var-aws: \f375; -$fa-var-redhat: \f7bc; -$fa-var-yoast: \f2b1; -$fa-var-cloudflare: \e07d; -$fa-var-ups: \f7e0; -$fa-var-pixiv: \e640; -$fa-var-wpexplorer: \f2de; -$fa-var-dyalog: \f399; -$fa-var-bity: \f37a; -$fa-var-stackpath: \f842; -$fa-var-buysellads: \f20d; -$fa-var-first-order: \f2b0; -$fa-var-modx: \f285; -$fa-var-guilded: \e07e; -$fa-var-vnv: \f40b; -$fa-var-square-js: \f3b9; -$fa-var-js-square: \f3b9; -$fa-var-microsoft: \f3ca; -$fa-var-qq: \f1d6; -$fa-var-orcid: \f8d2; -$fa-var-java: \f4e4; -$fa-var-invision: \f7b0; -$fa-var-creative-commons-pd-alt: \f4ed; -$fa-var-centercode: \f380; -$fa-var-glide-g: \f2a6; -$fa-var-drupal: \f1a9; -$fa-var-jxl: \e67b; -$fa-var-dart-lang: \e693; -$fa-var-hire-a-helper: \f3b0; -$fa-var-creative-commons-by: \f4e7; -$fa-var-unity: \e049; -$fa-var-whmcs: \f40d; -$fa-var-rocketchat: \f3e8; -$fa-var-vk: \f189; -$fa-var-untappd: \f405; -$fa-var-mailchimp: \f59e; -$fa-var-css3-alt: \f38b; -$fa-var-square-reddit: \f1a2; -$fa-var-reddit-square: \f1a2; -$fa-var-vimeo-v: \f27d; -$fa-var-contao: \f26d; -$fa-var-square-font-awesome: \e5ad; -$fa-var-deskpro: \f38f; -$fa-var-brave: \e63c; -$fa-var-sistrix: \f3ee; -$fa-var-square-instagram: \e055; -$fa-var-instagram-square: \e055; -$fa-var-battle-net: \f835; -$fa-var-the-red-yeti: \f69d; -$fa-var-square-hacker-news: \f3af; -$fa-var-hacker-news-square: \f3af; -$fa-var-edge: \f282; -$fa-var-threads: \e618; -$fa-var-napster: \f3d2; -$fa-var-square-snapchat: \f2ad; -$fa-var-snapchat-square: \f2ad; -$fa-var-google-plus-g: \f0d5; -$fa-var-artstation: \f77a; -$fa-var-markdown: \f60f; -$fa-var-sourcetree: \f7d3; -$fa-var-google-plus: \f2b3; -$fa-var-diaspora: \f791; -$fa-var-foursquare: \f180; -$fa-var-stack-overflow: \f16c; -$fa-var-github-alt: \f113; -$fa-var-phoenix-squadron: \f511; -$fa-var-pagelines: \f18c; -$fa-var-algolia: \f36c; -$fa-var-red-river: \f3e3; -$fa-var-creative-commons-sa: \f4ef; -$fa-var-safari: \f267; -$fa-var-google: \f1a0; -$fa-var-square-font-awesome-stroke: \f35c; -$fa-var-font-awesome-alt: \f35c; -$fa-var-atlassian: \f77b; -$fa-var-linkedin-in: \f0e1; -$fa-var-digital-ocean: \f391; -$fa-var-nimblr: \f5a8; -$fa-var-chromecast: \f838; -$fa-var-evernote: \f839; -$fa-var-hacker-news: \f1d4; -$fa-var-creative-commons-sampling: \f4f0; -$fa-var-adversal: \f36a; -$fa-var-creative-commons: \f25e; -$fa-var-watchman-monitoring: \e087; -$fa-var-fonticons: \f280; -$fa-var-weixin: \f1d7; -$fa-var-shirtsinbulk: \f214; -$fa-var-codepen: \f1cb; -$fa-var-git-alt: \f841; -$fa-var-lyft: \f3c3; -$fa-var-rev: \f5b2; -$fa-var-windows: \f17a; -$fa-var-wizards-of-the-coast: \f730; -$fa-var-square-viadeo: \f2aa; -$fa-var-viadeo-square: \f2aa; -$fa-var-meetup: \f2e0; -$fa-var-centos: \f789; -$fa-var-adn: \f170; -$fa-var-cloudsmith: \f384; -$fa-var-opensuse: \e62b; -$fa-var-pied-piper-alt: \f1a8; -$fa-var-square-dribbble: \f397; -$fa-var-dribbble-square: \f397; -$fa-var-codiepie: \f284; -$fa-var-node: \f419; -$fa-var-mix: \f3cb; -$fa-var-steam: \f1b6; -$fa-var-cc-apple-pay: \f416; -$fa-var-scribd: \f28a; -$fa-var-debian: \e60b; -$fa-var-openid: \f19b; -$fa-var-instalod: \e081; -$fa-var-files-pinwheel: \e69f; -$fa-var-expeditedssl: \f23e; -$fa-var-sellcast: \f2da; -$fa-var-square-twitter: \f081; -$fa-var-twitter-square: \f081; -$fa-var-r-project: \f4f7; -$fa-var-delicious: \f1a5; -$fa-var-freebsd: \f3a4; -$fa-var-vuejs: \f41f; -$fa-var-accusoft: \f369; -$fa-var-ioxhost: \f208; -$fa-var-fonticons-fi: \f3a2; -$fa-var-app-store: \f36f; -$fa-var-cc-mastercard: \f1f1; -$fa-var-itunes-note: \f3b5; -$fa-var-golang: \e40f; -$fa-var-kickstarter: \f3bb; -$fa-var-square-kickstarter: \f3bb; -$fa-var-grav: \f2d6; -$fa-var-weibo: \f18a; -$fa-var-uncharted: \e084; -$fa-var-firstdraft: \f3a1; -$fa-var-square-youtube: \f431; -$fa-var-youtube-square: \f431; -$fa-var-wikipedia-w: \f266; -$fa-var-wpressr: \f3e4; -$fa-var-rendact: \f3e4; -$fa-var-angellist: \f209; -$fa-var-galactic-republic: \f50c; -$fa-var-nfc-directional: \e530; -$fa-var-skype: \f17e; -$fa-var-joget: \f3b7; -$fa-var-fedora: \f798; -$fa-var-stripe-s: \f42a; -$fa-var-meta: \e49b; -$fa-var-laravel: \f3bd; -$fa-var-hotjar: \f3b1; -$fa-var-bluetooth-b: \f294; -$fa-var-square-letterboxd: \e62e; -$fa-var-sticker-mule: \f3f7; -$fa-var-creative-commons-zero: \f4f3; -$fa-var-hips: \f452; -$fa-var-css: \e6a2; -$fa-var-behance: \f1b4; -$fa-var-reddit: \f1a1; -$fa-var-discord: \f392; -$fa-var-chrome: \f268; -$fa-var-app-store-ios: \f370; -$fa-var-cc-discover: \f1f2; -$fa-var-wpbeginner: \f297; -$fa-var-confluence: \f78d; -$fa-var-shoelace: \e60c; -$fa-var-mdb: \f8ca; -$fa-var-dochub: \f394; -$fa-var-accessible-icon: \f368; -$fa-var-ebay: \f4f4; -$fa-var-amazon: \f270; -$fa-var-unsplash: \e07c; -$fa-var-yarn: \f7e3; -$fa-var-square-steam: \f1b7; -$fa-var-steam-square: \f1b7; -$fa-var-500px: \f26e; -$fa-var-square-vimeo: \f194; -$fa-var-vimeo-square: \f194; -$fa-var-asymmetrik: \f372; -$fa-var-font-awesome: \f2b4; -$fa-var-font-awesome-flag: \f2b4; -$fa-var-font-awesome-logo-full: \f2b4; -$fa-var-gratipay: \f184; -$fa-var-apple: \f179; -$fa-var-hive: \e07f; -$fa-var-gitkraken: \f3a6; -$fa-var-keybase: \f4f5; -$fa-var-apple-pay: \f415; -$fa-var-padlet: \e4a0; -$fa-var-amazon-pay: \f42c; -$fa-var-square-github: \f092; -$fa-var-github-square: \f092; -$fa-var-stumbleupon: \f1a4; -$fa-var-fedex: \f797; -$fa-var-phoenix-framework: \f3dc; -$fa-var-shopify: \e057; -$fa-var-neos: \f612; -$fa-var-square-threads: \e619; -$fa-var-hackerrank: \f5f7; -$fa-var-researchgate: \f4f8; -$fa-var-swift: \f8e1; -$fa-var-angular: \f420; -$fa-var-speakap: \f3f3; -$fa-var-angrycreative: \f36e; -$fa-var-y-combinator: \f23b; -$fa-var-empire: \f1d1; -$fa-var-envira: \f299; -$fa-var-google-scholar: \e63b; -$fa-var-square-gitlab: \e5ae; -$fa-var-gitlab-square: \e5ae; -$fa-var-studiovinari: \f3f8; -$fa-var-pied-piper: \f2ae; -$fa-var-wordpress: \f19a; -$fa-var-product-hunt: \f288; -$fa-var-firefox: \f269; -$fa-var-linode: \f2b8; -$fa-var-goodreads: \f3a8; -$fa-var-square-odnoklassniki: \f264; -$fa-var-odnoklassniki-square: \f264; -$fa-var-jsfiddle: \f1cc; -$fa-var-sith: \f512; -$fa-var-themeisle: \f2b2; -$fa-var-page4: \f3d7; -$fa-var-hashnode: \e499; -$fa-var-react: \f41b; -$fa-var-cc-paypal: \f1f4; -$fa-var-squarespace: \f5be; -$fa-var-cc-stripe: \f1f5; -$fa-var-creative-commons-share: \f4f2; -$fa-var-bitcoin: \f379; -$fa-var-keycdn: \f3ba; -$fa-var-opera: \f26a; -$fa-var-itch-io: \f83a; -$fa-var-umbraco: \f8e8; -$fa-var-galactic-senate: \f50d; -$fa-var-ubuntu: \f7df; -$fa-var-draft2digital: \f396; -$fa-var-stripe: \f429; -$fa-var-houzz: \f27c; -$fa-var-gg: \f260; -$fa-var-dhl: \f790; -$fa-var-square-pinterest: \f0d3; -$fa-var-pinterest-square: \f0d3; -$fa-var-xing: \f168; -$fa-var-blackberry: \f37b; -$fa-var-creative-commons-pd: \f4ec; -$fa-var-playstation: \f3df; -$fa-var-quinscape: \f459; -$fa-var-less: \f41d; -$fa-var-blogger-b: \f37d; -$fa-var-opencart: \f23d; -$fa-var-vine: \f1ca; -$fa-var-signal-messenger: \e663; -$fa-var-paypal: \f1ed; -$fa-var-gitlab: \f296; -$fa-var-typo3: \f42b; -$fa-var-reddit-alien: \f281; -$fa-var-yahoo: \f19e; -$fa-var-dailymotion: \e052; -$fa-var-affiliatetheme: \f36b; -$fa-var-pied-piper-pp: \f1a7; -$fa-var-bootstrap: \f836; -$fa-var-odnoklassniki: \f263; -$fa-var-nfc-symbol: \e531; -$fa-var-mintbit: \e62f; -$fa-var-ethereum: \f42e; -$fa-var-speaker-deck: \f83c; -$fa-var-creative-commons-nc-eu: \f4e9; -$fa-var-patreon: \f3d9; -$fa-var-avianex: \f374; -$fa-var-ello: \f5f1; -$fa-var-gofore: \f3a7; -$fa-var-bimobject: \f378; -$fa-var-brave-reverse: \e63d; -$fa-var-facebook-f: \f39e; -$fa-var-square-google-plus: \f0d4; -$fa-var-google-plus-square: \f0d4; -$fa-var-web-awesome: \e682; -$fa-var-mandalorian: \f50f; -$fa-var-first-order-alt: \f50a; -$fa-var-osi: \f41a; -$fa-var-google-wallet: \f1ee; -$fa-var-d-and-d-beyond: \f6ca; -$fa-var-periscope: \f3da; -$fa-var-fulcrum: \f50b; -$fa-var-cloudscale: \f383; -$fa-var-forumbee: \f211; -$fa-var-mizuni: \f3cc; -$fa-var-schlix: \f3ea; -$fa-var-square-xing: \f169; -$fa-var-xing-square: \f169; -$fa-var-bandcamp: \f2d5; -$fa-var-wpforms: \f298; -$fa-var-cloudversify: \f385; -$fa-var-usps: \f7e1; -$fa-var-megaport: \f5a3; -$fa-var-magento: \f3c4; -$fa-var-spotify: \f1bc; -$fa-var-optin-monster: \f23c; -$fa-var-fly: \f417; -$fa-var-square-bluesky: \e6a3; -$fa-var-aviato: \f421; -$fa-var-itunes: \f3b4; -$fa-var-cuttlefish: \f38c; -$fa-var-blogger: \f37c; -$fa-var-flickr: \f16e; -$fa-var-viber: \f409; -$fa-var-soundcloud: \f1be; -$fa-var-digg: \f1a6; -$fa-var-tencent-weibo: \f1d5; -$fa-var-letterboxd: \e62d; -$fa-var-symfony: \f83d; -$fa-var-maxcdn: \f136; -$fa-var-etsy: \f2d7; -$fa-var-facebook-messenger: \f39f; -$fa-var-audible: \f373; -$fa-var-think-peaks: \f731; -$fa-var-bilibili: \e3d9; -$fa-var-erlang: \f39d; -$fa-var-x-twitter: \e61b; -$fa-var-cotton-bureau: \f89e; -$fa-var-dashcube: \f210; -$fa-var-42-group: \e080; -$fa-var-innosoft: \e080; -$fa-var-stack-exchange: \f18d; -$fa-var-elementor: \f430; -$fa-var-square-pied-piper: \e01e; -$fa-var-pied-piper-square: \e01e; -$fa-var-creative-commons-nd: \f4eb; -$fa-var-palfed: \f3d8; -$fa-var-superpowers: \f2dd; -$fa-var-resolving: \f3e7; -$fa-var-xbox: \f412; -$fa-var-square-web-awesome-stroke: \e684; -$fa-var-searchengin: \f3eb; -$fa-var-tiktok: \e07b; -$fa-var-square-facebook: \f082; -$fa-var-facebook-square: \f082; -$fa-var-renren: \f18b; -$fa-var-linux: \f17c; -$fa-var-glide: \f2a5; -$fa-var-linkedin: \f08c; -$fa-var-hubspot: \f3b2; -$fa-var-deploydog: \f38e; -$fa-var-twitch: \f1e8; -$fa-var-flutter: \e694; -$fa-var-ravelry: \f2d9; -$fa-var-mixer: \e056; -$fa-var-square-lastfm: \f203; -$fa-var-lastfm-square: \f203; -$fa-var-vimeo: \f40a; -$fa-var-mendeley: \f7b3; -$fa-var-uniregistry: \f404; -$fa-var-figma: \f799; -$fa-var-creative-commons-remix: \f4ee; -$fa-var-cc-amazon-pay: \f42d; -$fa-var-dropbox: \f16b; -$fa-var-instagram: \f16d; -$fa-var-cmplid: \e360; -$fa-var-upwork: \e641; -$fa-var-facebook: \f09a; -$fa-var-gripfire: \f3ac; -$fa-var-jedi-order: \f50e; -$fa-var-uikit: \f403; -$fa-var-fort-awesome-alt: \f3a3; -$fa-var-phabricator: \f3db; -$fa-var-ussunnah: \f407; -$fa-var-earlybirds: \f39a; -$fa-var-trade-federation: \f513; -$fa-var-autoprefixer: \f41c; -$fa-var-whatsapp: \f232; -$fa-var-square-upwork: \e67c; -$fa-var-slideshare: \f1e7; -$fa-var-google-play: \f3ab; -$fa-var-viadeo: \f2a9; -$fa-var-line: \f3c0; -$fa-var-google-drive: \f3aa; -$fa-var-servicestack: \f3ec; -$fa-var-simplybuilt: \f215; -$fa-var-bitbucket: \f171; -$fa-var-imdb: \f2d8; -$fa-var-deezer: \e077; -$fa-var-raspberry-pi: \f7bb; -$fa-var-jira: \f7b1; -$fa-var-docker: \f395; -$fa-var-screenpal: \e570; -$fa-var-bluetooth: \f293; -$fa-var-gitter: \f426; -$fa-var-d-and-d: \f38d; -$fa-var-microblog: \e01a; -$fa-var-cc-diners-club: \f24c; -$fa-var-gg-circle: \f261; -$fa-var-pied-piper-hat: \f4e5; -$fa-var-kickstarter-k: \f3bc; -$fa-var-yandex: \f413; -$fa-var-readme: \f4d5; -$fa-var-html5: \f13b; -$fa-var-sellsy: \f213; -$fa-var-square-web-awesome: \e683; -$fa-var-sass: \f41e; -$fa-var-wirsindhandwerk: \e2d0; -$fa-var-wsh: \e2d0; -$fa-var-buromobelexperte: \f37f; -$fa-var-salesforce: \f83b; -$fa-var-octopus-deploy: \e082; -$fa-var-medapps: \f3c6; -$fa-var-ns8: \f3d5; -$fa-var-pinterest-p: \f231; -$fa-var-apper: \f371; -$fa-var-fort-awesome: \f286; -$fa-var-waze: \f83f; -$fa-var-bluesky: \e671; -$fa-var-cc-jcb: \f24b; -$fa-var-snapchat: \f2ab; -$fa-var-snapchat-ghost: \f2ab; -$fa-var-fantasy-flight-games: \f6dc; -$fa-var-rust: \e07a; -$fa-var-wix: \f5cf; -$fa-var-square-behance: \f1b5; -$fa-var-behance-square: \f1b5; -$fa-var-supple: \f3f9; -$fa-var-webflow: \e65c; -$fa-var-rebel: \f1d0; -$fa-var-css3: \f13c; -$fa-var-staylinked: \f3f5; -$fa-var-kaggle: \f5fa; -$fa-var-space-awesome: \e5ac; -$fa-var-deviantart: \f1bd; -$fa-var-cpanel: \f388; -$fa-var-goodreads-g: \f3a9; -$fa-var-square-git: \f1d2; -$fa-var-git-square: \f1d2; -$fa-var-square-tumblr: \f174; -$fa-var-tumblr-square: \f174; -$fa-var-trello: \f181; -$fa-var-creative-commons-nc-jp: \f4ea; -$fa-var-get-pocket: \f265; -$fa-var-perbyte: \e083; -$fa-var-grunt: \f3ad; -$fa-var-weebly: \f5cc; -$fa-var-connectdevelop: \f20e; -$fa-var-leanpub: \f212; -$fa-var-black-tie: \f27e; -$fa-var-themeco: \f5c6; -$fa-var-python: \f3e2; -$fa-var-android: \f17b; -$fa-var-bots: \e340; -$fa-var-free-code-camp: \f2c5; -$fa-var-hornbill: \f592; -$fa-var-js: \f3b8; -$fa-var-ideal: \e013; -$fa-var-git: \f1d3; -$fa-var-dev: \f6cc; -$fa-var-sketch: \f7c6; -$fa-var-yandex-international: \f414; -$fa-var-cc-amex: \f1f3; -$fa-var-uber: \f402; -$fa-var-github: \f09b; -$fa-var-php: \f457; -$fa-var-alipay: \f642; -$fa-var-youtube: \f167; -$fa-var-skyatlas: \f216; -$fa-var-firefox-browser: \e007; -$fa-var-replyd: \f3e6; -$fa-var-suse: \f7d6; -$fa-var-jenkins: \f3b6; -$fa-var-twitter: \f099; -$fa-var-rockrms: \f3e9; -$fa-var-pinterest: \f0d2; -$fa-var-buffer: \f837; -$fa-var-npm: \f3d4; -$fa-var-yammer: \f840; -$fa-var-btc: \f15a; -$fa-var-dribbble: \f17d; -$fa-var-stumbleupon-circle: \f1a3; -$fa-var-internet-explorer: \f26b; -$fa-var-stubber: \e5c7; -$fa-var-telegram: \f2c6; -$fa-var-telegram-plane: \f2c6; -$fa-var-old-republic: \f510; -$fa-var-odysee: \e5c6; -$fa-var-square-whatsapp: \f40c; -$fa-var-whatsapp-square: \f40c; -$fa-var-node-js: \f3d3; -$fa-var-edge-legacy: \e078; -$fa-var-slack: \f198; -$fa-var-slack-hash: \f198; -$fa-var-medrt: \f3c8; -$fa-var-usb: \f287; -$fa-var-tumblr: \f173; -$fa-var-vaadin: \f408; -$fa-var-quora: \f2c4; -$fa-var-square-x-twitter: \e61a; -$fa-var-reacteurope: \f75d; -$fa-var-medium: \f23a; -$fa-var-medium-m: \f23a; -$fa-var-amilia: \f36d; -$fa-var-mixcloud: \f289; -$fa-var-flipboard: \f44d; -$fa-var-viacoin: \f237; -$fa-var-critical-role: \f6c9; -$fa-var-sitrox: \e44a; -$fa-var-discourse: \f393; -$fa-var-joomla: \f1aa; -$fa-var-mastodon: \f4f6; -$fa-var-airbnb: \f834; -$fa-var-wolf-pack-battalion: \f514; -$fa-var-buy-n-large: \f8a6; -$fa-var-gulp: \f3ae; -$fa-var-creative-commons-sampling-plus: \f4f1; -$fa-var-strava: \f428; -$fa-var-ember: \f423; -$fa-var-canadian-maple-leaf: \f785; -$fa-var-teamspeak: \f4f9; -$fa-var-pushed: \f3e1; -$fa-var-wordpress-simple: \f411; -$fa-var-nutritionix: \f3d6; -$fa-var-wodu: \e088; -$fa-var-google-pay: \e079; -$fa-var-intercom: \f7af; -$fa-var-zhihu: \f63f; -$fa-var-korvue: \f42f; -$fa-var-pix: \e43a; -$fa-var-steam-symbol: \f3f6; - -$fa-icons: ( - "0": $fa-var-0, - "1": $fa-var-1, - "2": $fa-var-2, - "3": $fa-var-3, - "4": $fa-var-4, - "5": $fa-var-5, - "6": $fa-var-6, - "7": $fa-var-7, - "8": $fa-var-8, - "9": $fa-var-9, - "fill-drip": $fa-var-fill-drip, - "arrows-to-circle": $fa-var-arrows-to-circle, - "circle-chevron-right": $fa-var-circle-chevron-right, - "chevron-circle-right": $fa-var-chevron-circle-right, - "at": $fa-var-at, - "trash-can": $fa-var-trash-can, - "trash-alt": $fa-var-trash-alt, - "text-height": $fa-var-text-height, - "user-xmark": $fa-var-user-xmark, - "user-times": $fa-var-user-times, - "stethoscope": $fa-var-stethoscope, - "message": $fa-var-message, - "comment-alt": $fa-var-comment-alt, - "info": $fa-var-info, - "down-left-and-up-right-to-center": $fa-var-down-left-and-up-right-to-center, - "compress-alt": $fa-var-compress-alt, - "explosion": $fa-var-explosion, - "file-lines": $fa-var-file-lines, - "file-alt": $fa-var-file-alt, - "file-text": $fa-var-file-text, - "wave-square": $fa-var-wave-square, - "ring": $fa-var-ring, - "building-un": $fa-var-building-un, - "dice-three": $fa-var-dice-three, - "calendar-days": $fa-var-calendar-days, - "calendar-alt": $fa-var-calendar-alt, - "anchor-circle-check": $fa-var-anchor-circle-check, - "building-circle-arrow-right": $fa-var-building-circle-arrow-right, - "volleyball": $fa-var-volleyball, - "volleyball-ball": $fa-var-volleyball-ball, - "arrows-up-to-line": $fa-var-arrows-up-to-line, - "sort-down": $fa-var-sort-down, - "sort-desc": $fa-var-sort-desc, - "circle-minus": $fa-var-circle-minus, - "minus-circle": $fa-var-minus-circle, - "door-open": $fa-var-door-open, - "right-from-bracket": $fa-var-right-from-bracket, - "sign-out-alt": $fa-var-sign-out-alt, - "atom": $fa-var-atom, - "soap": $fa-var-soap, - "icons": $fa-var-icons, - "heart-music-camera-bolt": $fa-var-heart-music-camera-bolt, - "microphone-lines-slash": $fa-var-microphone-lines-slash, - "microphone-alt-slash": $fa-var-microphone-alt-slash, - "bridge-circle-check": $fa-var-bridge-circle-check, - "pump-medical": $fa-var-pump-medical, - "fingerprint": $fa-var-fingerprint, - "hand-point-right": $fa-var-hand-point-right, - "magnifying-glass-location": $fa-var-magnifying-glass-location, - "search-location": $fa-var-search-location, - "forward-step": $fa-var-forward-step, - "step-forward": $fa-var-step-forward, - "face-smile-beam": $fa-var-face-smile-beam, - "smile-beam": $fa-var-smile-beam, - "flag-checkered": $fa-var-flag-checkered, - "football": $fa-var-football, - "football-ball": $fa-var-football-ball, - "school-circle-exclamation": $fa-var-school-circle-exclamation, - "crop": $fa-var-crop, - "angles-down": $fa-var-angles-down, - "angle-double-down": $fa-var-angle-double-down, - "users-rectangle": $fa-var-users-rectangle, - "people-roof": $fa-var-people-roof, - "people-line": $fa-var-people-line, - "beer-mug-empty": $fa-var-beer-mug-empty, - "beer": $fa-var-beer, - "diagram-predecessor": $fa-var-diagram-predecessor, - "arrow-up-long": $fa-var-arrow-up-long, - "long-arrow-up": $fa-var-long-arrow-up, - "fire-flame-simple": $fa-var-fire-flame-simple, - "burn": $fa-var-burn, - "person": $fa-var-person, - "male": $fa-var-male, - "laptop": $fa-var-laptop, - "file-csv": $fa-var-file-csv, - "menorah": $fa-var-menorah, - "truck-plane": $fa-var-truck-plane, - "record-vinyl": $fa-var-record-vinyl, - "face-grin-stars": $fa-var-face-grin-stars, - "grin-stars": $fa-var-grin-stars, - "bong": $fa-var-bong, - "spaghetti-monster-flying": $fa-var-spaghetti-monster-flying, - "pastafarianism": $fa-var-pastafarianism, - "arrow-down-up-across-line": $fa-var-arrow-down-up-across-line, - "spoon": $fa-var-spoon, - "utensil-spoon": $fa-var-utensil-spoon, - "jar-wheat": $fa-var-jar-wheat, - "envelopes-bulk": $fa-var-envelopes-bulk, - "mail-bulk": $fa-var-mail-bulk, - "file-circle-exclamation": $fa-var-file-circle-exclamation, - "circle-h": $fa-var-circle-h, - "hospital-symbol": $fa-var-hospital-symbol, - "pager": $fa-var-pager, - "address-book": $fa-var-address-book, - "contact-book": $fa-var-contact-book, - "strikethrough": $fa-var-strikethrough, - "k": $fa-var-k, - "landmark-flag": $fa-var-landmark-flag, - "pencil": $fa-var-pencil, - "pencil-alt": $fa-var-pencil-alt, - "backward": $fa-var-backward, - "caret-right": $fa-var-caret-right, - "comments": $fa-var-comments, - "paste": $fa-var-paste, - "file-clipboard": $fa-var-file-clipboard, - "code-pull-request": $fa-var-code-pull-request, - "clipboard-list": $fa-var-clipboard-list, - "truck-ramp-box": $fa-var-truck-ramp-box, - "truck-loading": $fa-var-truck-loading, - "user-check": $fa-var-user-check, - "vial-virus": $fa-var-vial-virus, - "sheet-plastic": $fa-var-sheet-plastic, - "blog": $fa-var-blog, - "user-ninja": $fa-var-user-ninja, - "person-arrow-up-from-line": $fa-var-person-arrow-up-from-line, - "scroll-torah": $fa-var-scroll-torah, - "torah": $fa-var-torah, - "broom-ball": $fa-var-broom-ball, - "quidditch": $fa-var-quidditch, - "quidditch-broom-ball": $fa-var-quidditch-broom-ball, - "toggle-off": $fa-var-toggle-off, - "box-archive": $fa-var-box-archive, - "archive": $fa-var-archive, - "person-drowning": $fa-var-person-drowning, - "arrow-down-9-1": $fa-var-arrow-down-9-1, - "sort-numeric-desc": $fa-var-sort-numeric-desc, - "sort-numeric-down-alt": $fa-var-sort-numeric-down-alt, - "face-grin-tongue-squint": $fa-var-face-grin-tongue-squint, - "grin-tongue-squint": $fa-var-grin-tongue-squint, - "spray-can": $fa-var-spray-can, - "truck-monster": $fa-var-truck-monster, - "w": $fa-var-w, - "earth-africa": $fa-var-earth-africa, - "globe-africa": $fa-var-globe-africa, - "rainbow": $fa-var-rainbow, - "circle-notch": $fa-var-circle-notch, - "tablet-screen-button": $fa-var-tablet-screen-button, - "tablet-alt": $fa-var-tablet-alt, - "paw": $fa-var-paw, - "cloud": $fa-var-cloud, - "trowel-bricks": $fa-var-trowel-bricks, - "face-flushed": $fa-var-face-flushed, - "flushed": $fa-var-flushed, - "hospital-user": $fa-var-hospital-user, - "tent-arrow-left-right": $fa-var-tent-arrow-left-right, - "gavel": $fa-var-gavel, - "legal": $fa-var-legal, - "binoculars": $fa-var-binoculars, - "microphone-slash": $fa-var-microphone-slash, - "box-tissue": $fa-var-box-tissue, - "motorcycle": $fa-var-motorcycle, - "bell-concierge": $fa-var-bell-concierge, - "concierge-bell": $fa-var-concierge-bell, - "pen-ruler": $fa-var-pen-ruler, - "pencil-ruler": $fa-var-pencil-ruler, - "people-arrows": $fa-var-people-arrows, - "people-arrows-left-right": $fa-var-people-arrows-left-right, - "mars-and-venus-burst": $fa-var-mars-and-venus-burst, - "square-caret-right": $fa-var-square-caret-right, - "caret-square-right": $fa-var-caret-square-right, - "scissors": $fa-var-scissors, - "cut": $fa-var-cut, - "sun-plant-wilt": $fa-var-sun-plant-wilt, - "toilets-portable": $fa-var-toilets-portable, - "hockey-puck": $fa-var-hockey-puck, - "table": $fa-var-table, - "magnifying-glass-arrow-right": $fa-var-magnifying-glass-arrow-right, - "tachograph-digital": $fa-var-tachograph-digital, - "digital-tachograph": $fa-var-digital-tachograph, - "users-slash": $fa-var-users-slash, - "clover": $fa-var-clover, - "reply": $fa-var-reply, - "mail-reply": $fa-var-mail-reply, - "star-and-crescent": $fa-var-star-and-crescent, - "house-fire": $fa-var-house-fire, - "square-minus": $fa-var-square-minus, - "minus-square": $fa-var-minus-square, - "helicopter": $fa-var-helicopter, - "compass": $fa-var-compass, - "square-caret-down": $fa-var-square-caret-down, - "caret-square-down": $fa-var-caret-square-down, - "file-circle-question": $fa-var-file-circle-question, - "laptop-code": $fa-var-laptop-code, - "swatchbook": $fa-var-swatchbook, - "prescription-bottle": $fa-var-prescription-bottle, - "bars": $fa-var-bars, - "navicon": $fa-var-navicon, - "people-group": $fa-var-people-group, - "hourglass-end": $fa-var-hourglass-end, - "hourglass-3": $fa-var-hourglass-3, - "heart-crack": $fa-var-heart-crack, - "heart-broken": $fa-var-heart-broken, - "square-up-right": $fa-var-square-up-right, - "external-link-square-alt": $fa-var-external-link-square-alt, - "face-kiss-beam": $fa-var-face-kiss-beam, - "kiss-beam": $fa-var-kiss-beam, - "film": $fa-var-film, - "ruler-horizontal": $fa-var-ruler-horizontal, - "people-robbery": $fa-var-people-robbery, - "lightbulb": $fa-var-lightbulb, - "caret-left": $fa-var-caret-left, - "circle-exclamation": $fa-var-circle-exclamation, - "exclamation-circle": $fa-var-exclamation-circle, - "school-circle-xmark": $fa-var-school-circle-xmark, - "arrow-right-from-bracket": $fa-var-arrow-right-from-bracket, - "sign-out": $fa-var-sign-out, - "circle-chevron-down": $fa-var-circle-chevron-down, - "chevron-circle-down": $fa-var-chevron-circle-down, - "unlock-keyhole": $fa-var-unlock-keyhole, - "unlock-alt": $fa-var-unlock-alt, - "cloud-showers-heavy": $fa-var-cloud-showers-heavy, - "headphones-simple": $fa-var-headphones-simple, - "headphones-alt": $fa-var-headphones-alt, - "sitemap": $fa-var-sitemap, - "circle-dollar-to-slot": $fa-var-circle-dollar-to-slot, - "donate": $fa-var-donate, - "memory": $fa-var-memory, - "road-spikes": $fa-var-road-spikes, - "fire-burner": $fa-var-fire-burner, - "flag": $fa-var-flag, - "hanukiah": $fa-var-hanukiah, - "feather": $fa-var-feather, - "volume-low": $fa-var-volume-low, - "volume-down": $fa-var-volume-down, - "comment-slash": $fa-var-comment-slash, - "cloud-sun-rain": $fa-var-cloud-sun-rain, - "compress": $fa-var-compress, - "wheat-awn": $fa-var-wheat-awn, - "wheat-alt": $fa-var-wheat-alt, - "ankh": $fa-var-ankh, - "hands-holding-child": $fa-var-hands-holding-child, - "asterisk": $fa-var-asterisk, - "square-check": $fa-var-square-check, - "check-square": $fa-var-check-square, - "peseta-sign": $fa-var-peseta-sign, - "heading": $fa-var-heading, - "header": $fa-var-header, - "ghost": $fa-var-ghost, - "list": $fa-var-list, - "list-squares": $fa-var-list-squares, - "square-phone-flip": $fa-var-square-phone-flip, - "phone-square-alt": $fa-var-phone-square-alt, - "cart-plus": $fa-var-cart-plus, - "gamepad": $fa-var-gamepad, - "circle-dot": $fa-var-circle-dot, - "dot-circle": $fa-var-dot-circle, - "face-dizzy": $fa-var-face-dizzy, - "dizzy": $fa-var-dizzy, - "egg": $fa-var-egg, - "house-medical-circle-xmark": $fa-var-house-medical-circle-xmark, - "campground": $fa-var-campground, - "folder-plus": $fa-var-folder-plus, - "futbol": $fa-var-futbol, - "futbol-ball": $fa-var-futbol-ball, - "soccer-ball": $fa-var-soccer-ball, - "paintbrush": $fa-var-paintbrush, - "paint-brush": $fa-var-paint-brush, - "lock": $fa-var-lock, - "gas-pump": $fa-var-gas-pump, - "hot-tub-person": $fa-var-hot-tub-person, - "hot-tub": $fa-var-hot-tub, - "map-location": $fa-var-map-location, - "map-marked": $fa-var-map-marked, - "house-flood-water": $fa-var-house-flood-water, - "tree": $fa-var-tree, - "bridge-lock": $fa-var-bridge-lock, - "sack-dollar": $fa-var-sack-dollar, - "pen-to-square": $fa-var-pen-to-square, - "edit": $fa-var-edit, - "car-side": $fa-var-car-side, - "share-nodes": $fa-var-share-nodes, - "share-alt": $fa-var-share-alt, - "heart-circle-minus": $fa-var-heart-circle-minus, - "hourglass-half": $fa-var-hourglass-half, - "hourglass-2": $fa-var-hourglass-2, - "microscope": $fa-var-microscope, - "sink": $fa-var-sink, - "bag-shopping": $fa-var-bag-shopping, - "shopping-bag": $fa-var-shopping-bag, - "arrow-down-z-a": $fa-var-arrow-down-z-a, - "sort-alpha-desc": $fa-var-sort-alpha-desc, - "sort-alpha-down-alt": $fa-var-sort-alpha-down-alt, - "mitten": $fa-var-mitten, - "person-rays": $fa-var-person-rays, - "users": $fa-var-users, - "eye-slash": $fa-var-eye-slash, - "flask-vial": $fa-var-flask-vial, - "hand": $fa-var-hand, - "hand-paper": $fa-var-hand-paper, - "om": $fa-var-om, - "worm": $fa-var-worm, - "house-circle-xmark": $fa-var-house-circle-xmark, - "plug": $fa-var-plug, - "chevron-up": $fa-var-chevron-up, - "hand-spock": $fa-var-hand-spock, - "stopwatch": $fa-var-stopwatch, - "face-kiss": $fa-var-face-kiss, - "kiss": $fa-var-kiss, - "bridge-circle-xmark": $fa-var-bridge-circle-xmark, - "face-grin-tongue": $fa-var-face-grin-tongue, - "grin-tongue": $fa-var-grin-tongue, - "chess-bishop": $fa-var-chess-bishop, - "face-grin-wink": $fa-var-face-grin-wink, - "grin-wink": $fa-var-grin-wink, - "ear-deaf": $fa-var-ear-deaf, - "deaf": $fa-var-deaf, - "deafness": $fa-var-deafness, - "hard-of-hearing": $fa-var-hard-of-hearing, - "road-circle-check": $fa-var-road-circle-check, - "dice-five": $fa-var-dice-five, - "square-rss": $fa-var-square-rss, - "rss-square": $fa-var-rss-square, - "land-mine-on": $fa-var-land-mine-on, - "i-cursor": $fa-var-i-cursor, - "stamp": $fa-var-stamp, - "stairs": $fa-var-stairs, - "i": $fa-var-i, - "hryvnia-sign": $fa-var-hryvnia-sign, - "hryvnia": $fa-var-hryvnia, - "pills": $fa-var-pills, - "face-grin-wide": $fa-var-face-grin-wide, - "grin-alt": $fa-var-grin-alt, - "tooth": $fa-var-tooth, - "v": $fa-var-v, - "bangladeshi-taka-sign": $fa-var-bangladeshi-taka-sign, - "bicycle": $fa-var-bicycle, - "staff-snake": $fa-var-staff-snake, - "rod-asclepius": $fa-var-rod-asclepius, - "rod-snake": $fa-var-rod-snake, - "staff-aesculapius": $fa-var-staff-aesculapius, - "head-side-cough-slash": $fa-var-head-side-cough-slash, - "truck-medical": $fa-var-truck-medical, - "ambulance": $fa-var-ambulance, - "wheat-awn-circle-exclamation": $fa-var-wheat-awn-circle-exclamation, - "snowman": $fa-var-snowman, - "mortar-pestle": $fa-var-mortar-pestle, - "road-barrier": $fa-var-road-barrier, - "school": $fa-var-school, - "igloo": $fa-var-igloo, - "joint": $fa-var-joint, - "angle-right": $fa-var-angle-right, - "horse": $fa-var-horse, - "q": $fa-var-q, - "g": $fa-var-g, - "notes-medical": $fa-var-notes-medical, - "temperature-half": $fa-var-temperature-half, - "temperature-2": $fa-var-temperature-2, - "thermometer-2": $fa-var-thermometer-2, - "thermometer-half": $fa-var-thermometer-half, - "dong-sign": $fa-var-dong-sign, - "capsules": $fa-var-capsules, - "poo-storm": $fa-var-poo-storm, - "poo-bolt": $fa-var-poo-bolt, - "face-frown-open": $fa-var-face-frown-open, - "frown-open": $fa-var-frown-open, - "hand-point-up": $fa-var-hand-point-up, - "money-bill": $fa-var-money-bill, - "bookmark": $fa-var-bookmark, - "align-justify": $fa-var-align-justify, - "umbrella-beach": $fa-var-umbrella-beach, - "helmet-un": $fa-var-helmet-un, - "bullseye": $fa-var-bullseye, - "bacon": $fa-var-bacon, - "hand-point-down": $fa-var-hand-point-down, - "arrow-up-from-bracket": $fa-var-arrow-up-from-bracket, - "folder": $fa-var-folder, - "folder-blank": $fa-var-folder-blank, - "file-waveform": $fa-var-file-waveform, - "file-medical-alt": $fa-var-file-medical-alt, - "radiation": $fa-var-radiation, - "chart-simple": $fa-var-chart-simple, - "mars-stroke": $fa-var-mars-stroke, - "vial": $fa-var-vial, - "gauge": $fa-var-gauge, - "dashboard": $fa-var-dashboard, - "gauge-med": $fa-var-gauge-med, - "tachometer-alt-average": $fa-var-tachometer-alt-average, - "wand-magic-sparkles": $fa-var-wand-magic-sparkles, - "magic-wand-sparkles": $fa-var-magic-wand-sparkles, - "e": $fa-var-e, - "pen-clip": $fa-var-pen-clip, - "pen-alt": $fa-var-pen-alt, - "bridge-circle-exclamation": $fa-var-bridge-circle-exclamation, - "user": $fa-var-user, - "school-circle-check": $fa-var-school-circle-check, - "dumpster": $fa-var-dumpster, - "van-shuttle": $fa-var-van-shuttle, - "shuttle-van": $fa-var-shuttle-van, - "building-user": $fa-var-building-user, - "square-caret-left": $fa-var-square-caret-left, - "caret-square-left": $fa-var-caret-square-left, - "highlighter": $fa-var-highlighter, - "key": $fa-var-key, - "bullhorn": $fa-var-bullhorn, - "globe": $fa-var-globe, - "synagogue": $fa-var-synagogue, - "person-half-dress": $fa-var-person-half-dress, - "road-bridge": $fa-var-road-bridge, - "location-arrow": $fa-var-location-arrow, - "c": $fa-var-c, - "tablet-button": $fa-var-tablet-button, - "building-lock": $fa-var-building-lock, - "pizza-slice": $fa-var-pizza-slice, - "money-bill-wave": $fa-var-money-bill-wave, - "chart-area": $fa-var-chart-area, - "area-chart": $fa-var-area-chart, - "house-flag": $fa-var-house-flag, - "person-circle-minus": $fa-var-person-circle-minus, - "ban": $fa-var-ban, - "cancel": $fa-var-cancel, - "camera-rotate": $fa-var-camera-rotate, - "spray-can-sparkles": $fa-var-spray-can-sparkles, - "air-freshener": $fa-var-air-freshener, - "star": $fa-var-star, - "repeat": $fa-var-repeat, - "cross": $fa-var-cross, - "box": $fa-var-box, - "venus-mars": $fa-var-venus-mars, - "arrow-pointer": $fa-var-arrow-pointer, - "mouse-pointer": $fa-var-mouse-pointer, - "maximize": $fa-var-maximize, - "expand-arrows-alt": $fa-var-expand-arrows-alt, - "charging-station": $fa-var-charging-station, - "shapes": $fa-var-shapes, - "triangle-circle-square": $fa-var-triangle-circle-square, - "shuffle": $fa-var-shuffle, - "random": $fa-var-random, - "person-running": $fa-var-person-running, - "running": $fa-var-running, - "mobile-retro": $fa-var-mobile-retro, - "grip-lines-vertical": $fa-var-grip-lines-vertical, - "spider": $fa-var-spider, - "hands-bound": $fa-var-hands-bound, - "file-invoice-dollar": $fa-var-file-invoice-dollar, - "plane-circle-exclamation": $fa-var-plane-circle-exclamation, - "x-ray": $fa-var-x-ray, - "spell-check": $fa-var-spell-check, - "slash": $fa-var-slash, - "computer-mouse": $fa-var-computer-mouse, - "mouse": $fa-var-mouse, - "arrow-right-to-bracket": $fa-var-arrow-right-to-bracket, - "sign-in": $fa-var-sign-in, - "shop-slash": $fa-var-shop-slash, - "store-alt-slash": $fa-var-store-alt-slash, - "server": $fa-var-server, - "virus-covid-slash": $fa-var-virus-covid-slash, - "shop-lock": $fa-var-shop-lock, - "hourglass-start": $fa-var-hourglass-start, - "hourglass-1": $fa-var-hourglass-1, - "blender-phone": $fa-var-blender-phone, - "building-wheat": $fa-var-building-wheat, - "person-breastfeeding": $fa-var-person-breastfeeding, - "right-to-bracket": $fa-var-right-to-bracket, - "sign-in-alt": $fa-var-sign-in-alt, - "venus": $fa-var-venus, - "passport": $fa-var-passport, - "thumbtack-slash": $fa-var-thumbtack-slash, - "thumb-tack-slash": $fa-var-thumb-tack-slash, - "heart-pulse": $fa-var-heart-pulse, - "heartbeat": $fa-var-heartbeat, - "people-carry-box": $fa-var-people-carry-box, - "people-carry": $fa-var-people-carry, - "temperature-high": $fa-var-temperature-high, - "microchip": $fa-var-microchip, - "crown": $fa-var-crown, - "weight-hanging": $fa-var-weight-hanging, - "xmarks-lines": $fa-var-xmarks-lines, - "file-prescription": $fa-var-file-prescription, - "weight-scale": $fa-var-weight-scale, - "weight": $fa-var-weight, - "user-group": $fa-var-user-group, - "user-friends": $fa-var-user-friends, - "arrow-up-a-z": $fa-var-arrow-up-a-z, - "sort-alpha-up": $fa-var-sort-alpha-up, - "chess-knight": $fa-var-chess-knight, - "face-laugh-squint": $fa-var-face-laugh-squint, - "laugh-squint": $fa-var-laugh-squint, - "wheelchair": $fa-var-wheelchair, - "circle-arrow-up": $fa-var-circle-arrow-up, - "arrow-circle-up": $fa-var-arrow-circle-up, - "toggle-on": $fa-var-toggle-on, - "person-walking": $fa-var-person-walking, - "walking": $fa-var-walking, - "l": $fa-var-l, - "fire": $fa-var-fire, - "bed-pulse": $fa-var-bed-pulse, - "procedures": $fa-var-procedures, - "shuttle-space": $fa-var-shuttle-space, - "space-shuttle": $fa-var-space-shuttle, - "face-laugh": $fa-var-face-laugh, - "laugh": $fa-var-laugh, - "folder-open": $fa-var-folder-open, - "heart-circle-plus": $fa-var-heart-circle-plus, - "code-fork": $fa-var-code-fork, - "city": $fa-var-city, - "microphone-lines": $fa-var-microphone-lines, - "microphone-alt": $fa-var-microphone-alt, - "pepper-hot": $fa-var-pepper-hot, - "unlock": $fa-var-unlock, - "colon-sign": $fa-var-colon-sign, - "headset": $fa-var-headset, - "store-slash": $fa-var-store-slash, - "road-circle-xmark": $fa-var-road-circle-xmark, - "user-minus": $fa-var-user-minus, - "mars-stroke-up": $fa-var-mars-stroke-up, - "mars-stroke-v": $fa-var-mars-stroke-v, - "champagne-glasses": $fa-var-champagne-glasses, - "glass-cheers": $fa-var-glass-cheers, - "clipboard": $fa-var-clipboard, - "house-circle-exclamation": $fa-var-house-circle-exclamation, - "file-arrow-up": $fa-var-file-arrow-up, - "file-upload": $fa-var-file-upload, - "wifi": $fa-var-wifi, - "wifi-3": $fa-var-wifi-3, - "wifi-strong": $fa-var-wifi-strong, - "bath": $fa-var-bath, - "bathtub": $fa-var-bathtub, - "underline": $fa-var-underline, - "user-pen": $fa-var-user-pen, - "user-edit": $fa-var-user-edit, - "signature": $fa-var-signature, - "stroopwafel": $fa-var-stroopwafel, - "bold": $fa-var-bold, - "anchor-lock": $fa-var-anchor-lock, - "building-ngo": $fa-var-building-ngo, - "manat-sign": $fa-var-manat-sign, - "not-equal": $fa-var-not-equal, - "border-top-left": $fa-var-border-top-left, - "border-style": $fa-var-border-style, - "map-location-dot": $fa-var-map-location-dot, - "map-marked-alt": $fa-var-map-marked-alt, - "jedi": $fa-var-jedi, - "square-poll-vertical": $fa-var-square-poll-vertical, - "poll": $fa-var-poll, - "mug-hot": $fa-var-mug-hot, - "car-battery": $fa-var-car-battery, - "battery-car": $fa-var-battery-car, - "gift": $fa-var-gift, - "dice-two": $fa-var-dice-two, - "chess-queen": $fa-var-chess-queen, - "glasses": $fa-var-glasses, - "chess-board": $fa-var-chess-board, - "building-circle-check": $fa-var-building-circle-check, - "person-chalkboard": $fa-var-person-chalkboard, - "mars-stroke-right": $fa-var-mars-stroke-right, - "mars-stroke-h": $fa-var-mars-stroke-h, - "hand-back-fist": $fa-var-hand-back-fist, - "hand-rock": $fa-var-hand-rock, - "square-caret-up": $fa-var-square-caret-up, - "caret-square-up": $fa-var-caret-square-up, - "cloud-showers-water": $fa-var-cloud-showers-water, - "chart-bar": $fa-var-chart-bar, - "bar-chart": $fa-var-bar-chart, - "hands-bubbles": $fa-var-hands-bubbles, - "hands-wash": $fa-var-hands-wash, - "less-than-equal": $fa-var-less-than-equal, - "train": $fa-var-train, - "eye-low-vision": $fa-var-eye-low-vision, - "low-vision": $fa-var-low-vision, - "crow": $fa-var-crow, - "sailboat": $fa-var-sailboat, - "window-restore": $fa-var-window-restore, - "square-plus": $fa-var-square-plus, - "plus-square": $fa-var-plus-square, - "torii-gate": $fa-var-torii-gate, - "frog": $fa-var-frog, - "bucket": $fa-var-bucket, - "image": $fa-var-image, - "microphone": $fa-var-microphone, - "cow": $fa-var-cow, - "caret-up": $fa-var-caret-up, - "screwdriver": $fa-var-screwdriver, - "folder-closed": $fa-var-folder-closed, - "house-tsunami": $fa-var-house-tsunami, - "square-nfi": $fa-var-square-nfi, - "arrow-up-from-ground-water": $fa-var-arrow-up-from-ground-water, - "martini-glass": $fa-var-martini-glass, - "glass-martini-alt": $fa-var-glass-martini-alt, - "square-binary": $fa-var-square-binary, - "rotate-left": $fa-var-rotate-left, - "rotate-back": $fa-var-rotate-back, - "rotate-backward": $fa-var-rotate-backward, - "undo-alt": $fa-var-undo-alt, - "table-columns": $fa-var-table-columns, - "columns": $fa-var-columns, - "lemon": $fa-var-lemon, - "head-side-mask": $fa-var-head-side-mask, - "handshake": $fa-var-handshake, - "gem": $fa-var-gem, - "dolly": $fa-var-dolly, - "dolly-box": $fa-var-dolly-box, - "smoking": $fa-var-smoking, - "minimize": $fa-var-minimize, - "compress-arrows-alt": $fa-var-compress-arrows-alt, - "monument": $fa-var-monument, - "snowplow": $fa-var-snowplow, - "angles-right": $fa-var-angles-right, - "angle-double-right": $fa-var-angle-double-right, - "cannabis": $fa-var-cannabis, - "circle-play": $fa-var-circle-play, - "play-circle": $fa-var-play-circle, - "tablets": $fa-var-tablets, - "ethernet": $fa-var-ethernet, - "euro-sign": $fa-var-euro-sign, - "eur": $fa-var-eur, - "euro": $fa-var-euro, - "chair": $fa-var-chair, - "circle-check": $fa-var-circle-check, - "check-circle": $fa-var-check-circle, - "circle-stop": $fa-var-circle-stop, - "stop-circle": $fa-var-stop-circle, - "compass-drafting": $fa-var-compass-drafting, - "drafting-compass": $fa-var-drafting-compass, - "plate-wheat": $fa-var-plate-wheat, - "icicles": $fa-var-icicles, - "person-shelter": $fa-var-person-shelter, - "neuter": $fa-var-neuter, - "id-badge": $fa-var-id-badge, - "marker": $fa-var-marker, - "face-laugh-beam": $fa-var-face-laugh-beam, - "laugh-beam": $fa-var-laugh-beam, - "helicopter-symbol": $fa-var-helicopter-symbol, - "universal-access": $fa-var-universal-access, - "circle-chevron-up": $fa-var-circle-chevron-up, - "chevron-circle-up": $fa-var-chevron-circle-up, - "lari-sign": $fa-var-lari-sign, - "volcano": $fa-var-volcano, - "person-walking-dashed-line-arrow-right": $fa-var-person-walking-dashed-line-arrow-right, - "sterling-sign": $fa-var-sterling-sign, - "gbp": $fa-var-gbp, - "pound-sign": $fa-var-pound-sign, - "viruses": $fa-var-viruses, - "square-person-confined": $fa-var-square-person-confined, - "user-tie": $fa-var-user-tie, - "arrow-down-long": $fa-var-arrow-down-long, - "long-arrow-down": $fa-var-long-arrow-down, - "tent-arrow-down-to-line": $fa-var-tent-arrow-down-to-line, - "certificate": $fa-var-certificate, - "reply-all": $fa-var-reply-all, - "mail-reply-all": $fa-var-mail-reply-all, - "suitcase": $fa-var-suitcase, - "person-skating": $fa-var-person-skating, - "skating": $fa-var-skating, - "filter-circle-dollar": $fa-var-filter-circle-dollar, - "funnel-dollar": $fa-var-funnel-dollar, - "camera-retro": $fa-var-camera-retro, - "circle-arrow-down": $fa-var-circle-arrow-down, - "arrow-circle-down": $fa-var-arrow-circle-down, - "file-import": $fa-var-file-import, - "arrow-right-to-file": $fa-var-arrow-right-to-file, - "square-arrow-up-right": $fa-var-square-arrow-up-right, - "external-link-square": $fa-var-external-link-square, - "box-open": $fa-var-box-open, - "scroll": $fa-var-scroll, - "spa": $fa-var-spa, - "location-pin-lock": $fa-var-location-pin-lock, - "pause": $fa-var-pause, - "hill-avalanche": $fa-var-hill-avalanche, - "temperature-empty": $fa-var-temperature-empty, - "temperature-0": $fa-var-temperature-0, - "thermometer-0": $fa-var-thermometer-0, - "thermometer-empty": $fa-var-thermometer-empty, - "bomb": $fa-var-bomb, - "registered": $fa-var-registered, - "address-card": $fa-var-address-card, - "contact-card": $fa-var-contact-card, - "vcard": $fa-var-vcard, - "scale-unbalanced-flip": $fa-var-scale-unbalanced-flip, - "balance-scale-right": $fa-var-balance-scale-right, - "subscript": $fa-var-subscript, - "diamond-turn-right": $fa-var-diamond-turn-right, - "directions": $fa-var-directions, - "burst": $fa-var-burst, - "house-laptop": $fa-var-house-laptop, - "laptop-house": $fa-var-laptop-house, - "face-tired": $fa-var-face-tired, - "tired": $fa-var-tired, - "money-bills": $fa-var-money-bills, - "smog": $fa-var-smog, - "crutch": $fa-var-crutch, - "cloud-arrow-up": $fa-var-cloud-arrow-up, - "cloud-upload": $fa-var-cloud-upload, - "cloud-upload-alt": $fa-var-cloud-upload-alt, - "palette": $fa-var-palette, - "arrows-turn-right": $fa-var-arrows-turn-right, - "vest": $fa-var-vest, - "ferry": $fa-var-ferry, - "arrows-down-to-people": $fa-var-arrows-down-to-people, - "seedling": $fa-var-seedling, - "sprout": $fa-var-sprout, - "left-right": $fa-var-left-right, - "arrows-alt-h": $fa-var-arrows-alt-h, - "boxes-packing": $fa-var-boxes-packing, - "circle-arrow-left": $fa-var-circle-arrow-left, - "arrow-circle-left": $fa-var-arrow-circle-left, - "group-arrows-rotate": $fa-var-group-arrows-rotate, - "bowl-food": $fa-var-bowl-food, - "candy-cane": $fa-var-candy-cane, - "arrow-down-wide-short": $fa-var-arrow-down-wide-short, - "sort-amount-asc": $fa-var-sort-amount-asc, - "sort-amount-down": $fa-var-sort-amount-down, - "cloud-bolt": $fa-var-cloud-bolt, - "thunderstorm": $fa-var-thunderstorm, - "text-slash": $fa-var-text-slash, - "remove-format": $fa-var-remove-format, - "face-smile-wink": $fa-var-face-smile-wink, - "smile-wink": $fa-var-smile-wink, - "file-word": $fa-var-file-word, - "file-powerpoint": $fa-var-file-powerpoint, - "arrows-left-right": $fa-var-arrows-left-right, - "arrows-h": $fa-var-arrows-h, - "house-lock": $fa-var-house-lock, - "cloud-arrow-down": $fa-var-cloud-arrow-down, - "cloud-download": $fa-var-cloud-download, - "cloud-download-alt": $fa-var-cloud-download-alt, - "children": $fa-var-children, - "chalkboard": $fa-var-chalkboard, - "blackboard": $fa-var-blackboard, - "user-large-slash": $fa-var-user-large-slash, - "user-alt-slash": $fa-var-user-alt-slash, - "envelope-open": $fa-var-envelope-open, - "handshake-simple-slash": $fa-var-handshake-simple-slash, - "handshake-alt-slash": $fa-var-handshake-alt-slash, - "mattress-pillow": $fa-var-mattress-pillow, - "guarani-sign": $fa-var-guarani-sign, - "arrows-rotate": $fa-var-arrows-rotate, - "refresh": $fa-var-refresh, - "sync": $fa-var-sync, - "fire-extinguisher": $fa-var-fire-extinguisher, - "cruzeiro-sign": $fa-var-cruzeiro-sign, - "greater-than-equal": $fa-var-greater-than-equal, - "shield-halved": $fa-var-shield-halved, - "shield-alt": $fa-var-shield-alt, - "book-atlas": $fa-var-book-atlas, - "atlas": $fa-var-atlas, - "virus": $fa-var-virus, - "envelope-circle-check": $fa-var-envelope-circle-check, - "layer-group": $fa-var-layer-group, - "arrows-to-dot": $fa-var-arrows-to-dot, - "archway": $fa-var-archway, - "heart-circle-check": $fa-var-heart-circle-check, - "house-chimney-crack": $fa-var-house-chimney-crack, - "house-damage": $fa-var-house-damage, - "file-zipper": $fa-var-file-zipper, - "file-archive": $fa-var-file-archive, - "square": $fa-var-square, - "martini-glass-empty": $fa-var-martini-glass-empty, - "glass-martini": $fa-var-glass-martini, - "couch": $fa-var-couch, - "cedi-sign": $fa-var-cedi-sign, - "italic": $fa-var-italic, - "table-cells-column-lock": $fa-var-table-cells-column-lock, - "church": $fa-var-church, - "comments-dollar": $fa-var-comments-dollar, - "democrat": $fa-var-democrat, - "z": $fa-var-z, - "person-skiing": $fa-var-person-skiing, - "skiing": $fa-var-skiing, - "road-lock": $fa-var-road-lock, - "a": $fa-var-a, - "temperature-arrow-down": $fa-var-temperature-arrow-down, - "temperature-down": $fa-var-temperature-down, - "feather-pointed": $fa-var-feather-pointed, - "feather-alt": $fa-var-feather-alt, - "p": $fa-var-p, - "snowflake": $fa-var-snowflake, - "newspaper": $fa-var-newspaper, - "rectangle-ad": $fa-var-rectangle-ad, - "ad": $fa-var-ad, - "circle-arrow-right": $fa-var-circle-arrow-right, - "arrow-circle-right": $fa-var-arrow-circle-right, - "filter-circle-xmark": $fa-var-filter-circle-xmark, - "locust": $fa-var-locust, - "sort": $fa-var-sort, - "unsorted": $fa-var-unsorted, - "list-ol": $fa-var-list-ol, - "list-1-2": $fa-var-list-1-2, - "list-numeric": $fa-var-list-numeric, - "person-dress-burst": $fa-var-person-dress-burst, - "money-check-dollar": $fa-var-money-check-dollar, - "money-check-alt": $fa-var-money-check-alt, - "vector-square": $fa-var-vector-square, - "bread-slice": $fa-var-bread-slice, - "language": $fa-var-language, - "face-kiss-wink-heart": $fa-var-face-kiss-wink-heart, - "kiss-wink-heart": $fa-var-kiss-wink-heart, - "filter": $fa-var-filter, - "question": $fa-var-question, - "file-signature": $fa-var-file-signature, - "up-down-left-right": $fa-var-up-down-left-right, - "arrows-alt": $fa-var-arrows-alt, - "house-chimney-user": $fa-var-house-chimney-user, - "hand-holding-heart": $fa-var-hand-holding-heart, - "puzzle-piece": $fa-var-puzzle-piece, - "money-check": $fa-var-money-check, - "star-half-stroke": $fa-var-star-half-stroke, - "star-half-alt": $fa-var-star-half-alt, - "code": $fa-var-code, - "whiskey-glass": $fa-var-whiskey-glass, - "glass-whiskey": $fa-var-glass-whiskey, - "building-circle-exclamation": $fa-var-building-circle-exclamation, - "magnifying-glass-chart": $fa-var-magnifying-glass-chart, - "arrow-up-right-from-square": $fa-var-arrow-up-right-from-square, - "external-link": $fa-var-external-link, - "cubes-stacked": $fa-var-cubes-stacked, - "won-sign": $fa-var-won-sign, - "krw": $fa-var-krw, - "won": $fa-var-won, - "virus-covid": $fa-var-virus-covid, - "austral-sign": $fa-var-austral-sign, - "f": $fa-var-f, - "leaf": $fa-var-leaf, - "road": $fa-var-road, - "taxi": $fa-var-taxi, - "cab": $fa-var-cab, - "person-circle-plus": $fa-var-person-circle-plus, - "chart-pie": $fa-var-chart-pie, - "pie-chart": $fa-var-pie-chart, - "bolt-lightning": $fa-var-bolt-lightning, - "sack-xmark": $fa-var-sack-xmark, - "file-excel": $fa-var-file-excel, - "file-contract": $fa-var-file-contract, - "fish-fins": $fa-var-fish-fins, - "building-flag": $fa-var-building-flag, - "face-grin-beam": $fa-var-face-grin-beam, - "grin-beam": $fa-var-grin-beam, - "object-ungroup": $fa-var-object-ungroup, - "poop": $fa-var-poop, - "location-pin": $fa-var-location-pin, - "map-marker": $fa-var-map-marker, - "kaaba": $fa-var-kaaba, - "toilet-paper": $fa-var-toilet-paper, - "helmet-safety": $fa-var-helmet-safety, - "hard-hat": $fa-var-hard-hat, - "hat-hard": $fa-var-hat-hard, - "eject": $fa-var-eject, - "circle-right": $fa-var-circle-right, - "arrow-alt-circle-right": $fa-var-arrow-alt-circle-right, - "plane-circle-check": $fa-var-plane-circle-check, - "face-rolling-eyes": $fa-var-face-rolling-eyes, - "meh-rolling-eyes": $fa-var-meh-rolling-eyes, - "object-group": $fa-var-object-group, - "chart-line": $fa-var-chart-line, - "line-chart": $fa-var-line-chart, - "mask-ventilator": $fa-var-mask-ventilator, - "arrow-right": $fa-var-arrow-right, - "signs-post": $fa-var-signs-post, - "map-signs": $fa-var-map-signs, - "cash-register": $fa-var-cash-register, - "person-circle-question": $fa-var-person-circle-question, - "h": $fa-var-h, - "tarp": $fa-var-tarp, - "screwdriver-wrench": $fa-var-screwdriver-wrench, - "tools": $fa-var-tools, - "arrows-to-eye": $fa-var-arrows-to-eye, - "plug-circle-bolt": $fa-var-plug-circle-bolt, - "heart": $fa-var-heart, - "mars-and-venus": $fa-var-mars-and-venus, - "house-user": $fa-var-house-user, - "home-user": $fa-var-home-user, - "dumpster-fire": $fa-var-dumpster-fire, - "house-crack": $fa-var-house-crack, - "martini-glass-citrus": $fa-var-martini-glass-citrus, - "cocktail": $fa-var-cocktail, - "face-surprise": $fa-var-face-surprise, - "surprise": $fa-var-surprise, - "bottle-water": $fa-var-bottle-water, - "circle-pause": $fa-var-circle-pause, - "pause-circle": $fa-var-pause-circle, - "toilet-paper-slash": $fa-var-toilet-paper-slash, - "apple-whole": $fa-var-apple-whole, - "apple-alt": $fa-var-apple-alt, - "kitchen-set": $fa-var-kitchen-set, - "r": $fa-var-r, - "temperature-quarter": $fa-var-temperature-quarter, - "temperature-1": $fa-var-temperature-1, - "thermometer-1": $fa-var-thermometer-1, - "thermometer-quarter": $fa-var-thermometer-quarter, - "cube": $fa-var-cube, - "bitcoin-sign": $fa-var-bitcoin-sign, - "shield-dog": $fa-var-shield-dog, - "solar-panel": $fa-var-solar-panel, - "lock-open": $fa-var-lock-open, - "elevator": $fa-var-elevator, - "money-bill-transfer": $fa-var-money-bill-transfer, - "money-bill-trend-up": $fa-var-money-bill-trend-up, - "house-flood-water-circle-arrow-right": $fa-var-house-flood-water-circle-arrow-right, - "square-poll-horizontal": $fa-var-square-poll-horizontal, - "poll-h": $fa-var-poll-h, - "circle": $fa-var-circle, - "backward-fast": $fa-var-backward-fast, - "fast-backward": $fa-var-fast-backward, - "recycle": $fa-var-recycle, - "user-astronaut": $fa-var-user-astronaut, - "plane-slash": $fa-var-plane-slash, - "trademark": $fa-var-trademark, - "basketball": $fa-var-basketball, - "basketball-ball": $fa-var-basketball-ball, - "satellite-dish": $fa-var-satellite-dish, - "circle-up": $fa-var-circle-up, - "arrow-alt-circle-up": $fa-var-arrow-alt-circle-up, - "mobile-screen-button": $fa-var-mobile-screen-button, - "mobile-alt": $fa-var-mobile-alt, - "volume-high": $fa-var-volume-high, - "volume-up": $fa-var-volume-up, - "users-rays": $fa-var-users-rays, - "wallet": $fa-var-wallet, - "clipboard-check": $fa-var-clipboard-check, - "file-audio": $fa-var-file-audio, - "burger": $fa-var-burger, - "hamburger": $fa-var-hamburger, - "wrench": $fa-var-wrench, - "bugs": $fa-var-bugs, - "rupee-sign": $fa-var-rupee-sign, - "rupee": $fa-var-rupee, - "file-image": $fa-var-file-image, - "circle-question": $fa-var-circle-question, - "question-circle": $fa-var-question-circle, - "plane-departure": $fa-var-plane-departure, - "handshake-slash": $fa-var-handshake-slash, - "book-bookmark": $fa-var-book-bookmark, - "code-branch": $fa-var-code-branch, - "hat-cowboy": $fa-var-hat-cowboy, - "bridge": $fa-var-bridge, - "phone-flip": $fa-var-phone-flip, - "phone-alt": $fa-var-phone-alt, - "truck-front": $fa-var-truck-front, - "cat": $fa-var-cat, - "anchor-circle-exclamation": $fa-var-anchor-circle-exclamation, - "truck-field": $fa-var-truck-field, - "route": $fa-var-route, - "clipboard-question": $fa-var-clipboard-question, - "panorama": $fa-var-panorama, - "comment-medical": $fa-var-comment-medical, - "teeth-open": $fa-var-teeth-open, - "file-circle-minus": $fa-var-file-circle-minus, - "tags": $fa-var-tags, - "wine-glass": $fa-var-wine-glass, - "forward-fast": $fa-var-forward-fast, - "fast-forward": $fa-var-fast-forward, - "face-meh-blank": $fa-var-face-meh-blank, - "meh-blank": $fa-var-meh-blank, - "square-parking": $fa-var-square-parking, - "parking": $fa-var-parking, - "house-signal": $fa-var-house-signal, - "bars-progress": $fa-var-bars-progress, - "tasks-alt": $fa-var-tasks-alt, - "faucet-drip": $fa-var-faucet-drip, - "cart-flatbed": $fa-var-cart-flatbed, - "dolly-flatbed": $fa-var-dolly-flatbed, - "ban-smoking": $fa-var-ban-smoking, - "smoking-ban": $fa-var-smoking-ban, - "terminal": $fa-var-terminal, - "mobile-button": $fa-var-mobile-button, - "house-medical-flag": $fa-var-house-medical-flag, - "basket-shopping": $fa-var-basket-shopping, - "shopping-basket": $fa-var-shopping-basket, - "tape": $fa-var-tape, - "bus-simple": $fa-var-bus-simple, - "bus-alt": $fa-var-bus-alt, - "eye": $fa-var-eye, - "face-sad-cry": $fa-var-face-sad-cry, - "sad-cry": $fa-var-sad-cry, - "audio-description": $fa-var-audio-description, - "person-military-to-person": $fa-var-person-military-to-person, - "file-shield": $fa-var-file-shield, - "user-slash": $fa-var-user-slash, - "pen": $fa-var-pen, - "tower-observation": $fa-var-tower-observation, - "file-code": $fa-var-file-code, - "signal": $fa-var-signal, - "signal-5": $fa-var-signal-5, - "signal-perfect": $fa-var-signal-perfect, - "bus": $fa-var-bus, - "heart-circle-xmark": $fa-var-heart-circle-xmark, - "house-chimney": $fa-var-house-chimney, - "home-lg": $fa-var-home-lg, - "window-maximize": $fa-var-window-maximize, - "face-frown": $fa-var-face-frown, - "frown": $fa-var-frown, - "prescription": $fa-var-prescription, - "shop": $fa-var-shop, - "store-alt": $fa-var-store-alt, - "floppy-disk": $fa-var-floppy-disk, - "save": $fa-var-save, - "vihara": $fa-var-vihara, - "scale-unbalanced": $fa-var-scale-unbalanced, - "balance-scale-left": $fa-var-balance-scale-left, - "sort-up": $fa-var-sort-up, - "sort-asc": $fa-var-sort-asc, - "comment-dots": $fa-var-comment-dots, - "commenting": $fa-var-commenting, - "plant-wilt": $fa-var-plant-wilt, - "diamond": $fa-var-diamond, - "face-grin-squint": $fa-var-face-grin-squint, - "grin-squint": $fa-var-grin-squint, - "hand-holding-dollar": $fa-var-hand-holding-dollar, - "hand-holding-usd": $fa-var-hand-holding-usd, - "chart-diagram": $fa-var-chart-diagram, - "bacterium": $fa-var-bacterium, - "hand-pointer": $fa-var-hand-pointer, - "drum-steelpan": $fa-var-drum-steelpan, - "hand-scissors": $fa-var-hand-scissors, - "hands-praying": $fa-var-hands-praying, - "praying-hands": $fa-var-praying-hands, - "arrow-rotate-right": $fa-var-arrow-rotate-right, - "arrow-right-rotate": $fa-var-arrow-right-rotate, - "arrow-rotate-forward": $fa-var-arrow-rotate-forward, - "redo": $fa-var-redo, - "biohazard": $fa-var-biohazard, - "location-crosshairs": $fa-var-location-crosshairs, - "location": $fa-var-location, - "mars-double": $fa-var-mars-double, - "child-dress": $fa-var-child-dress, - "users-between-lines": $fa-var-users-between-lines, - "lungs-virus": $fa-var-lungs-virus, - "face-grin-tears": $fa-var-face-grin-tears, - "grin-tears": $fa-var-grin-tears, - "phone": $fa-var-phone, - "calendar-xmark": $fa-var-calendar-xmark, - "calendar-times": $fa-var-calendar-times, - "child-reaching": $fa-var-child-reaching, - "head-side-virus": $fa-var-head-side-virus, - "user-gear": $fa-var-user-gear, - "user-cog": $fa-var-user-cog, - "arrow-up-1-9": $fa-var-arrow-up-1-9, - "sort-numeric-up": $fa-var-sort-numeric-up, - "door-closed": $fa-var-door-closed, - "shield-virus": $fa-var-shield-virus, - "dice-six": $fa-var-dice-six, - "mosquito-net": $fa-var-mosquito-net, - "file-fragment": $fa-var-file-fragment, - "bridge-water": $fa-var-bridge-water, - "person-booth": $fa-var-person-booth, - "text-width": $fa-var-text-width, - "hat-wizard": $fa-var-hat-wizard, - "pen-fancy": $fa-var-pen-fancy, - "person-digging": $fa-var-person-digging, - "digging": $fa-var-digging, - "trash": $fa-var-trash, - "gauge-simple": $fa-var-gauge-simple, - "gauge-simple-med": $fa-var-gauge-simple-med, - "tachometer-average": $fa-var-tachometer-average, - "book-medical": $fa-var-book-medical, - "poo": $fa-var-poo, - "quote-right": $fa-var-quote-right, - "quote-right-alt": $fa-var-quote-right-alt, - "shirt": $fa-var-shirt, - "t-shirt": $fa-var-t-shirt, - "tshirt": $fa-var-tshirt, - "cubes": $fa-var-cubes, - "divide": $fa-var-divide, - "tenge-sign": $fa-var-tenge-sign, - "tenge": $fa-var-tenge, - "headphones": $fa-var-headphones, - "hands-holding": $fa-var-hands-holding, - "hands-clapping": $fa-var-hands-clapping, - "republican": $fa-var-republican, - "arrow-left": $fa-var-arrow-left, - "person-circle-xmark": $fa-var-person-circle-xmark, - "ruler": $fa-var-ruler, - "align-left": $fa-var-align-left, - "dice-d6": $fa-var-dice-d6, - "restroom": $fa-var-restroom, - "j": $fa-var-j, - "users-viewfinder": $fa-var-users-viewfinder, - "file-video": $fa-var-file-video, - "up-right-from-square": $fa-var-up-right-from-square, - "external-link-alt": $fa-var-external-link-alt, - "table-cells": $fa-var-table-cells, - "th": $fa-var-th, - "file-pdf": $fa-var-file-pdf, - "book-bible": $fa-var-book-bible, - "bible": $fa-var-bible, - "o": $fa-var-o, - "suitcase-medical": $fa-var-suitcase-medical, - "medkit": $fa-var-medkit, - "user-secret": $fa-var-user-secret, - "otter": $fa-var-otter, - "person-dress": $fa-var-person-dress, - "female": $fa-var-female, - "comment-dollar": $fa-var-comment-dollar, - "business-time": $fa-var-business-time, - "briefcase-clock": $fa-var-briefcase-clock, - "table-cells-large": $fa-var-table-cells-large, - "th-large": $fa-var-th-large, - "book-tanakh": $fa-var-book-tanakh, - "tanakh": $fa-var-tanakh, - "phone-volume": $fa-var-phone-volume, - "volume-control-phone": $fa-var-volume-control-phone, - "hat-cowboy-side": $fa-var-hat-cowboy-side, - "clipboard-user": $fa-var-clipboard-user, - "child": $fa-var-child, - "lira-sign": $fa-var-lira-sign, - "satellite": $fa-var-satellite, - "plane-lock": $fa-var-plane-lock, - "tag": $fa-var-tag, - "comment": $fa-var-comment, - "cake-candles": $fa-var-cake-candles, - "birthday-cake": $fa-var-birthday-cake, - "cake": $fa-var-cake, - "envelope": $fa-var-envelope, - "angles-up": $fa-var-angles-up, - "angle-double-up": $fa-var-angle-double-up, - "paperclip": $fa-var-paperclip, - "arrow-right-to-city": $fa-var-arrow-right-to-city, - "ribbon": $fa-var-ribbon, - "lungs": $fa-var-lungs, - "arrow-up-9-1": $fa-var-arrow-up-9-1, - "sort-numeric-up-alt": $fa-var-sort-numeric-up-alt, - "litecoin-sign": $fa-var-litecoin-sign, - "border-none": $fa-var-border-none, - "circle-nodes": $fa-var-circle-nodes, - "parachute-box": $fa-var-parachute-box, - "indent": $fa-var-indent, - "truck-field-un": $fa-var-truck-field-un, - "hourglass": $fa-var-hourglass, - "hourglass-empty": $fa-var-hourglass-empty, - "mountain": $fa-var-mountain, - "user-doctor": $fa-var-user-doctor, - "user-md": $fa-var-user-md, - "circle-info": $fa-var-circle-info, - "info-circle": $fa-var-info-circle, - "cloud-meatball": $fa-var-cloud-meatball, - "camera": $fa-var-camera, - "camera-alt": $fa-var-camera-alt, - "square-virus": $fa-var-square-virus, - "meteor": $fa-var-meteor, - "car-on": $fa-var-car-on, - "sleigh": $fa-var-sleigh, - "arrow-down-1-9": $fa-var-arrow-down-1-9, - "sort-numeric-asc": $fa-var-sort-numeric-asc, - "sort-numeric-down": $fa-var-sort-numeric-down, - "hand-holding-droplet": $fa-var-hand-holding-droplet, - "hand-holding-water": $fa-var-hand-holding-water, - "water": $fa-var-water, - "calendar-check": $fa-var-calendar-check, - "braille": $fa-var-braille, - "prescription-bottle-medical": $fa-var-prescription-bottle-medical, - "prescription-bottle-alt": $fa-var-prescription-bottle-alt, - "landmark": $fa-var-landmark, - "truck": $fa-var-truck, - "crosshairs": $fa-var-crosshairs, - "person-cane": $fa-var-person-cane, - "tent": $fa-var-tent, - "vest-patches": $fa-var-vest-patches, - "check-double": $fa-var-check-double, - "arrow-down-a-z": $fa-var-arrow-down-a-z, - "sort-alpha-asc": $fa-var-sort-alpha-asc, - "sort-alpha-down": $fa-var-sort-alpha-down, - "money-bill-wheat": $fa-var-money-bill-wheat, - "cookie": $fa-var-cookie, - "arrow-rotate-left": $fa-var-arrow-rotate-left, - "arrow-left-rotate": $fa-var-arrow-left-rotate, - "arrow-rotate-back": $fa-var-arrow-rotate-back, - "arrow-rotate-backward": $fa-var-arrow-rotate-backward, - "undo": $fa-var-undo, - "hard-drive": $fa-var-hard-drive, - "hdd": $fa-var-hdd, - "face-grin-squint-tears": $fa-var-face-grin-squint-tears, - "grin-squint-tears": $fa-var-grin-squint-tears, - "dumbbell": $fa-var-dumbbell, - "rectangle-list": $fa-var-rectangle-list, - "list-alt": $fa-var-list-alt, - "tarp-droplet": $fa-var-tarp-droplet, - "house-medical-circle-check": $fa-var-house-medical-circle-check, - "person-skiing-nordic": $fa-var-person-skiing-nordic, - "skiing-nordic": $fa-var-skiing-nordic, - "calendar-plus": $fa-var-calendar-plus, - "plane-arrival": $fa-var-plane-arrival, - "circle-left": $fa-var-circle-left, - "arrow-alt-circle-left": $fa-var-arrow-alt-circle-left, - "train-subway": $fa-var-train-subway, - "subway": $fa-var-subway, - "chart-gantt": $fa-var-chart-gantt, - "indian-rupee-sign": $fa-var-indian-rupee-sign, - "indian-rupee": $fa-var-indian-rupee, - "inr": $fa-var-inr, - "crop-simple": $fa-var-crop-simple, - "crop-alt": $fa-var-crop-alt, - "money-bill-1": $fa-var-money-bill-1, - "money-bill-alt": $fa-var-money-bill-alt, - "left-long": $fa-var-left-long, - "long-arrow-alt-left": $fa-var-long-arrow-alt-left, - "dna": $fa-var-dna, - "virus-slash": $fa-var-virus-slash, - "minus": $fa-var-minus, - "subtract": $fa-var-subtract, - "chess": $fa-var-chess, - "arrow-left-long": $fa-var-arrow-left-long, - "long-arrow-left": $fa-var-long-arrow-left, - "plug-circle-check": $fa-var-plug-circle-check, - "street-view": $fa-var-street-view, - "franc-sign": $fa-var-franc-sign, - "volume-off": $fa-var-volume-off, - "hands-asl-interpreting": $fa-var-hands-asl-interpreting, - "american-sign-language-interpreting": $fa-var-american-sign-language-interpreting, - "asl-interpreting": $fa-var-asl-interpreting, - "hands-american-sign-language-interpreting": $fa-var-hands-american-sign-language-interpreting, - "gear": $fa-var-gear, - "cog": $fa-var-cog, - "droplet-slash": $fa-var-droplet-slash, - "tint-slash": $fa-var-tint-slash, - "mosque": $fa-var-mosque, - "mosquito": $fa-var-mosquito, - "star-of-david": $fa-var-star-of-david, - "person-military-rifle": $fa-var-person-military-rifle, - "cart-shopping": $fa-var-cart-shopping, - "shopping-cart": $fa-var-shopping-cart, - "vials": $fa-var-vials, - "plug-circle-plus": $fa-var-plug-circle-plus, - "place-of-worship": $fa-var-place-of-worship, - "grip-vertical": $fa-var-grip-vertical, - "hexagon-nodes": $fa-var-hexagon-nodes, - "arrow-turn-up": $fa-var-arrow-turn-up, - "level-up": $fa-var-level-up, - "u": $fa-var-u, - "square-root-variable": $fa-var-square-root-variable, - "square-root-alt": $fa-var-square-root-alt, - "clock": $fa-var-clock, - "clock-four": $fa-var-clock-four, - "backward-step": $fa-var-backward-step, - "step-backward": $fa-var-step-backward, - "pallet": $fa-var-pallet, - "faucet": $fa-var-faucet, - "baseball-bat-ball": $fa-var-baseball-bat-ball, - "s": $fa-var-s, - "timeline": $fa-var-timeline, - "keyboard": $fa-var-keyboard, - "caret-down": $fa-var-caret-down, - "house-chimney-medical": $fa-var-house-chimney-medical, - "clinic-medical": $fa-var-clinic-medical, - "temperature-three-quarters": $fa-var-temperature-three-quarters, - "temperature-3": $fa-var-temperature-3, - "thermometer-3": $fa-var-thermometer-3, - "thermometer-three-quarters": $fa-var-thermometer-three-quarters, - "mobile-screen": $fa-var-mobile-screen, - "mobile-android-alt": $fa-var-mobile-android-alt, - "plane-up": $fa-var-plane-up, - "piggy-bank": $fa-var-piggy-bank, - "battery-half": $fa-var-battery-half, - "battery-3": $fa-var-battery-3, - "mountain-city": $fa-var-mountain-city, - "coins": $fa-var-coins, - "khanda": $fa-var-khanda, - "sliders": $fa-var-sliders, - "sliders-h": $fa-var-sliders-h, - "folder-tree": $fa-var-folder-tree, - "network-wired": $fa-var-network-wired, - "map-pin": $fa-var-map-pin, - "hamsa": $fa-var-hamsa, - "cent-sign": $fa-var-cent-sign, - "flask": $fa-var-flask, - "person-pregnant": $fa-var-person-pregnant, - "wand-sparkles": $fa-var-wand-sparkles, - "ellipsis-vertical": $fa-var-ellipsis-vertical, - "ellipsis-v": $fa-var-ellipsis-v, - "ticket": $fa-var-ticket, - "power-off": $fa-var-power-off, - "right-long": $fa-var-right-long, - "long-arrow-alt-right": $fa-var-long-arrow-alt-right, - "flag-usa": $fa-var-flag-usa, - "laptop-file": $fa-var-laptop-file, - "tty": $fa-var-tty, - "teletype": $fa-var-teletype, - "diagram-next": $fa-var-diagram-next, - "person-rifle": $fa-var-person-rifle, - "house-medical-circle-exclamation": $fa-var-house-medical-circle-exclamation, - "closed-captioning": $fa-var-closed-captioning, - "person-hiking": $fa-var-person-hiking, - "hiking": $fa-var-hiking, - "venus-double": $fa-var-venus-double, - "images": $fa-var-images, - "calculator": $fa-var-calculator, - "people-pulling": $fa-var-people-pulling, - "n": $fa-var-n, - "cable-car": $fa-var-cable-car, - "tram": $fa-var-tram, - "cloud-rain": $fa-var-cloud-rain, - "building-circle-xmark": $fa-var-building-circle-xmark, - "ship": $fa-var-ship, - "arrows-down-to-line": $fa-var-arrows-down-to-line, - "download": $fa-var-download, - "face-grin": $fa-var-face-grin, - "grin": $fa-var-grin, - "delete-left": $fa-var-delete-left, - "backspace": $fa-var-backspace, - "eye-dropper": $fa-var-eye-dropper, - "eye-dropper-empty": $fa-var-eye-dropper-empty, - "eyedropper": $fa-var-eyedropper, - "file-circle-check": $fa-var-file-circle-check, - "forward": $fa-var-forward, - "mobile": $fa-var-mobile, - "mobile-android": $fa-var-mobile-android, - "mobile-phone": $fa-var-mobile-phone, - "face-meh": $fa-var-face-meh, - "meh": $fa-var-meh, - "align-center": $fa-var-align-center, - "book-skull": $fa-var-book-skull, - "book-dead": $fa-var-book-dead, - "id-card": $fa-var-id-card, - "drivers-license": $fa-var-drivers-license, - "outdent": $fa-var-outdent, - "dedent": $fa-var-dedent, - "heart-circle-exclamation": $fa-var-heart-circle-exclamation, - "house": $fa-var-house, - "home": $fa-var-home, - "home-alt": $fa-var-home-alt, - "home-lg-alt": $fa-var-home-lg-alt, - "calendar-week": $fa-var-calendar-week, - "laptop-medical": $fa-var-laptop-medical, - "b": $fa-var-b, - "file-medical": $fa-var-file-medical, - "dice-one": $fa-var-dice-one, - "kiwi-bird": $fa-var-kiwi-bird, - "arrow-right-arrow-left": $fa-var-arrow-right-arrow-left, - "exchange": $fa-var-exchange, - "rotate-right": $fa-var-rotate-right, - "redo-alt": $fa-var-redo-alt, - "rotate-forward": $fa-var-rotate-forward, - "utensils": $fa-var-utensils, - "cutlery": $fa-var-cutlery, - "arrow-up-wide-short": $fa-var-arrow-up-wide-short, - "sort-amount-up": $fa-var-sort-amount-up, - "mill-sign": $fa-var-mill-sign, - "bowl-rice": $fa-var-bowl-rice, - "skull": $fa-var-skull, - "tower-broadcast": $fa-var-tower-broadcast, - "broadcast-tower": $fa-var-broadcast-tower, - "truck-pickup": $fa-var-truck-pickup, - "up-long": $fa-var-up-long, - "long-arrow-alt-up": $fa-var-long-arrow-alt-up, - "stop": $fa-var-stop, - "code-merge": $fa-var-code-merge, - "upload": $fa-var-upload, - "hurricane": $fa-var-hurricane, - "mound": $fa-var-mound, - "toilet-portable": $fa-var-toilet-portable, - "compact-disc": $fa-var-compact-disc, - "file-arrow-down": $fa-var-file-arrow-down, - "file-download": $fa-var-file-download, - "caravan": $fa-var-caravan, - "shield-cat": $fa-var-shield-cat, - "bolt": $fa-var-bolt, - "zap": $fa-var-zap, - "glass-water": $fa-var-glass-water, - "oil-well": $fa-var-oil-well, - "vault": $fa-var-vault, - "mars": $fa-var-mars, - "toilet": $fa-var-toilet, - "plane-circle-xmark": $fa-var-plane-circle-xmark, - "yen-sign": $fa-var-yen-sign, - "cny": $fa-var-cny, - "jpy": $fa-var-jpy, - "rmb": $fa-var-rmb, - "yen": $fa-var-yen, - "ruble-sign": $fa-var-ruble-sign, - "rouble": $fa-var-rouble, - "rub": $fa-var-rub, - "ruble": $fa-var-ruble, - "sun": $fa-var-sun, - "guitar": $fa-var-guitar, - "face-laugh-wink": $fa-var-face-laugh-wink, - "laugh-wink": $fa-var-laugh-wink, - "horse-head": $fa-var-horse-head, - "bore-hole": $fa-var-bore-hole, - "industry": $fa-var-industry, - "circle-down": $fa-var-circle-down, - "arrow-alt-circle-down": $fa-var-arrow-alt-circle-down, - "arrows-turn-to-dots": $fa-var-arrows-turn-to-dots, - "florin-sign": $fa-var-florin-sign, - "arrow-down-short-wide": $fa-var-arrow-down-short-wide, - "sort-amount-desc": $fa-var-sort-amount-desc, - "sort-amount-down-alt": $fa-var-sort-amount-down-alt, - "less-than": $fa-var-less-than, - "angle-down": $fa-var-angle-down, - "car-tunnel": $fa-var-car-tunnel, - "head-side-cough": $fa-var-head-side-cough, - "grip-lines": $fa-var-grip-lines, - "thumbs-down": $fa-var-thumbs-down, - "user-lock": $fa-var-user-lock, - "arrow-right-long": $fa-var-arrow-right-long, - "long-arrow-right": $fa-var-long-arrow-right, - "anchor-circle-xmark": $fa-var-anchor-circle-xmark, - "ellipsis": $fa-var-ellipsis, - "ellipsis-h": $fa-var-ellipsis-h, - "chess-pawn": $fa-var-chess-pawn, - "kit-medical": $fa-var-kit-medical, - "first-aid": $fa-var-first-aid, - "person-through-window": $fa-var-person-through-window, - "toolbox": $fa-var-toolbox, - "hands-holding-circle": $fa-var-hands-holding-circle, - "bug": $fa-var-bug, - "credit-card": $fa-var-credit-card, - "credit-card-alt": $fa-var-credit-card-alt, - "car": $fa-var-car, - "automobile": $fa-var-automobile, - "hand-holding-hand": $fa-var-hand-holding-hand, - "book-open-reader": $fa-var-book-open-reader, - "book-reader": $fa-var-book-reader, - "mountain-sun": $fa-var-mountain-sun, - "arrows-left-right-to-line": $fa-var-arrows-left-right-to-line, - "dice-d20": $fa-var-dice-d20, - "truck-droplet": $fa-var-truck-droplet, - "file-circle-xmark": $fa-var-file-circle-xmark, - "temperature-arrow-up": $fa-var-temperature-arrow-up, - "temperature-up": $fa-var-temperature-up, - "medal": $fa-var-medal, - "bed": $fa-var-bed, - "square-h": $fa-var-square-h, - "h-square": $fa-var-h-square, - "podcast": $fa-var-podcast, - "temperature-full": $fa-var-temperature-full, - "temperature-4": $fa-var-temperature-4, - "thermometer-4": $fa-var-thermometer-4, - "thermometer-full": $fa-var-thermometer-full, - "bell": $fa-var-bell, - "superscript": $fa-var-superscript, - "plug-circle-xmark": $fa-var-plug-circle-xmark, - "star-of-life": $fa-var-star-of-life, - "phone-slash": $fa-var-phone-slash, - "paint-roller": $fa-var-paint-roller, - "handshake-angle": $fa-var-handshake-angle, - "hands-helping": $fa-var-hands-helping, - "location-dot": $fa-var-location-dot, - "map-marker-alt": $fa-var-map-marker-alt, - "file": $fa-var-file, - "greater-than": $fa-var-greater-than, - "person-swimming": $fa-var-person-swimming, - "swimmer": $fa-var-swimmer, - "arrow-down": $fa-var-arrow-down, - "droplet": $fa-var-droplet, - "tint": $fa-var-tint, - "eraser": $fa-var-eraser, - "earth-americas": $fa-var-earth-americas, - "earth": $fa-var-earth, - "earth-america": $fa-var-earth-america, - "globe-americas": $fa-var-globe-americas, - "person-burst": $fa-var-person-burst, - "dove": $fa-var-dove, - "battery-empty": $fa-var-battery-empty, - "battery-0": $fa-var-battery-0, - "socks": $fa-var-socks, - "inbox": $fa-var-inbox, - "section": $fa-var-section, - "gauge-high": $fa-var-gauge-high, - "tachometer-alt": $fa-var-tachometer-alt, - "tachometer-alt-fast": $fa-var-tachometer-alt-fast, - "envelope-open-text": $fa-var-envelope-open-text, - "hospital": $fa-var-hospital, - "hospital-alt": $fa-var-hospital-alt, - "hospital-wide": $fa-var-hospital-wide, - "wine-bottle": $fa-var-wine-bottle, - "chess-rook": $fa-var-chess-rook, - "bars-staggered": $fa-var-bars-staggered, - "reorder": $fa-var-reorder, - "stream": $fa-var-stream, - "dharmachakra": $fa-var-dharmachakra, - "hotdog": $fa-var-hotdog, - "person-walking-with-cane": $fa-var-person-walking-with-cane, - "blind": $fa-var-blind, - "drum": $fa-var-drum, - "ice-cream": $fa-var-ice-cream, - "heart-circle-bolt": $fa-var-heart-circle-bolt, - "fax": $fa-var-fax, - "paragraph": $fa-var-paragraph, - "check-to-slot": $fa-var-check-to-slot, - "vote-yea": $fa-var-vote-yea, - "star-half": $fa-var-star-half, - "boxes-stacked": $fa-var-boxes-stacked, - "boxes": $fa-var-boxes, - "boxes-alt": $fa-var-boxes-alt, - "link": $fa-var-link, - "chain": $fa-var-chain, - "ear-listen": $fa-var-ear-listen, - "assistive-listening-systems": $fa-var-assistive-listening-systems, - "tree-city": $fa-var-tree-city, - "play": $fa-var-play, - "font": $fa-var-font, - "table-cells-row-lock": $fa-var-table-cells-row-lock, - "rupiah-sign": $fa-var-rupiah-sign, - "magnifying-glass": $fa-var-magnifying-glass, - "search": $fa-var-search, - "table-tennis-paddle-ball": $fa-var-table-tennis-paddle-ball, - "ping-pong-paddle-ball": $fa-var-ping-pong-paddle-ball, - "table-tennis": $fa-var-table-tennis, - "person-dots-from-line": $fa-var-person-dots-from-line, - "diagnoses": $fa-var-diagnoses, - "trash-can-arrow-up": $fa-var-trash-can-arrow-up, - "trash-restore-alt": $fa-var-trash-restore-alt, - "naira-sign": $fa-var-naira-sign, - "cart-arrow-down": $fa-var-cart-arrow-down, - "walkie-talkie": $fa-var-walkie-talkie, - "file-pen": $fa-var-file-pen, - "file-edit": $fa-var-file-edit, - "receipt": $fa-var-receipt, - "square-pen": $fa-var-square-pen, - "pen-square": $fa-var-pen-square, - "pencil-square": $fa-var-pencil-square, - "suitcase-rolling": $fa-var-suitcase-rolling, - "person-circle-exclamation": $fa-var-person-circle-exclamation, - "chevron-down": $fa-var-chevron-down, - "battery-full": $fa-var-battery-full, - "battery": $fa-var-battery, - "battery-5": $fa-var-battery-5, - "skull-crossbones": $fa-var-skull-crossbones, - "code-compare": $fa-var-code-compare, - "list-ul": $fa-var-list-ul, - "list-dots": $fa-var-list-dots, - "school-lock": $fa-var-school-lock, - "tower-cell": $fa-var-tower-cell, - "down-long": $fa-var-down-long, - "long-arrow-alt-down": $fa-var-long-arrow-alt-down, - "ranking-star": $fa-var-ranking-star, - "chess-king": $fa-var-chess-king, - "person-harassing": $fa-var-person-harassing, - "brazilian-real-sign": $fa-var-brazilian-real-sign, - "landmark-dome": $fa-var-landmark-dome, - "landmark-alt": $fa-var-landmark-alt, - "arrow-up": $fa-var-arrow-up, - "tv": $fa-var-tv, - "television": $fa-var-television, - "tv-alt": $fa-var-tv-alt, - "shrimp": $fa-var-shrimp, - "list-check": $fa-var-list-check, - "tasks": $fa-var-tasks, - "jug-detergent": $fa-var-jug-detergent, - "circle-user": $fa-var-circle-user, - "user-circle": $fa-var-user-circle, - "user-shield": $fa-var-user-shield, - "wind": $fa-var-wind, - "car-burst": $fa-var-car-burst, - "car-crash": $fa-var-car-crash, - "y": $fa-var-y, - "person-snowboarding": $fa-var-person-snowboarding, - "snowboarding": $fa-var-snowboarding, - "truck-fast": $fa-var-truck-fast, - "shipping-fast": $fa-var-shipping-fast, - "fish": $fa-var-fish, - "user-graduate": $fa-var-user-graduate, - "circle-half-stroke": $fa-var-circle-half-stroke, - "adjust": $fa-var-adjust, - "clapperboard": $fa-var-clapperboard, - "circle-radiation": $fa-var-circle-radiation, - "radiation-alt": $fa-var-radiation-alt, - "baseball": $fa-var-baseball, - "baseball-ball": $fa-var-baseball-ball, - "jet-fighter-up": $fa-var-jet-fighter-up, - "diagram-project": $fa-var-diagram-project, - "project-diagram": $fa-var-project-diagram, - "copy": $fa-var-copy, - "volume-xmark": $fa-var-volume-xmark, - "volume-mute": $fa-var-volume-mute, - "volume-times": $fa-var-volume-times, - "hand-sparkles": $fa-var-hand-sparkles, - "grip": $fa-var-grip, - "grip-horizontal": $fa-var-grip-horizontal, - "share-from-square": $fa-var-share-from-square, - "share-square": $fa-var-share-square, - "child-combatant": $fa-var-child-combatant, - "child-rifle": $fa-var-child-rifle, - "gun": $fa-var-gun, - "square-phone": $fa-var-square-phone, - "phone-square": $fa-var-phone-square, - "plus": $fa-var-plus, - "add": $fa-var-add, - "expand": $fa-var-expand, - "computer": $fa-var-computer, - "xmark": $fa-var-xmark, - "close": $fa-var-close, - "multiply": $fa-var-multiply, - "remove": $fa-var-remove, - "times": $fa-var-times, - "arrows-up-down-left-right": $fa-var-arrows-up-down-left-right, - "arrows": $fa-var-arrows, - "chalkboard-user": $fa-var-chalkboard-user, - "chalkboard-teacher": $fa-var-chalkboard-teacher, - "peso-sign": $fa-var-peso-sign, - "building-shield": $fa-var-building-shield, - "baby": $fa-var-baby, - "users-line": $fa-var-users-line, - "quote-left": $fa-var-quote-left, - "quote-left-alt": $fa-var-quote-left-alt, - "tractor": $fa-var-tractor, - "trash-arrow-up": $fa-var-trash-arrow-up, - "trash-restore": $fa-var-trash-restore, - "arrow-down-up-lock": $fa-var-arrow-down-up-lock, - "lines-leaning": $fa-var-lines-leaning, - "ruler-combined": $fa-var-ruler-combined, - "copyright": $fa-var-copyright, - "equals": $fa-var-equals, - "blender": $fa-var-blender, - "teeth": $fa-var-teeth, - "shekel-sign": $fa-var-shekel-sign, - "ils": $fa-var-ils, - "shekel": $fa-var-shekel, - "sheqel": $fa-var-sheqel, - "sheqel-sign": $fa-var-sheqel-sign, - "map": $fa-var-map, - "rocket": $fa-var-rocket, - "photo-film": $fa-var-photo-film, - "photo-video": $fa-var-photo-video, - "folder-minus": $fa-var-folder-minus, - "hexagon-nodes-bolt": $fa-var-hexagon-nodes-bolt, - "store": $fa-var-store, - "arrow-trend-up": $fa-var-arrow-trend-up, - "plug-circle-minus": $fa-var-plug-circle-minus, - "sign-hanging": $fa-var-sign-hanging, - "sign": $fa-var-sign, - "bezier-curve": $fa-var-bezier-curve, - "bell-slash": $fa-var-bell-slash, - "tablet": $fa-var-tablet, - "tablet-android": $fa-var-tablet-android, - "school-flag": $fa-var-school-flag, - "fill": $fa-var-fill, - "angle-up": $fa-var-angle-up, - "drumstick-bite": $fa-var-drumstick-bite, - "holly-berry": $fa-var-holly-berry, - "chevron-left": $fa-var-chevron-left, - "bacteria": $fa-var-bacteria, - "hand-lizard": $fa-var-hand-lizard, - "notdef": $fa-var-notdef, - "disease": $fa-var-disease, - "briefcase-medical": $fa-var-briefcase-medical, - "genderless": $fa-var-genderless, - "chevron-right": $fa-var-chevron-right, - "retweet": $fa-var-retweet, - "car-rear": $fa-var-car-rear, - "car-alt": $fa-var-car-alt, - "pump-soap": $fa-var-pump-soap, - "video-slash": $fa-var-video-slash, - "battery-quarter": $fa-var-battery-quarter, - "battery-2": $fa-var-battery-2, - "radio": $fa-var-radio, - "baby-carriage": $fa-var-baby-carriage, - "carriage-baby": $fa-var-carriage-baby, - "traffic-light": $fa-var-traffic-light, - "thermometer": $fa-var-thermometer, - "vr-cardboard": $fa-var-vr-cardboard, - "hand-middle-finger": $fa-var-hand-middle-finger, - "percent": $fa-var-percent, - "percentage": $fa-var-percentage, - "truck-moving": $fa-var-truck-moving, - "glass-water-droplet": $fa-var-glass-water-droplet, - "display": $fa-var-display, - "face-smile": $fa-var-face-smile, - "smile": $fa-var-smile, - "thumbtack": $fa-var-thumbtack, - "thumb-tack": $fa-var-thumb-tack, - "trophy": $fa-var-trophy, - "person-praying": $fa-var-person-praying, - "pray": $fa-var-pray, - "hammer": $fa-var-hammer, - "hand-peace": $fa-var-hand-peace, - "rotate": $fa-var-rotate, - "sync-alt": $fa-var-sync-alt, - "spinner": $fa-var-spinner, - "robot": $fa-var-robot, - "peace": $fa-var-peace, - "gears": $fa-var-gears, - "cogs": $fa-var-cogs, - "warehouse": $fa-var-warehouse, - "arrow-up-right-dots": $fa-var-arrow-up-right-dots, - "splotch": $fa-var-splotch, - "face-grin-hearts": $fa-var-face-grin-hearts, - "grin-hearts": $fa-var-grin-hearts, - "dice-four": $fa-var-dice-four, - "sim-card": $fa-var-sim-card, - "transgender": $fa-var-transgender, - "transgender-alt": $fa-var-transgender-alt, - "mercury": $fa-var-mercury, - "arrow-turn-down": $fa-var-arrow-turn-down, - "level-down": $fa-var-level-down, - "person-falling-burst": $fa-var-person-falling-burst, - "award": $fa-var-award, - "ticket-simple": $fa-var-ticket-simple, - "ticket-alt": $fa-var-ticket-alt, - "building": $fa-var-building, - "angles-left": $fa-var-angles-left, - "angle-double-left": $fa-var-angle-double-left, - "qrcode": $fa-var-qrcode, - "clock-rotate-left": $fa-var-clock-rotate-left, - "history": $fa-var-history, - "face-grin-beam-sweat": $fa-var-face-grin-beam-sweat, - "grin-beam-sweat": $fa-var-grin-beam-sweat, - "file-export": $fa-var-file-export, - "arrow-right-from-file": $fa-var-arrow-right-from-file, - "shield": $fa-var-shield, - "shield-blank": $fa-var-shield-blank, - "arrow-up-short-wide": $fa-var-arrow-up-short-wide, - "sort-amount-up-alt": $fa-var-sort-amount-up-alt, - "comment-nodes": $fa-var-comment-nodes, - "house-medical": $fa-var-house-medical, - "golf-ball-tee": $fa-var-golf-ball-tee, - "golf-ball": $fa-var-golf-ball, - "circle-chevron-left": $fa-var-circle-chevron-left, - "chevron-circle-left": $fa-var-chevron-circle-left, - "house-chimney-window": $fa-var-house-chimney-window, - "pen-nib": $fa-var-pen-nib, - "tent-arrow-turn-left": $fa-var-tent-arrow-turn-left, - "tents": $fa-var-tents, - "wand-magic": $fa-var-wand-magic, - "magic": $fa-var-magic, - "dog": $fa-var-dog, - "carrot": $fa-var-carrot, - "moon": $fa-var-moon, - "wine-glass-empty": $fa-var-wine-glass-empty, - "wine-glass-alt": $fa-var-wine-glass-alt, - "cheese": $fa-var-cheese, - "yin-yang": $fa-var-yin-yang, - "music": $fa-var-music, - "code-commit": $fa-var-code-commit, - "temperature-low": $fa-var-temperature-low, - "person-biking": $fa-var-person-biking, - "biking": $fa-var-biking, - "broom": $fa-var-broom, - "shield-heart": $fa-var-shield-heart, - "gopuram": $fa-var-gopuram, - "earth-oceania": $fa-var-earth-oceania, - "globe-oceania": $fa-var-globe-oceania, - "square-xmark": $fa-var-square-xmark, - "times-square": $fa-var-times-square, - "xmark-square": $fa-var-xmark-square, - "hashtag": $fa-var-hashtag, - "up-right-and-down-left-from-center": $fa-var-up-right-and-down-left-from-center, - "expand-alt": $fa-var-expand-alt, - "oil-can": $fa-var-oil-can, - "t": $fa-var-t, - "hippo": $fa-var-hippo, - "chart-column": $fa-var-chart-column, - "infinity": $fa-var-infinity, - "vial-circle-check": $fa-var-vial-circle-check, - "person-arrow-down-to-line": $fa-var-person-arrow-down-to-line, - "voicemail": $fa-var-voicemail, - "fan": $fa-var-fan, - "person-walking-luggage": $fa-var-person-walking-luggage, - "up-down": $fa-var-up-down, - "arrows-alt-v": $fa-var-arrows-alt-v, - "cloud-moon-rain": $fa-var-cloud-moon-rain, - "calendar": $fa-var-calendar, - "trailer": $fa-var-trailer, - "bahai": $fa-var-bahai, - "haykal": $fa-var-haykal, - "sd-card": $fa-var-sd-card, - "dragon": $fa-var-dragon, - "shoe-prints": $fa-var-shoe-prints, - "circle-plus": $fa-var-circle-plus, - "plus-circle": $fa-var-plus-circle, - "face-grin-tongue-wink": $fa-var-face-grin-tongue-wink, - "grin-tongue-wink": $fa-var-grin-tongue-wink, - "hand-holding": $fa-var-hand-holding, - "plug-circle-exclamation": $fa-var-plug-circle-exclamation, - "link-slash": $fa-var-link-slash, - "chain-broken": $fa-var-chain-broken, - "chain-slash": $fa-var-chain-slash, - "unlink": $fa-var-unlink, - "clone": $fa-var-clone, - "person-walking-arrow-loop-left": $fa-var-person-walking-arrow-loop-left, - "arrow-up-z-a": $fa-var-arrow-up-z-a, - "sort-alpha-up-alt": $fa-var-sort-alpha-up-alt, - "fire-flame-curved": $fa-var-fire-flame-curved, - "fire-alt": $fa-var-fire-alt, - "tornado": $fa-var-tornado, - "file-circle-plus": $fa-var-file-circle-plus, - "book-quran": $fa-var-book-quran, - "quran": $fa-var-quran, - "anchor": $fa-var-anchor, - "border-all": $fa-var-border-all, - "face-angry": $fa-var-face-angry, - "angry": $fa-var-angry, - "cookie-bite": $fa-var-cookie-bite, - "arrow-trend-down": $fa-var-arrow-trend-down, - "rss": $fa-var-rss, - "feed": $fa-var-feed, - "draw-polygon": $fa-var-draw-polygon, - "scale-balanced": $fa-var-scale-balanced, - "balance-scale": $fa-var-balance-scale, - "gauge-simple-high": $fa-var-gauge-simple-high, - "tachometer": $fa-var-tachometer, - "tachometer-fast": $fa-var-tachometer-fast, - "shower": $fa-var-shower, - "desktop": $fa-var-desktop, - "desktop-alt": $fa-var-desktop-alt, - "m": $fa-var-m, - "table-list": $fa-var-table-list, - "th-list": $fa-var-th-list, - "comment-sms": $fa-var-comment-sms, - "sms": $fa-var-sms, - "book": $fa-var-book, - "user-plus": $fa-var-user-plus, - "check": $fa-var-check, - "battery-three-quarters": $fa-var-battery-three-quarters, - "battery-4": $fa-var-battery-4, - "house-circle-check": $fa-var-house-circle-check, - "angle-left": $fa-var-angle-left, - "diagram-successor": $fa-var-diagram-successor, - "truck-arrow-right": $fa-var-truck-arrow-right, - "arrows-split-up-and-left": $fa-var-arrows-split-up-and-left, - "hand-fist": $fa-var-hand-fist, - "fist-raised": $fa-var-fist-raised, - "cloud-moon": $fa-var-cloud-moon, - "briefcase": $fa-var-briefcase, - "person-falling": $fa-var-person-falling, - "image-portrait": $fa-var-image-portrait, - "portrait": $fa-var-portrait, - "user-tag": $fa-var-user-tag, - "rug": $fa-var-rug, - "earth-europe": $fa-var-earth-europe, - "globe-europe": $fa-var-globe-europe, - "cart-flatbed-suitcase": $fa-var-cart-flatbed-suitcase, - "luggage-cart": $fa-var-luggage-cart, - "rectangle-xmark": $fa-var-rectangle-xmark, - "rectangle-times": $fa-var-rectangle-times, - "times-rectangle": $fa-var-times-rectangle, - "window-close": $fa-var-window-close, - "baht-sign": $fa-var-baht-sign, - "book-open": $fa-var-book-open, - "book-journal-whills": $fa-var-book-journal-whills, - "journal-whills": $fa-var-journal-whills, - "handcuffs": $fa-var-handcuffs, - "triangle-exclamation": $fa-var-triangle-exclamation, - "exclamation-triangle": $fa-var-exclamation-triangle, - "warning": $fa-var-warning, - "database": $fa-var-database, - "share": $fa-var-share, - "mail-forward": $fa-var-mail-forward, - "bottle-droplet": $fa-var-bottle-droplet, - "mask-face": $fa-var-mask-face, - "hill-rockslide": $fa-var-hill-rockslide, - "right-left": $fa-var-right-left, - "exchange-alt": $fa-var-exchange-alt, - "paper-plane": $fa-var-paper-plane, - "road-circle-exclamation": $fa-var-road-circle-exclamation, - "dungeon": $fa-var-dungeon, - "align-right": $fa-var-align-right, - "money-bill-1-wave": $fa-var-money-bill-1-wave, - "money-bill-wave-alt": $fa-var-money-bill-wave-alt, - "life-ring": $fa-var-life-ring, - "hands": $fa-var-hands, - "sign-language": $fa-var-sign-language, - "signing": $fa-var-signing, - "calendar-day": $fa-var-calendar-day, - "water-ladder": $fa-var-water-ladder, - "ladder-water": $fa-var-ladder-water, - "swimming-pool": $fa-var-swimming-pool, - "arrows-up-down": $fa-var-arrows-up-down, - "arrows-v": $fa-var-arrows-v, - "face-grimace": $fa-var-face-grimace, - "grimace": $fa-var-grimace, - "wheelchair-move": $fa-var-wheelchair-move, - "wheelchair-alt": $fa-var-wheelchair-alt, - "turn-down": $fa-var-turn-down, - "level-down-alt": $fa-var-level-down-alt, - "person-walking-arrow-right": $fa-var-person-walking-arrow-right, - "square-envelope": $fa-var-square-envelope, - "envelope-square": $fa-var-envelope-square, - "dice": $fa-var-dice, - "bowling-ball": $fa-var-bowling-ball, - "brain": $fa-var-brain, - "bandage": $fa-var-bandage, - "band-aid": $fa-var-band-aid, - "calendar-minus": $fa-var-calendar-minus, - "circle-xmark": $fa-var-circle-xmark, - "times-circle": $fa-var-times-circle, - "xmark-circle": $fa-var-xmark-circle, - "gifts": $fa-var-gifts, - "hotel": $fa-var-hotel, - "earth-asia": $fa-var-earth-asia, - "globe-asia": $fa-var-globe-asia, - "id-card-clip": $fa-var-id-card-clip, - "id-card-alt": $fa-var-id-card-alt, - "magnifying-glass-plus": $fa-var-magnifying-glass-plus, - "search-plus": $fa-var-search-plus, - "thumbs-up": $fa-var-thumbs-up, - "user-clock": $fa-var-user-clock, - "hand-dots": $fa-var-hand-dots, - "allergies": $fa-var-allergies, - "file-invoice": $fa-var-file-invoice, - "window-minimize": $fa-var-window-minimize, - "mug-saucer": $fa-var-mug-saucer, - "coffee": $fa-var-coffee, - "brush": $fa-var-brush, - "file-half-dashed": $fa-var-file-half-dashed, - "mask": $fa-var-mask, - "magnifying-glass-minus": $fa-var-magnifying-glass-minus, - "search-minus": $fa-var-search-minus, - "ruler-vertical": $fa-var-ruler-vertical, - "user-large": $fa-var-user-large, - "user-alt": $fa-var-user-alt, - "train-tram": $fa-var-train-tram, - "user-nurse": $fa-var-user-nurse, - "syringe": $fa-var-syringe, - "cloud-sun": $fa-var-cloud-sun, - "stopwatch-20": $fa-var-stopwatch-20, - "square-full": $fa-var-square-full, - "magnet": $fa-var-magnet, - "jar": $fa-var-jar, - "note-sticky": $fa-var-note-sticky, - "sticky-note": $fa-var-sticky-note, - "bug-slash": $fa-var-bug-slash, - "arrow-up-from-water-pump": $fa-var-arrow-up-from-water-pump, - "bone": $fa-var-bone, - "table-cells-row-unlock": $fa-var-table-cells-row-unlock, - "user-injured": $fa-var-user-injured, - "face-sad-tear": $fa-var-face-sad-tear, - "sad-tear": $fa-var-sad-tear, - "plane": $fa-var-plane, - "tent-arrows-down": $fa-var-tent-arrows-down, - "exclamation": $fa-var-exclamation, - "arrows-spin": $fa-var-arrows-spin, - "print": $fa-var-print, - "turkish-lira-sign": $fa-var-turkish-lira-sign, - "try": $fa-var-try, - "turkish-lira": $fa-var-turkish-lira, - "dollar-sign": $fa-var-dollar-sign, - "dollar": $fa-var-dollar, - "usd": $fa-var-usd, - "x": $fa-var-x, - "magnifying-glass-dollar": $fa-var-magnifying-glass-dollar, - "search-dollar": $fa-var-search-dollar, - "users-gear": $fa-var-users-gear, - "users-cog": $fa-var-users-cog, - "person-military-pointing": $fa-var-person-military-pointing, - "building-columns": $fa-var-building-columns, - "bank": $fa-var-bank, - "institution": $fa-var-institution, - "museum": $fa-var-museum, - "university": $fa-var-university, - "umbrella": $fa-var-umbrella, - "trowel": $fa-var-trowel, - "d": $fa-var-d, - "stapler": $fa-var-stapler, - "masks-theater": $fa-var-masks-theater, - "theater-masks": $fa-var-theater-masks, - "kip-sign": $fa-var-kip-sign, - "hand-point-left": $fa-var-hand-point-left, - "handshake-simple": $fa-var-handshake-simple, - "handshake-alt": $fa-var-handshake-alt, - "jet-fighter": $fa-var-jet-fighter, - "fighter-jet": $fa-var-fighter-jet, - "square-share-nodes": $fa-var-square-share-nodes, - "share-alt-square": $fa-var-share-alt-square, - "barcode": $fa-var-barcode, - "plus-minus": $fa-var-plus-minus, - "video": $fa-var-video, - "video-camera": $fa-var-video-camera, - "graduation-cap": $fa-var-graduation-cap, - "mortar-board": $fa-var-mortar-board, - "hand-holding-medical": $fa-var-hand-holding-medical, - "person-circle-check": $fa-var-person-circle-check, - "turn-up": $fa-var-turn-up, - "level-up-alt": $fa-var-level-up-alt, -); - -$fa-brand-icons: ( - "monero": $fa-var-monero, - "hooli": $fa-var-hooli, - "yelp": $fa-var-yelp, - "cc-visa": $fa-var-cc-visa, - "lastfm": $fa-var-lastfm, - "shopware": $fa-var-shopware, - "creative-commons-nc": $fa-var-creative-commons-nc, - "aws": $fa-var-aws, - "redhat": $fa-var-redhat, - "yoast": $fa-var-yoast, - "cloudflare": $fa-var-cloudflare, - "ups": $fa-var-ups, - "pixiv": $fa-var-pixiv, - "wpexplorer": $fa-var-wpexplorer, - "dyalog": $fa-var-dyalog, - "bity": $fa-var-bity, - "stackpath": $fa-var-stackpath, - "buysellads": $fa-var-buysellads, - "first-order": $fa-var-first-order, - "modx": $fa-var-modx, - "guilded": $fa-var-guilded, - "vnv": $fa-var-vnv, - "square-js": $fa-var-square-js, - "js-square": $fa-var-js-square, - "microsoft": $fa-var-microsoft, - "qq": $fa-var-qq, - "orcid": $fa-var-orcid, - "java": $fa-var-java, - "invision": $fa-var-invision, - "creative-commons-pd-alt": $fa-var-creative-commons-pd-alt, - "centercode": $fa-var-centercode, - "glide-g": $fa-var-glide-g, - "drupal": $fa-var-drupal, - "jxl": $fa-var-jxl, - "dart-lang": $fa-var-dart-lang, - "hire-a-helper": $fa-var-hire-a-helper, - "creative-commons-by": $fa-var-creative-commons-by, - "unity": $fa-var-unity, - "whmcs": $fa-var-whmcs, - "rocketchat": $fa-var-rocketchat, - "vk": $fa-var-vk, - "untappd": $fa-var-untappd, - "mailchimp": $fa-var-mailchimp, - "css3-alt": $fa-var-css3-alt, - "square-reddit": $fa-var-square-reddit, - "reddit-square": $fa-var-reddit-square, - "vimeo-v": $fa-var-vimeo-v, - "contao": $fa-var-contao, - "square-font-awesome": $fa-var-square-font-awesome, - "deskpro": $fa-var-deskpro, - "brave": $fa-var-brave, - "sistrix": $fa-var-sistrix, - "square-instagram": $fa-var-square-instagram, - "instagram-square": $fa-var-instagram-square, - "battle-net": $fa-var-battle-net, - "the-red-yeti": $fa-var-the-red-yeti, - "square-hacker-news": $fa-var-square-hacker-news, - "hacker-news-square": $fa-var-hacker-news-square, - "edge": $fa-var-edge, - "threads": $fa-var-threads, - "napster": $fa-var-napster, - "square-snapchat": $fa-var-square-snapchat, - "snapchat-square": $fa-var-snapchat-square, - "google-plus-g": $fa-var-google-plus-g, - "artstation": $fa-var-artstation, - "markdown": $fa-var-markdown, - "sourcetree": $fa-var-sourcetree, - "google-plus": $fa-var-google-plus, - "diaspora": $fa-var-diaspora, - "foursquare": $fa-var-foursquare, - "stack-overflow": $fa-var-stack-overflow, - "github-alt": $fa-var-github-alt, - "phoenix-squadron": $fa-var-phoenix-squadron, - "pagelines": $fa-var-pagelines, - "algolia": $fa-var-algolia, - "red-river": $fa-var-red-river, - "creative-commons-sa": $fa-var-creative-commons-sa, - "safari": $fa-var-safari, - "google": $fa-var-google, - "square-font-awesome-stroke": $fa-var-square-font-awesome-stroke, - "font-awesome-alt": $fa-var-font-awesome-alt, - "atlassian": $fa-var-atlassian, - "linkedin-in": $fa-var-linkedin-in, - "digital-ocean": $fa-var-digital-ocean, - "nimblr": $fa-var-nimblr, - "chromecast": $fa-var-chromecast, - "evernote": $fa-var-evernote, - "hacker-news": $fa-var-hacker-news, - "creative-commons-sampling": $fa-var-creative-commons-sampling, - "adversal": $fa-var-adversal, - "creative-commons": $fa-var-creative-commons, - "watchman-monitoring": $fa-var-watchman-monitoring, - "fonticons": $fa-var-fonticons, - "weixin": $fa-var-weixin, - "shirtsinbulk": $fa-var-shirtsinbulk, - "codepen": $fa-var-codepen, - "git-alt": $fa-var-git-alt, - "lyft": $fa-var-lyft, - "rev": $fa-var-rev, - "windows": $fa-var-windows, - "wizards-of-the-coast": $fa-var-wizards-of-the-coast, - "square-viadeo": $fa-var-square-viadeo, - "viadeo-square": $fa-var-viadeo-square, - "meetup": $fa-var-meetup, - "centos": $fa-var-centos, - "adn": $fa-var-adn, - "cloudsmith": $fa-var-cloudsmith, - "opensuse": $fa-var-opensuse, - "pied-piper-alt": $fa-var-pied-piper-alt, - "square-dribbble": $fa-var-square-dribbble, - "dribbble-square": $fa-var-dribbble-square, - "codiepie": $fa-var-codiepie, - "node": $fa-var-node, - "mix": $fa-var-mix, - "steam": $fa-var-steam, - "cc-apple-pay": $fa-var-cc-apple-pay, - "scribd": $fa-var-scribd, - "debian": $fa-var-debian, - "openid": $fa-var-openid, - "instalod": $fa-var-instalod, - "files-pinwheel": $fa-var-files-pinwheel, - "expeditedssl": $fa-var-expeditedssl, - "sellcast": $fa-var-sellcast, - "square-twitter": $fa-var-square-twitter, - "twitter-square": $fa-var-twitter-square, - "r-project": $fa-var-r-project, - "delicious": $fa-var-delicious, - "freebsd": $fa-var-freebsd, - "vuejs": $fa-var-vuejs, - "accusoft": $fa-var-accusoft, - "ioxhost": $fa-var-ioxhost, - "fonticons-fi": $fa-var-fonticons-fi, - "app-store": $fa-var-app-store, - "cc-mastercard": $fa-var-cc-mastercard, - "itunes-note": $fa-var-itunes-note, - "golang": $fa-var-golang, - "kickstarter": $fa-var-kickstarter, - "square-kickstarter": $fa-var-square-kickstarter, - "grav": $fa-var-grav, - "weibo": $fa-var-weibo, - "uncharted": $fa-var-uncharted, - "firstdraft": $fa-var-firstdraft, - "square-youtube": $fa-var-square-youtube, - "youtube-square": $fa-var-youtube-square, - "wikipedia-w": $fa-var-wikipedia-w, - "wpressr": $fa-var-wpressr, - "rendact": $fa-var-rendact, - "angellist": $fa-var-angellist, - "galactic-republic": $fa-var-galactic-republic, - "nfc-directional": $fa-var-nfc-directional, - "skype": $fa-var-skype, - "joget": $fa-var-joget, - "fedora": $fa-var-fedora, - "stripe-s": $fa-var-stripe-s, - "meta": $fa-var-meta, - "laravel": $fa-var-laravel, - "hotjar": $fa-var-hotjar, - "bluetooth-b": $fa-var-bluetooth-b, - "square-letterboxd": $fa-var-square-letterboxd, - "sticker-mule": $fa-var-sticker-mule, - "creative-commons-zero": $fa-var-creative-commons-zero, - "hips": $fa-var-hips, - "css": $fa-var-css, - "behance": $fa-var-behance, - "reddit": $fa-var-reddit, - "discord": $fa-var-discord, - "chrome": $fa-var-chrome, - "app-store-ios": $fa-var-app-store-ios, - "cc-discover": $fa-var-cc-discover, - "wpbeginner": $fa-var-wpbeginner, - "confluence": $fa-var-confluence, - "shoelace": $fa-var-shoelace, - "mdb": $fa-var-mdb, - "dochub": $fa-var-dochub, - "accessible-icon": $fa-var-accessible-icon, - "ebay": $fa-var-ebay, - "amazon": $fa-var-amazon, - "unsplash": $fa-var-unsplash, - "yarn": $fa-var-yarn, - "square-steam": $fa-var-square-steam, - "steam-square": $fa-var-steam-square, - "500px": $fa-var-500px, - "square-vimeo": $fa-var-square-vimeo, - "vimeo-square": $fa-var-vimeo-square, - "asymmetrik": $fa-var-asymmetrik, - "font-awesome": $fa-var-font-awesome, - "font-awesome-flag": $fa-var-font-awesome-flag, - "font-awesome-logo-full": $fa-var-font-awesome-logo-full, - "gratipay": $fa-var-gratipay, - "apple": $fa-var-apple, - "hive": $fa-var-hive, - "gitkraken": $fa-var-gitkraken, - "keybase": $fa-var-keybase, - "apple-pay": $fa-var-apple-pay, - "padlet": $fa-var-padlet, - "amazon-pay": $fa-var-amazon-pay, - "square-github": $fa-var-square-github, - "github-square": $fa-var-github-square, - "stumbleupon": $fa-var-stumbleupon, - "fedex": $fa-var-fedex, - "phoenix-framework": $fa-var-phoenix-framework, - "shopify": $fa-var-shopify, - "neos": $fa-var-neos, - "square-threads": $fa-var-square-threads, - "hackerrank": $fa-var-hackerrank, - "researchgate": $fa-var-researchgate, - "swift": $fa-var-swift, - "angular": $fa-var-angular, - "speakap": $fa-var-speakap, - "angrycreative": $fa-var-angrycreative, - "y-combinator": $fa-var-y-combinator, - "empire": $fa-var-empire, - "envira": $fa-var-envira, - "google-scholar": $fa-var-google-scholar, - "square-gitlab": $fa-var-square-gitlab, - "gitlab-square": $fa-var-gitlab-square, - "studiovinari": $fa-var-studiovinari, - "pied-piper": $fa-var-pied-piper, - "wordpress": $fa-var-wordpress, - "product-hunt": $fa-var-product-hunt, - "firefox": $fa-var-firefox, - "linode": $fa-var-linode, - "goodreads": $fa-var-goodreads, - "square-odnoklassniki": $fa-var-square-odnoklassniki, - "odnoklassniki-square": $fa-var-odnoklassniki-square, - "jsfiddle": $fa-var-jsfiddle, - "sith": $fa-var-sith, - "themeisle": $fa-var-themeisle, - "page4": $fa-var-page4, - "hashnode": $fa-var-hashnode, - "react": $fa-var-react, - "cc-paypal": $fa-var-cc-paypal, - "squarespace": $fa-var-squarespace, - "cc-stripe": $fa-var-cc-stripe, - "creative-commons-share": $fa-var-creative-commons-share, - "bitcoin": $fa-var-bitcoin, - "keycdn": $fa-var-keycdn, - "opera": $fa-var-opera, - "itch-io": $fa-var-itch-io, - "umbraco": $fa-var-umbraco, - "galactic-senate": $fa-var-galactic-senate, - "ubuntu": $fa-var-ubuntu, - "draft2digital": $fa-var-draft2digital, - "stripe": $fa-var-stripe, - "houzz": $fa-var-houzz, - "gg": $fa-var-gg, - "dhl": $fa-var-dhl, - "square-pinterest": $fa-var-square-pinterest, - "pinterest-square": $fa-var-pinterest-square, - "xing": $fa-var-xing, - "blackberry": $fa-var-blackberry, - "creative-commons-pd": $fa-var-creative-commons-pd, - "playstation": $fa-var-playstation, - "quinscape": $fa-var-quinscape, - "less": $fa-var-less, - "blogger-b": $fa-var-blogger-b, - "opencart": $fa-var-opencart, - "vine": $fa-var-vine, - "signal-messenger": $fa-var-signal-messenger, - "paypal": $fa-var-paypal, - "gitlab": $fa-var-gitlab, - "typo3": $fa-var-typo3, - "reddit-alien": $fa-var-reddit-alien, - "yahoo": $fa-var-yahoo, - "dailymotion": $fa-var-dailymotion, - "affiliatetheme": $fa-var-affiliatetheme, - "pied-piper-pp": $fa-var-pied-piper-pp, - "bootstrap": $fa-var-bootstrap, - "odnoklassniki": $fa-var-odnoklassniki, - "nfc-symbol": $fa-var-nfc-symbol, - "mintbit": $fa-var-mintbit, - "ethereum": $fa-var-ethereum, - "speaker-deck": $fa-var-speaker-deck, - "creative-commons-nc-eu": $fa-var-creative-commons-nc-eu, - "patreon": $fa-var-patreon, - "avianex": $fa-var-avianex, - "ello": $fa-var-ello, - "gofore": $fa-var-gofore, - "bimobject": $fa-var-bimobject, - "brave-reverse": $fa-var-brave-reverse, - "facebook-f": $fa-var-facebook-f, - "square-google-plus": $fa-var-square-google-plus, - "google-plus-square": $fa-var-google-plus-square, - "web-awesome": $fa-var-web-awesome, - "mandalorian": $fa-var-mandalorian, - "first-order-alt": $fa-var-first-order-alt, - "osi": $fa-var-osi, - "google-wallet": $fa-var-google-wallet, - "d-and-d-beyond": $fa-var-d-and-d-beyond, - "periscope": $fa-var-periscope, - "fulcrum": $fa-var-fulcrum, - "cloudscale": $fa-var-cloudscale, - "forumbee": $fa-var-forumbee, - "mizuni": $fa-var-mizuni, - "schlix": $fa-var-schlix, - "square-xing": $fa-var-square-xing, - "xing-square": $fa-var-xing-square, - "bandcamp": $fa-var-bandcamp, - "wpforms": $fa-var-wpforms, - "cloudversify": $fa-var-cloudversify, - "usps": $fa-var-usps, - "megaport": $fa-var-megaport, - "magento": $fa-var-magento, - "spotify": $fa-var-spotify, - "optin-monster": $fa-var-optin-monster, - "fly": $fa-var-fly, - "square-bluesky": $fa-var-square-bluesky, - "aviato": $fa-var-aviato, - "itunes": $fa-var-itunes, - "cuttlefish": $fa-var-cuttlefish, - "blogger": $fa-var-blogger, - "flickr": $fa-var-flickr, - "viber": $fa-var-viber, - "soundcloud": $fa-var-soundcloud, - "digg": $fa-var-digg, - "tencent-weibo": $fa-var-tencent-weibo, - "letterboxd": $fa-var-letterboxd, - "symfony": $fa-var-symfony, - "maxcdn": $fa-var-maxcdn, - "etsy": $fa-var-etsy, - "facebook-messenger": $fa-var-facebook-messenger, - "audible": $fa-var-audible, - "think-peaks": $fa-var-think-peaks, - "bilibili": $fa-var-bilibili, - "erlang": $fa-var-erlang, - "x-twitter": $fa-var-x-twitter, - "cotton-bureau": $fa-var-cotton-bureau, - "dashcube": $fa-var-dashcube, - "42-group": $fa-var-42-group, - "innosoft": $fa-var-innosoft, - "stack-exchange": $fa-var-stack-exchange, - "elementor": $fa-var-elementor, - "square-pied-piper": $fa-var-square-pied-piper, - "pied-piper-square": $fa-var-pied-piper-square, - "creative-commons-nd": $fa-var-creative-commons-nd, - "palfed": $fa-var-palfed, - "superpowers": $fa-var-superpowers, - "resolving": $fa-var-resolving, - "xbox": $fa-var-xbox, - "square-web-awesome-stroke": $fa-var-square-web-awesome-stroke, - "searchengin": $fa-var-searchengin, - "tiktok": $fa-var-tiktok, - "square-facebook": $fa-var-square-facebook, - "facebook-square": $fa-var-facebook-square, - "renren": $fa-var-renren, - "linux": $fa-var-linux, - "glide": $fa-var-glide, - "linkedin": $fa-var-linkedin, - "hubspot": $fa-var-hubspot, - "deploydog": $fa-var-deploydog, - "twitch": $fa-var-twitch, - "flutter": $fa-var-flutter, - "ravelry": $fa-var-ravelry, - "mixer": $fa-var-mixer, - "square-lastfm": $fa-var-square-lastfm, - "lastfm-square": $fa-var-lastfm-square, - "vimeo": $fa-var-vimeo, - "mendeley": $fa-var-mendeley, - "uniregistry": $fa-var-uniregistry, - "figma": $fa-var-figma, - "creative-commons-remix": $fa-var-creative-commons-remix, - "cc-amazon-pay": $fa-var-cc-amazon-pay, - "dropbox": $fa-var-dropbox, - "instagram": $fa-var-instagram, - "cmplid": $fa-var-cmplid, - "upwork": $fa-var-upwork, - "facebook": $fa-var-facebook, - "gripfire": $fa-var-gripfire, - "jedi-order": $fa-var-jedi-order, - "uikit": $fa-var-uikit, - "fort-awesome-alt": $fa-var-fort-awesome-alt, - "phabricator": $fa-var-phabricator, - "ussunnah": $fa-var-ussunnah, - "earlybirds": $fa-var-earlybirds, - "trade-federation": $fa-var-trade-federation, - "autoprefixer": $fa-var-autoprefixer, - "whatsapp": $fa-var-whatsapp, - "square-upwork": $fa-var-square-upwork, - "slideshare": $fa-var-slideshare, - "google-play": $fa-var-google-play, - "viadeo": $fa-var-viadeo, - "line": $fa-var-line, - "google-drive": $fa-var-google-drive, - "servicestack": $fa-var-servicestack, - "simplybuilt": $fa-var-simplybuilt, - "bitbucket": $fa-var-bitbucket, - "imdb": $fa-var-imdb, - "deezer": $fa-var-deezer, - "raspberry-pi": $fa-var-raspberry-pi, - "jira": $fa-var-jira, - "docker": $fa-var-docker, - "screenpal": $fa-var-screenpal, - "bluetooth": $fa-var-bluetooth, - "gitter": $fa-var-gitter, - "d-and-d": $fa-var-d-and-d, - "microblog": $fa-var-microblog, - "cc-diners-club": $fa-var-cc-diners-club, - "gg-circle": $fa-var-gg-circle, - "pied-piper-hat": $fa-var-pied-piper-hat, - "kickstarter-k": $fa-var-kickstarter-k, - "yandex": $fa-var-yandex, - "readme": $fa-var-readme, - "html5": $fa-var-html5, - "sellsy": $fa-var-sellsy, - "square-web-awesome": $fa-var-square-web-awesome, - "sass": $fa-var-sass, - "wirsindhandwerk": $fa-var-wirsindhandwerk, - "wsh": $fa-var-wsh, - "buromobelexperte": $fa-var-buromobelexperte, - "salesforce": $fa-var-salesforce, - "octopus-deploy": $fa-var-octopus-deploy, - "medapps": $fa-var-medapps, - "ns8": $fa-var-ns8, - "pinterest-p": $fa-var-pinterest-p, - "apper": $fa-var-apper, - "fort-awesome": $fa-var-fort-awesome, - "waze": $fa-var-waze, - "bluesky": $fa-var-bluesky, - "cc-jcb": $fa-var-cc-jcb, - "snapchat": $fa-var-snapchat, - "snapchat-ghost": $fa-var-snapchat-ghost, - "fantasy-flight-games": $fa-var-fantasy-flight-games, - "rust": $fa-var-rust, - "wix": $fa-var-wix, - "square-behance": $fa-var-square-behance, - "behance-square": $fa-var-behance-square, - "supple": $fa-var-supple, - "webflow": $fa-var-webflow, - "rebel": $fa-var-rebel, - "css3": $fa-var-css3, - "staylinked": $fa-var-staylinked, - "kaggle": $fa-var-kaggle, - "space-awesome": $fa-var-space-awesome, - "deviantart": $fa-var-deviantart, - "cpanel": $fa-var-cpanel, - "goodreads-g": $fa-var-goodreads-g, - "square-git": $fa-var-square-git, - "git-square": $fa-var-git-square, - "square-tumblr": $fa-var-square-tumblr, - "tumblr-square": $fa-var-tumblr-square, - "trello": $fa-var-trello, - "creative-commons-nc-jp": $fa-var-creative-commons-nc-jp, - "get-pocket": $fa-var-get-pocket, - "perbyte": $fa-var-perbyte, - "grunt": $fa-var-grunt, - "weebly": $fa-var-weebly, - "connectdevelop": $fa-var-connectdevelop, - "leanpub": $fa-var-leanpub, - "black-tie": $fa-var-black-tie, - "themeco": $fa-var-themeco, - "python": $fa-var-python, - "android": $fa-var-android, - "bots": $fa-var-bots, - "free-code-camp": $fa-var-free-code-camp, - "hornbill": $fa-var-hornbill, - "js": $fa-var-js, - "ideal": $fa-var-ideal, - "git": $fa-var-git, - "dev": $fa-var-dev, - "sketch": $fa-var-sketch, - "yandex-international": $fa-var-yandex-international, - "cc-amex": $fa-var-cc-amex, - "uber": $fa-var-uber, - "github": $fa-var-github, - "php": $fa-var-php, - "alipay": $fa-var-alipay, - "youtube": $fa-var-youtube, - "skyatlas": $fa-var-skyatlas, - "firefox-browser": $fa-var-firefox-browser, - "replyd": $fa-var-replyd, - "suse": $fa-var-suse, - "jenkins": $fa-var-jenkins, - "twitter": $fa-var-twitter, - "rockrms": $fa-var-rockrms, - "pinterest": $fa-var-pinterest, - "buffer": $fa-var-buffer, - "npm": $fa-var-npm, - "yammer": $fa-var-yammer, - "btc": $fa-var-btc, - "dribbble": $fa-var-dribbble, - "stumbleupon-circle": $fa-var-stumbleupon-circle, - "internet-explorer": $fa-var-internet-explorer, - "stubber": $fa-var-stubber, - "telegram": $fa-var-telegram, - "telegram-plane": $fa-var-telegram-plane, - "old-republic": $fa-var-old-republic, - "odysee": $fa-var-odysee, - "square-whatsapp": $fa-var-square-whatsapp, - "whatsapp-square": $fa-var-whatsapp-square, - "node-js": $fa-var-node-js, - "edge-legacy": $fa-var-edge-legacy, - "slack": $fa-var-slack, - "slack-hash": $fa-var-slack-hash, - "medrt": $fa-var-medrt, - "usb": $fa-var-usb, - "tumblr": $fa-var-tumblr, - "vaadin": $fa-var-vaadin, - "quora": $fa-var-quora, - "square-x-twitter": $fa-var-square-x-twitter, - "reacteurope": $fa-var-reacteurope, - "medium": $fa-var-medium, - "medium-m": $fa-var-medium-m, - "amilia": $fa-var-amilia, - "mixcloud": $fa-var-mixcloud, - "flipboard": $fa-var-flipboard, - "viacoin": $fa-var-viacoin, - "critical-role": $fa-var-critical-role, - "sitrox": $fa-var-sitrox, - "discourse": $fa-var-discourse, - "joomla": $fa-var-joomla, - "mastodon": $fa-var-mastodon, - "airbnb": $fa-var-airbnb, - "wolf-pack-battalion": $fa-var-wolf-pack-battalion, - "buy-n-large": $fa-var-buy-n-large, - "gulp": $fa-var-gulp, - "creative-commons-sampling-plus": $fa-var-creative-commons-sampling-plus, - "strava": $fa-var-strava, - "ember": $fa-var-ember, - "canadian-maple-leaf": $fa-var-canadian-maple-leaf, - "teamspeak": $fa-var-teamspeak, - "pushed": $fa-var-pushed, - "wordpress-simple": $fa-var-wordpress-simple, - "nutritionix": $fa-var-nutritionix, - "wodu": $fa-var-wodu, - "google-pay": $fa-var-google-pay, - "intercom": $fa-var-intercom, - "zhihu": $fa-var-zhihu, - "korvue": $fa-var-korvue, - "pix": $fa-var-pix, - "steam-symbol": $fa-var-steam-symbol, -); diff --git a/tubesync/common/static/styles/fontawesome/brands.scss b/tubesync/common/static/styles/fontawesome/brands.scss deleted file mode 120000 index 1c880b911..000000000 --- a/tubesync/common/static/styles/fontawesome/brands.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/brands.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/fontawesome.scss b/tubesync/common/static/styles/fontawesome/fontawesome.scss deleted file mode 120000 index 0651eff38..000000000 --- a/tubesync/common/static/styles/fontawesome/fontawesome.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/fontawesome.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/regular.scss b/tubesync/common/static/styles/fontawesome/regular.scss deleted file mode 120000 index 524138620..000000000 --- a/tubesync/common/static/styles/fontawesome/regular.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/regular.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/solid.scss b/tubesync/common/static/styles/fontawesome/solid.scss deleted file mode 120000 index 877a13a76..000000000 --- a/tubesync/common/static/styles/fontawesome/solid.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/solid.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/fontawesome/v4-shims.scss b/tubesync/common/static/styles/fontawesome/v4-shims.scss deleted file mode 120000 index 843942234..000000000 --- a/tubesync/common/static/styles/fontawesome/v4-shims.scss +++ /dev/null @@ -1 +0,0 @@ -../../../../../assets/fontawesome-free/6/fontawesome-free-6.7.2-web/scss/v4-shims.scss \ No newline at end of file diff --git a/tubesync/common/static/styles/_fonts.scss b/tubesync/common/static/styles/fonts.css similarity index 59% rename from tubesync/common/static/styles/_fonts.scss rename to tubesync/common/static/styles/fonts.css index 4548b96d6..7e3d5566f 100644 --- a/tubesync/common/static/styles/_fonts.scss +++ b/tubesync/common/static/styles/fonts.css @@ -1,20 +1,23 @@ @font-face { - font-family: 'roboto'; + font-family: 'Roboto'; src: url('../fonts/roboto/roboto-light.woff') format('woff'); - font-weight: lighter; + font-weight: 300; font-style: normal; + font-display: swap; } @font-face { - font-family: 'roboto'; + font-family: 'Roboto'; src: url('../fonts/roboto/roboto-regular.woff') format('woff'); - font-weight: normal; + font-weight: 400; font-style: normal; + font-display: swap; } @font-face { - font-family: 'roboto'; + font-family: 'Roboto'; src: url('../fonts/roboto/roboto-bold.woff') format('woff'); - font-weight: bold; + font-weight: 700; font-style: normal; + font-display: swap; } diff --git a/tubesync/common/static/styles/materializecss/components/_badges.scss b/tubesync/common/static/styles/materializecss/components/_badges.scss deleted file mode 100644 index ffed87dc4..000000000 --- a/tubesync/common/static/styles/materializecss/components/_badges.scss +++ /dev/null @@ -1,55 +0,0 @@ -// Badges -span.badge { - min-width: 3rem; - padding: 0 6px; - margin-left: 14px; - text-align: center; - font-size: 1rem; - line-height: $badge-height; - height: $badge-height; - color: color('grey', 'darken-1'); - float: right; - box-sizing: border-box; - - &.new { - font-weight: 300; - font-size: 0.8rem; - color: #fff; - background-color: $badge-bg-color; - border-radius: 2px; - } - &.new:after { - content: " new"; - } - - &[data-badge-caption]::after { - content: " " attr(data-badge-caption); - } -} - -// Special cases -nav ul a span.badge { - display: inline-block; - float: none; - margin-left: 4px; - line-height: $badge-height; - height: $badge-height; - -webkit-font-smoothing: auto; -} - -// Line height centering -.collection-item span.badge { - margin-top: calc(#{$collection-line-height / 2} - #{$badge-height / 2}); -} -.collapsible span.badge { - margin-left: auto; -} -.sidenav span.badge { - margin-top: calc(#{$sidenav-line-height / 2} - #{$badge-height / 2}); -} - -table span.badge { - display: inline-block; - float: none; - margin-left: auto; -} diff --git a/tubesync/common/static/styles/materializecss/components/_buttons.scss b/tubesync/common/static/styles/materializecss/components/_buttons.scss deleted file mode 100644 index 44b80c8dd..000000000 --- a/tubesync/common/static/styles/materializecss/components/_buttons.scss +++ /dev/null @@ -1,322 +0,0 @@ -// shared styles -.btn, -.btn-flat { - border: $button-border; - border-radius: $button-radius; - display: inline-block; - height: $button-height; - line-height: $button-height; - padding: $button-padding; - text-transform: uppercase; - vertical-align: middle; - -webkit-tap-highlight-color: transparent; // Gets rid of tap active state -} - -// Disabled shared style -.btn.disabled, -.btn-floating.disabled, -.btn-large.disabled, -.btn-small.disabled, -.btn-flat.disabled, -.btn:disabled, -.btn-floating:disabled, -.btn-large:disabled, -.btn-small:disabled, -.btn-flat:disabled, -.btn[disabled], -.btn-floating[disabled], -.btn-large[disabled], -.btn-small[disabled], -.btn-flat[disabled] { - pointer-events: none; - background-color: $button-disabled-background !important; - box-shadow: none; - color: $button-disabled-color !important; - cursor: default; - &:hover { - background-color: $button-disabled-background !important; - color: $button-disabled-color !important; - } -} - -// Shared icon styles -.btn, -.btn-floating, -.btn-large, -.btn-small, -.btn-flat { - font-size: $button-font-size; - outline: 0; - i { - font-size: $button-icon-font-size; - line-height: inherit; - } -} - -// Shared focus button style -.btn, -.btn-floating { - &:focus { - background-color: darken($button-raised-background, 10%); - } -} - -// Raised Button -.btn { - text-decoration: none; - color: $button-raised-color; - background-color: $button-raised-background; - text-align: center; - letter-spacing: .5px; - @extend .z-depth-1; - transition: background-color .2s ease-out; - cursor: pointer; - &:hover { - background-color: $button-raised-background-hover; - @extend .z-depth-1-half; - } -} - -// Floating button -.btn-floating { - &:hover { - background-color: $button-floating-background-hover; - @extend .z-depth-1-half; - } - &:before { - border-radius: 0; - } - &.btn-large { - &.halfway-fab { - bottom: -$button-floating-large-size / 2; - } - width: $button-floating-large-size; - height: $button-floating-large-size; - padding: 0; - i { - line-height: $button-floating-large-size; - } - } - - &.btn-small { - &.halfway-fab { - bottom: -$button-floating-small-size / 2; - } - width: $button-floating-small-size; - height: $button-floating-small-size; - i { - line-height: $button-floating-small-size; - } - } - - &.halfway-fab { - &.left { - right: auto; - left: 24px; - } - position: absolute; - right: 24px; - bottom: -$button-floating-size / 2; - } - display: inline-block; - color: $button-floating-color; - position: relative; - overflow: hidden; - z-index: 1; - width: $button-floating-size; - height: $button-floating-size; - line-height: $button-floating-size; - padding: 0; - background-color: $button-floating-background; - border-radius: $button-floating-radius; - @extend .z-depth-1; - transition: background-color .3s; - cursor: pointer; - vertical-align: middle; - i { - width: inherit; - display: inline-block; - text-align: center; - color: $button-floating-color; - font-size: $button-large-icon-font-size; - line-height: $button-floating-size; - } -} - -// button fix -button.btn-floating { - border: $button-border; -} - -// Fixed Action Button -.fixed-action-btn { - &.active { - ul { - visibility: visible; - } - } - - // Directions - &.direction-left, - &.direction-right { - padding: 0 0 0 15px; - ul { - text-align: right; - right: 64px; - top: 50%; - transform: translateY(-50%); - height: 100%; - left: auto; - /*width 100% only goes to width of button container */ - width: 500px; - li { - display: inline-block; - margin: 7.5px 15px 0 0; - } - } - } - &.direction-right { - padding: 0 15px 0 0; - ul { - text-align: left; - direction: rtl; - left: 64px; - right: auto; - li { - margin: 7.5px 0 0 15px; - } - } - } - &.direction-bottom { - padding: 0 0 15px 0; - ul { - top: 64px; - bottom: auto; - display: flex; - flex-direction: column-reverse; - li { - margin: 15px 0 0 0; - } - } - } - &.toolbar { - &.active { - &>a i { - opacity: 0; - } - } - padding: 0; - height: $button-floating-large-size; - ul { - display: flex; - top: 0; - bottom: 0; - z-index: 1; - li { - flex: 1; - display: inline-block; - margin: 0; - height: 100%; - transition: none; - a { - display: block; - overflow: hidden; - position: relative; - width: 100%; - height: 100%; - background-color: transparent; - box-shadow: none; - color: #fff; - line-height: $button-floating-large-size; - z-index: 1; - i { - line-height: inherit; - } - } - } - } - } - position: fixed; - right: 23px; - bottom: 23px; - padding-top: 15px; - margin-bottom: 0; - z-index: 997; - ul { - left: 0; - right: 0; - text-align: center; - position: absolute; - bottom: 64px; - margin: 0; - visibility: hidden; - li { - margin-bottom: 15px; - } - a.btn-floating { - opacity: 0; - } - } - .fab-backdrop { - position: absolute; - top: 0; - left: 0; - z-index: -1; - width: $button-floating-size; - height: $button-floating-size; - background-color: $button-floating-background; - border-radius: $button-floating-radius; - transform: scale(0); - } -} - -// Flat button -.btn-flat { - box-shadow: none; - background-color: transparent; - color: $button-flat-color; - cursor: pointer; - transition: background-color .2s; - &:focus, - &:hover { - box-shadow: none; - } - &:focus { - background-color: rgba(0, 0, 0, .1); - } - &.disabled, - &.btn-flat[disabled] { - background-color: transparent !important; - color: $button-flat-disabled-color !important; - cursor: default; - } -} - -// Large button -.btn-large { - @extend .btn; - height: $button-large-height; - line-height: $button-large-height; - font-size: $button-large-font-size; - padding: 0 28px; - - i { - font-size: $button-large-icon-font-size; - } -} - -// Small button -.btn-small { - @extend .btn; - height: $button-small-height; - line-height: $button-small-height; - font-size: $button-small-font-size; - i { - font-size: $button-small-icon-font-size; - } -} - -// Block button -.btn-block { - display: block; -} diff --git a/tubesync/common/static/styles/materializecss/components/_cards.scss b/tubesync/common/static/styles/materializecss/components/_cards.scss deleted file mode 100644 index fcbf28ece..000000000 --- a/tubesync/common/static/styles/materializecss/components/_cards.scss +++ /dev/null @@ -1,195 +0,0 @@ - - -.card-panel { - transition: box-shadow .25s; - padding: $card-padding; - margin: $element-top-margin 0 $element-bottom-margin 0; - border-radius: 2px; - @extend .z-depth-1; - background-color: $card-bg-color; -} - -.card { - position: relative; - margin: $element-top-margin 0 $element-bottom-margin 0; - background-color: $card-bg-color; - transition: box-shadow .25s; - border-radius: 2px; - @extend .z-depth-1; - - - .card-title { - font-size: 24px; - font-weight: 300; - &.activator { - cursor: pointer; - } - } - - // Card Sizes - &.small, &.medium, &.large { - position: relative; - - .card-image { - max-height: 60%; - overflow: hidden; - } - .card-image + .card-content { - max-height: 40%; - } - .card-content { - max-height: 100%; - overflow: hidden; - } - .card-action { - position: absolute; - bottom: 0; - left: 0; - right: 0; - } - } - - &.small { - height: 300px; - } - - &.medium { - height: 400px; - } - - &.large { - height: 500px; - } - - // Horizontal Cards - &.horizontal { - &.small, &.medium, &.large { - .card-image { - height: 100%; - max-height: none; - overflow: visible; - - img { - height: 100%; - } - } - } - - display: flex; - - .card-image { - max-width: 50%; - img { - border-radius: 2px 0 0 2px; - max-width: 100%; - width: auto; - } - } - - .card-stacked { - display: flex; - flex-direction: column; - flex: 1; - position: relative; - - .card-content { - flex-grow: 1; - } - } - } - - // Sticky Action Section - &.sticky-action { - .card-action { - z-index: 2; - } - - .card-reveal { - z-index: 1; - padding-bottom: 64px; - } - } - - - - - .card-image { - position: relative; - - // Image background for content - img { - display: block; - border-radius: 2px 2px 0 0; - position: relative; - left: 0; - right: 0; - top: 0; - bottom: 0; - width: 100%; - } - - .card-title { - color: $card-bg-color; - position: absolute; - bottom: 0; - left: 0; - max-width: 100%; - padding: $card-padding; - } - } - - .card-content { - padding: $card-padding; - border-radius: 0 0 2px 2px; - - p { - margin: 0; - } - .card-title { - display: block; - line-height: 32px; - margin-bottom: 8px; - - i { - line-height: 32px; - } - } - } - - .card-action { - &:last-child { - border-radius: 0 0 2px 2px; - } - background-color: inherit; // Use inherit to inherit color classes - border-top: 1px solid rgba(160,160,160,.2); - position: relative; - padding: 16px $card-padding; - - a:not(.btn):not(.btn-large):not(.btn-floating) { - color: $card-link-color; - margin-right: $card-padding; - transition: color .3s ease; - text-transform: uppercase; - - &:hover { color: $card-link-color-light; } - } - } - - .card-reveal { - padding: $card-padding; - position: absolute; - background-color: $card-bg-color; - width: 100%; - overflow-y: auto; - left: 0; - top: 100%; - height: 100%; - z-index: 3; - display: none; - - .card-title { - cursor: pointer; - display: block; - } - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_carousel.scss b/tubesync/common/static/styles/materializecss/components/_carousel.scss deleted file mode 100644 index cc36d4b3a..000000000 --- a/tubesync/common/static/styles/materializecss/components/_carousel.scss +++ /dev/null @@ -1,90 +0,0 @@ -.carousel { - &.carousel-slider { - top: 0; - left: 0; - - .carousel-fixed-item { - &.with-indicators { - bottom: 68px; - } - - position: absolute; - left: 0; - right: 0; - bottom: 20px; - z-index: 1; - } - - .carousel-item { - width: 100%; - height: 100%; - min-height: $carousel-height; - position: absolute; - top: 0; - left: 0; - - h2 { - font-size: 24px; - font-weight: 500; - line-height: 32px; - } - - p { - font-size: 15px; - } - } - } - - overflow: hidden; - position: relative; - width: 100%; - height: $carousel-height; - perspective: 500px; - transform-style: preserve-3d; - transform-origin: 0% 50%; - - .carousel-item { - visibility: hidden; - width: $carousel-item-width; - height: $carousel-item-height; - position: absolute; - top: 0; - left: 0; - - & > img { - width: 100%; - } - } - - .indicators { - position: absolute; - text-align: center; - left: 0; - right: 0; - bottom: 0; - margin: 0; - - .indicator-item { - &.active { - background-color: #fff; - } - - display: inline-block; - position: relative; - cursor: pointer; - height: 8px; - width: 8px; - margin: 24px 4px; - background-color: rgba(255,255,255,.5); - - transition: background-color .3s; - border-radius: 50%; - } - } - - // Materialbox compatibility - &.scrolling .carousel-item .materialboxed, - .carousel-item:not(.active) .materialboxed { - pointer-events: none; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_chips.scss b/tubesync/common/static/styles/materializecss/components/_chips.scss deleted file mode 100644 index 27744a8bd..000000000 --- a/tubesync/common/static/styles/materializecss/components/_chips.scss +++ /dev/null @@ -1,90 +0,0 @@ -.chip { - &:focus { - outline: none; - background-color: $chip-selected-color; - color: #fff; - } - - display: inline-block; - height: 32px; - font-size: 13px; - font-weight: 500; - color: rgba(0,0,0,.6); - line-height: 32px; - padding: 0 12px; - border-radius: 16px; - background-color: $chip-bg-color; - margin-bottom: $chip-margin; - margin-right: $chip-margin; - - > img { - float: left; - margin: 0 8px 0 -12px; - height: 32px; - width: 32px; - border-radius: 50%; - } - - .close { - cursor: pointer; - float: right; - font-size: 16px; - line-height: 32px; - padding-left: 8px; - } -} - -.chips { - border: none; - border-bottom: 1px solid $chip-border-color; - box-shadow: none; - margin: $input-margin; - min-height: 45px; - outline: none; - transition: all .3s; - - &.focus { - border-bottom: 1px solid $chip-selected-color; - box-shadow: 0 1px 0 0 $chip-selected-color; - } - - &:hover { - cursor: text; - } - - .input { - background: none; - border: 0; - color: rgba(0,0,0,.6); - display: inline-block; - font-size: $input-font-size; - height: $input-height; - line-height: 32px; - outline: 0; - margin: 0; - padding: 0 !important; - width: 120px !important; - } - - .input:focus { - border: 0 !important; - box-shadow: none !important; - } - - // Autocomplete - .autocomplete-content { - margin-top: 0; - margin-bottom: 0; - } -} - -// Form prefix -.prefix ~ .chips { - margin-left: 3rem; - width: 92%; - width: calc(100% - 3rem); -} -.chips:empty ~ label { - font-size: 0.8rem; - transform: translateY(-140%); -} diff --git a/tubesync/common/static/styles/materializecss/components/_collapsible.scss b/tubesync/common/static/styles/materializecss/components/_collapsible.scss deleted file mode 100644 index 024324fdb..000000000 --- a/tubesync/common/static/styles/materializecss/components/_collapsible.scss +++ /dev/null @@ -1,91 +0,0 @@ -.collapsible { - border-top: 1px solid $collapsible-border-color; - border-right: 1px solid $collapsible-border-color; - border-left: 1px solid $collapsible-border-color; - margin: $element-top-margin 0 $element-bottom-margin 0; - @extend .z-depth-1; -} - -.collapsible-header { - &:focus { - outline: 0 - } - - display: flex; - cursor: pointer; - -webkit-tap-highlight-color: transparent; - line-height: 1.5; - padding: 1rem; - background-color: $collapsible-header-color; - border-bottom: 1px solid $collapsible-border-color; - - i { - width: 2rem; - font-size: 1.6rem; - display: inline-block; - text-align: center; - margin-right: 1rem; - } -} -.keyboard-focused .collapsible-header:focus { - background-color: #eee; -} - -.collapsible-body { - display: none; - border-bottom: 1px solid $collapsible-border-color; - box-sizing: border-box; - padding: 2rem; -} - -// Sidenav collapsible styling -.sidenav, -.sidenav.fixed { - - .collapsible { - border: none; - box-shadow: none; - - li { padding: 0; } - } - - .collapsible-header { - background-color: transparent; - border: none; - line-height: inherit; - height: inherit; - padding: 0 $sidenav-padding; - - &:hover { background-color: rgba(0,0,0,.05); } - i { line-height: inherit; } - } - - .collapsible-body { - border: 0; - background-color: $collapsible-header-color; - - li a { - padding: 0 (7.5px + $sidenav-padding) - 0 (15px + $sidenav-padding); - } - } - -} - -// Popout Collapsible - -.collapsible.popout { - border: none; - box-shadow: none; - > li { - box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); - // transform: scaleX(.92); - margin: 0 24px; - transition: margin .35s cubic-bezier(0.250, 0.460, 0.450, 0.940); - } - > li.active { - box-shadow: 0 5px 11px 0 rgba(0, 0, 0, 0.18), 0 4px 15px 0 rgba(0, 0, 0, 0.15); - margin: 16px 0; - // transform: scaleX(1); - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_color-classes.scss b/tubesync/common/static/styles/materializecss/components/_color-classes.scss deleted file mode 100644 index 155cecd12..000000000 --- a/tubesync/common/static/styles/materializecss/components/_color-classes.scss +++ /dev/null @@ -1,32 +0,0 @@ -// Color Classes - -@each $color_name, $color in $colors { - @each $color_type, $color_value in $color { - @if $color_type == "base" { - .#{$color_name} { - background-color: $color_value !important; - } - .#{$color_name}-text { - color: $color_value !important; - } - } - @else if $color_name != "shades" { - .#{$color_name}.#{$color_type} { - background-color: $color_value !important; - } - .#{$color_name}-text.text-#{$color_type} { - color: $color_value !important; - } - } - } -} - -// Shade classes -@each $color, $color_value in $shades { - .#{$color} { - background-color: $color_value !important; - } - .#{$color}-text { - color: $color_value !important; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_color-variables.scss b/tubesync/common/static/styles/materializecss/components/_color-variables.scss deleted file mode 100644 index 062f6a56c..000000000 --- a/tubesync/common/static/styles/materializecss/components/_color-variables.scss +++ /dev/null @@ -1,370 +0,0 @@ -// Google Color Palette defined: http://www.google.com/design/spec/style/color.html - -$materialize-red: ( - "base": #e51c23, - "lighten-5": #fdeaeb, - "lighten-4": #f8c1c3, - "lighten-3": #f3989b, - "lighten-2": #ee6e73, - "lighten-1": #ea454b, - "darken-1": #d0181e, - "darken-2": #b9151b, - "darken-3": #a21318, - "darken-4": #8b1014, -); - -$red: ( - "base": #F44336, - "lighten-5": #FFEBEE, - "lighten-4": #FFCDD2, - "lighten-3": #EF9A9A, - "lighten-2": #E57373, - "lighten-1": #EF5350, - "darken-1": #E53935, - "darken-2": #D32F2F, - "darken-3": #C62828, - "darken-4": #B71C1C, - "accent-1": #FF8A80, - "accent-2": #FF5252, - "accent-3": #FF1744, - "accent-4": #D50000 -); - -$pink: ( - "base": #e91e63, - "lighten-5": #fce4ec, - "lighten-4": #f8bbd0, - "lighten-3": #f48fb1, - "lighten-2": #f06292, - "lighten-1": #ec407a, - "darken-1": #d81b60, - "darken-2": #c2185b, - "darken-3": #ad1457, - "darken-4": #880e4f, - "accent-1": #ff80ab, - "accent-2": #ff4081, - "accent-3": #f50057, - "accent-4": #c51162 -); - -$purple: ( - "base": #9c27b0, - "lighten-5": #f3e5f5, - "lighten-4": #e1bee7, - "lighten-3": #ce93d8, - "lighten-2": #ba68c8, - "lighten-1": #ab47bc, - "darken-1": #8e24aa, - "darken-2": #7b1fa2, - "darken-3": #6a1b9a, - "darken-4": #4a148c, - "accent-1": #ea80fc, - "accent-2": #e040fb, - "accent-3": #d500f9, - "accent-4": #aa00ff -); - -$deep-purple: ( - "base": #673ab7, - "lighten-5": #ede7f6, - "lighten-4": #d1c4e9, - "lighten-3": #b39ddb, - "lighten-2": #9575cd, - "lighten-1": #7e57c2, - "darken-1": #5e35b1, - "darken-2": #512da8, - "darken-3": #4527a0, - "darken-4": #311b92, - "accent-1": #b388ff, - "accent-2": #7c4dff, - "accent-3": #651fff, - "accent-4": #6200ea -); - -$indigo: ( - "base": #3f51b5, - "lighten-5": #e8eaf6, - "lighten-4": #c5cae9, - "lighten-3": #9fa8da, - "lighten-2": #7986cb, - "lighten-1": #5c6bc0, - "darken-1": #3949ab, - "darken-2": #303f9f, - "darken-3": #283593, - "darken-4": #1a237e, - "accent-1": #8c9eff, - "accent-2": #536dfe, - "accent-3": #3d5afe, - "accent-4": #304ffe -); - -$blue: ( - "base": #2196F3, - "lighten-5": #E3F2FD, - "lighten-4": #BBDEFB, - "lighten-3": #90CAF9, - "lighten-2": #64B5F6, - "lighten-1": #42A5F5, - "darken-1": #1E88E5, - "darken-2": #1976D2, - "darken-3": #1565C0, - "darken-4": #0D47A1, - "accent-1": #82B1FF, - "accent-2": #448AFF, - "accent-3": #2979FF, - "accent-4": #2962FF -); - -$light-blue: ( - "base": #03a9f4, - "lighten-5": #e1f5fe, - "lighten-4": #b3e5fc, - "lighten-3": #81d4fa, - "lighten-2": #4fc3f7, - "lighten-1": #29b6f6, - "darken-1": #039be5, - "darken-2": #0288d1, - "darken-3": #0277bd, - "darken-4": #01579b, - "accent-1": #80d8ff, - "accent-2": #40c4ff, - "accent-3": #00b0ff, - "accent-4": #0091ea -); - -$cyan: ( - "base": #00bcd4, - "lighten-5": #e0f7fa, - "lighten-4": #b2ebf2, - "lighten-3": #80deea, - "lighten-2": #4dd0e1, - "lighten-1": #26c6da, - "darken-1": #00acc1, - "darken-2": #0097a7, - "darken-3": #00838f, - "darken-4": #006064, - "accent-1": #84ffff, - "accent-2": #18ffff, - "accent-3": #00e5ff, - "accent-4": #00b8d4 -); - -$teal: ( - "base": #009688, - "lighten-5": #e0f2f1, - "lighten-4": #b2dfdb, - "lighten-3": #80cbc4, - "lighten-2": #4db6ac, - "lighten-1": #26a69a, - "darken-1": #00897b, - "darken-2": #00796b, - "darken-3": #00695c, - "darken-4": #004d40, - "accent-1": #a7ffeb, - "accent-2": #64ffda, - "accent-3": #1de9b6, - "accent-4": #00bfa5 -); - -$green: ( - "base": #4CAF50, - "lighten-5": #E8F5E9, - "lighten-4": #C8E6C9, - "lighten-3": #A5D6A7, - "lighten-2": #81C784, - "lighten-1": #66BB6A, - "darken-1": #43A047, - "darken-2": #388E3C, - "darken-3": #2E7D32, - "darken-4": #1B5E20, - "accent-1": #B9F6CA, - "accent-2": #69F0AE, - "accent-3": #00E676, - "accent-4": #00C853 -); - -$light-green: ( - "base": #8bc34a, - "lighten-5": #f1f8e9, - "lighten-4": #dcedc8, - "lighten-3": #c5e1a5, - "lighten-2": #aed581, - "lighten-1": #9ccc65, - "darken-1": #7cb342, - "darken-2": #689f38, - "darken-3": #558b2f, - "darken-4": #33691e, - "accent-1": #ccff90, - "accent-2": #b2ff59, - "accent-3": #76ff03, - "accent-4": #64dd17 -); - -$lime: ( - "base": #cddc39, - "lighten-5": #f9fbe7, - "lighten-4": #f0f4c3, - "lighten-3": #e6ee9c, - "lighten-2": #dce775, - "lighten-1": #d4e157, - "darken-1": #c0ca33, - "darken-2": #afb42b, - "darken-3": #9e9d24, - "darken-4": #827717, - "accent-1": #f4ff81, - "accent-2": #eeff41, - "accent-3": #c6ff00, - "accent-4": #aeea00 -); - -$yellow: ( - "base": #ffeb3b, - "lighten-5": #fffde7, - "lighten-4": #fff9c4, - "lighten-3": #fff59d, - "lighten-2": #fff176, - "lighten-1": #ffee58, - "darken-1": #fdd835, - "darken-2": #fbc02d, - "darken-3": #f9a825, - "darken-4": #f57f17, - "accent-1": #ffff8d, - "accent-2": #ffff00, - "accent-3": #ffea00, - "accent-4": #ffd600 -); - -$amber: ( - "base": #ffc107, - "lighten-5": #fff8e1, - "lighten-4": #ffecb3, - "lighten-3": #ffe082, - "lighten-2": #ffd54f, - "lighten-1": #ffca28, - "darken-1": #ffb300, - "darken-2": #ffa000, - "darken-3": #ff8f00, - "darken-4": #ff6f00, - "accent-1": #ffe57f, - "accent-2": #ffd740, - "accent-3": #ffc400, - "accent-4": #ffab00 -); - -$orange: ( - "base": #ff9800, - "lighten-5": #fff3e0, - "lighten-4": #ffe0b2, - "lighten-3": #ffcc80, - "lighten-2": #ffb74d, - "lighten-1": #ffa726, - "darken-1": #fb8c00, - "darken-2": #f57c00, - "darken-3": #ef6c00, - "darken-4": #e65100, - "accent-1": #ffd180, - "accent-2": #ffab40, - "accent-3": #ff9100, - "accent-4": #ff6d00 -); - -$deep-orange: ( - "base": #ff5722, - "lighten-5": #fbe9e7, - "lighten-4": #ffccbc, - "lighten-3": #ffab91, - "lighten-2": #ff8a65, - "lighten-1": #ff7043, - "darken-1": #f4511e, - "darken-2": #e64a19, - "darken-3": #d84315, - "darken-4": #bf360c, - "accent-1": #ff9e80, - "accent-2": #ff6e40, - "accent-3": #ff3d00, - "accent-4": #dd2c00 -); - -$brown: ( - "base": #795548, - "lighten-5": #efebe9, - "lighten-4": #d7ccc8, - "lighten-3": #bcaaa4, - "lighten-2": #a1887f, - "lighten-1": #8d6e63, - "darken-1": #6d4c41, - "darken-2": #5d4037, - "darken-3": #4e342e, - "darken-4": #3e2723 -); - -$blue-grey: ( - "base": #607d8b, - "lighten-5": #eceff1, - "lighten-4": #cfd8dc, - "lighten-3": #b0bec5, - "lighten-2": #90a4ae, - "lighten-1": #78909c, - "darken-1": #546e7a, - "darken-2": #455a64, - "darken-3": #37474f, - "darken-4": #263238 -); - -$grey: ( - "base": #9e9e9e, - "lighten-5": #fafafa, - "lighten-4": #f5f5f5, - "lighten-3": #eeeeee, - "lighten-2": #e0e0e0, - "lighten-1": #bdbdbd, - "darken-1": #757575, - "darken-2": #616161, - "darken-3": #424242, - "darken-4": #212121 -); - -$shades: ( - "black": #000000, - "white": #FFFFFF, - "transparent": transparent -); - -$colors: ( - "materialize-red": $materialize-red, - "red": $red, - "pink": $pink, - "purple": $purple, - "deep-purple": $deep-purple, - "indigo": $indigo, - "blue": $blue, - "light-blue": $light-blue, - "cyan": $cyan, - "teal": $teal, - "green": $green, - "light-green": $light-green, - "lime": $lime, - "yellow": $yellow, - "amber": $amber, - "orange": $orange, - "deep-orange": $deep-orange, - "brown": $brown, - "blue-grey": $blue-grey, - "grey": $grey, - "shades": $shades -) !default; - - -// usage: color("name_of_color", "type_of_color") -// to avoid to repeating map-get($colors, ...) - -@function color($color, $type) { - @if map-has-key($colors, $color) { - $curr_color: map-get($colors, $color); - @if map-has-key($curr_color, $type) { - @return map-get($curr_color, $type); - } - } - @warn "Unknown `#{$color}` - `#{$type}` in $colors."; - @return null; -} diff --git a/tubesync/common/static/styles/materializecss/components/_datepicker.scss b/tubesync/common/static/styles/materializecss/components/_datepicker.scss deleted file mode 100644 index d2c920b25..000000000 --- a/tubesync/common/static/styles/materializecss/components/_datepicker.scss +++ /dev/null @@ -1,191 +0,0 @@ -/* Modal */ -.datepicker-modal { - max-width: 325px; - min-width: 300px; - max-height: none; -} - -.datepicker-container.modal-content { - display: flex; - flex-direction: column; - padding: 0; -} - -.datepicker-controls { - display: flex; - justify-content: space-between; - width: 280px; - margin: 0 auto; - - .selects-container { - display: flex; - } - - .select-wrapper { - input { - &:focus { - border-bottom: none; - } - border-bottom: none; - text-align: center; - margin: 0; - } - - .caret { - display: none; - } - } - - .select-year input { - width: 50px; - } - - .select-month input { - width: 70px; - } -} - -.month-prev, .month-next { - margin-top: 4px; - cursor: pointer; - background-color: transparent; - border: none; -} - - -/* Date Display */ -.datepicker-date-display { - flex: 1 auto; - background-color: $secondary-color; - color: #fff; - padding: 20px 22px; - font-weight: 500; - - .year-text { - display: block; - font-size: 1.5rem; - line-height: 25px; - color: $datepicker-year; - } - - .date-text { - display: block; - font-size: 2.8rem; - line-height: 47px; - font-weight: 500; - } -} - - -/* Calendar */ -.datepicker-calendar-container { - flex: 2.5 auto; -} - -.datepicker-table { - width: 280px; - font-size: 1rem; - margin: 0 auto; - - thead { - border-bottom: none; - } - - th { - padding: 10px 5px; - text-align: center; - } - - tr { - border: none; - } - - abbr { - text-decoration: none; - color: $datepicker-calendar-header-color; - } - - td { - &.is-today { - color: $secondary-color; - } - - &.is-selected { - background-color: $secondary-color; - color: #fff; - } - - &.is-outside-current-month, - &.is-disabled { - color: $datepicker-disabled-day-color; - pointer-events: none; - } - - border-radius: 50%; - padding: 0; - } -} - -.datepicker-day-button { - &:focus { - background-color: $datepicker-day-focus; - } - - background-color: transparent; - border: none; - line-height: 38px; - display: block; - width: 100%; - border-radius: 50%; - padding: 0 5px; - cursor: pointer; - color: inherit; -} - - -/* Footer */ -.datepicker-footer { - width: 280px; - margin: 0 auto; - padding-bottom: 5px; - display: flex; - justify-content: space-between; -} - -.datepicker-cancel, -.datepicker-clear, -.datepicker-today, -.datepicker-done { - color: $secondary-color; - padding: 0 1rem; -} - -.datepicker-clear { - color: $error-color; -} - - -/* Media Queries */ -@media #{$medium-and-up} { - .datepicker-modal { - max-width: 625px; - } - - .datepicker-container.modal-content { - flex-direction: row; - } - - .datepicker-date-display { - flex: 0 1 270px; - } - - .datepicker-controls, - .datepicker-table, - .datepicker-footer { - width: 320px; - } - - .datepicker-day-button { - line-height: 44px; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_dropdown.scss b/tubesync/common/static/styles/materializecss/components/_dropdown.scss deleted file mode 100644 index 0caae65d4..000000000 --- a/tubesync/common/static/styles/materializecss/components/_dropdown.scss +++ /dev/null @@ -1,85 +0,0 @@ -.dropdown-content { - &:focus { - outline: 0; - } - - - @extend .z-depth-1; - background-color: $dropdown-bg-color; - margin: 0; - display: none; - min-width: 100px; - overflow-y: auto; - opacity: 0; - position: absolute; - left: 0; - top: 0; - z-index: 9999; // TODO: Check if this doesn't break other things - transform-origin: 0 0; - - - li { - &:hover, &.active { - background-color: $dropdown-hover-bg-color; - } - - &:focus { - outline: none; - } - - &.divider { - min-height: 0; - height: 1px; - } - - & > a, & > span { - font-size: 16px; - color: $dropdown-color; - display: block; - line-height: 22px; - padding: (($dropdown-item-height - 22) / 2) 16px; - } - - & > span > label { - top: 1px; - left: 0; - height: 18px; - } - - // Icon alignment override - & > a > i { - height: inherit; - line-height: inherit; - float: left; - margin: 0 24px 0 0; - width: 24px; - } - - - clear: both; - color: $off-black; - cursor: pointer; - min-height: $dropdown-item-height; - line-height: 1.5rem; - width: 100%; - text-align: left; - } -} - -body.keyboard-focused { - .dropdown-content li:focus { - background-color: darken($dropdown-hover-bg-color, 8%); - } -} - -// Input field specificity bugfix -.input-field.col .dropdown-content [type="checkbox"] + label { - top: 1px; - left: 0; - height: 18px; - transform: none; -} - -.dropdown-trigger { - cursor: pointer; -} \ No newline at end of file diff --git a/tubesync/common/static/styles/materializecss/components/_global.scss b/tubesync/common/static/styles/materializecss/components/_global.scss deleted file mode 100644 index 39f33db0d..000000000 --- a/tubesync/common/static/styles/materializecss/components/_global.scss +++ /dev/null @@ -1,769 +0,0 @@ -//Default styles - -html { - box-sizing: border-box; -} -*, *:before, *:after { - box-sizing: inherit; -} - -body { - // display: flex; - // min-height: 100vh; - // flex-direction: column; -} - -main { - // flex: 1 0 auto; -} - -button, -input, -optgroup, -select, -textarea { - font-family: $font-stack; -} - -ul { - &:not(.browser-default) { - padding-left: 0; - list-style-type: none; - - & > li { - list-style-type: none; - } - } -} - -a { - color: $link-color; - text-decoration: none; - - // Gets rid of tap active state - -webkit-tap-highlight-color: transparent; -} - - -// Positioning -.valign-wrapper { - display: flex; - align-items: center; -} - - -// classic clearfix -.clearfix { - clear: both; -} - - -// Z-levels -.z-depth-0 { - box-shadow: none !important; -} - -/* 2dp elevation modified*/ -.z-depth-1 { - box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), - 0 3px 1px -2px rgba(0,0,0,0.12), - 0 1px 5px 0 rgba(0,0,0,0.2); -} -.z-depth-1-half { - box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.14), 0 1px 7px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -1px rgba(0, 0, 0, 0.2); -} - -/* 6dp elevation modified*/ -.z-depth-2 { - box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), - 0 1px 10px 0 rgba(0,0,0,0.12), - 0 2px 4px -1px rgba(0,0,0,0.3); -} - -/* 12dp elevation modified*/ -.z-depth-3 { - box-shadow: 0 8px 17px 2px rgba(0,0,0,0.14), - 0 3px 14px 2px rgba(0,0,0,0.12), - 0 5px 5px -3px rgba(0, 0, 0, 0.2); -} - -/* 16dp elevation */ -.z-depth-4 { - box-shadow: 0 16px 24px 2px rgba(0,0,0,0.14), - 0 6px 30px 5px rgba(0,0,0,0.12), - 0 8px 10px -7px rgba(0,0,0,0.2); -} - -/* 24dp elevation */ -.z-depth-5 { - box-shadow: 0 24px 38px 3px rgba(0,0,0,0.14), - 0 9px 46px 8px rgba(0,0,0,0.12), - 0 11px 15px -7px rgba(0,0,0,0.2); -} - -.hoverable { - transition: box-shadow .25s; - - &:hover { - box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); - } -} - -// Dividers - -.divider { - height: 1px; - overflow: hidden; - background-color: color("grey", "lighten-2"); -} - - -// Blockquote - -blockquote { - margin: 20px 0; - padding-left: 1.5rem; - border-left: 5px solid $primary-color; -} - -// Icon Styles - -i { - line-height: inherit; - - &.left { - float: left; - margin-right: 15px; - } - &.right { - float: right; - margin-left: 15px; - } - &.tiny { - font-size: 1rem; - } - &.small { - font-size: 2rem; - } - &.medium { - font-size: 4rem; - } - &.large { - font-size: 6rem; - } -} - -// Images -img.responsive-img, -video.responsive-video { - max-width: 100%; - height: auto; -} - - -// Pagination - -.pagination { - - li { - display: inline-block; - border-radius: 2px; - text-align: center; - vertical-align: top; - height: 30px; - - a { - color: #444; - display: inline-block; - font-size: 1.2rem; - padding: 0 10px; - line-height: 30px; - } - - &.active a { color: #fff; } - - &.active { background-color: $primary-color; } - - &.disabled a { - cursor: default; - color: #999; - } - - i { - font-size: 2rem; - } - } - - - li.pages ul li { - display: inline-block; - float: none; - } -} -@media #{$medium-and-down} { - .pagination { - width: 100%; - - li.prev, - li.next { - width: 10%; - } - - li.pages { - width: 80%; - overflow: hidden; - white-space: nowrap; - } - } -} - -// Breadcrumbs -.breadcrumb { - font-size: 18px; - color: rgba(255,255,255, .7); - - i, - [class^="mdi-"], [class*="mdi-"], - i.material-icons { - display: inline-block; - float: left; - font-size: 24px; - } - - &:before { - content: '\E5CC'; - color: rgba(255,255,255, .7); - vertical-align: top; - display: inline-block; - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 25px; - margin: 0 10px 0 8px; - -webkit-font-smoothing: antialiased; - } - - &:first-child:before { - display: none; - } - - &:last-child { - color: #fff; - } -} - -// Parallax -.parallax-container { - position: relative; - overflow: hidden; - height: 500px; - - .parallax { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: -1; - - img { - opacity: 0; - position: absolute; - left: 50%; - bottom: 0; - min-width: 100%; - min-height: 100%; - transform: translate3d(0,0,0); - transform: translateX(-50%); - } - } -} - -// Pushpin -.pin-top, .pin-bottom { - position: relative; -} -.pinned { - position: fixed !important; -} - -/********************* - Transition Classes -**********************/ - -ul.staggered-list li { - opacity: 0; -} - -.fade-in { - opacity: 0; - transform-origin: 0 50%; -} - - -/********************* - Media Query Classes -**********************/ -.hide-on-small-only, .hide-on-small-and-down { - @media #{$small-and-down} { - display: none !important; - } -} -.hide-on-med-and-down { - @media #{$medium-and-down} { - display: none !important; - } -} -.hide-on-med-and-up { - @media #{$medium-and-up} { - display: none !important; - } -} -.hide-on-med-only { - @media only screen and (min-width: $small-screen) and (max-width: $medium-screen) { - display: none !important; - } -} -.hide-on-large-only { - @media #{$large-and-up} { - display: none !important; - } -} -.hide-on-extra-large-only { - @media #{$extra-large-and-up} { - display: none !important; - } -} -.show-on-extra-large { - @media #{$extra-large-and-up} { - display: block !important; - } -} -.show-on-large { - @media #{$large-and-up} { - display: block !important; - } -} -.show-on-medium { - @media only screen and (min-width: $small-screen) and (max-width: $medium-screen) { - display: block !important; - } -} -.show-on-small { - @media #{$small-and-down} { - display: block !important; - } -} -.show-on-medium-and-up { - @media #{$medium-and-up} { - display: block !important; - } -} -.show-on-medium-and-down { - @media #{$medium-and-down} { - display: block !important; - } -} - - -// Center text on mobile -.center-on-small-only { - @media #{$small-and-down} { - text-align: center; - } -} - -// Footer -.page-footer { - padding-top: 20px; - color: $footer-font-color; - background-color: $footer-bg-color; - - .footer-copyright { - overflow: hidden; - min-height: 50px; - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 0px; - color: $footer-copyright-font-color; - background-color: $footer-copyright-bg-color; - } -} - -// Tables -table, th, td { - border: none; -} - -table { - width:100%; - display: table; - border-collapse: collapse; - border-spacing: 0; - - &.striped { - tr { - border-bottom: none; - } - - > tbody { - > tr:nth-child(odd) { - background-color: $table-striped-color; - } - - > tr > td { - border-radius: 0; - } - } - } - - &.highlight > tbody > tr { - transition: background-color .25s ease; - &:hover { - background-color: $table-striped-color; - } - } - - &.centered { - thead tr th, tbody tr td { - text-align: center; - } - } -} - -tr { - border-bottom: 1px solid $table-border-color; -} - -td, th{ - padding: 15px 5px; - display: table-cell; - text-align: left; - vertical-align: middle; - border-radius: 2px; -} - -// Responsive Table -@media #{$medium-and-down} { - - table.responsive-table { - width: 100%; - border-collapse: collapse; - border-spacing: 0; - display: block; - position: relative; - - td:empty:before { - content: '\00a0'; - } - - th, - td { - margin: 0; - vertical-align: top; - } - - th { text-align: left; } - thead { - display: block; - float: left; - - tr { - display: block; - padding: 0 10px 0 0; - - th::before { - content: "\00a0"; - } - } - } - tbody { - display: block; - width: auto; - position: relative; - overflow-x: auto; - white-space: nowrap; - - tr { - display: inline-block; - vertical-align: top; - } - } - th { - display: block; - text-align: right; - } - td { - display: block; - min-height: 1.25em; - text-align: left; - } - tr { - border-bottom: none; - padding: 0 10px; - } - - /* sort out borders */ - thead { - border: 0; - border-right: 1px solid $table-border-color; - } - } - -} - - -// Collections -.collection { - margin: $element-top-margin 0 $element-bottom-margin 0; - border: 1px solid $collection-border-color; - border-radius: 2px; - overflow: hidden; - position: relative; - - .collection-item { - background-color: $collection-bg-color; - line-height: $collection-line-height; - padding: 10px 20px; - margin: 0; - border-bottom: 1px solid $collection-border-color; - - // Avatar Collection - &.avatar { - min-height: 84px; - padding-left: 72px; - position: relative; - - // Don't style circles inside preloader classes. - &:not(.circle-clipper) > .circle, - :not(.circle-clipper) > .circle { - position: absolute; - width: 42px; - height: 42px; - overflow: hidden; - left: 15px; - display: inline-block; - vertical-align: middle; - } - i.circle { - font-size: 18px; - line-height: 42px; - color: #fff; - background-color: #999; - text-align: center; - } - - - .title { - font-size: 16px; - } - - p { - margin: 0; - } - - .secondary-content { - position: absolute; - top: 16px; - right: 16px; - } - - } - - - &:last-child { - border-bottom: none; - } - - &.active { - background-color: $collection-active-bg-color; - color: $collection-active-color; - - .secondary-content { - color: #fff; - } - } - } - a.collection-item{ - display: block; - transition: .25s; - color: $collection-link-color; - &:not(.active) { - &:hover { - background-color: $collection-hover-bg-color; - } - } - } - - &.with-header { - .collection-header { - background-color: $collection-bg-color; - border-bottom: 1px solid $collection-border-color; - padding: 10px 20px; - } - .collection-item { - padding-left: 30px; - } - .collection-item.avatar { - padding-left: 72px; - } - } - -} -// Made less specific to allow easier overriding -.secondary-content { - float: right; - color: $secondary-color; -} -.collapsible .collection { - margin: 0; - border: none; -} - - - -// Responsive Videos -.video-container { - position: relative; - padding-bottom: 56.25%; - height: 0; - overflow: hidden; - - iframe, object, embed { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - } -} - -// Progress Bar -.progress { - position: relative; - height: 4px; - display: block; - width: 100%; - background-color: lighten($progress-bar-color, 40%); - border-radius: 2px; - margin: $element-top-margin 0 $element-bottom-margin 0; - overflow: hidden; - .determinate { - position: absolute; - top: 0; - left: 0; - bottom: 0; - background-color: $progress-bar-color; - transition: width .3s linear; - } - .indeterminate { - background-color: $progress-bar-color; - &:before { - content: ''; - position: absolute; - background-color: inherit; - top: 0; - left:0; - bottom: 0; - will-change: left, right; - // Custom bezier - animation: indeterminate 2.1s cubic-bezier(0.650, 0.815, 0.735, 0.395) infinite; - - } - &:after { - content: ''; - position: absolute; - background-color: inherit; - top: 0; - left:0; - bottom: 0; - will-change: left, right; - // Custom bezier - animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.840, 0.440, 1.000) infinite; - animation-delay: 1.15s; - } - } -} -@keyframes indeterminate { - 0% { - left: -35%; - right:100%; - } - 60% { - left: 100%; - right: -90%; - } - 100% { - left: 100%; - right: -90%; - } -} - -@keyframes indeterminate-short { - 0% { - left: -200%; - right: 100%; - } - 60% { - left: 107%; - right: -8%; - } - 100% { - left: 107%; - right: -8%; - } -} - - -/******************* - Utility Classes -*******************/ - -.hide { - display: none !important; -} - -// Text Align -.left-align { - text-align: left; -} -.right-align { - text-align: right -} -.center, .center-align { - text-align: center; -} - -.left { - float: left !important; -} -.right { - float: right !important; -} - -// No Text Select -.no-select { - user-select: none; -} - -.circle { - border-radius: 50%; -} - -.center-block { - display: block; - margin-left: auto; - margin-right: auto; -} - -.truncate { - display: block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.no-padding { - padding: 0 !important; -} diff --git a/tubesync/common/static/styles/materializecss/components/_grid.scss b/tubesync/common/static/styles/materializecss/components/_grid.scss deleted file mode 100644 index 8892f0503..000000000 --- a/tubesync/common/static/styles/materializecss/components/_grid.scss +++ /dev/null @@ -1,156 +0,0 @@ -.container { - margin: 0 auto; - max-width: 1280px; - width: 90%; -} -@media #{$medium-and-up} { - .container { - width: 85%; - } -} -@media #{$large-and-up} { - .container { - width: 70%; - } -} -.col .row { - margin-left: (-1 * $gutter-width / 2); - margin-right: (-1 * $gutter-width / 2); -} - -.section { - padding-top: 1rem; - padding-bottom: 1rem; - - &.no-pad { - padding: 0; - } - &.no-pad-bot { - padding-bottom: 0; - } - &.no-pad-top { - padding-top: 0; - } -} - - -// Mixins to eliminate code repitition -@mixin reset-offset { - margin-left: auto; - left: auto; - right: auto; -} -@mixin grid-classes($size, $i, $perc) { - &.offset-#{$size}#{$i} { - margin-left: $perc; - } - &.pull-#{$size}#{$i} { - right: $perc; - } - &.push-#{$size}#{$i} { - left: $perc; - } -} - - -.row { - margin-left: auto; - margin-right: auto; - margin-bottom: 20px; - - // Clear floating children - &:after { - content: ""; - display: table; - clear: both; - } - - .col { - float: left; - box-sizing: border-box; - padding: 0 $gutter-width / 2; - min-height: 1px; - - &[class*="push-"], - &[class*="pull-"] { - position: relative; - } - - $i: 1; - @while $i <= $num-cols { - $perc: unquote((100 / ($num-cols / $i)) + "%"); - &.s#{$i} { - width: $perc; - @include reset-offset; - } - $i: $i + 1; - } - - $i: 1; - @while $i <= $num-cols { - $perc: unquote((100 / ($num-cols / $i)) + "%"); - @include grid-classes("s", $i, $perc); - $i: $i + 1; - } - - @media #{$medium-and-up} { - - $i: 1; - @while $i <= $num-cols { - $perc: unquote((100 / ($num-cols / $i)) + "%"); - &.m#{$i} { - width: $perc; - @include reset-offset; - } - $i: $i + 1 - } - - $i: 1; - @while $i <= $num-cols { - $perc: unquote((100 / ($num-cols / $i)) + "%"); - @include grid-classes("m", $i, $perc); - $i: $i + 1; - } - } - - @media #{$large-and-up} { - - $i: 1; - @while $i <= $num-cols { - $perc: unquote((100 / ($num-cols / $i)) + "%"); - &.l#{$i} { - width: $perc; - @include reset-offset; - } - $i: $i + 1; - } - - $i: 1; - @while $i <= $num-cols { - $perc: unquote((100 / ($num-cols / $i)) + "%"); - @include grid-classes("l", $i, $perc); - $i: $i + 1; - } - } - - @media #{$extra-large-and-up} { - - $i: 1; - @while $i <= $num-cols { - $perc: unquote((100 / ($num-cols / $i)) + "%"); - &.xl#{$i} { - width: $perc; - @include reset-offset; - } - $i: $i + 1; - } - - $i: 1; - @while $i <= $num-cols { - $perc: unquote((100 / ($num-cols / $i)) + "%"); - @include grid-classes("xl", $i, $perc); - $i: $i + 1; - } - } - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_icons-material-design.scss b/tubesync/common/static/styles/materializecss/components/_icons-material-design.scss deleted file mode 100644 index 2aa6a4ae9..000000000 --- a/tubesync/common/static/styles/materializecss/components/_icons-material-design.scss +++ /dev/null @@ -1,5 +0,0 @@ -/* This is needed for some mobile phones to display the Google Icon font properly */ -.material-icons { - text-rendering: optimizeLegibility; - font-feature-settings: 'liga'; -} diff --git a/tubesync/common/static/styles/materializecss/components/_materialbox.scss b/tubesync/common/static/styles/materializecss/components/_materialbox.scss deleted file mode 100644 index 30276672f..000000000 --- a/tubesync/common/static/styles/materializecss/components/_materialbox.scss +++ /dev/null @@ -1,43 +0,0 @@ -.materialboxed { - &:hover { - &:not(.active) { - opacity: .8; - } - } - - display: block; - cursor: zoom-in; - position: relative; - transition: opacity .4s; - -webkit-backface-visibility: hidden; - - &.active { - cursor: zoom-out; - } -} - -#materialbox-overlay { - position:fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: #292929; - z-index: 1000; - will-change: opacity; -} - -.materialbox-caption { - position: fixed; - display: none; - color: #fff; - line-height: 50px; - bottom: 0; - left: 0; - width: 100%; - text-align: center; - padding: 0% 15%; - height: 50px; - z-index: 1000; - -webkit-font-smoothing: antialiased; -} \ No newline at end of file diff --git a/tubesync/common/static/styles/materializecss/components/_modal.scss b/tubesync/common/static/styles/materializecss/components/_modal.scss deleted file mode 100644 index 38cf3ce8f..000000000 --- a/tubesync/common/static/styles/materializecss/components/_modal.scss +++ /dev/null @@ -1,94 +0,0 @@ -.modal { - &:focus { - outline: none; - } - - @extend .z-depth-5; - - display: none; - position: fixed; - left: 0; - right: 0; - background-color: #fafafa; - padding: 0; - max-height: 70%; - width: 55%; - margin: auto; - overflow-y: auto; - - border-radius: 2px; - will-change: top, opacity; - - @media #{$medium-and-down} { - width: 80%; - } - - h1,h2,h3,h4 { - margin-top: 0; - } - - .modal-content { - padding: 24px; - } - .modal-close { - cursor: pointer; - } - - .modal-footer { - border-radius: 0 0 2px 2px; - background-color: #fafafa; - padding: 4px 6px; - height: 56px; - width: 100%; - text-align: right; - - .btn, .btn-flat { - margin: 6px 0; - } - } -} -.modal-overlay { - position: fixed; - z-index: 999; - top: -25%; - left: 0; - bottom: 0; - right: 0; - height: 125%; - width: 100%; - background: #000; - display: none; - - will-change: opacity; -} - -// Modal with fixed action footer -.modal.modal-fixed-footer { - padding: 0; - height: 70%; - - .modal-content { - position: absolute; - height: calc(100% - 56px); - max-height: 100%; - width: 100%; - overflow-y: auto; - } - - .modal-footer { - border-top: 1px solid rgba(0,0,0,.1); - position: absolute; - bottom: 0; - } -} - -// Modal Bottom Sheet Style -.modal.bottom-sheet { - top: auto; - bottom: -100%; - margin: 0; - width: 100%; - max-height: 45%; - border-radius: 0; - will-change: bottom, opacity; -} diff --git a/tubesync/common/static/styles/materializecss/components/_navbar.scss b/tubesync/common/static/styles/materializecss/components/_navbar.scss deleted file mode 100644 index 0317bb262..000000000 --- a/tubesync/common/static/styles/materializecss/components/_navbar.scss +++ /dev/null @@ -1,208 +0,0 @@ -nav { - &.nav-extended { - height: auto; - - .nav-wrapper { - min-height: $navbar-height-mobile; - height: auto; - } - - .nav-content { - position: relative; - line-height: normal; - } - } - - color: $navbar-font-color; - @extend .z-depth-1; - background-color: $primary-color; - width: 100%; - height: $navbar-height-mobile; - line-height: $navbar-line-height-mobile; - - a { color: $navbar-font-color; } - - i, - [class^="mdi-"], [class*="mdi-"], - i.material-icons { - display: block; - font-size: 24px; - height: $navbar-height-mobile; - line-height: $navbar-line-height-mobile; - } - - .nav-wrapper { - position: relative; - height: 100%; - } - - @media #{$large-and-up} { - a.sidenav-trigger { display: none; } - } - - - // Collapse button - .sidenav-trigger { - float: left; - position: relative; - z-index: 1; - height: $navbar-height-mobile; - margin: 0 18px; - - i { - height: $navbar-height-mobile; - line-height: $navbar-line-height-mobile; - } - } - - - // Logo - .brand-logo { - position: absolute; - color: $navbar-font-color; - display: inline-block; - font-size: $navbar-brand-font-size; - padding: 0; - - &.center { - left: 50%; - transform: translateX(-50%); - } - - @media #{$medium-and-down} { - left: 50%; - transform: translateX(-50%); - - &.left, &.right { - padding: 0; - transform: none; - } - - &.left { left: 0.5rem; } - &.right { - right: 0.5rem; - left: auto; - } - } - - &.right { - right: 0.5rem; - padding: 0; - } - - i, - [class^="mdi-"], [class*="mdi-"], - i.material-icons { - float: left; - margin-right: 15px; - } - } - - - // Title - .nav-title { - display: inline-block; - font-size: 32px; - padding: 28px 0; - } - - - // Navbar Links - ul { - margin: 0; - - li { - transition: background-color .3s; - float: left; - padding: 0; - - &.active { - background-color: rgba(0,0,0,.1); - } - } - a { - transition: background-color .3s; - font-size: $navbar-font-size; - color: $navbar-font-color; - display: block; - padding: 0 15px; - cursor: pointer; - - &.btn, &.btn-large, &.btn-flat, &.btn-floating { - margin-top: -2px; - margin-left: 15px; - margin-right: 15px; - - & > .material-icons { - height: inherit; - line-height: inherit; - } - } - - &:hover { - background-color: rgba(0,0,0,.1); - } - } - - &.left { - float: left; - } - } - - // Navbar Search Form - form { - height: 100%; - } - - .input-field { - margin: 0; - height: 100%; - - input { - height: 100%; - font-size: 1.2rem; - border: none; - padding-left: 2rem; - - &:focus, &[type=text]:valid, &[type=password]:valid, - &[type=email]:valid, &[type=url]:valid, &[type=date]:valid { - border: none; - box-shadow: none; - } - } - - label { - top: 0; - left: 0; - - i { - color: rgba(255,255,255,.7); - transition: color .3s; - } - &.active i { color: $navbar-font-color; } - } - } -} - -// Fixed Navbar -.navbar-fixed { - position: relative; - height: $navbar-height-mobile; - z-index: 997; - - nav { - position: fixed; - } -} -@media #{$medium-and-up} { - nav.nav-extended .nav-wrapper { - min-height: $navbar-height; - } - nav, nav .nav-wrapper i, nav a.sidenav-trigger, nav a.sidenav-trigger i { - height: $navbar-height; - line-height: $navbar-line-height; - } - .navbar-fixed { - height: $navbar-height; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_normalize.scss b/tubesync/common/static/styles/materializecss/components/_normalize.scss deleted file mode 100644 index fa4e73dd4..000000000 --- a/tubesync/common/static/styles/materializecss/components/_normalize.scss +++ /dev/null @@ -1,447 +0,0 @@ -/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ - -/* Document - ========================================================================== */ - -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in - * IE on Windows Phone and in iOS. - */ - -html { - line-height: 1.15; /* 1 */ - -ms-text-size-adjust: 100%; /* 2 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers (opinionated). - */ - -body { - margin: 0; -} - -/** - * Add the correct display in IE 9-. - */ - -article, -aside, -footer, -header, -nav, -section { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - * 1. Add the correct display in IE. - */ - -figcaption, -figure, -main { /* 1 */ - display: block; -} - -/** - * Add the correct margin in IE 8. - */ - -figure { - margin: 1em 40px; -} - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * 1. Remove the gray background on active links in IE 10. - * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. - */ - -a { - background-color: transparent; /* 1 */ - -webkit-text-decoration-skip: objects; /* 2 */ -} - -/** - * 1. Remove the bottom border in Chrome 57- and Firefox 39-. - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Prevent the duplicate application of `bolder` by the next rule in Safari 6. - */ - -b, -strong { - font-weight: inherit; -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font style in Android 4.3-. - */ - -dfn { - font-style: italic; -} - -/** - * Add the correct background and color in IE 9-. - */ - -mark { - background-color: #ff0; - color: #000; -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - */ - -audio, -video { - display: inline-block; -} - -/** - * Add the correct display in iOS 4-7. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/** - * Remove the border on images inside links in IE 10-. - */ - -img { - border-style: none; -} - -/** - * Hide the overflow in IE. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers (opinionated). - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: sans-serif; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` - * controls in Android 4. - * 2. Correct the inability to style clickable types in iOS and Safari. - */ - -button, -html [type="button"], /* 1 */ -[type="reset"], -[type="submit"] { - -webkit-appearance: button; /* 2 */ -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Correct the padding in Firefox. - */ - -fieldset { - padding: 0.35em 0.75em 0.625em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * 1. Add the correct display in IE 9-. - * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - display: inline-block; /* 1 */ - vertical-align: baseline; /* 2 */ -} - -/** - * Remove the default vertical scrollbar in IE. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10-. - * 2. Remove the padding in IE 10-. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-cancel-button, -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in IE 9-. - * 1. Add the correct display in Edge, IE, and Firefox. - */ - -details, /* 1 */ -menu { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Scripting - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - */ - -canvas { - display: inline-block; -} - -/** - * Add the correct display in IE. - */ - -template { - display: none; -} - -/* Hidden - ========================================================================== */ - -/** - * Add the correct display in IE 10-. - */ - -[hidden] { - display: none; -} diff --git a/tubesync/common/static/styles/materializecss/components/_preloader.scss b/tubesync/common/static/styles/materializecss/components/_preloader.scss deleted file mode 100644 index cfe299396..000000000 --- a/tubesync/common/static/styles/materializecss/components/_preloader.scss +++ /dev/null @@ -1,334 +0,0 @@ -/* - @license - Copyright (c) 2014 The Polymer Project Authors. All rights reserved. - This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - Code distributed by Google as part of the polymer project is also - subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ - -/**************************/ -/* STYLES FOR THE SPINNER */ -/**************************/ - -/* - * Constants: - * STROKEWIDTH = 3px - * ARCSIZE = 270 degrees (amount of circle the arc takes up) - * ARCTIME = 1333ms (time it takes to expand and contract arc) - * ARCSTARTROT = 216 degrees (how much the start location of the arc - * should rotate each time, 216 gives us a - * 5 pointed star shape (it's 360/5 * 3). - * For a 7 pointed star, we might do - * 360/7 * 3 = 154.286) - * CONTAINERWIDTH = 28px - * SHRINK_TIME = 400ms - */ - - -.preloader-wrapper { - display: inline-block; - position: relative; - width: 50px; - height: 50px; - - &.small { - width: 36px; - height: 36px; - } - - &.big { - width: 64px; - height: 64px; - } - - &.active { - /* duration: 360 * ARCTIME / (ARCSTARTROT + (360-ARCSIZE)) */ - -webkit-animation: container-rotate 1568ms linear infinite; - animation: container-rotate 1568ms linear infinite; - } -} - -@-webkit-keyframes container-rotate { - to { -webkit-transform: rotate(360deg) } -} - -@keyframes container-rotate { - to { transform: rotate(360deg) } -} - -.spinner-layer { - position: absolute; - width: 100%; - height: 100%; - opacity: 0; - border-color: $spinner-default-color; -} - -.spinner-blue, -.spinner-blue-only { - border-color: #4285f4; -} - -.spinner-red, -.spinner-red-only { - border-color: #db4437; -} - -.spinner-yellow, -.spinner-yellow-only { - border-color: #f4b400; -} - -.spinner-green, -.spinner-green-only { - border-color: #0f9d58; -} - -/** - * IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee): - * - * iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't - * guarantee that the animation will start _exactly_ after that value. So we avoid using - * animation-delay and instead set custom keyframes for each color (as redundant as it - * seems). - * - * We write out each animation in full (instead of separating animation-name, - * animation-duration, etc.) because under the polyfill, Safari does not recognize those - * specific properties properly, treats them as -webkit-animation, and overrides the - * other animation rules. See https://github.com/Polymer/platform/issues/53. - */ -.active .spinner-layer.spinner-blue { - /* durations: 4 * ARCTIME */ - -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, blue-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; - animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, blue-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; -} - -.active .spinner-layer.spinner-red { - /* durations: 4 * ARCTIME */ - -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, red-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; - animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, red-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; -} - -.active .spinner-layer.spinner-yellow { - /* durations: 4 * ARCTIME */ - -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, yellow-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; - animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, yellow-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; -} - -.active .spinner-layer.spinner-green { - /* durations: 4 * ARCTIME */ - -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, green-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; - animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, green-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; -} - -.active .spinner-layer, -.active .spinner-layer.spinner-blue-only, -.active .spinner-layer.spinner-red-only, -.active .spinner-layer.spinner-yellow-only, -.active .spinner-layer.spinner-green-only { - /* durations: 4 * ARCTIME */ - opacity: 1; - -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; - animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; -} - -@-webkit-keyframes fill-unfill-rotate { - 12.5% { -webkit-transform: rotate(135deg); } /* 0.5 * ARCSIZE */ - 25% { -webkit-transform: rotate(270deg); } /* 1 * ARCSIZE */ - 37.5% { -webkit-transform: rotate(405deg); } /* 1.5 * ARCSIZE */ - 50% { -webkit-transform: rotate(540deg); } /* 2 * ARCSIZE */ - 62.5% { -webkit-transform: rotate(675deg); } /* 2.5 * ARCSIZE */ - 75% { -webkit-transform: rotate(810deg); } /* 3 * ARCSIZE */ - 87.5% { -webkit-transform: rotate(945deg); } /* 3.5 * ARCSIZE */ - to { -webkit-transform: rotate(1080deg); } /* 4 * ARCSIZE */ -} - -@keyframes fill-unfill-rotate { - 12.5% { transform: rotate(135deg); } /* 0.5 * ARCSIZE */ - 25% { transform: rotate(270deg); } /* 1 * ARCSIZE */ - 37.5% { transform: rotate(405deg); } /* 1.5 * ARCSIZE */ - 50% { transform: rotate(540deg); } /* 2 * ARCSIZE */ - 62.5% { transform: rotate(675deg); } /* 2.5 * ARCSIZE */ - 75% { transform: rotate(810deg); } /* 3 * ARCSIZE */ - 87.5% { transform: rotate(945deg); } /* 3.5 * ARCSIZE */ - to { transform: rotate(1080deg); } /* 4 * ARCSIZE */ -} - -@-webkit-keyframes blue-fade-in-out { - from { opacity: 1; } - 25% { opacity: 1; } - 26% { opacity: 0; } - 89% { opacity: 0; } - 90% { opacity: 1; } - 100% { opacity: 1; } -} - -@keyframes blue-fade-in-out { - from { opacity: 1; } - 25% { opacity: 1; } - 26% { opacity: 0; } - 89% { opacity: 0; } - 90% { opacity: 1; } - 100% { opacity: 1; } -} - -@-webkit-keyframes red-fade-in-out { - from { opacity: 0; } - 15% { opacity: 0; } - 25% { opacity: 1; } - 50% { opacity: 1; } - 51% { opacity: 0; } -} - -@keyframes red-fade-in-out { - from { opacity: 0; } - 15% { opacity: 0; } - 25% { opacity: 1; } - 50% { opacity: 1; } - 51% { opacity: 0; } -} - -@-webkit-keyframes yellow-fade-in-out { - from { opacity: 0; } - 40% { opacity: 0; } - 50% { opacity: 1; } - 75% { opacity: 1; } - 76% { opacity: 0; } -} - -@keyframes yellow-fade-in-out { - from { opacity: 0; } - 40% { opacity: 0; } - 50% { opacity: 1; } - 75% { opacity: 1; } - 76% { opacity: 0; } -} - -@-webkit-keyframes green-fade-in-out { - from { opacity: 0; } - 65% { opacity: 0; } - 75% { opacity: 1; } - 90% { opacity: 1; } - 100% { opacity: 0; } -} - -@keyframes green-fade-in-out { - from { opacity: 0; } - 65% { opacity: 0; } - 75% { opacity: 1; } - 90% { opacity: 1; } - 100% { opacity: 0; } -} - -/** - * Patch the gap that appear between the two adjacent div.circle-clipper while the - * spinner is rotating (appears on Chrome 38, Safari 7.1, and IE 11). - */ -.gap-patch { - position: absolute; - top: 0; - left: 45%; - width: 10%; - height: 100%; - overflow: hidden; - border-color: inherit; -} - -.gap-patch .circle { - width: 1000%; - left: -450%; -} - -.circle-clipper { - display: inline-block; - position: relative; - width: 50%; - height: 100%; - overflow: hidden; - border-color: inherit; - - .circle { - width: 200%; - height: 100%; - border-width: 3px; /* STROKEWIDTH */ - border-style: solid; - border-color: inherit; - border-bottom-color: transparent !important; - border-radius: 50%; - -webkit-animation: none; - animation: none; - position: absolute; - top: 0; - right: 0; - bottom: 0; - } - - &.left .circle { - left: 0; - border-right-color: transparent !important; - -webkit-transform: rotate(129deg); - transform: rotate(129deg); - } - &.right .circle { - left: -100%; - border-left-color: transparent !important; - -webkit-transform: rotate(-129deg); - transform: rotate(-129deg); - } -} - - - -.active .circle-clipper.left .circle { - /* duration: ARCTIME */ - -webkit-animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; - animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; -} - -.active .circle-clipper.right .circle { - /* duration: ARCTIME */ - -webkit-animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; - animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both; -} - -@-webkit-keyframes left-spin { - from { -webkit-transform: rotate(130deg); } - 50% { -webkit-transform: rotate(-5deg); } - to { -webkit-transform: rotate(130deg); } -} - -@keyframes left-spin { - from { transform: rotate(130deg); } - 50% { transform: rotate(-5deg); } - to { transform: rotate(130deg); } -} - -@-webkit-keyframes right-spin { - from { -webkit-transform: rotate(-130deg); } - 50% { -webkit-transform: rotate(5deg); } - to { -webkit-transform: rotate(-130deg); } -} - -@keyframes right-spin { - from { transform: rotate(-130deg); } - 50% { transform: rotate(5deg); } - to { transform: rotate(-130deg); } -} - -#spinnerContainer.cooldown { - /* duration: SHRINK_TIME */ - -webkit-animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1); - animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1); -} - -@-webkit-keyframes fade-out { - from { opacity: 1; } - to { opacity: 0; } -} - -@keyframes fade-out { - from { opacity: 1; } - to { opacity: 0; } -} diff --git a/tubesync/common/static/styles/materializecss/components/_pulse.scss b/tubesync/common/static/styles/materializecss/components/_pulse.scss deleted file mode 100644 index a690f367d..000000000 --- a/tubesync/common/static/styles/materializecss/components/_pulse.scss +++ /dev/null @@ -1,34 +0,0 @@ -.pulse { - &::before { - content: ''; - display: block; - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - background-color: inherit; - border-radius: inherit; - transition: opacity .3s, transform .3s; - animation: pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite; - z-index: -1; - } - - overflow: visible; - position: relative; -} - -@keyframes pulse-animation { - 0% { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 0; - transform: scale(1.5); - } - 100% { - opacity: 0; - transform: scale(1.5); - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_sidenav.scss b/tubesync/common/static/styles/materializecss/components/_sidenav.scss deleted file mode 100644 index 98a71d8a2..000000000 --- a/tubesync/common/static/styles/materializecss/components/_sidenav.scss +++ /dev/null @@ -1,216 +0,0 @@ -.sidenav { - position: fixed; - width: $sidenav-width; - left: 0; - top: 0; - margin: 0; - transform: translateX(-100%); - height: 100%; - height: calc(100% + 60px); - height: -moz-calc(100%); //Temporary Firefox Fix - padding-bottom: 60px; - background-color: $sidenav-bg-color; - z-index: 999; - overflow-y: auto; - will-change: transform; - backface-visibility: hidden; - transform: translateX(-105%); - - @extend .z-depth-1; - - // Right Align - &.right-aligned { - right: 0; - transform: translateX(105%); - left: auto; - transform: translateX(100%); - } - - .collapsible { - margin: 0; - } - - - li { - float: none; - line-height: $sidenav-line-height; - - &.active { background-color: rgba(0,0,0,.05); } - } - - li > a { - color: $sidenav-font-color; - display: block; - font-size: $sidenav-font-size; - font-weight: 500; - height: $sidenav-item-height; - line-height: $sidenav-line-height; - padding: 0 ($sidenav-padding * 2); - - &:hover { background-color: rgba(0,0,0,.05);} - - &.btn, &.btn-large, &.btn-flat, &.btn-floating { - margin: 10px 15px; - } - - &.btn, - &.btn-large, - &.btn-floating { color: $button-raised-color; } - &.btn-flat { color: $button-flat-color; } - - &.btn:hover, - &.btn-large:hover { background-color: lighten($button-raised-background, 5%); } - &.btn-floating:hover { background-color: $button-raised-background; } - - & > i, - & > [class^="mdi-"], li > a > [class*="mdi-"], - & > i.material-icons { - float: left; - height: $sidenav-item-height; - line-height: $sidenav-line-height; - margin: 0 ($sidenav-padding * 2) 0 0; - width: $sidenav-item-height / 2; - color: rgba(0,0,0,.54); - } - } - - - .divider { - margin: ($sidenav-padding / 2) 0 0 0; - } - - .subheader { - &:hover { - background-color: transparent; - } - - cursor: initial; - pointer-events: none; - color: rgba(0,0,0,.54); - font-size: $sidenav-font-size; - font-weight: 500; - line-height: $sidenav-line-height; - } - - .user-view { - position: relative; - padding: ($sidenav-padding * 2) ($sidenav-padding * 2) 0; - margin-bottom: $sidenav-padding / 2; - - & > a { - &:hover { background-color: transparent; } - height: auto; - padding: 0; - } - - .background { - overflow: hidden; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: -1; - } - - .circle, .name, .email { - display: block; - } - - .circle { - height: 64px; - width: 64px; - } - - .name, - .email { - font-size: $sidenav-font-size; - line-height: $sidenav-line-height / 2; - } - - .name { - margin-top: 16px; - font-weight: 500; - } - - .email { - padding-bottom: 16px; - font-weight: 400; - } - } -} - - -// Touch interaction -.drag-target { - // Right Align - &.right-aligned { - right: 0; - } - - height: 100%; - width: 10px; - position: fixed; - top: 0; - z-index: 998; -} - - -// Fixed Sidenav shown -.sidenav.sidenav-fixed { - // Right Align - &.right-aligned { - right: 0; - left: auto; - } - - left: 0; - transform: translateX(0); - position: fixed; -} - -// Fixed Sidenav hide on smaller -@media #{$medium-and-down} { - .sidenav { - &.sidenav-fixed { - transform: translateX(-105%); - - &.right-aligned { - transform: translateX(105%); - } - } - - > a { - padding: 0 $sidenav-padding; - } - - .user-view { - padding: $sidenav-padding $sidenav-padding 0; - } - } -} - - -.sidenav .collapsible-body > ul:not(.collapsible) > li.active, -.sidenav.sidenav-fixed .collapsible-body > ul:not(.collapsible) > li.active { - background-color: $primary-color; - a { - color: $sidenav-bg-color; - } -} -.sidenav .collapsible-body { - padding: 0; -} - - -.sidenav-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - opacity: 0; - height: 120vh; - background-color: rgba(0,0,0,.5); - z-index: 997; - display: none; -} diff --git a/tubesync/common/static/styles/materializecss/components/_slider.scss b/tubesync/common/static/styles/materializecss/components/_slider.scss deleted file mode 100644 index 5d7c27ed8..000000000 --- a/tubesync/common/static/styles/materializecss/components/_slider.scss +++ /dev/null @@ -1,92 +0,0 @@ -.slider { - position: relative; - height: 400px; - width: 100%; - - // Fullscreen slider - &.fullscreen { - height: 100%; - width: 100%; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - - ul.slides { - height: 100%; - } - - ul.indicators { - z-index: 2; - bottom: 30px; - } - } - - .slides { - background-color: $slider-bg-color; - margin: 0; - height: 400px; - - li { - opacity: 0; - position: absolute; - top: 0; - left: 0; - z-index: 1; - width: 100%; - height: inherit; - overflow: hidden; - - img { - height: 100%; - width: 100%; - background-size: cover; - background-position: center; - } - - .caption { - color: #fff; - position: absolute; - top: 15%; - left: 15%; - width: 70%; - opacity: 0; - - p { color: $slider-bg-color-light; } - } - - &.active { - z-index: 2; - } - } - } - - - .indicators { - position: absolute; - text-align: center; - left: 0; - right: 0; - bottom: 0; - margin: 0; - - .indicator-item { - display: inline-block; - position: relative; - cursor: pointer; - height: 16px; - width: 16px; - margin: 0 12px; - background-color: $slider-bg-color-light; - - transition: background-color .3s; - border-radius: 50%; - - &.active { - background-color: $slider-indicator-color; - } - } - } - -} \ No newline at end of file diff --git a/tubesync/common/static/styles/materializecss/components/_table_of_contents.scss b/tubesync/common/static/styles/materializecss/components/_table_of_contents.scss deleted file mode 100644 index 638009d97..000000000 --- a/tubesync/common/static/styles/materializecss/components/_table_of_contents.scss +++ /dev/null @@ -1,33 +0,0 @@ -/*************** - Nav List -***************/ -.table-of-contents { - &.fixed { - position: fixed; - } - - li { - padding: 2px 0; - } - a { - display: inline-block; - font-weight: 300; - color: #757575; - padding-left: 16px; - height: 1.5rem; - line-height: 1.5rem; - letter-spacing: .4; - display: inline-block; - - &:hover { - color: lighten(#757575, 20%); - padding-left: 15px; - border-left: 1px solid $primary-color; - } - &.active { - font-weight: 500; - padding-left: 14px; - border-left: 2px solid $primary-color; - } - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_tabs.scss b/tubesync/common/static/styles/materializecss/components/_tabs.scss deleted file mode 100644 index 072d4b6cb..000000000 --- a/tubesync/common/static/styles/materializecss/components/_tabs.scss +++ /dev/null @@ -1,99 +0,0 @@ -.tabs { - &.tabs-transparent { - background-color: transparent; - - .tab a, - .tab.disabled a, - .tab.disabled a:hover { - color: rgba(255,255,255,0.7); - } - - .tab a:hover, - .tab a.active { - color: #fff; - } - - .indicator { - background-color: #fff; - } - } - - &.tabs-fixed-width { - display: flex; - - .tab { - flex-grow: 1; - } - } - - position: relative; - overflow-x: auto; - overflow-y: hidden; - height: 48px; - width: 100%; - background-color: $tabs-bg-color; - margin: 0 auto; - white-space: nowrap; - - .tab { - display: inline-block; - text-align: center; - line-height: 48px; - height: 48px; - padding: 0; - margin: 0; - text-transform: uppercase; - - a { - &:focus, - &:focus.active { - background-color: transparentize($tabs-underline-color, .8); - outline: none; - } - - &:hover, - &.active { - background-color: transparent; - color: $tabs-text-color; - } - - color: rgba($tabs-text-color, .7); - display: block; - width: 100%; - height: 100%; - padding: 0 24px; - font-size: 14px; - text-overflow: ellipsis; - overflow: hidden; - transition: color .28s ease, background-color .28s ease; - } - - &.disabled a, - &.disabled a:hover { - color: rgba($tabs-text-color, .4); - cursor: default; - } - } - .indicator { - position: absolute; - bottom: 0; - height: 2px; - background-color: $tabs-underline-color; - will-change: left, right; - } -} - -// Fixed Sidenav hide on smaller -@media #{$medium-and-down} { - .tabs { - display: flex; - - .tab { - flex-grow: 1; - - a { - padding: 0 12px; - } - } - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_tapTarget.scss b/tubesync/common/static/styles/materializecss/components/_tapTarget.scss deleted file mode 100644 index 49aecd561..000000000 --- a/tubesync/common/static/styles/materializecss/components/_tapTarget.scss +++ /dev/null @@ -1,103 +0,0 @@ -.tap-target-wrapper { - width: 800px; - height: 800px; - position: fixed; - z-index: 1000; - visibility: hidden; - transition: visibility 0s .3s; -} - -.tap-target-wrapper.open { - visibility: visible; - transition: visibility 0s; - - .tap-target { - transform: scale(1); - opacity: .95; - transition: - transform .3s cubic-bezier(.42,0,.58,1), - opacity .3s cubic-bezier(.42,0,.58,1); - } - - .tap-target-wave::before { - transform: scale(1); - } - .tap-target-wave::after { - visibility: visible; - animation: pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite; - transition: - opacity .3s, - transform .3s, - visibility 0s 1s; - } -} - -.tap-target { - position: absolute; - font-size: 1rem; - border-radius: 50%; - background-color: $primary-color; - box-shadow: 0 20px 20px 0 rgba(0,0,0,0.14), 0 10px 50px 0 rgba(0,0,0,0.12), 0 30px 10px -20px rgba(0,0,0,0.2); - width: 100%; - height: 100%; - opacity: 0; - transform: scale(0); - transition: - transform .3s cubic-bezier(.42,0,.58,1), - opacity .3s cubic-bezier(.42,0,.58,1); -} - -.tap-target-content { - position: relative; - display: table-cell; -} - -.tap-target-wave { - &::before, - &::after { - content: ''; - display: block; - position: absolute; - width: 100%; - height: 100%; - border-radius: 50%; - background-color: #ffffff; - } - &::before { - transform: scale(0); - transition: transform .3s; - } - &::after { - visibility: hidden; - transition: - opacity .3s, - transform .3s, - visibility 0s; - z-index: -1; - } - - position: absolute; - border-radius: 50%; - z-index: 10001; -} - -.tap-target-origin { - &:not(.btn), - &:not(.btn):hover { - background: none; - } - - top: 50%; - left: 50%; - transform: translate(-50%,-50%); - - z-index: 10002; - position: absolute !important; -} - -@media only screen and (max-width: 600px) { - .tap-target, .tap-target-wrapper { - width: 600px; - height: 600px; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_timepicker.scss b/tubesync/common/static/styles/materializecss/components/_timepicker.scss deleted file mode 100644 index fa602fbbd..000000000 --- a/tubesync/common/static/styles/materializecss/components/_timepicker.scss +++ /dev/null @@ -1,183 +0,0 @@ -/* Timepicker Containers */ -.timepicker-modal { - max-width: 325px; - max-height: none; -} - -.timepicker-container.modal-content { - display: flex; - flex-direction: column; - padding: 0; -} - -.text-primary { - color: rgba(255, 255, 255, 1); -} - - -/* Clock Digital Display */ -.timepicker-digital-display { - flex: 1 auto; - background-color: $secondary-color; - padding: 10px; - font-weight: 300; -} - -.timepicker-text-container { - font-size: 4rem; - font-weight: bold; - text-align: center; - color: rgba(255, 255, 255, 0.6); - font-weight: 400; - position: relative; - user-select: none; -} - -.timepicker-span-hours, -.timepicker-span-minutes, -.timepicker-span-am-pm div { - cursor: pointer; -} - -.timepicker-span-hours { - margin-right: 3px; -} - -.timepicker-span-minutes { - margin-left: 3px; -} - -.timepicker-display-am-pm { - font-size: 1.3rem; - position: absolute; - right: 1rem; - bottom: 1rem; - font-weight: 400; -} - - -/* Analog Clock Display */ -.timepicker-analog-display { - flex: 2.5 auto; -} - -.timepicker-plate { - background-color: $timepicker-clock-plate-bg; - border-radius: 50%; - width: 270px; - height: 270px; - overflow: visible; - position: relative; - margin: auto; - margin-top: 25px; - margin-bottom: 5px; - user-select: none; -} - -.timepicker-canvas, -.timepicker-dial { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; -} -.timepicker-minutes { - visibility: hidden; -} - -.timepicker-tick { - border-radius: 50%; - color: $timepicker-clock-color; - line-height: 40px; - text-align: center; - width: 40px; - height: 40px; - position: absolute; - cursor: pointer; - font-size: 15px; -} - -.timepicker-tick.active, -.timepicker-tick:hover { - background-color: transparentize($secondary-color, .75); -} -.timepicker-dial { - transition: transform 350ms, opacity 350ms; -} -.timepicker-dial-out { - &.timepicker-hours { - transform: scale(1.1, 1.1); - } - - &.timepicker-minutes { - transform: scale(.8, .8); - } - - opacity: 0; -} -.timepicker-canvas { - transition: opacity 175ms; - - line { - stroke: $secondary-color; - stroke-width: 4; - stroke-linecap: round; - } -} -.timepicker-canvas-out { - opacity: 0.25; -} -.timepicker-canvas-bearing { - stroke: none; - fill: $secondary-color; -} -.timepicker-canvas-bg { - stroke: none; - fill: $secondary-color; -} - - -/* Footer */ -.timepicker-footer { - margin: 0 auto; - padding: 5px 1rem; - display: flex; - justify-content: space-between; -} - -.timepicker-clear { - color: $error-color; -} - -.timepicker-close { - color: $secondary-color; -} - -.timepicker-clear, -.timepicker-close { - padding: 0 20px; -} - -/* Media Queries */ -@media #{$medium-and-up} { - .timepicker-modal { - max-width: 600px; - } - - .timepicker-container.modal-content { - flex-direction: row; - } - - .timepicker-text-container { - top: 32%; - } - - .timepicker-display-am-pm { - position: relative; - right: auto; - bottom: auto; - text-align: center; - margin-top: 1.2rem; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_toast.scss b/tubesync/common/static/styles/materializecss/components/_toast.scss deleted file mode 100644 index 412400ff2..000000000 --- a/tubesync/common/static/styles/materializecss/components/_toast.scss +++ /dev/null @@ -1,58 +0,0 @@ -#toast-container { - display:block; - position: fixed; - z-index: 10000; - - @media #{$small-and-down} { - min-width: 100%; - bottom: 0%; - } - @media #{$medium-only} { - left: 5%; - bottom: 7%; - max-width: 90%; - } - @media #{$large-and-up} { - top: 10%; - right: 7%; - max-width: 86%; - } -} - -.toast { - @extend .z-depth-1; - border-radius: 2px; - top: 35px; - width: auto; - margin-top: 10px; - position: relative; - max-width:100%; - height: auto; - min-height: $toast-height; - line-height: 1.5em; - background-color: $toast-color; - padding: 10px 25px; - font-size: 1.1rem; - font-weight: 300; - color: $toast-text-color; - display: flex; - align-items: center; - justify-content: space-between; - cursor: default; - - .toast-action { - color: $toast-action-color; - font-weight: 500; - margin-right: -25px; - margin-left: 3rem; - } - - &.rounded{ - border-radius: 24px; - } - - @media #{$small-and-down} { - width: 100%; - border-radius: 0; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_tooltip.scss b/tubesync/common/static/styles/materializecss/components/_tooltip.scss deleted file mode 100644 index 5ec4299b2..000000000 --- a/tubesync/common/static/styles/materializecss/components/_tooltip.scss +++ /dev/null @@ -1,32 +0,0 @@ -.material-tooltip { - padding: 10px 8px; - font-size: 1rem; - z-index: 2000; - background-color: transparent; - border-radius: 2px; - color: #fff; - min-height: 36px; - line-height: 120%; - opacity: 0; - position: absolute; - text-align: center; - max-width: calc(100% - 4px); - overflow: hidden; - left: 0; - top: 0; - pointer-events: none; - visibility: hidden; - background-color: #323232; -} - -.backdrop { - position: absolute; - opacity: 0; - height: 7px; - width: 14px; - border-radius: 0 0 50% 50%; - background-color: #323232; - z-index: -1; - transform-origin: 50% 0%; - visibility: hidden; -} diff --git a/tubesync/common/static/styles/materializecss/components/_transitions.scss b/tubesync/common/static/styles/materializecss/components/_transitions.scss deleted file mode 100644 index cb9f60db7..000000000 --- a/tubesync/common/static/styles/materializecss/components/_transitions.scss +++ /dev/null @@ -1,13 +0,0 @@ -// Scale transition -.scale-transition { - &.scale-out { - transform: scale(0); - transition: transform .2s !important; - } - - &.scale-in { - transform: scale(1); - } - - transition: transform .3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important; -} \ No newline at end of file diff --git a/tubesync/common/static/styles/materializecss/components/_typography.scss b/tubesync/common/static/styles/materializecss/components/_typography.scss deleted file mode 100644 index b9b93b3ec..000000000 --- a/tubesync/common/static/styles/materializecss/components/_typography.scss +++ /dev/null @@ -1,60 +0,0 @@ - -a { - text-decoration: none; -} - -html{ - line-height: 1.5; - - @media only screen and (min-width: 0) { - font-size: 14px; - } - - @media only screen and (min-width: $medium-screen) { - font-size: 14.5px; - } - - @media only screen and (min-width: $large-screen) { - font-size: 15px; - } - - font-family: $font-stack; - font-weight: normal; - color: $off-black; -} -h1, h2, h3, h4, h5, h6 { - font-weight: 400; - line-height: 1.3; -} - -// Header Styles -h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; } -h1 { font-size: $h1-fontsize; line-height: 110%; margin: ($h1-fontsize / 1.5) 0 ($h1-fontsize / 2.5) 0;} -h2 { font-size: $h2-fontsize; line-height: 110%; margin: ($h2-fontsize / 1.5) 0 ($h2-fontsize / 2.5) 0;} -h3 { font-size: $h3-fontsize; line-height: 110%; margin: ($h3-fontsize / 1.5) 0 ($h3-fontsize / 2.5) 0;} -h4 { font-size: $h4-fontsize; line-height: 110%; margin: ($h4-fontsize / 1.5) 0 ($h4-fontsize / 2.5) 0;} -h5 { font-size: $h5-fontsize; line-height: 110%; margin: ($h5-fontsize / 1.5) 0 ($h5-fontsize / 2.5) 0;} -h6 { font-size: $h6-fontsize; line-height: 110%; margin: ($h6-fontsize / 1.5) 0 ($h6-fontsize / 2.5) 0;} - -// Text Styles -em { font-style: italic; } -strong { font-weight: 500; } -small { font-size: 75%; } -.light { font-weight: 300; } -.thin { font-weight: 200; } - - -.flow-text{ - $i: 0; - @while $i <= $intervals { - @media only screen and (min-width : 360 + ($i * $interval-size)) { - font-size: 1.2rem * (1 + (.02 * $i)); - } - $i: $i + 1; - } - - // Handle below 360px screen - @media only screen and (max-width: 360px) { - font-size: 1.2rem; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/_variables.scss b/tubesync/common/static/styles/materializecss/components/_variables.scss deleted file mode 100644 index 4c59c1278..000000000 --- a/tubesync/common/static/styles/materializecss/components/_variables.scss +++ /dev/null @@ -1,349 +0,0 @@ -// ========================================================================== -// Materialize variables -// ========================================================================== -// -// Table of Contents: -// -// 1. Colors -// 2. Badges -// 3. Buttons -// 4. Cards -// 5. Carousel -// 6. Collapsible -// 7. Chips -// 8. Date + Time Picker -// 9. Dropdown -// 10. Forms -// 11. Global -// 12. Grid -// 13. Navigation Bar -// 14. Side Navigation -// 15. Photo Slider -// 16. Spinners | Loaders -// 17. Tabs -// 18. Tables -// 19. Toasts -// 20. Typography -// 21. Footer -// 22. Flow Text -// 23. Collections -// 24. Progress Bar - - - -// 1. Colors -// ========================================================================== - -$primary-color: color("materialize-red", "lighten-2") !default; -$primary-color-light: lighten($primary-color, 15%) !default; -$primary-color-dark: darken($primary-color, 15%) !default; - -$secondary-color: color("teal", "lighten-1") !default; -$success-color: color("green", "base") !default; -$error-color: color("red", "base") !default; -$link-color: color("light-blue", "darken-1") !default; - - -// 2. Badges -// ========================================================================== - -$badge-bg-color: $secondary-color !default; -$badge-height: 22px !default; - - -// 3. Buttons -// ========================================================================== - -// Shared styles -$button-border: none !default; -$button-background-focus: lighten($secondary-color, 4%) !default; -$button-font-size: 14px !default; -$button-icon-font-size: 1.3rem !default; -$button-height: 36px !default; -$button-padding: 0 16px !default; -$button-radius: 2px !default; - -// Disabled styles -$button-disabled-background: #DFDFDF !default; -$button-disabled-color: #9F9F9F !default; - -// Raised buttons -$button-raised-background: $secondary-color !default; -$button-raised-background-hover: lighten($button-raised-background, 5%) !default; -$button-raised-color: #fff !default; - -// Large buttons -$button-large-font-size: 15px !default; -$button-large-icon-font-size: 1.6rem !default; -$button-large-height: $button-height * 1.5 !default; -$button-floating-large-size: 56px !default; - -// Small buttons -$button-small-font-size: 13px !default; -$button-small-icon-font-size: 1.2rem !default; -$button-small-height: $button-height * .9 !default; -$button-floating-small-size: $button-height * .9 !default; - -// Flat buttons -$button-flat-color: #343434 !default; -$button-flat-disabled-color: lighten(#999, 10%) !default; - -// Floating buttons -$button-floating-background: $secondary-color !default; -$button-floating-background-hover: $button-floating-background !default; -$button-floating-color: #fff !default; -$button-floating-size: 40px !default; -$button-floating-radius: 50% !default; - - -// 4. Cards -// ========================================================================== - -$card-padding: 24px !default; -$card-bg-color: #fff !default; -$card-link-color: color("orange", "accent-2") !default; -$card-link-color-light: lighten($card-link-color, 20%) !default; - - -// 5. Carousel -// ========================================================================== - -$carousel-height: 400px !default; -$carousel-item-height: $carousel-height / 2 !default; -$carousel-item-width: $carousel-item-height !default; - - -// 6. Collapsible -// ========================================================================== - -$collapsible-height: 3rem !default; -$collapsible-line-height: $collapsible-height !default; -$collapsible-header-color: #fff !default; -$collapsible-border-color: #ddd !default; - - -// 7. Chips -// ========================================================================== - -$chip-bg-color: #e4e4e4 !default; -$chip-border-color: #9e9e9e !default; -$chip-selected-color: #26a69a !default; -$chip-margin: 5px !default; - - -// 8. Date + Time Picker -// ========================================================================== - -$datepicker-display-font-size: 2.8rem; -$datepicker-calendar-header-color: #999; -$datepicker-weekday-color: rgba(0, 0, 0, .87) !default; -$datepicker-weekday-bg: darken($secondary-color, 7%) !default; -$datepicker-date-bg: $secondary-color !default; -$datepicker-year: rgba(255, 255, 255, .7) !default; -$datepicker-focus: rgba(0,0,0, .05) !default; -$datepicker-selected: $secondary-color !default; -$datepicker-selected-outfocus: desaturate(lighten($secondary-color, 35%), 15%) !default; -$datepicker-day-focus: transparentize(desaturate($secondary-color, 5%), .75) !default; -$datepicker-disabled-day-color: rgba(0, 0, 0, .3) !default; - -$timepicker-clock-color: rgba(0, 0, 0, .87) !default; -$timepicker-clock-plate-bg: #eee !default; - - -// 9. Dropdown -// ========================================================================== - -$dropdown-bg-color: #fff !default; -$dropdown-hover-bg-color: #eee !default; -$dropdown-color: $secondary-color !default; -$dropdown-item-height: 50px !default; - - -// 10. Forms -// ========================================================================== - -// Text Inputs + Textarea -$input-height: 3rem !default; -$input-border-color: color("grey", "base") !default; -$input-border: 1px solid $input-border-color !default; -$input-background: #fff !default; -$input-error-color: $error-color !default; -$input-success-color: $success-color !default; -$input-focus-color: $secondary-color !default; -$input-font-size: 16px !default; -$input-margin-bottom: 8px; -$input-margin: 0 0 $input-margin-bottom 0 !default; -$input-padding: 0 !default; -$label-font-size: .8rem !default; -$input-disabled-color: rgba(0,0,0, .42) !default; -$input-disabled-solid-color: #949494 !default; -$input-disabled-border: 1px dotted $input-disabled-color !default; -$input-invalid-border: 1px solid $input-error-color !default; -$input-icon-size: 2rem; -$placeholder-text-color: lighten($input-border-color, 20%) !default; - -// Radio Buttons -$radio-fill-color: $secondary-color !default; -$radio-empty-color: #5a5a5a !default; -$radio-border: 2px solid $radio-fill-color !default; - -// Range -$range-height: 14px !default; -$range-width: 14px !default; -$track-height: 3px !default; - -// Select -$select-border: 1px solid #f2f2f2 !default; -$select-background: rgba(255, 255, 255, 0.90) !default; -$select-focus: 1px solid lighten($secondary-color, 47%) !default; -$select-option-hover: rgba(0,0,0,.08) !default; -$select-option-focus: rgba(0,0,0,.08) !default; -$select-option-selected: rgba(0,0,0,.03) !default; -$select-padding: 5px !default; -$select-radius: 2px !default; -$select-disabled-color: rgba(0,0,0,.3) !default; - -// Switches -$switch-bg-color: $secondary-color !default; -$switch-checked-lever-bg: desaturate(lighten($switch-bg-color, 25%), 25%) !default; -$switch-unchecked-bg: #F1F1F1 !default; -$switch-unchecked-lever-bg: rgba(0,0,0,.38) !default; -$switch-radius: 15px !default; - - -// 11. Global -// ========================================================================== - -// Media Query Ranges -$small-screen-up: 601px !default; -$medium-screen-up: 993px !default; -$large-screen-up: 1201px !default; -$small-screen: 600px !default; -$medium-screen: 992px !default; -$large-screen: 1200px !default; - -$medium-and-up: "only screen and (min-width : #{$small-screen-up})" !default; -$large-and-up: "only screen and (min-width : #{$medium-screen-up})" !default; -$extra-large-and-up: "only screen and (min-width : #{$large-screen-up})" !default; -$small-and-down: "only screen and (max-width : #{$small-screen})" !default; -$medium-and-down: "only screen and (max-width : #{$medium-screen})" !default; -$medium-only: "only screen and (min-width : #{$small-screen-up}) and (max-width : #{$medium-screen})" !default; - - -// 12. Grid -// ========================================================================== - -$num-cols: 12 !default; -$gutter-width: 1.5rem !default; -$element-top-margin: $gutter-width/3 !default; -$element-bottom-margin: ($gutter-width*2)/3 !default; - - -// 13. Navigation Bar -// ========================================================================== - -$navbar-height: 64px !default; -$navbar-line-height: $navbar-height !default; -$navbar-height-mobile: 56px !default; -$navbar-line-height-mobile: $navbar-height-mobile !default; -$navbar-font-size: 1rem !default; -$navbar-font-color: #fff !default; -$navbar-brand-font-size: 2.1rem !default; - -// 14. Side Navigation -// ========================================================================== - -$sidenav-width: 300px !default; -$sidenav-font-size: 14px !default; -$sidenav-font-color: rgba(0,0,0,.87) !default; -$sidenav-bg-color: #fff !default; -$sidenav-padding: 16px !default; -$sidenav-item-height: 48px !default; -$sidenav-line-height: $sidenav-item-height !default; - - -// 15. Photo Slider -// ========================================================================== - -$slider-bg-color: color('grey', 'base') !default; -$slider-bg-color-light: color('grey', 'lighten-2') !default; -$slider-indicator-color: color('green', 'base') !default; - - -// 16. Spinners | Loaders -// ========================================================================== - -$spinner-default-color: $secondary-color !default; - - -// 17. Tabs -// ========================================================================== - -$tabs-underline-color: $primary-color-light !default; -$tabs-text-color: $primary-color !default; -$tabs-bg-color: #fff !default; - - -// 18. Tables -// ========================================================================== - -$table-border-color: rgba(0,0,0,.12) !default; -$table-striped-color: rgba(242, 242, 242, 0.5) !default; - - -// 19. Toasts -// ========================================================================== - -$toast-height: 48px !default; -$toast-color: #323232 !default; -$toast-text-color: #fff !default; -$toast-action-color: #eeff41; - - -// 20. Typography -// ========================================================================== - -$font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !default; -$off-black: rgba(0, 0, 0, 0.87) !default; -// Header Styles -$h1-fontsize: 4.2rem !default; -$h2-fontsize: 3.56rem !default; -$h3-fontsize: 2.92rem !default; -$h4-fontsize: 2.28rem !default; -$h5-fontsize: 1.64rem !default; -$h6-fontsize: 1.15rem !default; - - -// 21. Footer -// ========================================================================== - -$footer-font-color: #fff !default; -$footer-bg-color: $primary-color !default; -$footer-copyright-font-color: rgba(255,255,255,.8) !default; -$footer-copyright-bg-color: rgba(51,51,51,.08) !default; - - -// 22. Flow Text -// ========================================================================== - -$range : $large-screen - $small-screen !default; -$intervals: 20 !default; -$interval-size: $range / $intervals !default; - - -// 23. Collections -// ========================================================================== - -$collection-border-color: #e0e0e0 !default; -$collection-bg-color: #fff !default; -$collection-active-bg-color: $secondary-color !default; -$collection-active-color: lighten($secondary-color, 55%) !default; -$collection-hover-bg-color: #ddd !default; -$collection-link-color: $secondary-color !default; -$collection-line-height: 1.5rem !default; - - -// 24. Progress Bar -// ========================================================================== - -$progress-bar-color: $secondary-color !default; diff --git a/tubesync/common/static/styles/materializecss/components/_waves.scss b/tubesync/common/static/styles/materializecss/components/_waves.scss deleted file mode 100644 index b36c71817..000000000 --- a/tubesync/common/static/styles/materializecss/components/_waves.scss +++ /dev/null @@ -1,114 +0,0 @@ - -/*! - * Waves v0.6.0 - * http://fian.my.id/Waves - * - * Copyright 2014 Alfiana E. Sibuea and other contributors - * Released under the MIT license - * https://github.com/fians/Waves/blob/master/LICENSE - */ - - -.waves-effect { - position: relative; - cursor: pointer; - display: inline-block; - overflow: hidden; - user-select: none; - -webkit-tap-highlight-color: transparent; - vertical-align: middle; - z-index: 1; - transition: .3s ease-out; - - .waves-ripple { - position: absolute; - border-radius: 50%; - width: 20px; - height: 20px; - margin-top:-10px; - margin-left:-10px; - opacity: 0; - - background: rgba(0,0,0,0.2); - transition: all 0.7s ease-out; - transition-property: transform, opacity; - transform: scale(0); - pointer-events: none; - } - - // Waves Colors - &.waves-light .waves-ripple { - background-color: rgba(255, 255, 255, 0.45); - } - &.waves-red .waves-ripple { - background-color: rgba(244, 67, 54, .70); - } - &.waves-yellow .waves-ripple { - background-color: rgba(255, 235, 59, .70); - } - &.waves-orange .waves-ripple { - background-color: rgba(255, 152, 0, .70); - } - &.waves-purple .waves-ripple { - background-color: rgba(156, 39, 176, 0.70); - } - &.waves-green .waves-ripple { - background-color: rgba(76, 175, 80, 0.70); - } - &.waves-teal .waves-ripple { - background-color: rgba(0, 150, 136, 0.70); - } - - // Style input button bug. - input[type="button"], input[type="reset"], input[type="submit"] { - border: 0; - font-style: normal; - font-size: inherit; - text-transform: inherit; - background: none; - } - - img { - position: relative; - z-index: -1; - } -} - -.waves-notransition { - transition: none #{"!important"}; -} - -.waves-circle { - transform: translateZ(0); - -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); -} - -.waves-input-wrapper { - border-radius: 0.2em; - vertical-align: bottom; - - .waves-button-input { - position: relative; - top: 0; - left: 0; - z-index: 1; - } -} - -.waves-circle { - text-align: center; - width: 2.5em; - height: 2.5em; - line-height: 2.5em; - border-radius: 50%; - -webkit-mask-image: none; -} - -.waves-block { - display: block; -} - -/* Firefox Bug: link not triggered */ -.waves-effect .waves-ripple { - z-index: -1; -} \ No newline at end of file diff --git a/tubesync/common/static/styles/materializecss/components/forms/_checkboxes.scss b/tubesync/common/static/styles/materializecss/components/forms/_checkboxes.scss deleted file mode 100644 index 22a0e3376..000000000 --- a/tubesync/common/static/styles/materializecss/components/forms/_checkboxes.scss +++ /dev/null @@ -1,200 +0,0 @@ -/* Checkboxes - ========================================================================== */ - -/* Remove default checkbox */ -[type="checkbox"]:not(:checked), -[type="checkbox"]:checked { - position: absolute; - opacity: 0; - pointer-events: none; -} - -// Checkbox Styles -[type="checkbox"] { - // Text Label Style - + span:not(.lever) { - position: relative; - padding-left: 27px; - cursor: pointer; - display: inline-block; - height: 25px; - line-height: 25px; - font-size: 1rem; - user-select: none; - } - - /* checkbox aspect */ - + span:not(.lever):before, - &:not(.filled-in) + span:not(.lever):after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 18px; - height: 18px; - z-index: 0; - border: 2px solid $radio-empty-color; - border-radius: 1px; - margin-top: 3px; - transition: .2s; - } - - &:not(.filled-in) + span:not(.lever):after { - border: 0; - transform: scale(0); - } - - &:not(:checked):disabled + span:not(.lever):before { - border: none; - background-color: $input-disabled-color; - } - - // Focused styles - &.tabbed:focus + span:not(.lever):after { - transform: scale(1); - border: 0; - border-radius: 50%; - box-shadow: 0 0 0 10px rgba(0,0,0,.1); - background-color: rgba(0,0,0,.1); - } -} - -[type="checkbox"]:checked { - + span:not(.lever):before { - top: -4px; - left: -5px; - width: 12px; - height: 22px; - border-top: 2px solid transparent; - border-left: 2px solid transparent; - border-right: $radio-border; - border-bottom: $radio-border; - transform: rotate(40deg); - backface-visibility: hidden; - transform-origin: 100% 100%; - } - - &:disabled + span:before { - border-right: 2px solid $input-disabled-color; - border-bottom: 2px solid $input-disabled-color; - } -} - -/* Indeterminate checkbox */ -[type="checkbox"]:indeterminate { - + span:not(.lever):before { - top: -11px; - left: -12px; - width: 10px; - height: 22px; - border-top: none; - border-left: none; - border-right: $radio-border; - border-bottom: none; - transform: rotate(90deg); - backface-visibility: hidden; - transform-origin: 100% 100%; - } - - // Disabled indeterminate - &:disabled + span:not(.lever):before { - border-right: 2px solid $input-disabled-color; - background-color: transparent; - } -} - -// Filled in Style -[type="checkbox"].filled-in { - // General - + span:not(.lever):after { - border-radius: 2px; - } - - + span:not(.lever):before, - + span:not(.lever):after { - content: ''; - left: 0; - position: absolute; - /* .1s delay is for check animation */ - transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s; - z-index: 1; - } - - // Unchecked style - &:not(:checked) + span:not(.lever):before { - width: 0; - height: 0; - border: 3px solid transparent; - left: 6px; - top: 10px; - transform: rotateZ(37deg); - transform-origin: 100% 100%; - } - - &:not(:checked) + span:not(.lever):after { - height: 20px; - width: 20px; - background-color: transparent; - border: 2px solid $radio-empty-color; - top: 0px; - z-index: 0; - } - - // Checked style - &:checked { - + span:not(.lever):before { - top: 0; - left: 1px; - width: 8px; - height: 13px; - border-top: 2px solid transparent; - border-left: 2px solid transparent; - border-right: 2px solid $input-background; - border-bottom: 2px solid $input-background; - transform: rotateZ(37deg); - transform-origin: 100% 100%; - } - - + span:not(.lever):after { - top: 0; - width: 20px; - height: 20px; - border: 2px solid $secondary-color; - background-color: $secondary-color; - z-index: 0; - } - } - - // Focused styles - &.tabbed:focus + span:not(.lever):after { - border-radius: 2px; - border-color: $radio-empty-color; - background-color: rgba(0,0,0,.1); - } - - &.tabbed:checked:focus + span:not(.lever):after { - border-radius: 2px; - background-color: $secondary-color; - border-color: $secondary-color; - } - - // Disabled style - &:disabled:not(:checked) + span:not(.lever):before { - background-color: transparent; - border: 2px solid transparent; - } - - &:disabled:not(:checked) + span:not(.lever):after { - border-color: transparent; - background-color: $input-disabled-solid-color; - } - - &:disabled:checked + span:not(.lever):before { - background-color: transparent; - } - - &:disabled:checked + span:not(.lever):after { - background-color: $input-disabled-solid-color; - border-color: $input-disabled-solid-color; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/forms/_file-input.scss b/tubesync/common/static/styles/materializecss/components/forms/_file-input.scss deleted file mode 100644 index e0f7ef736..000000000 --- a/tubesync/common/static/styles/materializecss/components/forms/_file-input.scss +++ /dev/null @@ -1,44 +0,0 @@ -/* File Input - ========================================================================== */ - -.file-field { - position: relative; - - .file-path-wrapper { - overflow: hidden; - padding-left: 10px; - } - - input.file-path { width: 100%; } - - .btn { - float: left; - height: $input-height; - line-height: $input-height; - } - - span { - cursor: pointer; - } - - input[type=file] { - - // Needed to override webkit button - &::-webkit-file-upload-button { - display: none; - } - - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - width: 100%; - margin: 0; - padding: 0; - font-size: 20px; - cursor: pointer; - opacity: 0; - filter: alpha(opacity=0); - } -} diff --git a/tubesync/common/static/styles/materializecss/components/forms/_forms.scss b/tubesync/common/static/styles/materializecss/components/forms/_forms.scss deleted file mode 100644 index 4c19f4c85..000000000 --- a/tubesync/common/static/styles/materializecss/components/forms/_forms.scss +++ /dev/null @@ -1,22 +0,0 @@ -// Remove Focus Boxes -select:focus { - outline: $select-focus; -} - -button:focus { - outline: none; - background-color: $button-background-focus; -} - -label { - font-size: $label-font-size; - color: $input-border-color; -} - -@import 'input-fields'; -@import 'radio-buttons'; -@import 'checkboxes'; -@import 'switches'; -@import 'select'; -@import 'file-input'; -@import 'range'; diff --git a/tubesync/common/static/styles/materializecss/components/forms/_input-fields.scss b/tubesync/common/static/styles/materializecss/components/forms/_input-fields.scss deleted file mode 100644 index f18c2f809..000000000 --- a/tubesync/common/static/styles/materializecss/components/forms/_input-fields.scss +++ /dev/null @@ -1,354 +0,0 @@ -/* Text Inputs + Textarea - ========================================================================== */ - -/* Style Placeholders */ - -::placeholder { - color: $placeholder-text-color; -} - -/* Text inputs */ - -input:not([type]), -input[type=text]:not(.browser-default), -input[type=password]:not(.browser-default), -input[type=email]:not(.browser-default), -input[type=url]:not(.browser-default), -input[type=time]:not(.browser-default), -input[type=date]:not(.browser-default), -input[type=datetime]:not(.browser-default), -input[type=datetime-local]:not(.browser-default), -input[type=tel]:not(.browser-default), -input[type=number]:not(.browser-default), -input[type=search]:not(.browser-default), -textarea.materialize-textarea { - - // General Styles - background-color: transparent; - border: none; - border-bottom: $input-border; - border-radius: 0; - outline: none; - height: $input-height; - width: 100%; - font-size: $input-font-size; - margin: $input-margin; - padding: $input-padding; - box-shadow: none; - box-sizing: content-box; - transition: box-shadow .3s, border .3s; - - // Disabled input style - &:disabled, - &[readonly="readonly"] { - color: $input-disabled-color; - border-bottom: $input-disabled-border; - } - - // Disabled label style - &:disabled+label, - &[readonly="readonly"]+label { - color: $input-disabled-color; - } - - // Focused input style - &:focus:not([readonly]) { - border-bottom: 1px solid $input-focus-color; - box-shadow: 0 1px 0 0 $input-focus-color; - } - - // Focused label style - &:focus:not([readonly])+label { - color: $input-focus-color; - } - - // Hide helper text on data message - &.valid ~ .helper-text[data-success], - &:focus.valid ~ .helper-text[data-success], - &.invalid ~ .helper-text[data-error], - &:focus.invalid ~ .helper-text[data-error] { - @extend %hidden-text; - } - - // Valid Input Style - &.valid, - &:focus.valid { - @extend %valid-input-style; - } - - // Custom Success Message - &.valid ~ .helper-text:after, - &:focus.valid ~ .helper-text:after { - @extend %custom-success-message; - } - &:focus.valid ~ label { - color: $input-success-color; - } - - // Invalid Input Style - &.invalid, - &:focus.invalid { - @extend %invalid-input-style; - } - - // Custom Error message - &.invalid ~ .helper-text:after, - &:focus.invalid ~ .helper-text:after { - @extend %custom-error-message; - } - &:focus.invalid ~ label { - color: $input-error-color; - } - - // Full width label when using validate for error messages - &.validate + label { - width: 100%; - } - - // Form Message Shared Styles - & + label:after { - @extend %input-after-style; - } -} - - -/* Validation Sass Placeholders */ -%valid-input-style { - border-bottom: 1px solid $input-success-color; - box-shadow: 0 1px 0 0 $input-success-color; -} -%invalid-input-style { - border-bottom: $input-invalid-border; - box-shadow: 0 1px 0 0 $input-error-color; -} -%hidden-text { - color: transparent; - user-select: none; - pointer-events: none; -} -%custom-success-message { - content: attr(data-success); - color: $input-success-color; -} -%custom-error-message { - content: attr(data-error); - color: $input-error-color; -} -%input-after-style { - display: block; - content: ""; - position: absolute; - top: 100%; - left: 0; - opacity: 0; - transition: .2s opacity ease-out, .2s color ease-out; -} - - -// Styling for input field wrapper -.input-field { - // Inline styles - &.inline { - display: inline-block; - vertical-align: middle; - margin-left: 5px; - - input, - .select-dropdown { - margin-bottom: 1rem; - } - } - - // Gutter spacing - &.col { - label { - left: $gutter-width / 2; - } - - .prefix ~ label, - .prefix ~ .validate ~ label { - width: calc(100% - 3rem - #{$gutter-width}); - } - } - - position: relative; - margin-top: 1rem; - margin-bottom: 1rem; - - & > label { - color: $input-border-color; - position: absolute; - top: 0; - left: 0; - font-size: 1rem; - cursor: text; - transition: transform .2s ease-out, color .2s ease-out; - transform-origin: 0% 100%; - text-align: initial; - transform: translateY(12px); - - &:not(.label-icon).active { - transform: translateY(-14px) scale(.8); - transform-origin: 0 0; - } - } - - // Autofill + date + time inputs - & > input[type]:-webkit-autofill:not(.browser-default):not([type="search"]) + label, - & > input[type=date]:not(.browser-default) + label, - & > input[type=time]:not(.browser-default) + label { - transform: translateY(-14px) scale(.8); - transform-origin: 0 0; - } - - .helper-text { - &::after { - opacity: 1; - position: absolute; - top: 0; - left: 0; - } - - position: relative; - min-height: 18px; - display: block; - font-size: 12px; - color: rgba(0,0,0,.54); - } - - // Prefix Icons - .prefix { - position: absolute; - width: $input-height; - font-size: $input-icon-size; - transition: color .2s; - top: ($input-height - $input-icon-size) / 2; - - &.active { color: $input-focus-color; } - } - - .prefix ~ input, - .prefix ~ textarea, - .prefix ~ label, - .prefix ~ .validate ~ label, - .prefix ~ .helper-text, - .prefix ~ .autocomplete-content { - margin-left: 3rem; - width: 92%; - width: calc(100% - 3rem); - } - - .prefix ~ label { margin-left: 3rem; } - - @media #{$medium-and-down} { - .prefix ~ input { - width: 86%; - width: calc(100% - 3rem); - } - } - - @media #{$small-and-down} { - .prefix ~ input { - width: 80%; - width: calc(100% - 3rem); - } - } -} - - -/* Search Field */ - -.input-field input[type=search] { - display: block; - line-height: inherit; - transition: .3s background-color; - - .nav-wrapper & { - height: inherit; - padding-left: 4rem; - width: calc(100% - 4rem); - border: 0; - box-shadow: none; - } - - &:focus:not(.browser-default) { - background-color: $input-background; - border: 0; - box-shadow: none; - color: #444; - - & + label i, - & ~ .mdi-navigation-close, - & ~ .material-icons { - color: #444; - } - } - - & + .label-icon { - transform: none; - left: 1rem; - } - - & ~ .mdi-navigation-close, - & ~ .material-icons { - position: absolute; - top: 0; - right: 1rem; - color: transparent; - cursor: pointer; - font-size: $input-icon-size; - transition: .3s color; - } -} - - -/* Textarea */ - -// Default textarea -textarea { - width: 100%; - height: $input-height; - background-color: transparent; - - &.materialize-textarea { - line-height: normal; - overflow-y: hidden; /* prevents scroll bar flash */ - padding: .8rem 0 .8rem 0; /* prevents text jump on Enter keypress */ - resize: none; - min-height: $input-height; - box-sizing: border-box; - } -} - -// For textarea autoresize -.hiddendiv { - visibility: hidden; - white-space: pre-wrap; - word-wrap: break-word; - overflow-wrap: break-word; /* future version of deprecated 'word-wrap' */ - padding-top: 1.2rem; /* prevents text jump on Enter keypress */ - - // Reduces repaints - position: absolute; - top: 0; - z-index: -1; -} - - -/* Autocomplete */ -.autocomplete-content { - li { - .highlight { color: #444; } - - img { - height: $dropdown-item-height - 10; - width: $dropdown-item-height - 10; - margin: 5px 15px; - } - } -} - -/* Character Counter */ -.character-counter { - min-height: 18px; -} diff --git a/tubesync/common/static/styles/materializecss/components/forms/_radio-buttons.scss b/tubesync/common/static/styles/materializecss/components/forms/_radio-buttons.scss deleted file mode 100644 index c9f72962c..000000000 --- a/tubesync/common/static/styles/materializecss/components/forms/_radio-buttons.scss +++ /dev/null @@ -1,115 +0,0 @@ -/* Radio Buttons - ========================================================================== */ - -// Remove default Radio Buttons -[type="radio"]:not(:checked), -[type="radio"]:checked { - position: absolute; - opacity: 0; - pointer-events: none; -} - -[type="radio"]:not(:checked) + span, -[type="radio"]:checked + span { - position: relative; - padding-left: 35px; - cursor: pointer; - display: inline-block; - height: 25px; - line-height: 25px; - font-size: 1rem; - transition: .28s ease; - user-select: none; -} - -[type="radio"] + span:before, -[type="radio"] + span:after { - content: ''; - position: absolute; - left: 0; - top: 0; - margin: 4px; - width: 16px; - height: 16px; - z-index: 0; - transition: .28s ease; -} - -/* Unchecked styles */ -[type="radio"]:not(:checked) + span:before, -[type="radio"]:not(:checked) + span:after, -[type="radio"]:checked + span:before, -[type="radio"]:checked + span:after, -[type="radio"].with-gap:checked + span:before, -[type="radio"].with-gap:checked + span:after { - border-radius: 50%; -} - -[type="radio"]:not(:checked) + span:before, -[type="radio"]:not(:checked) + span:after { - border: 2px solid $radio-empty-color; -} - -[type="radio"]:not(:checked) + span:after { - transform: scale(0); -} - -/* Checked styles */ -[type="radio"]:checked + span:before { - border: 2px solid transparent; -} - -[type="radio"]:checked + span:after, -[type="radio"].with-gap:checked + span:before, -[type="radio"].with-gap:checked + span:after { - border: $radio-border; -} - -[type="radio"]:checked + span:after, -[type="radio"].with-gap:checked + span:after { - background-color: $radio-fill-color; -} - -[type="radio"]:checked + span:after { - transform: scale(1.02); -} - -/* Radio With gap */ -[type="radio"].with-gap:checked + span:after { - transform: scale(.5); -} - -/* Focused styles */ -[type="radio"].tabbed:focus + span:before { - box-shadow: 0 0 0 10px rgba(0,0,0,.1); -} - -/* Disabled Radio With gap */ -[type="radio"].with-gap:disabled:checked + span:before { - border: 2px solid $input-disabled-color; -} - -[type="radio"].with-gap:disabled:checked + span:after { - border: none; - background-color: $input-disabled-color; -} - -/* Disabled style */ -[type="radio"]:disabled:not(:checked) + span:before, -[type="radio"]:disabled:checked + span:before { - background-color: transparent; - border-color: $input-disabled-color; -} - -[type="radio"]:disabled + span { - color: $input-disabled-color; -} - -[type="radio"]:disabled:not(:checked) + span:before { - border-color: $input-disabled-color; -} - -[type="radio"]:disabled:checked + span:after { - background-color: $input-disabled-color; - border-color: $input-disabled-solid-color; -} diff --git a/tubesync/common/static/styles/materializecss/components/forms/_range.scss b/tubesync/common/static/styles/materializecss/components/forms/_range.scss deleted file mode 100644 index 18607f5d0..000000000 --- a/tubesync/common/static/styles/materializecss/components/forms/_range.scss +++ /dev/null @@ -1,161 +0,0 @@ -/* Range - ========================================================================== */ - -.range-field { - position: relative; -} - -input[type=range], -input[type=range] + .thumb { - @extend .no-select; - cursor: pointer; -} - -input[type=range] { - position: relative; - background-color: transparent; - border: none; - outline: none; - width: 100%; - margin: 15px 0; - padding: 0; - - &:focus { - outline: none; - } -} - -input[type=range] + .thumb { - position: absolute; - top: 10px; - left: 0; - border: none; - height: 0; - width: 0; - border-radius: 50%; - background-color: $radio-fill-color; - margin-left: 7px; - - transform-origin: 50% 50%; - transform: rotate(-45deg); - - .value { - display: block; - width: 30px; - text-align: center; - color: $radio-fill-color; - font-size: 0; - transform: rotate(45deg); - } - - &.active { - border-radius: 50% 50% 50% 0; - - .value { - color: $input-background; - margin-left: -1px; - margin-top: 8px; - font-size: 10px; - } - } -} - -// Shared -@mixin range-track { - height: $track-height; - background: #c2c0c2; - border: none; -} - -@mixin range-thumb { - border: none; - height: $range-height; - width: $range-width; - border-radius: 50%; - background: $radio-fill-color; - transition: box-shadow .3s; -} - -// WebKit -input[type=range] { - -webkit-appearance: none; -} - -input[type=range]::-webkit-slider-runnable-track { - @include range-track; -} - -input[type=range]::-webkit-slider-thumb { - @include range-thumb; - -webkit-appearance: none; - background-color: $radio-fill-color; - transform-origin: 50% 50%; - margin: -5px 0 0 0; - -} - -.keyboard-focused input[type=range]:focus:not(.active)::-webkit-slider-thumb { - box-shadow: 0 0 0 10px rgba($radio-fill-color, .26); -} - -// FireFox -input[type=range] { - /* fix for FF unable to apply focus style bug */ - border: 1px solid white; - - /*required for proper track sizing in FF*/ -} - -input[type=range]::-moz-range-track { - @include range-track; -} - -input[type=range]::-moz-focus-inner { - border: 0; -} - -input[type=range]::-moz-range-thumb { - @include range-thumb; - margin-top: -5px; -} - -// hide the outline behind the border -input[type=range]:-moz-focusring { - outline: 1px solid #fff; - outline-offset: -1px; -} - -.keyboard-focused input[type=range]:focus:not(.active)::-moz-range-thumb { - box-shadow: 0 0 0 10px rgba($radio-fill-color, .26); -} - -// IE 10+ -input[type=range]::-ms-track { - height: $track-height; - - // remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead - background: transparent; - - // leave room for the larger thumb to overflow with a transparent border */ - border-color: transparent; - border-width: 6px 0; - - /*remove default tick marks*/ - color: transparent; -} - -input[type=range]::-ms-fill-lower { - background: #777; -} - -input[type=range]::-ms-fill-upper { - background: #ddd; -} - -input[type=range]::-ms-thumb { - @include range-thumb; -} - -.keyboard-focused input[type=range]:focus:not(.active)::-ms-thumb { - box-shadow: 0 0 0 10px rgba($radio-fill-color, .26); -} diff --git a/tubesync/common/static/styles/materializecss/components/forms/_select.scss b/tubesync/common/static/styles/materializecss/components/forms/_select.scss deleted file mode 100644 index 2fd04d3cc..000000000 --- a/tubesync/common/static/styles/materializecss/components/forms/_select.scss +++ /dev/null @@ -1,180 +0,0 @@ -/* Select Field - ========================================================================== */ - -select { display: none; } -select.browser-default { display: block; } - -select { - background-color: $select-background; - width: 100%; - padding: $select-padding; - border: $select-border; - border-radius: $select-radius; - height: $input-height; -} - -.select-label { - position: absolute; -} - -.select-wrapper { - &.valid .helper-text[data-success], - &.invalid ~ .helper-text[data-error] { - @extend %hidden-text; - } - - &.valid { - & > input.select-dropdown { - @extend %valid-input-style; - } - - & ~ .helper-text:after { - @extend %custom-success-message; - } - } - - &.invalid { - & > input.select-dropdown, - & > input.select-dropdown:focus { - @extend %invalid-input-style; - } - - & ~ .helper-text:after { - @extend %custom-error-message; - } - } - - &.valid + label, - &.invalid + label { - width: 100%; - pointer-events: none; - } - - & + label:after { - @extend %input-after-style; - } - - position: relative; - - input.select-dropdown { - &:focus { - border-bottom: 1px solid $input-focus-color; - } - position: relative; - cursor: pointer; - background-color: transparent; - border: none; - border-bottom: $input-border; - outline: none; - height: $input-height; - line-height: $input-height; - width: 100%; - font-size: $input-font-size; - margin: $input-margin; - padding: 0; - display: block; - user-select:none; - z-index: 1; - } - - .caret { - position: absolute; - right: 0; - top: 0; - bottom: 0; - margin: auto 0; - z-index: 0; - fill: rgba(0,0,0,.87); - } - - & + label { - position: absolute; - top: -26px; - font-size: $label-font-size; - } -} - -// Disabled styles -select:disabled { - color: $input-disabled-color; -} - -.select-wrapper.disabled { - + label { - color: $input-disabled-color; - } - .caret { - fill: $input-disabled-color; - } -} - -.select-wrapper input.select-dropdown:disabled { - color: $input-disabled-color; - cursor: default; - user-select: none; -} - -.select-wrapper i { - color: $select-disabled-color; -} - -.select-dropdown li.disabled, -.select-dropdown li.disabled > span, -.select-dropdown li.optgroup { - color: $select-disabled-color; - background-color: transparent; -} - -body.keyboard-focused { - .select-dropdown.dropdown-content li:focus { - background-color: $select-option-focus; - } -} - -.select-dropdown.dropdown-content { - li { - &:hover { - background-color: $select-option-hover; - } - - &.selected { - background-color: $select-option-selected; - } - } -} - -// Prefix Icons -.prefix ~ .select-wrapper { - margin-left: 3rem; - width: 92%; - width: calc(100% - 3rem); -} - -.prefix ~ label { margin-left: 3rem; } - -// Icons -.select-dropdown li { - img { - height: $dropdown-item-height - 10; - width: $dropdown-item-height - 10; - margin: 5px 15px; - float: right; - } -} - -// Optgroup styles -.select-dropdown li.optgroup { - border-top: 1px solid $dropdown-hover-bg-color; - - &.selected > span { - color: rgba(0, 0, 0, .7); - } - - & > span { - color: rgba(0, 0, 0, .4); - } - - & ~ li.optgroup-option { - padding-left: 1rem; - } -} diff --git a/tubesync/common/static/styles/materializecss/components/forms/_switches.scss b/tubesync/common/static/styles/materializecss/components/forms/_switches.scss deleted file mode 100644 index 3296b12c6..000000000 --- a/tubesync/common/static/styles/materializecss/components/forms/_switches.scss +++ /dev/null @@ -1,89 +0,0 @@ -/* Switch - ========================================================================== */ - -.switch, -.switch * { - -webkit-tap-highlight-color: transparent; - user-select: none; -} - -.switch label { - cursor: pointer; -} - -.switch label input[type=checkbox] { - opacity: 0; - width: 0; - height: 0; - - &:checked + .lever { - background-color: $switch-checked-lever-bg; - - &:before, &:after { - left: 18px; - } - - &:after { - background-color: $switch-bg-color; - } - } -} - -.switch label .lever { - content: ""; - display: inline-block; - position: relative; - width: 36px; - height: 14px; - background-color: $switch-unchecked-lever-bg; - border-radius: $switch-radius; - margin-right: 10px; - transition: background 0.3s ease; - vertical-align: middle; - margin: 0 16px; - - &:before, &:after { - content: ""; - position: absolute; - display: inline-block; - width: 20px; - height: 20px; - border-radius: 50%; - left: 0; - top: -3px; - transition: left 0.3s ease, background .3s ease, box-shadow 0.1s ease, transform .1s ease; - } - - &:before { - background-color: transparentize($switch-bg-color, .85); - } - - &:after { - background-color: $switch-unchecked-bg; - box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12); - } -} - -// Switch active style -input[type=checkbox]:checked:not(:disabled) ~ .lever:active::before, -input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::before { - transform: scale(2.4); - background-color: transparentize($switch-bg-color, .85); -} - -input[type=checkbox]:not(:disabled) ~ .lever:active:before, -input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::before { - transform: scale(2.4); - background-color: rgba(0,0,0,.08); -} - -// Disabled Styles -.switch input[type=checkbox][disabled] + .lever { - cursor: default; - background-color: rgba(0,0,0,.12); -} - -.switch label input[type=checkbox][disabled] + .lever:after, -.switch label input[type=checkbox][disabled]:checked + .lever:after { - background-color: $input-disabled-solid-color; -} diff --git a/tubesync/common/static/styles/materializecss/materialize.scss b/tubesync/common/static/styles/materializecss/materialize.scss deleted file mode 100644 index 6a51657b0..000000000 --- a/tubesync/common/static/styles/materializecss/materialize.scss +++ /dev/null @@ -1,41 +0,0 @@ -@charset "UTF-8"; - -// Color -@import "components/color-variables"; -@import "components/color-classes"; - -// Variables; -@import "components/variables"; - -// Reset -@import "components/normalize"; - -// components -@import "components/global"; -@import "components/badges"; -@import "components/icons-material-design"; -@import "components/grid"; -@import "components/navbar"; -@import "components/typography"; -@import "components/transitions"; -@import "components/cards"; -@import "components/toast"; -@import "components/tabs"; -@import "components/tooltip"; -@import "components/buttons"; -@import "components/dropdown"; -@import "components/waves"; -@import "components/modal"; -@import "components/collapsible"; -@import "components/chips"; -@import "components/materialbox"; -@import "components/forms/forms"; -@import "components/table_of_contents"; -@import "components/sidenav"; -@import "components/preloader"; -@import "components/slider"; -@import "components/carousel"; -@import "components/tapTarget"; -@import "components/pulse"; -@import "components/datepicker"; -@import "components/timepicker"; diff --git a/tubesync/common/static/styles/tubesync.css b/tubesync/common/static/styles/tubesync.css new file mode 100644 index 000000000..6a2413f1c --- /dev/null +++ b/tubesync/common/static/styles/tubesync.css @@ -0,0 +1,137 @@ +@import "tailwindcss"; +@import "./fonts.css"; + +@theme { + --font-sans: 'Roboto', Arial, Helvetica, sans-serif; + + --color-background: #ffffff; + --color-foreground: #0f172a; + --color-card: #ffffff; + --color-border: #e2e8f0; + --color-input: #e2e8f0; + --color-ring: #1e5c83; + + --color-primary: #1e5c83; + --color-primary-foreground: #fdfffc; + --color-accent: #f97316; + --color-accent-foreground: #fdfffc; + --color-destructive: #e71d36; + --color-destructive-foreground: #fdfffc; + + --color-muted: #f1f5f9; + --color-muted-foreground: #64748b; + + --color-nav: #011627; + --color-nav-foreground: #fdfffc; + --color-header: #1e5c83; + --color-header-foreground: #fdfffc; + --color-footer: #0f172a; + --color-footer-foreground: #94a3b8; +} + +/* Base styles */ +html { + font-size: 1.05rem; +} + +body { + @apply bg-background text-foreground font-sans antialiased; + display: flex; + min-height: 100vh; + flex-direction: column; +} + +a { + @apply text-primary hover:text-accent transition-colors; + text-decoration: none; +} + +h1 { + @apply text-2xl font-bold tracking-tight text-foreground; + margin: 0; + padding: 0 0 0.5rem 0; +} + +h2 { + @apply text-xl font-semibold tracking-tight text-foreground; + margin: 0; + padding: 1.5rem 0 1rem 0; +} + +h3 { + @apply text-lg font-semibold text-foreground; +} + +strong { + font-weight: 700; +} + +/* SVG logo */ +svg.tubesync-logo .logo-icon, +svg.tubesync-logo .logo-left-arrow, +svg.tubesync-logo .logo-right-arrow { + fill: currentColor !important; +} + +/* Truncate helper */ +.truncate { + @apply overflow-hidden text-ellipsis whitespace-nowrap; +} + +/* Utility classes used in templates */ +.nowrap { + white-space: nowrap; +} + +/* Error display for forms */ +.errors { + @apply rounded-lg border border-destructive/20 bg-destructive/5 p-4 mb-4; +} + +.errors li, +.errorlist li { + @apply text-destructive text-sm py-1 px-2; +} + +.error-text { + @apply text-destructive; +} + +/* Form inputs - Django renders raw HTML inputs, style them globally */ +input[type="text"], +input[type="url"], +input[type="email"], +input[type="password"], +input[type="number"], +input[type="search"], +textarea { + @apply w-full px-3 py-2 rounded-md border border-input bg-background text-sm text-foreground; + @apply outline-none focus:ring-2 focus:ring-ring focus:border-ring; + @apply transition-colors; +} + +textarea { + min-height: 120px; +} + +select { + @apply w-full px-3 py-2 rounded-md border border-input bg-background text-sm text-foreground; + @apply outline-none focus:ring-2 focus:ring-ring focus:border-ring; + appearance: auto; +} + +input[type="checkbox"] { + @apply rounded border-border text-primary; + width: 1rem; + height: 1rem; +} + +/* Code blocks */ +code { + @apply bg-muted px-1.5 py-0.5 rounded text-xs font-mono; +} + +/* Collection of checkbox options */ +.no-text-transform { + text-transform: none; +} diff --git a/tubesync/common/static/styles/tubesync.scss b/tubesync/common/static/styles/tubesync.scss deleted file mode 100644 index 1012ecfc8..000000000 --- a/tubesync/common/static/styles/tubesync.scss +++ /dev/null @@ -1,37 +0,0 @@ -@charset "UTF-8"; - -@import "materializecss/materialize"; -@import "fontawesome/fontawesome"; -@import "fontawesome/regular"; -@import "fontawesome/solid"; -@import "fontawesome/brands"; - -@import "fonts"; -@import "variables"; -@import "colours"; -@import "helpers"; -@import "forms"; -@import "template"; - -html { - visibility: visible; - opacity: 1; -} - -.flex-collection-container { - display: flex !important; - align-items: center; -} - -.flex-grow { - flex-grow: 1; -} - -.help-text > i { - padding-right: 6px; -} - -.issue-641 { - display: block !important; - overflow-wrap: anywhere; -} diff --git a/tubesync/common/templates/base.html b/tubesync/common/templates/base.html index 719ac166f..fca63adee 100644 --- a/tubesync/common/templates/base.html +++ b/tubesync/common/templates/base.html @@ -1,65 +1,60 @@ -{% load static %}{% load sass_tags %} - +{% load static %} + - - - + + + TubeSync - {% block headtitle %}Synchronize YouTube to your local media server{% endblock %} - + +
+
+ {% block content %}{% endblock %} +
+
-