From 2405c4b497760353a6a75aef001594f2f2fad4ed Mon Sep 17 00:00:00 2001 From: MystyPy Date: Sat, 4 May 2024 17:38:48 +1000 Subject: [PATCH] Initial HTMX version --- .env-template | 6 - .gitattributes | 9 - .gitignore | 171 +- config-template.json | 75 - config.template.toml | 23 + core/__init__.py | 21 + core/config.py | 28 + core/database.py | 179 + core/models.py | 84 + core/server.py | 57 + core/utils.py | 108 + docker-compose.yaml | 63 - launcher.py | 49 + mystbin/backend/Dockerfile | 9 - mystbin/backend/app.py | 152 - mystbin/backend/dev-requirements.txt | 1 - mystbin/backend/main.py | 73 - mystbin/backend/models/errors.py | 49 - mystbin/backend/models/payloads.py | 102 - mystbin/backend/models/responses.py | 179 - mystbin/backend/mystbin_models.py | 45 - mystbin/backend/requirements.txt | 18 - mystbin/backend/routers/admin.py | 316 - mystbin/backend/routers/apps.py | 258 - mystbin/backend/routers/pastes.py | 531 - mystbin/backend/routers/user.py | 152 - mystbin/backend/utils/cli.py | 245 - mystbin/backend/utils/db.py | 979 -- mystbin/backend/utils/embed.py | 61 - mystbin/backend/utils/ratelimits.py | 418 - mystbin/backend/utils/tokens.py | 23 - mystbin/backend/utils/webhooks.py | 33 - mystbin/backend/utils/words.txt | 9886 ----------------- mystbin/database/schema.sql | 76 - mystbin/fallback/fallback.py | 49 - mystbin/fallback/maintenance.html | 49 - mystbin/fallback/requirements.txt | 1 - mystbin/fallback/service_failure.html | 50 - mystbin/frontend/Dockerfile | 11 - mystbin/frontend/components/Base.tsx | 47 - mystbin/frontend/components/EditorTabs.tsx | 358 - mystbin/frontend/components/ExpiryModal.tsx | 100 - mystbin/frontend/components/LoginModal.tsx | 91 - mystbin/frontend/components/MonacoEditor.tsx | 105 - mystbin/frontend/components/NewTabButton.tsx | 11 - mystbin/frontend/components/OptsBar.tsx | 397 - mystbin/frontend/components/PasswordInput.tsx | 48 - mystbin/frontend/components/PasswordModal.tsx | 68 - mystbin/frontend/components/PrettySeconds.tsx | 17 - .../frontend/components/SetPasswordModal.tsx | 80 - mystbin/frontend/components/Sleeper.tsx | 19 - mystbin/frontend/components/Tab.tsx | 73 - mystbin/frontend/components/TipModal.tsx | 62 - .../frontend/dispatchers/PasteDispatcher.tsx | 4 - .../frontend/fonts/WhitneySemiboldItalic.woff | Bin 16308 -> 0 bytes mystbin/frontend/fonts/whitneybold.woff | Bin 15748 -> 0 bytes mystbin/frontend/fonts/whitneybook.woff | Bin 15372 -> 0 bytes mystbin/frontend/fonts/whitneybookitalic.woff | Bin 16004 -> 0 bytes mystbin/frontend/fonts/whitneylight.woff | Bin 15176 -> 0 bytes .../frontend/fonts/whitneylightitalic.woff | Bin 15612 -> 0 bytes mystbin/frontend/fonts/whitneymedium.woff | Bin 15032 -> 0 bytes .../frontend/fonts/whitneymediumitalic.woff | Bin 15528 -> 0 bytes mystbin/frontend/fonts/whitneysemibold.woff | Bin 15724 -> 0 bytes mystbin/frontend/icons/DiscordColour.tsx | 26 - mystbin/frontend/icons/GoogleIcon.tsx | 35 - mystbin/frontend/icons/LoginIcon.tsx | 14 - mystbin/frontend/icons/PatreonFirey.tsx | 19 - mystbin/frontend/next-env.d.ts | 6 - mystbin/frontend/package.json | 56 - mystbin/frontend/pages/[pid].tsx | 89 - mystbin/frontend/pages/_app.tsx | 13 - mystbin/frontend/pages/_document.jsx | 25 - mystbin/frontend/pages/dashboard.tsx | 759 -- mystbin/frontend/pages/discord_auth.tsx | 42 - mystbin/frontend/pages/github_auth.tsx | 39 - mystbin/frontend/pages/google_auth.tsx | 39 - mystbin/frontend/pages/index.tsx | 22 - mystbin/frontend/pages/success.tsx | 14 - mystbin/frontend/public/LogoMain.tsx | 32 - mystbin/frontend/public/LogoMinimalMain.tsx | 33 - mystbin/frontend/public/favicon.ico | Bin 3098 -> 0 bytes mystbin/frontend/public/heart.png | Bin 5935 -> 0 bytes mystbin/frontend/public/success.gif | Bin 1908750 -> 0 bytes mystbin/frontend/stores/PasteStore.tsx | 31 - mystbin/frontend/stores/languageStore.tsx | 46 - mystbin/frontend/styles/Base.module.css | 108 - mystbin/frontend/styles/Dash.module.css | 259 - mystbin/frontend/styles/EditorTabs.module.css | 130 - mystbin/frontend/styles/Home.module.css | 118 - mystbin/frontend/styles/Login.module.css | 48 - mystbin/frontend/styles/Nav.module.css | 6 - .../frontend/styles/NewTabButton.module.css | 14 - mystbin/frontend/styles/OptsBar.module.css | 135 - .../frontend/styles/PasswordInput.module.css | 22 - .../frontend/styles/PasswordModal.module.css | 21 - mystbin/frontend/styles/Success.module.css | 13 - mystbin/frontend/styles/Tab.module.css | 61 - mystbin/frontend/styles/TipModal.module.css | 40 - mystbin/frontend/styles/globals.css | 235 - mystbin/frontend/tsconfig.json | 29 - mystbin/frontend/yarn.lock | 4522 -------- mystbin/nginx/backend.conf | 32 - mystbin/nginx/frontend.conf | 31 - mystbin/systemd/backend_with_venv.service | 9 - mystbin/systemd/backend_without_venv.service | 9 - mystbin/systemd/fallback.service | 10 - mystbin/systemd/frontend.service | 9 - pyproject.toml | 87 +- requirements.txt | 6 + res/logo_full.svg | 1 - res/logo_mini.svg | 1 - res/mystbin_logo_light_full.svg | 1 - schema.sql | 21 + types_/config.py | 58 + views/__init__.py | 20 + views/api.py | 124 + views/htmx.py | 278 + web/index.html | 95 + web/password.html | 83 + web/paste.html | 74 + web/static/images/logo.svg | 1 + web/static/images/vsc.svg | 1 + web/static/packages/highlight-ln.min.js | 1 + web/static/packages/highlight.min.js | 1659 +++ web/static/packages/htmx.min.js | 1 + web/static/scripts/files.js | 51 + web/static/scripts/hidecopy.js | 43 + web/static/scripts/highlights.js | 18 + web/static/scripts/themes.js | 44 + web/static/styles/global.css | 482 + web/static/styles/highlights.css | 188 + 131 files changed, 4017 insertions(+), 22641 deletions(-) delete mode 100644 .env-template delete mode 100644 .gitattributes delete mode 100644 config-template.json create mode 100644 config.template.toml create mode 100644 core/__init__.py create mode 100644 core/config.py create mode 100644 core/database.py create mode 100644 core/models.py create mode 100644 core/server.py create mode 100644 core/utils.py delete mode 100644 docker-compose.yaml create mode 100644 launcher.py delete mode 100644 mystbin/backend/Dockerfile delete mode 100644 mystbin/backend/app.py delete mode 100644 mystbin/backend/dev-requirements.txt delete mode 100644 mystbin/backend/main.py delete mode 100644 mystbin/backend/models/errors.py delete mode 100644 mystbin/backend/models/payloads.py delete mode 100644 mystbin/backend/models/responses.py delete mode 100644 mystbin/backend/mystbin_models.py delete mode 100644 mystbin/backend/requirements.txt delete mode 100644 mystbin/backend/routers/admin.py delete mode 100644 mystbin/backend/routers/apps.py delete mode 100644 mystbin/backend/routers/pastes.py delete mode 100644 mystbin/backend/routers/user.py delete mode 100644 mystbin/backend/utils/cli.py delete mode 100644 mystbin/backend/utils/db.py delete mode 100644 mystbin/backend/utils/embed.py delete mode 100644 mystbin/backend/utils/ratelimits.py delete mode 100644 mystbin/backend/utils/tokens.py delete mode 100644 mystbin/backend/utils/webhooks.py delete mode 100644 mystbin/backend/utils/words.txt delete mode 100644 mystbin/database/schema.sql delete mode 100644 mystbin/fallback/fallback.py delete mode 100644 mystbin/fallback/maintenance.html delete mode 100644 mystbin/fallback/requirements.txt delete mode 100644 mystbin/fallback/service_failure.html delete mode 100644 mystbin/frontend/Dockerfile delete mode 100644 mystbin/frontend/components/Base.tsx delete mode 100644 mystbin/frontend/components/EditorTabs.tsx delete mode 100644 mystbin/frontend/components/ExpiryModal.tsx delete mode 100644 mystbin/frontend/components/LoginModal.tsx delete mode 100644 mystbin/frontend/components/MonacoEditor.tsx delete mode 100644 mystbin/frontend/components/NewTabButton.tsx delete mode 100644 mystbin/frontend/components/OptsBar.tsx delete mode 100644 mystbin/frontend/components/PasswordInput.tsx delete mode 100644 mystbin/frontend/components/PasswordModal.tsx delete mode 100644 mystbin/frontend/components/PrettySeconds.tsx delete mode 100644 mystbin/frontend/components/SetPasswordModal.tsx delete mode 100644 mystbin/frontend/components/Sleeper.tsx delete mode 100644 mystbin/frontend/components/Tab.tsx delete mode 100644 mystbin/frontend/components/TipModal.tsx delete mode 100644 mystbin/frontend/dispatchers/PasteDispatcher.tsx delete mode 100644 mystbin/frontend/fonts/WhitneySemiboldItalic.woff delete mode 100644 mystbin/frontend/fonts/whitneybold.woff delete mode 100644 mystbin/frontend/fonts/whitneybook.woff delete mode 100644 mystbin/frontend/fonts/whitneybookitalic.woff delete mode 100644 mystbin/frontend/fonts/whitneylight.woff delete mode 100644 mystbin/frontend/fonts/whitneylightitalic.woff delete mode 100644 mystbin/frontend/fonts/whitneymedium.woff delete mode 100644 mystbin/frontend/fonts/whitneymediumitalic.woff delete mode 100644 mystbin/frontend/fonts/whitneysemibold.woff delete mode 100644 mystbin/frontend/icons/DiscordColour.tsx delete mode 100644 mystbin/frontend/icons/GoogleIcon.tsx delete mode 100644 mystbin/frontend/icons/LoginIcon.tsx delete mode 100644 mystbin/frontend/icons/PatreonFirey.tsx delete mode 100644 mystbin/frontend/next-env.d.ts delete mode 100644 mystbin/frontend/package.json delete mode 100644 mystbin/frontend/pages/[pid].tsx delete mode 100644 mystbin/frontend/pages/_app.tsx delete mode 100644 mystbin/frontend/pages/_document.jsx delete mode 100644 mystbin/frontend/pages/dashboard.tsx delete mode 100644 mystbin/frontend/pages/discord_auth.tsx delete mode 100644 mystbin/frontend/pages/github_auth.tsx delete mode 100644 mystbin/frontend/pages/google_auth.tsx delete mode 100644 mystbin/frontend/pages/index.tsx delete mode 100644 mystbin/frontend/pages/success.tsx delete mode 100644 mystbin/frontend/public/LogoMain.tsx delete mode 100644 mystbin/frontend/public/LogoMinimalMain.tsx delete mode 100644 mystbin/frontend/public/favicon.ico delete mode 100644 mystbin/frontend/public/heart.png delete mode 100644 mystbin/frontend/public/success.gif delete mode 100644 mystbin/frontend/stores/PasteStore.tsx delete mode 100644 mystbin/frontend/stores/languageStore.tsx delete mode 100644 mystbin/frontend/styles/Base.module.css delete mode 100644 mystbin/frontend/styles/Dash.module.css delete mode 100644 mystbin/frontend/styles/EditorTabs.module.css delete mode 100644 mystbin/frontend/styles/Home.module.css delete mode 100644 mystbin/frontend/styles/Login.module.css delete mode 100644 mystbin/frontend/styles/Nav.module.css delete mode 100644 mystbin/frontend/styles/NewTabButton.module.css delete mode 100644 mystbin/frontend/styles/OptsBar.module.css delete mode 100644 mystbin/frontend/styles/PasswordInput.module.css delete mode 100644 mystbin/frontend/styles/PasswordModal.module.css delete mode 100644 mystbin/frontend/styles/Success.module.css delete mode 100644 mystbin/frontend/styles/Tab.module.css delete mode 100644 mystbin/frontend/styles/TipModal.module.css delete mode 100644 mystbin/frontend/styles/globals.css delete mode 100644 mystbin/frontend/tsconfig.json delete mode 100644 mystbin/frontend/yarn.lock delete mode 100644 mystbin/nginx/backend.conf delete mode 100644 mystbin/nginx/frontend.conf delete mode 100644 mystbin/systemd/backend_with_venv.service delete mode 100644 mystbin/systemd/backend_without_venv.service delete mode 100644 mystbin/systemd/fallback.service delete mode 100644 mystbin/systemd/frontend.service create mode 100644 requirements.txt delete mode 100644 res/logo_full.svg delete mode 100644 res/logo_mini.svg delete mode 100644 res/mystbin_logo_light_full.svg create mode 100644 schema.sql create mode 100644 types_/config.py create mode 100644 views/__init__.py create mode 100644 views/api.py create mode 100644 views/htmx.py create mode 100644 web/index.html create mode 100644 web/password.html create mode 100644 web/paste.html create mode 100644 web/static/images/logo.svg create mode 100644 web/static/images/vsc.svg create mode 100644 web/static/packages/highlight-ln.min.js create mode 100644 web/static/packages/highlight.min.js create mode 100644 web/static/packages/htmx.min.js create mode 100644 web/static/scripts/files.js create mode 100644 web/static/scripts/hidecopy.js create mode 100644 web/static/scripts/highlights.js create mode 100644 web/static/scripts/themes.js create mode 100644 web/static/styles/global.css create mode 100644 web/static/styles/highlights.css diff --git a/.env-template b/.env-template deleted file mode 100644 index 144d6712..00000000 --- a/.env-template +++ /dev/null @@ -1,6 +0,0 @@ -# Username for the Docker instance of PostgreSQL -POSTGRES_USER= myuser -# Password for the Docker instance of PostgreSQL -POSTGRES_PASSWORD=mypassword -# debug or not, remove key if not -DEBUG=true diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 728c11fb..00000000 --- a/.gitattributes +++ /dev/null @@ -1,9 +0,0 @@ -# Set the default behavior, in case people don't have core.autocrlf set. -* text=auto eol=lf - -# Explicitly declare text files you want to always be normalized and converted -# to native line endings on checkout. -*.sh text -*.py text -*.yaml text -*.yml text \ No newline at end of file diff --git a/.gitignore b/.gitignore index 06848ad5..8350ffdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,164 @@ +# Byte-compiled / optimized / DLL files __pycache__/ -.vscode/ -.idea/ -node_modules/ -config.toml -config.json +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments .env .venv -venv -.next/ -.pytest_cache/ -notes.txt -poetry.lock +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Config Files... +config.toml +.config.toml \ No newline at end of file diff --git a/config-template.json b/config-template.json deleted file mode 100644 index b54f88ba..00000000 --- a/config-template.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "maintenance": false, - "_comment_maintenance": "set this to True to tell the fallback that the server is under maintenance, and not randomly down.", - "database": { - "dsn": "postgresql://:@:/" - }, - "redis": { - "_comment": "redis is used for ratelimits. To disable this behaviour, and use in-memory ratelimits, set use-redis to false. Workers will be disabled if this is false.", - "use-redis": true, - "host": "127.0.0.1", - "port": 6379, - "user": null, - "password": null, - "db": 0 - }, - "apps": { - "discord_application_id": "", - "discord_application_secret": "", - "github_application_id": "", - "github_application_secret": "", - "google_application_id": "", - "google_application_secret": "", - "github_bot_token": "" - }, - "bunny_cdn": { - "hostname": "mystbin", - "token": "" - }, - "site": { - "frontend_site": "https://mysite.com", - "backend_site": "https://api.mysite.com", - "frontend_port": 4340, - "backend_port": 4341, - "fallback_port": 4342 - }, - "paste": { - "character_limit": 300000, - "file_limit": 5, - "filesize_limit": "8mb", - "log_ip": true - }, - "debug": { - "db": false, - "site": false - }, - "sentry": { - "_comment": "Performance and error data via sentry.io. If left blank sentry will be disabled", - "dsn": "", - "_comment_traces": "See Sentry docs for more info.", - "traces_sample_rate": 0.3, - "_commend_webhook": "If filled, and a sentry webhook is configured to mysite.com/callbacks/sentry, will forward all new errors to the webhook", - "discord_webhook": "" - }, - "ratelimits": { - "_comment": "Ratelimit values. Written in the format of `time/unit` (60/minute)", - "global": "60/minute", - "authed_global": "120/minute", - "premium_global": "360/minute", - "postpastes": "5/minute", - "authed_postpastes": "10/minute", - "premium_postpastes": "20/minute", - "getpaste": "20/minute", - "authed_getpaste": "40/minute", - "premium_getpaste": "80/minute", - "deletepaste": "1/minute", - "authed_deletepaste": "10/minute", - "premium_deletepaste": "20/minute", - "self": "20/minute", - "tokengen": "2/hour", - "admin": "5/second", - "apps": "5/hour", - "sentry": "10/minute", - "bookmarks": "30/minute" - } -} \ No newline at end of file diff --git a/config.template.toml b/config.template.toml new file mode 100644 index 00000000..9c28c4d9 --- /dev/null +++ b/config.template.toml @@ -0,0 +1,23 @@ +[SERVER] +host = "localhost" +port = 8181 +domain = "https://mystb.in" + +[DATABASE] +dsn = "postgres://USER:PASSWORD@localhost:5432/mystbin" + +[REDIS] +limiter = "" +sessions = "" + +[LIMITS] +paste_get = {rate=30, per=60, priority=1, bucket="ip"} +paste_get_day = {rate=7200, per=86400, priority=2, bucket="ip"} +paste_post = {rate=10, per=60, priority=1, bucket="ip"} +paste_post_day = {rate=1440, per=86400, priority=2, bucket="ip"} +global_limit = {rate=21600, per=86400, priority=1, bucket="ip"} + +[PASTES] +char_limit = 300_000 +file_limit = 5 +name_limit = 25 \ No newline at end of file diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 00000000..57aab0c8 --- /dev/null +++ b/core/__init__.py @@ -0,0 +1,21 @@ +"""MystBin. Share code easily. + +Copyright (C) 2020-Current PythonistaGuild + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from .config import CONFIG as CONFIG +from .database import Database as Database +from .server import Application as Application diff --git a/core/config.py b/core/config.py new file mode 100644 index 00000000..16835a3c --- /dev/null +++ b/core/config.py @@ -0,0 +1,28 @@ +"""MystBin. Share code easily. + +Copyright (C) 2020-Current PythonistaGuild + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import tomllib + +from types_.config import Config + + +__all__ = ("CONFIG",) + + +with open("config.toml", "rb") as fp: + CONFIG: Config = tomllib.load(fp) # type: ignore diff --git a/core/database.py b/core/database.py new file mode 100644 index 00000000..5e37e8ab --- /dev/null +++ b/core/database.py @@ -0,0 +1,179 @@ +"""MystBin. Share code easily. + +Copyright (C) 2020-Current PythonistaGuild + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import asyncio +import datetime +import logging +from typing import TYPE_CHECKING, Any, Self + +import asyncpg + +from core import CONFIG + +from . import utils +from .models import FileModel, PasteModel + + +if TYPE_CHECKING: + _Pool = asyncpg.Pool[asyncpg.Record] +else: + _Pool = asyncpg.Pool + + +logger: logging.Logger = logging.getLogger(__name__) + + +class Database: + pool: _Pool + + def __init__(self, *, dsn: str) -> None: + self._dsn: str = dsn + + async def __aenter__(self) -> Self: + await self.connect() + return self + + async def __aexit__(self, *_: Any) -> None: + await self.close() + + async def connect(self) -> None: + try: + pool: asyncpg.Pool[asyncpg.Record] | None = await asyncpg.create_pool(dsn=self._dsn) + except Exception as e: + raise RuntimeError from e + + if not pool: + raise RuntimeError("Failed to connect to the database... No additional information.") + + with open("schema.sql") as fp: + await pool.execute(fp.read()) + + self.pool = pool + logger.info("Successfully connected to the database.") + + async def close(self) -> None: + try: + await asyncio.wait_for(self.pool.close(), timeout=10) + except TimeoutError: + logger.warning("Failed to greacefully close the database connection...") + else: + logger.info("Successfully closed the database connection.") + + async def fetch_paste(self, identifier: str, *, password: str | None) -> PasteModel | None: + assert self.pool + + paste_query: str = """ + UPDATE pastes SET views = views + 1 WHERE id = $1 + RETURNING *, + CASE WHEN password IS NOT NULL THEN true + ELSE false END AS has_password, + CASE WHEN password = CRYPT($2, password) THEN true + ELSE false END AS password_ok + """ + + file_query: str = """ + SELECT * FROM files WHERE parent_id = $1 + """ + + async with self.pool.acquire() as connection: + record: asyncpg.Record | None = await connection.fetchrow(paste_query, identifier, password) + + if not record: + return + + paste: PasteModel = PasteModel(record) + if paste.expires and paste.expires <= datetime.datetime.now(tz=datetime.timezone.utc): + await connection.execute("DELETE FROM pastes WHERE id = $1", identifier) + return + + if paste.has_password and not paste.password_ok: + return paste + + records: list[asyncpg.Record] = await connection.fetch(file_query, identifier) + paste.files = [FileModel(d) for d in records] + + return paste + + async def create_paste(self, *, data: dict[str, Any]) -> PasteModel: + assert self.pool + + paste_query: str = """ + INSERT INTO pastes (id, expires, password, safety) + VALUES ($1, $2, (SELECT crypt($3, gen_salt('bf')) WHERE $3 is not null), $4) + RETURNING * + """ + + file_query: str = """ + INSERT INTO files (parent_id, content, filename, loc, annotation) + VALUES ($1, $2, $3, $4, $5) + RETURNING * + """ + + files: list[dict[str, Any]] = data["files"] + expiry: str | None = data["expires"] + password: str | None = data["password"] + + async with self.pool.acquire() as connection: + identifier: str = await utils.generate_id(self) + safety: str = utils.generate_safety_token() + + paster: asyncpg.Record | None = await connection.fetchrow(paste_query, identifier, expiry, password, safety) + if not paster: + raise RuntimeError("Unable to create new paste.") + + paste: PasteModel = PasteModel(paster) + async with connection.transaction(): + for index, file in enumerate(files, 1): + name: str = (file.get("filename") or f"file_{index}")[-CONFIG["PASTES"]["name_limit"] :] + content: str = file["content"] + loc: int = file["content"].count("\n") + 1 + annotation: str = "" + + tokens = [t for t in utils.TOKEN_REGEX.findall(content) if utils.validate_discord_token(t)] + if tokens: + annotation = "Contains possibly sensitive information: Discord Token(s)" + + row: asyncpg.Record | None = await connection.fetchrow( + file_query, paste.id, content, name, loc, annotation + ) + + if row: + paste.files.append(FileModel(row)) + + return paste + + async def fetch_paste_security(self, *, token: str) -> PasteModel | None: + query: str = """SELECT * FROM pastes WHERE safety = $1""" + + async with self.pool.acquire() as connection: + record: asyncpg.Record | None = await connection.fetchrow(query, token) + if not record: + return + + paste: PasteModel = PasteModel(record=record) + if paste.expires and paste.expires <= datetime.datetime.now(tz=datetime.timezone.utc): + await connection.execute("DELETE FROM pastes WHERE id = $1", token) + return + + return paste + + async def delete_paste_security(self, *, token: str) -> None: + query: str = """DELETE FROM pastes WHERE safety = $1""" + + async with self.pool.acquire() as connection: + await connection.execute(query, token) diff --git a/core/models.py b/core/models.py new file mode 100644 index 00000000..342d7396 --- /dev/null +++ b/core/models.py @@ -0,0 +1,84 @@ +"""MystBin. Share code easily. + +Copyright (C) 2020-Current PythonistaGuild + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import datetime +from collections.abc import Iterator, Mapping +from typing import Any + +import asyncpg + + +class BaseModel(Mapping[str, Any]): + __slots__ = ("record",) + + def __init__(self, record: asyncpg.Record | dict[str, Any], /) -> None: + self.record: dict[str, Any] = dict(record) + + def __getitem__(self, key: str) -> Any: + return self.record[key] + + def __iter__(self) -> Iterator[str]: + return iter(self.record) + + def __len__(self) -> int: + return len(self.record) + + def serialize(self, *, exclude: list[str] = ["index"]) -> dict[str, Any]: + new: dict[str, Any] = {} + + for key, value in self.record.items(): + if key in exclude: + continue + + if isinstance(value, datetime.datetime): + new[key] = value.isoformat() + else: + new[key] = value + + if isinstance(self, PasteModel) and self.files: + new["files"] = [f.serialize() for f in self.files] + + return new + + +class FileModel(BaseModel): + def __init__(self, record: asyncpg.Record | dict[str, Any]) -> None: + super().__init__(record) + + self.parent_id: str = record["parent_id"] + self.content: str = record["content"] + self.filename: str = record["filename"] + self.loc: int = record["loc"] + self.charcount: int = record["charcount"] + self.index: int = record["index"] + self.annotation: str = record["annotation"] + + +class PasteModel(BaseModel): + def __init__(self, record: asyncpg.Record) -> None: + super().__init__(record) + + self.id: str = record["id"] + self.created_at: datetime.datetime = record["created_at"] + self.expires: datetime.datetime | None = record["expires"] + self.password: str | None = record["password"] + self.views: int = record["views"] + self.safety: str = record["safety"] + self.has_password: bool | None = record.get("has_password", None) + self.password_ok: bool | None = record.get("password_ok", None) + self.files: list[FileModel] = [] diff --git a/core/server.py b/core/server.py new file mode 100644 index 00000000..e9878898 --- /dev/null +++ b/core/server.py @@ -0,0 +1,57 @@ +"""MystBin. Share code easily. + +Copyright (C) 2020-Current PythonistaGuild + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import logging + +import starlette_plus +from starlette.middleware import Middleware +from starlette.staticfiles import StaticFiles + +from core.database import Database +from views import * + +from .config import CONFIG + + +logger: logging.Logger = logging.getLogger(__name__) + + +class Application(starlette_plus.Application): + def __init__(self, *, database: Database) -> None: + self.database: Database = database + + views: list[starlette_plus.View] = [HTMXView(self), APIView(self)] + routes = [starlette_plus.Mount("/static", app=StaticFiles(directory="web/static"), name="static")] + + limit_redis = starlette_plus.Redis(url=CONFIG["REDIS"]["limiter"]) if CONFIG["REDIS"]["limiter"] else None + # sess_redis = starlette_plus.Redis(url=CONFIG["REDIS"]["sessions"]) if CONFIG["REDIS"]["sessions"] else None + + global_limits = [CONFIG["LIMITS"]["global_limit"]] + middleware = [ + Middleware( + starlette_plus.middleware.RatelimitMiddleware, + ignore_localhost=True, + redis=limit_redis, + global_limits=global_limits, + ) + ] + + super().__init__(on_startup=[self.event_ready], views=views, routes=routes, middleware=middleware) + + async def event_ready(self) -> None: + logger.info("MystBin application has successfully started!") diff --git a/core/utils.py b/core/utils.py new file mode 100644 index 00000000..9a94d333 --- /dev/null +++ b/core/utils.py @@ -0,0 +1,108 @@ +"""MystBin. Share code easily. + +Copyright (C) 2020-Current PythonistaGuild + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from __future__ import annotations + +import base64 +import binascii +import json +import re +import secrets +from typing import TYPE_CHECKING, Any + +import starlette_plus + +from core import CONFIG + + +if TYPE_CHECKING: + from .database import Database + + +TOKEN_REGEX = re.compile(r"[a-zA-Z0-9_-]{23,28}\.[a-zA-Z0-9_-]{6,7}\.[a-zA-Z0-9_-]{27,}") + + +async def generate_id(database: Database) -> str: + while True: + identifier: str = secrets.token_hex(8) + + if not await database.fetch_paste(identifier, password=None): + return identifier + + +def generate_safety_token() -> str: + return secrets.token_urlsafe(64) + + +async def json_or_text(request: starlette_plus.Request) -> dict[str, Any] | str: + text: str = str(await request.body()) + + try: + data: dict[str, Any] = json.loads(text) + except json.JSONDecodeError: + return text + + return data + + +def validate_paste(data: dict[str, Any]) -> starlette_plus.Response | None: + limit: int = CONFIG["PASTES"]["char_limit"] + file_limit: int = CONFIG["PASTES"]["file_limit"] + + try: + files: list[dict[str, str | None]] = data["files"] + except KeyError: + return starlette_plus.JSONResponse({"error": 'Missing the "files" parameter.'}, status_code=400) + + if len(files) > file_limit: + return starlette_plus.JSONResponse( + {"error": f'Paste exceeds the file limit of "{file_limit}" files.'}, + status_code=400, + ) + + for index, file in enumerate(files): + try: + content: str | None = file["content"] + except KeyError: + return starlette_plus.JSONResponse( + {"error": f'The file at index "{index}" is missing the content parameter.'}, + status_code=400, + ) + + if not content: + return starlette_plus.JSONResponse( + {"error": f'The file at index "{index}" has no content.'}, + status_code=400, + ) + + if len(content) > limit: + return starlette_plus.JSONResponse( + {"error": f'The file at index "{index}" exceeds content size limits of "{limit}" characters.'}, + status_code=400, + ) + + +def validate_discord_token(token: str) -> bool: + try: + # Just check if the first part validates as a user ID + (user_id, _, _) = token.split(".") + user_id = int(base64.b64decode(user_id + "==", validate=True)) + except (ValueError, binascii.Error): + return False + else: + return True diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index 4d35d366..00000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,63 +0,0 @@ -version: "3.7" - -services: - api: - build: mystbin/backend - ports: - - 127.0.0.1:5557:9000/tcp - volumes: - - ./mystbin/database/schema.sql:/etc/schema.sql:ro - - ./config.json:/usr/src/app/config.json:ro - environment: - DEBUG: "${DEBUG}" - container_name: "mystbin-api" - networks: - main: - ipv4_address: 172.25.0.11 - depends_on: - - database - - database: - image: postgres:14-alpine - ports: - - 127.0.0.1:5558:5432 - container_name: "mystbin-database" - volumes: - - postgresdata:/var/lib/postgresql/data/pgdata - - ./mystbin/database/schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro - environment: - POSTGRES_DB: mystbin - POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" - POSTGRES_USER: "${POSTGRES_USER}" - POSTGRES_HOST_AUTH_METHOD: "md5" - PGDATA: /var/lib/postgresql/data/pgdata - networks: - main: - ipv4_address: 172.25.0.12 - - frontend: - image: node:latest - volumes: - - node_modules:/app/node_modules - - ./config.json:/app/config.json:ro - ports: - - 127.0.0.1:5559:3000 - container_name: "mystbin-frontend" - build: ./mystbin/frontend - depends_on: - - api - networks: - main: - ipv4_address: 172.25.0.13 - -volumes: - postgresdata: - node_modules: - - -networks: - main: - ipam: - driver: default - config: - - subnet: 172.25.0.0/16 diff --git a/launcher.py b/launcher.py new file mode 100644 index 00000000..95e54425 --- /dev/null +++ b/launcher.py @@ -0,0 +1,49 @@ +"""MystBin. Share code easily. + +Copyright (C) 2020-Current PythonistaGuild + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import asyncio +import logging + +import starlette_plus +import uvicorn + +import core +import core.config + + +starlette_plus.setup_logging(level=logging.INFO) +logger: logging.Logger = logging.getLogger(__name__) + + +async def main() -> None: + async with core.Database(dsn=core.CONFIG["DATABASE"]["dsn"]) as database: + app: core.Application = core.Application(database=database) + + host: str = core.CONFIG["SERVER"]["host"] + port: int = core.CONFIG["SERVER"]["port"] + + config: uvicorn.Config = uvicorn.Config(app=app, host=host, port=port, access_log=False) + server: uvicorn.Server = uvicorn.Server(config) + + await server.serve() + + +try: + asyncio.run(main()) +except KeyboardInterrupt: + logger.info("Closing the MystBin application due to KeyboardInterrupt.") diff --git a/mystbin/backend/Dockerfile b/mystbin/backend/Dockerfile deleted file mode 100644 index 2e67eca7..00000000 --- a/mystbin/backend/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM python:3.10-slim - -WORKDIR /usr/src/app - -COPY . ./ -RUN pip install --no-cache-dir -U -r requirements.txt -ENV ISDOCKER=true - -CMD ["python", "main.py", "-nc"] diff --git a/mystbin/backend/app.py b/mystbin/backend/app.py deleted file mode 100644 index 271485b3..00000000 --- a/mystbin/backend/app.py +++ /dev/null @@ -1,152 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -import asyncio -import datetime -import os -import pathlib -from typing import Any, Callable, Coroutine - -import aiohttp -import sentry_sdk -import ujson -from fastapi import FastAPI, Response -from redis import asyncio as aioredis # fuckin lol -from sentry_sdk.integrations.asgi import SentryAsgiMiddleware -from starlette.middleware.base import BaseHTTPMiddleware - -from mystbin_models import MystbinRequest, MystbinState -from routers import admin, apps, pastes, user -from utils import cli as _cli, ratelimits -from utils.db import Database - - -METHODS: tuple[str, ...] = ("DELETE", "GET", "OPTIONS", "PATCH", "POST", "PUT") - - -class MystbinApp(FastAPI): - """Subclassed API for Mystbin.""" - - redis: aioredis.Redis | None - cli: _cli.CLIHandler | None = None - state: MystbinState - - def __init__(self, *, loop: asyncio.AbstractEventLoop | None = None, config: pathlib.Path | None = None): - self.loop: asyncio.AbstractEventLoop = loop or asyncio.get_event_loop_policy().get_event_loop() - super().__init__( - title="MystBin", - version="3.0.0", - description="MystBin backend server", - loop=self.loop, - redoc_url="/docs", - docs_url=None, - ) - self._debug: bool = True if os.getenv("DEBUG") else False - - if not config: - config = pathlib.Path("config.json") - if not config.exists(): - config = pathlib.Path("../../config.json") - - with open(config) as f: - self.config: dict[str, dict[str, Any]] = ujson.load(f) - self.should_close = False - self.add_middleware(BaseHTTPMiddleware, dispatch=self.request_stats) - self.add_event_handler("startup", func=self.app_startup) - - async def cors_middleware( - self, - request: MystbinRequest, - call_next: Callable[[MystbinRequest], Coroutine[Any, Any, Response]], - ): - headers = { - "Access-Control-Allow-Headers": request.headers.get("Access-Control-Request-Headers", ""), - "Access-Control-Allow-Methods": ", ".join(METHODS), - "Access-Control-Allow-Origin": self.config["site"]["frontend_site"], - "Access-Control-Max-Age": "600", - "Vary": "Origin", - } - - if request.method == "OPTIONS": - return Response(headers=headers) - - resp = await call_next(request) - resp.headers.update(headers) - return resp - - async def request_stats(self, request: MystbinRequest, call_next): - request.app.state.request_stats["total"] += 1 - - if request.url.path != "/admin/stats": - request.app.state.request_stats["latest"] = datetime.datetime.utcnow() - - response = await call_next(request) - return response - - async def app_startup(self) -> None: - """Async app startup.""" - self.state.db = await Database(self.config).__ainit__() - self.state.client = aiohttp.ClientSession() - self.state.request_stats = {"total": 0, "latest": datetime.datetime.utcnow()} - self.state.webhook_url = self.config["sentry"].get("discord_webhook", None) - - if self.config["redis"]["use-redis"]: - self.redis = aioredis.Redis( - host=self.config["redis"]["host"], - port=self.config["redis"]["port"], - username=self.config["redis"]["user"], - password=self.config["redis"]["password"], - db=self.config["redis"]["db"], - ) - else: - self.redis = None - - ratelimits.limiter.startup(self) - self.middleware("http")(ratelimits.limiter.middleware) - self.middleware("http")(self.cors_middleware) - - nocli = pathlib.Path(".nocli") - if nocli.exists(): - return - - print() - - self.cli = _cli.CLIHandler(self.state.db) - self.loop.create_task(self.cli.parse_cli()) - - -app = MystbinApp() - -app.include_router(admin.router) -app.include_router(apps.router) -app.include_router(pastes.router) -app.include_router(user.router) - - -try: - sentry_dsn = app.config["sentry"]["dsn"] -except KeyError: - pass -else: - traces_sample_rate = app.config["sentry"].get("traces_sample_rate", 0.3) - sentry_sdk.init(dsn=sentry_dsn, traces_sample_rate=traces_sample_rate, attach_stacktrace=True) - - app.add_middleware(SentryAsgiMiddleware) - -# app.add_middleware(PrometheusMiddleware) -# app.add_route("/metrics/", metrics) diff --git a/mystbin/backend/dev-requirements.txt b/mystbin/backend/dev-requirements.txt deleted file mode 100644 index 08b31e90..00000000 --- a/mystbin/backend/dev-requirements.txt +++ /dev/null @@ -1 +0,0 @@ -asyncpg-stubs diff --git a/mystbin/backend/main.py b/mystbin/backend/main.py deleted file mode 100644 index 4582dc4d..00000000 --- a/mystbin/backend/main.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import annotations - -import argparse -import json -import os -import pathlib -import sys - -import uvicorn -from uvicorn.supervisors import Multiprocess - - -def get_config() -> dict[str, dict[str, int | str]]: - pth = pathlib.Path("config.json") - - if not pth.exists(): - pth = pathlib.Path("../../config.json") - - if not pth.exists(): - raise RuntimeError( - "No config.json was found. Please make sure you've copied the config-template.json into config.json and filled out the appropriate vallues" - ) - - with pth.open() as f: - data = json.load(f) - - return data - - -if __name__ == "__main__": - cfg = get_config() - port: int = cfg["site"]["backend_port"] # type: ignore # resolved when typed dict - parser = argparse.ArgumentParser(prog="Mystbin") - parser.add_argument("--no-workers", "-nw", action="store_true", default=False) - parser.add_argument("--no-cli", "-nc", action="store_true", default=False) - parser.add_argument("--workers", "-w", nargs=1, default=os.cpu_count() or 1) - - ns = parser.parse_args(sys.argv[1:]) - - use_workers: bool = not ns.no_workers - use_cli: bool = not ns.no_cli - worker_count: int = ns.workers - _cli_path = pathlib.Path(".nocli") - - if not use_cli: - with _cli_path.open("w") as f: - f.truncate() - f.flush() - else: - if _cli_path.exists(): - os.remove(_cli_path) - - if os.environ.get("ISDOCKER") is not None: - config = uvicorn.Config("app:app", port=port, host="0.0.0.0") - # allow from all hosts when in a docker container, so that requests can be proxied in - else: - config = uvicorn.Config("app:app", port=port, host="127.0.0.1") - - server = uvicorn.Server(config) - - if use_workers: - config.workers = worker_count - sock = config.bind_socket() - - runner = Multiprocess(config, target=server.run, sockets=[sock]) - else: - runner = server - - try: - runner.run() - finally: - if _cli_path.exists(): - os.remove(_cli_path) diff --git a/mystbin/backend/models/errors.py b/mystbin/backend/models/errors.py deleted file mode 100644 index 19a58490..00000000 --- a/mystbin/backend/models/errors.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" - -from pydantic import BaseModel - - -__all__ = ( - "Unauthorized", - "Forbidden", - "NotFound", - "BadRequest", -) - - -class Unauthorized(BaseModel): - error: str = "Unauthorized" - notice: str - - class Config: - schema_extras = {"example": {"error": "Unauthorized", "notice": "You must be signed in to use this route"}} - - -class Forbidden(BaseModel): - error: str = "Forbidden" - - -class NotFound(BaseModel): - error: str = "Not Found" - - -class BadRequest(BaseModel): - error: str = "Bad Request" - reason: str | None = None diff --git a/mystbin/backend/models/payloads.py b/mystbin/backend/models/payloads.py deleted file mode 100644 index 21e0f511..00000000 --- a/mystbin/backend/models/payloads.py +++ /dev/null @@ -1,102 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -from __future__ import annotations - -import datetime - -from pydantic import BaseModel - - -__all__ = ( - "PasteFile", - "RichPasteFile", - "PastePut", - "RichPastePost", - "PastePatch", - "PasteDelete", - "BookmarkPutDelete", -) - - -class PasteFile(BaseModel): - content: str - filename: str - - class Config: - schema_extra = {"example": {"content": "explosions everywhere", "filename": "kaboom.txt"}} - - -class RichPasteFile(PasteFile): - attachment: str | None - - class Config: - schema_extra = { - "example": {"content": "explosions everywhere", "filename": "kaboom.txt", "attachment": "image1.png"} - } - - -class PastePut(BaseModel): - expires: datetime.datetime | None = None - password: str | None = None - files: list[PasteFile] - - class Config: - schema_extra = { - "example": { - "expires": "2020-11-16T13:46:49.215Z", - "password": None, - "files": [ - {"content": "import this", "filename": "foo.py"}, - { - "filename": "doc.md", - "content": "**do not use this in production**", - }, - ], - } - } - - -class RichPastePost(PastePut): - files: list[RichPasteFile] - - class Config: - schema_extra = { - "example": { - "expires": "2020-11-16T13:46:49.215Z", - "password": None, - "files": [ - {"content": "import this", "filename": "foo.py", "attachment": None}, - {"filename": "doc.md", "content": "**do not use this in production**", "attachment": "image2.jpeg"}, - ], - } - } - - -class PastePatch(BaseModel): - new_expires: datetime.datetime | None = None - new_password: str | None = None - new_files: list[PasteFile] - - -class PasteDelete(BaseModel): - pastes: list[str] - - -class BookmarkPutDelete(BaseModel): - paste_id: str diff --git a/mystbin/backend/models/responses.py b/mystbin/backend/models/responses.py deleted file mode 100644 index e51d5e21..00000000 --- a/mystbin/backend/models/responses.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -from datetime import datetime - -from pydantic import BaseModel - - -__all__ = ( - "File", - "PastePostResponse", - "PasteGetResponse", - "PasteGetAll", - "PasteGetAllResponse", - "TokenResponse", - "User", - "SmallUser", - "UserCount", - "UserList", - "Bookmark", - "Bookmarks", -) - - -class File(BaseModel): - filename: str - content: str - loc: int - charcount: int - attachment: str | None - - class Config: - schema_extra = { - "example": { - "filename": "foo.py", - "content": "import datetime\\nprint(datetime.datetime.utcnow())", - "loc": 2, - "charcount": 49, - "attachment": "https://mystbin.b-cdn.com/umbra_sucks.jpeg", - } - } - - -class PastePostResponse(BaseModel): - id: str - author_id: int | None = None - created_at: datetime - expires: datetime | None = None - files: list[File] - notice: str | None - - class Config: - schema_extra = { - "example": { - "id": "FlyingHighKites", - "author_id": None, - "created_at": "2020-11-16T13:46:49.215Z", - "expires": None, - "files": [ - { - "filename": "foo.py", - "content": "import datetime\\nprint(datetime.datetime.utcnow())", - "loc": 2, - "charcount": 49, - "attachment": "https://mystbin.b-cdn.com/umbra_sucks.jpeg", - } - ], - "notice": "Found discord tokens and sent them to https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6 to be invalidated", - } - } - - -class PasteGetResponse(BaseModel): - id: str - author_id: int | None - created_at: datetime - expires: datetime | None = None - last_edited: datetime | None = None - views: int - files: list[File] - - class Config: - schema_extra = { - "example": { - "id": "FlyingHighKites", - "author_id": None, - "created_at": "2020-11-16T13:46:49.215Z", - "expires": None, - "last_edited": "2020-11-20T0:46:0.215Z", - "views": 48, - "files": [ - { - "filename": "foo.py", - "content": "import datetime\\nprint(datetime.datetime.utcnow())", - "loc": 2, - "charcount": 49, - "attachment": "https://mystbin.b-cdn.com/umbra_sucks.jpeg", - } - ], - } - } - - -class PasteGetAll(BaseModel): - id: str - author_id: int - created_at: datetime - views: int - expires: datetime | None = None - has_password: bool - - -class PasteGetAllResponse(BaseModel): - pastes: list[PasteGetAll] - - -class TokenResponse(BaseModel): - token: str - - -class User(BaseModel): - id: int - username: str - token: str - emails: list[str] - discord_id: str | None - github_id: str | None - google_id: str | None - admin: bool - theme: str - subscriber: bool - - -class SmallUser(BaseModel): - id: int - username: str - authorizations: list[str] - admin: bool - theme: str - subscriber: bool - banned: bool - last_seen: str | None - paste_count: int - - -class UserCount(BaseModel): - count: int - - -class UserList(BaseModel): - users: list[SmallUser] - page: int - page_count: int - - -class Bookmark(BaseModel): - id: str - created_at: datetime - expires: datetime - views: int - - -class Bookmarks(BaseModel): - bookmarks: list[Bookmark] diff --git a/mystbin/backend/mystbin_models.py b/mystbin/backend/mystbin_models.py deleted file mode 100644 index 8b02287e..00000000 --- a/mystbin/backend/mystbin_models.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, TypedDict - -import aiohttp -from fastapi import Request -from starlette.datastructures import State - -from utils.db import Database - - -if TYPE_CHECKING: - from app import MystbinApp - -__all__ = ( - "MystbinState", - "MystbinRequest", -) - - -class _StateUser(TypedDict): - id: int - token: str - emails: list[str] - discord_id: str | None - github_id: str | None - google_id: str | None - admin: bool - theme: str - subscriber: bool - username: str - _is_ip_banned: str | None - _is_user_banned: int | None - - -class MystbinState(State): - """Only for db typing.""" - - db: Database - user: _StateUser | None - session: aiohttp.ClientSession - - -class MystbinRequest(Request): - app: MystbinApp diff --git a/mystbin/backend/requirements.txt b/mystbin/backend/requirements.txt deleted file mode 100644 index 9fa75e57..00000000 --- a/mystbin/backend/requirements.txt +++ /dev/null @@ -1,18 +0,0 @@ -aiohttp==3.8.3 -asyncpg>=0.26.0 -fastapi>=0.109.1 -prometheus-client==0.12.0 -psutil==5.9.4 -pycryptodome==3.16.0 -pydantic==1.10.2 -pyjwt==2.6.0 -sentry-sdk==1.11.1 -starlette-prometheus==0.9.0 -ujson==5.6.0 -uvicorn[standard]==0.18.3 -yarl==1.8.2 -aioconsole==0.5.1 -tabulate==0.9.0 -redis==4.4.0 -typing-extensions -python-multipart diff --git a/mystbin/backend/routers/admin.py b/mystbin/backend/routers/admin.py deleted file mode 100644 index d09b6969..00000000 --- a/mystbin/backend/routers/admin.py +++ /dev/null @@ -1,316 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -from __future__ import annotations - -import datetime -import pathlib -import subprocess -from hashlib import sha256 -from hmac import HMAC, compare_digest - -import psutil -import ujson -from asyncpg import Record -from fastapi import APIRouter -from fastapi.responses import Response, UJSONResponse -from models import errors, responses - -from mystbin_models import MystbinRequest -from utils.db import _recursive_hook as recursive_hook -from utils.ratelimits import limit - - -router = APIRouter() - - -# Statistic consts -PROC = psutil.Process() -START_TIME = datetime.datetime.utcnow() - - -@router.get( - "/admin/users/{user_id}", - tags=["admin"], - response_model=responses.User, - responses={ - 200: {"model": responses.User}, - 401: {"model": errors.Unauthorized}, - 400: {"response": {"example": {"error": "The given user was not found"}}}, - }, - include_in_schema=False, -) -@limit("admin") -async def get_any_user(request: MystbinRequest, user_id: int) -> UJSONResponse | dict[str, str]: - """Returns the User object of the passed user_id. - * Requires admin authentication. - """ - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - data: Record | int | None = await request.app.state.db.get_user(user_id=user_id) - if data and not isinstance(data, int): - return UJSONResponse(dict(data)) - return UJSONResponse({"error": "The given user was not found"}, status_code=400) - - -@router.post("/admin/users/{user_id}/ban", tags=["admin"], include_in_schema=False) -@limit("admin") -async def ban_user( - request: MystbinRequest, - user_id: int, - ip: str | None = None, - reason: str | None = None, -) -> UJSONResponse: - """ - Bans a user from the service - * Requires admin authentication - """ - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - success = await request.app.state.db.ban_user(user_id, ip, reason) - return UJSONResponse({"success": success}) - - -@router.post("/admin/users/{user_id}/unban", tags=["admin"], include_in_schema=False) -@limit("admin") -async def unban_user( - request: MystbinRequest, - user_id: int, - ip: str | None = None, -) -> UJSONResponse: - """ - Unbans a user from the service - * Requires admin authentication - """ - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - success = await request.app.state.db.unban_user(user_id, ip) - return UJSONResponse({"success": success}) - - -@router.post("/admin/users/{user_id}/subscribe", tags=["admin"], include_in_schema=False) -@limit("admin") -async def subscribe_user(request: MystbinRequest, user_id: int) -> UJSONResponse: - """ - Gives a user subscriber access - * Requires admin authentication - """ - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - success = await request.app.state.db.toggle_subscription(user_id, True) - return UJSONResponse({"success": success}) - - -@router.post("/admin/users/{user_id}/unsubscribe", tags=["admin"], include_in_schema=False) -@limit("admin") -async def unsubscribe_user(request: MystbinRequest, user_id: int) -> UJSONResponse: - """ - Revokes a users subscriber access - * Requires admin authentication - """ - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - success = await request.app.state.db.toggle_subscription(user_id, False) - return UJSONResponse({"success": success}) - - -@router.get( - "/admin/users", - tags=["admin"], - response_model=responses.UserList, - responses={200: {"model": responses.UserList}, 401: {"model": errors.Unauthorized}}, - include_in_schema=False, -) -@limit("admin") -async def get_admin_userlist(request: MystbinRequest, page: int = 1): - """ - Returns a list of smaller user objects - * Requires admin authentication. - """ - if page < 1: - return UJSONResponse({"error": "page must be >= 1"}, status_code=400) - - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - data = await request.app.state.db.get_admin_userlist(page) - return UJSONResponse(data) - - -@router.get( - "/admin/users/count", - tags=["admin"], - response_model=responses.UserCount, - responses={ - 200: {"model": responses.UserCount}, - 401: {"model": errors.Unauthorized}, - }, - include_in_schema=False, -) -@limit("admin") -async def get_admin_usercount(request: MystbinRequest) -> UJSONResponse: - """ - Returns a count of how many users there are - * Requires admin authentication. - """ - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - count = await request.app.state.db.get_admin_usercount() - return UJSONResponse({"count": count}) - - -@router.get("/admin/bans", tags=["admin"], include_in_schema=False) -@limit("admin") -async def search_bans(request: MystbinRequest, search: str | None = None, page: int = 1) -> UJSONResponse: - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - if search is not None: - data = await request.app.state.db.search_bans(search=search) - if isinstance(data, str): - return UJSONResponse({"reason": data, "searches": []}) - - return UJSONResponse({"reason": None, "searches": data}) - else: - return UJSONResponse(await request.app.state.db.get_bans(page)) - - -@router.post("/admin/bans", tags=["admin"], include_in_schema=False) -@limit("admin") -async def post_ban( - request: MystbinRequest, - reason: str, - ip: str | None = None, - userid: int | None = None, -) -> UJSONResponse: - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - data = await request.app.state.db.ban_user(ip=ip, userid=userid, reason=reason) - if data and request.app.redis: - if ip is not None: - await request.app.redis.set(f"ban-ip-{ip}", reason, ex=120) - - return UJSONResponse({"success": data}) - - -@router.delete("/admin/bans", tags=["admin"], include_in_schema=False) -@limit("admin") -async def remove_ban(request: MystbinRequest, ip: str | None = None, userid: int | None = None) -> UJSONResponse | Response: - if not ip and not userid: - return UJSONResponse({"error": "Bad Request"}, status_code=400) - - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - if await request.app.state.db.unban_user(userid, ip): - if ip is not None and request.app.redis: - await request.app.redis.set(f"ban-ip-{ip}", "", ex=120) - - return Response(status_code=204) - - return UJSONResponse({"error": "Ban not found"}, status_code=400) - - -@router.get("/admin/stats", tags=["admin"], include_in_schema=False) -@limit("admin") -async def get_server_stats(request: MystbinRequest) -> UJSONResponse: - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - data = { - "memory": PROC.memory_full_info().uss / 1024**2, - "memory_percent": PROC.memory_percent(memtype="uss"), - "cpu_percent": PROC.cpu_percent(), - "uptime": (datetime.datetime.utcnow() - START_TIME).total_seconds(), - "total_pastes": await request.app.state.db.get_paste_count(), - "requests": request.app.state.request_stats["total"], - "latest_request": request.app.state.request_stats["latest"].isoformat(), - } - - return UJSONResponse(data, status_code=200) - - -@router.post("/admin/release_hook", tags=["admin"], include_in_schema=False) -@limit("admin") -async def release_hook(request: MystbinRequest) -> UJSONResponse | Response: - - config = pathlib.Path("config.json") - if not config.exists(): - config = pathlib.Path("../../config.json") - - with open(config) as f: - config = ujson.load(f) - - SECRET = config["github_secret"].encode() - - received_sign = request.headers.get("X-Hub-Signature-256").split("sha256=")[-1].strip() - expected_sign = HMAC(key=SECRET, msg=await request.body(), digestmod=sha256).hexdigest() - if not compare_digest(received_sign, expected_sign): - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - command = "cd /root/MystBin/; git pull;" - subprocess.run(command, stdout=subprocess.PIPE, shell=True) - - return Response(status_code=200) - - -@router.get( - "/admin/pastes/{paste_id}", - tags=["admin"], - response_model=responses.PasteGetResponse, - responses={ - 200: {"model": responses.PasteGetResponse}, - 401: {"model": errors.Unauthorized}, - 404: {"model": errors.NotFound}, - }, - name="Retrieve paste file(s)", - include_in_schema=False, -) -@limit("admin") -async def get_paste(request: MystbinRequest, paste_id: str, password: str | None = None) -> Response: - """Get a paste from MystBin.""" - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - paste = await request.app.state.db.get_paste(paste_id, password) - if paste is None: - return Response(status_code=404) - - resp = responses.PasteGetResponse(**paste) - return UJSONResponse(recursive_hook(resp.dict())) - - -@router.get("/admin/pastes", tags=["admin"], include_in_schema=False) -@limit("admin") -async def get_all_pastes(request: MystbinRequest, count: int, page: int = 0, oldest_first: bool = False): - if not request.state.user or not request.state.user["admin"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - if page < 0: - return UJSONResponse({"error": "Page must be greater than 0"}, status_code=421) - elif count < 1: - return UJSONResponse({"error": "Count must be greater than 1"}, status_code=421) - - return UJSONResponse({"pastes": await request.app.state.db.get_all_pastes(page, count, reverse=oldest_first)}) diff --git a/mystbin/backend/routers/apps.py b/mystbin/backend/routers/apps.py deleted file mode 100644 index e8e9541c..00000000 --- a/mystbin/backend/routers/apps.py +++ /dev/null @@ -1,258 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -from __future__ import annotations - -import datetime - -import yarl -from fastapi import APIRouter -from fastapi.responses import Response, UJSONResponse -from models import responses - -from mystbin_models import MystbinRequest -from utils.embed import Embed -from utils.ratelimits import limit - - -router = APIRouter() - - -@limit("apps") -@router.post("/users/connect/discord", response_model=responses.TokenResponse, include_in_schema=False) -async def auth_from_discord(request: MystbinRequest) -> dict[str, str | None] | UJSONResponse: - """Allows user to authenticate from Discord OAuth.""" - try: - data = await request.json() - code = data.get("code", None) - except: - return UJSONResponse({"error": "Bad JSON body passed"}, status_code=421) - - if not code: - return UJSONResponse({"error": "Missing code query"}, status_code=400) - - client_id = request.app.config["apps"]["discord_application_id"] - client_secret = request.app.config["apps"]["discord_application_secret"] - url = yarl.URL(request.app.config["site"]["frontend_site"]).with_path("/discord_auth") - - data = { - "client_id": client_id, - "client_secret": client_secret, - "grant_type": "authorization_code", - "code": code, - "redirect_uri": url, - "scope": "identify email", - } - headers = {"Content-Type": "application/x-www-form-urlencoded"} - - async with request.app.state.client.post("https://discord.com/api/v8/oauth2/token", data=data, headers=headers) as resp: - data = await resp.json() - token = data["access_token"] - - async with request.app.state.client.get( - "https://discord.com/api/v8/users/@me", - headers={"Authorization": f"Bearer {token}"}, - ) as resp: - resp.raise_for_status() - data = await resp.json() - userid = data["id"] - email = [data["email"]] - username = f"{data['username']}#{data['discriminator']}" - - if request.state.user is not None: - token = await request.app.state.db.update_user(request.state.user["id"], discord_id=userid, emails=email) - return {"token": token} - - elif _id := await request.app.state.db.check_email(email): - token = await request.app.state.db.update_user(_id, discord_id=userid, emails=email) - return {"token": token} - - else: - data = await request.app.state.db.new_user(email, username, userid) - return {"token": data["token"]} - - -@router.post("/users/connect/google", response_model=responses.TokenResponse, include_in_schema=False) -@limit("apps") -async def auth_from_google(request: MystbinRequest) -> dict[str, str] | UJSONResponse: - """Allows user to authenticate from Google OAuth.""" - try: - data = await request.json() - code = data.get("code", None) - except: - return UJSONResponse({"error": "Bad JSON body passed"}, status_code=421) - - if not code: - return UJSONResponse({"error": "Missing code query"}, status_code=400) - - client_id = request.app.config["apps"]["google_application_id"] - client_secret = request.app.config["apps"]["google_application_secret"] - url = yarl.URL(request.app.config["site"]["frontend_site"]).with_path("/google_auth") - - data = { - "client_id": client_id, - "client_secret": client_secret, - "redirect_uri": url, - "grant_type": "authorization_code", - "code": code, - } - headers = {"Content-Type": "application/x-www-form-urlencoded"} - - async with request.app.state.client.post("https://oauth2.googleapis.com/token", data=data, headers=headers) as resp: - resp.raise_for_status() - data = await resp.json() - token = data["access_token"] - - async with request.app.state.client.get( - "https://www.googleapis.com/userinfo/v2/me", - headers={"Authorization": f"Bearer {token}"}, - ) as resp: - resp.raise_for_status() - data = await resp.json() - email = [data["email"]] - userid = data["id"] - username = data.get("name") or data["email"].split("@")[0] - - if request.state.user is not None: - token = await request.app.state.db.update_user(request.state.user["id"], google_id=userid, emails=email) - return UJSONResponse({"token": token}) - - elif _id := await request.app.state.db.check_email(email): - token = await request.app.state.db.update_user(_id, google_id=userid, emails=email) - return UJSONResponse({"token": token}) - - else: - data = await request.app.state.db.new_user(email, username, google_id=userid) - return UJSONResponse({"token": data["token"]}) - - -@router.post("/users/connect/github", response_model=responses.TokenResponse, include_in_schema=False) -@limit("apps") -async def auth_from_github(request: MystbinRequest) -> Response | UJSONResponse: - """Allows user to authenticate with GitHub OAuth.""" - try: - data = await request.json() - code = data.get("code", None) - except: - return UJSONResponse({"error": "Bad JSON body passed"}, status_code=421) - - if not code: - return UJSONResponse({"error": "Missing code query"}, status_code=400) - - client_id = request.app.config["apps"]["github_application_id"] - client_secret = request.app.config["apps"]["github_application_secret"] - url = yarl.URL(request.app.config["site"]["frontend_site"]).with_path("/github_auth") - - data = { - "client_id": client_id, - "client_secret": client_secret, - "redirect_uri": url, - "grant_type": "authorization_code", - "code": code, - } - headers = { - "Content-Type": "application/x-www-form-urlencoded", - "Accept": "application/json", - } - - async with request.app.state.client.post( - "https://github.com/login/oauth/access_token", data=data, headers=headers - ) as resp: - resp.raise_for_status() - data = await resp.json() - token = data["access_token"] - - async with request.app.state.client.get( - "https://api.github.com/user", headers={"Authorization": f"Bearer {token}"} - ) as resp: - resp.raise_for_status() - data = await resp.json() - userid = data["id"] - username = data["name"] or data["login"] - - async with request.app.state.client.get( - "https://api.github.com/user/emails", - headers={"Authorization": f"Bearer {token}"}, - ) as resp: - resp.raise_for_status() - data = await resp.json() - email = [] - for entry in data: - if "users.noreply.github.com" in entry["email"]: - continue - - email.append(entry["email"]) - - if request.state.user is not None: - token = await request.app.state.db.update_user(request.state.user["id"], github_id=userid, emails=email) - return UJSONResponse({"token": token}) - - elif _id := await request.app.state.db.check_email(email): - token = await request.app.state.db.update_user(_id, github_id=userid, emails=email) - return UJSONResponse({"token": token}) - - else: - data = await request.app.state.db.new_user(email, username, github_id=userid) - return UJSONResponse({"token": data["token"]}) - - -@router.delete("/users/connect/{app}", include_in_schema=False) -@limit("apps") -async def disconnect_app(request: MystbinRequest, app: str): - if app not in ("github", "discord", "google"): - return Response(status_code=404) - - if not request.state.user: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - if not await request.app.state.db.unlink_account(request.state.user["id"], app): - return UJSONResponse({"error": "Account not linked"}, status_code=400) - - return Response(status_code=204) - - -@router.post("/callbacks/sentry", include_in_schema=False) -@limit("sentry") -async def sentry_callback(request: MystbinRequest): - data = await request.json() - - title = data["data"]["issue"]["title"] - author = {"name": data["action"]} - description = ( - f"Issue id: {data['data']['issue']['id']}\n" - f"Times seen: {data['data']['issue']['count']}\n" - f"Errored at: {data['data']['issue']['culprit']}" - ) - timestamp = datetime.datetime.strptime(data["data"]["issue"]["lastSeen"], "%Y-%m-%dT%H:%M:%S.%fZ") - footer = { - "text": "Last seen at:", - "icon_url": "https://cdn.discordapp.com/avatars/698366484975714355/9bad78779883b3bd6dfd4022d997e406.png", - } - - embed = Embed( - title=title, - author=author, - description=description, - timestamp=timestamp, - footer=footer, - ) - - if request.app.state.webhook_url: - await request.app.state.client.post(request.app.state.webhook_url, json={"embeds": [embed.to_dict()]}) - - return Response(status_code=204) diff --git a/mystbin/backend/routers/pastes.py b/mystbin/backend/routers/pastes.py deleted file mode 100644 index c58c1d81..00000000 --- a/mystbin/backend/routers/pastes.py +++ /dev/null @@ -1,531 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -from __future__ import annotations - -import asyncio -import datetime -import json -import pathlib -import re -import uuid -from random import sample -from typing import Coroutine - -import pydantic -from asyncpg import Record -from fastapi import APIRouter, File, Response, UploadFile -from fastapi.encoders import jsonable_encoder -from fastapi.responses import UJSONResponse -from models import errors, payloads, responses - -from mystbin_models import MystbinRequest -from utils.db import _recursive_hook as recursive_hook -from utils.ratelimits import limit - - -try: - import ujson - - _loads = ujson.loads -except ModuleNotFoundError: - import json - - _loads = json.loads - - -_WORDS_LIST = open(pathlib.Path("utils/words.txt")).readlines() -word_list = [word.title() for word in _WORDS_LIST if len(word) > 3] -del _WORDS_LIST - -TOKEN_RE = re.compile(r"[a-zA-Z0-9_-]{23,28}\.[a-zA-Z0-9_-]{6,7}\.[a-zA-Z0-9_-]{27}") - -router = APIRouter() - -__p = pathlib.Path("./config.json") -if not __p.exists(): - __p = pathlib.Path("../../config.json") - -with __p.open() as __f: - __config = json.load(__f) - -del __p, __f # micro-opt, don't keep unneeded variables in-ram -HAS_BUNNYCDN = True -if not __config.get("bunny_cdn"): - HAS_BUNNYCDN = False -elif not __config["bunny_cdn"].get("token"): - HAS_BUNNYCDN = False - - -def generate_paste_id(n: int = 3): - """Generate three random words.""" - word_samples = sample(word_list, n) - return "".join(word_samples).replace("\n", "") - - -def enforce_paste_limit(app, paste: payloads.PasteFile, n=1): - charlim = app.config["paste"]["character_limit"] - if len(paste.content) > charlim: - return UJSONResponse( - { - "error": f"files.{n}.content ({paste.filename}): maximum length per file is {charlim} characters. " - f"You are {len(paste.content)-charlim} characters over the limit" - }, - status_code=400, - ) - - return None - - -def enforce_multipaste_limit(app, pastes: payloads.PastePut): - filelim = app.config["paste"]["character_limit"] - if len(pastes.files) < 1: - return UJSONResponse({"error": "files.length: you have not provided any files"}, status_code=400) - if len(pastes.files) > filelim: - return UJSONResponse( - { - "error": f"files.length: maximum file count is {filelim} files. You are " - f"{len(pastes.files) - filelim} files over the limit" - }, - status_code=400, - ) - - for n, file in enumerate(pastes.files): - if err := enforce_paste_limit(app, file, n): - return err - - return None - - -async def upload_to_gist(request: MystbinRequest, tokens: str) -> dict: - headers = { - "Accept": "application/vnd.github.v3+json", - "Authorization": f"token {request.app.config['apps']['github_bot_token']}", - } - - filename = "output.txt" - data = {"public": True, "files": {filename: {"content": tokens}}} - - async with request.app.state.client.post("https://api.github.com/gists", headers=headers, data=data) as resp: - if 300 > resp.status >= 200: - return await resp.json() - resp.raise_for_status() - raise RuntimeError - - -async def find_discord_tokens(request: MystbinRequest, pastes: payloads.PastePut): - if not request.app.config["apps"].get("github_bot_token", None): - return None - - tokens = [] - - for file in pastes.files: - v = TOKEN_RE.findall(file.content) - if v: - tokens += v - - return tokens or None - - -def respect_dnt(request: MystbinRequest): - if request.headers.get("DNT", None) == "1": - return "DNT" - - if request.app.config["paste"]["log_ip"]: - return request.headers.get("X-Forwarded-For", request.client.host) - - return None - - -desc = f"""Post a paste. - -This endpoint falls under the `postpastes` ratelimit bucket. -The `postpastes` bucket has a default ratelimit of {__config['ratelimits']['postpastes']}, and a ratelimit of {__config['ratelimits']['authed_postpastes']} when signed in -""" - - -@router.put( - "/paste", - tags=["pastes"], - response_model=responses.PastePostResponse, - responses={ - 201: {"model": responses.PastePostResponse}, - 400: {"content": {"application/json": {"example": {"error": "files.length: You have provided too many files"}}}}, - }, - status_code=201, - name="Create paste", - description=desc, -) -@limit("postpastes") -async def put_pastes( - request: MystbinRequest, - payload: payloads.PastePut, -) -> dict[str, str | int | datetime.datetime | None] | UJSONResponse: - author_: Record | None = request.state.user - - if err := enforce_multipaste_limit(request.app, payload): - return err - - notice = None - - if tokens := await find_discord_tokens(request, payload): - data = await upload_to_gist(request, "\n".join(tokens)) - notice = f"Discord tokens have been found and uploaded to {data['html_url']}" - - author: int | None = author_["id"] if author_ else None - - paste = await request.app.state.db.put_paste( - paste_id=generate_paste_id(), - pages=payload.files, - expires=payload.expires, - author=author, - password=payload.password, - origin_ip=respect_dnt(request), - ) - - paste["notice"] = notice - paste = responses.PastePostResponse(**paste) # type: ignore # this is a problem for future me #TODO - paste = recursive_hook(paste.dict()) - return UJSONResponse(paste, status_code=201) - - -@router.post( - "/rich-paste", - tags=["pastes"], - response_model=responses.PastePostResponse, - responses={ - 201: {"model": responses.PastePostResponse}, - 400: {"content": {"application/json": {"example": {"error": "files.length: You have provided too many files"}}}}, - }, - status_code=201, - name="Create Rich Paste", -) -@limit("postpastes") -async def post_rich_paste( - request: MystbinRequest, - data: bytes = File(None, max_length=(__config["paste"]["character_limit"] * __config["paste"]["file_limit"]) + 500), - images: list[UploadFile] = File(None), -) -> UJSONResponse: - - reads = data - try: - payload = payloads.RichPastePost.parse_raw(reads, content_type="application/json") - except pydantic.ValidationError as e: - return UJSONResponse({"detail": e.errors()}, status_code=422) - except: - return UJSONResponse({"error": f"multipart.section.data: Invalid JSON"}) - - paste_id = generate_paste_id() - - if images and HAS_BUNNYCDN: - - async def _partial(target, spool: UploadFile): - data = await spool.read() - await request.app.state.client.put( - target, data=data, headers=headers - ) # TODO figure out how to pass spooled object instead of load into memory - - image_idx = {} - headers = {"Content-Type": "application/octet-stream", "AccessKey": f"{__config['bunny_cdn']['token']}"} - partials: list[Coroutine] = [] - - for image in images: # TODO honour config filesize limit - origin = image.filename.split(".")[-1] - new_name = f"{('%032x' % uuid.uuid4().int)[:8]}-{paste_id}.{origin}" - url = f"https://{__config['bunny_cdn']['hostname']}.b-cdn.net/images/{new_name}" - image_idx[image.filename] = url - target = f"https://storage.bunnycdn.com/{__config['bunny_cdn']['hostname']}/images/{new_name}" - partials.append(_partial(target, image)) - - for n, file in enumerate(payload.files): - if file.attachment is not None: - if file.attachment not in image_idx: - return UJSONResponse({"error": f"files.{n}.attachment: Unkown attachment '{file.attachment}'"}) - - file.__dict__["attachment"] = image_idx[file.attachment] - - await asyncio.wait(partials, return_when=asyncio.ALL_COMPLETED) - - if err := enforce_multipaste_limit(request.app, payload): - return err - - notice = None - - if tokens := await find_discord_tokens(request, payload): - gist = await upload_to_gist(request, "\n".join(tokens)) - notice = f"Discord tokens have been found and uploaded to {gist['html_url']}" - - author: int | None = request.state.user["id"] if request.state.user else None - - paste = await request.app.state.db.put_paste( - paste_id=paste_id, - pages=payload.files, - expires=payload.expires, - author=author, - password=payload.password, - origin_ip=respect_dnt(request), - ) - - paste["notice"] = notice - paste = responses.PastePostResponse(**paste) # type: ignore # this is a problem for future me #TODO - paste = recursive_hook(paste.dict()) - return UJSONResponse(paste, status_code=201) - - -desc = f"""Get a paste by ID. - -This endpoint falls under the `getpaste` ratelimit bucket. -The `getpaste` bucket has a default ratelimit of {__config['ratelimits']['getpaste']}, and a ratelimit of {__config['ratelimits']['authed_getpaste']} when signed in -""" - - -@router.get( - "/paste/{paste_id}", - tags=["pastes"], - response_model=responses.PasteGetResponse, - responses={ - 200: {"model": responses.PasteGetResponse}, - 401: {"model": errors.Unauthorized}, - 404: {"model": errors.NotFound}, - }, - name="Get paste", - description=desc, -) -@limit("getpaste") -async def get_paste(request: MystbinRequest, paste_id: str, password: str | None = None) -> UJSONResponse: - paste = await request.app.state.db.get_paste(paste_id, password) - if paste is None: - return UJSONResponse({"error": "Not Found"}, status_code=404) - - if paste["has_password"] and not paste["password_ok"]: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - resp = responses.PasteGetResponse(**paste) - return UJSONResponse(recursive_hook(resp.dict())) - - -desc = f"""Get all pastes for the user you are signed in as via the Authorization header. -* Requires authentication. - -This endpoint falls under the `getpaste` ratelimit bucket. -The `getpaste` bucket has a default ratelimit of {__config['ratelimits']['getpaste']}, and a ratelimit of {__config['ratelimits']['authed_getpaste']} when signed in -""" - - -@router.get( - "/pastes/@me", - tags=["pastes"], - response_model=list[responses.PasteGetAllResponse], - responses={ - 200: {"model": list[responses.PasteGetAllResponse] | None}, - 400: {"content": {"application/json": {"example": {"error": "You have provided an invalid query argument"}}}}, - 401: {"model": errors.Unauthorized}, - }, - name="Get user pastes", - description=desc, -) -@limit("getpaste") -async def get_all_pastes( - request: MystbinRequest, limit: int = 50, page: int = 1 -) -> UJSONResponse | dict[str, list[dict[str, str]]]: - user = request.state.user - if not user: - return UJSONResponse({"error": "Unathorized", "notice": "You must be signed in to use this route"}, status_code=401) - - if limit < 1 or page < 1: - return UJSONResponse({"error": "limit and page must be greater than 1"}, status_code=400) - - pastes = await request.app.state.db.get_all_user_pastes(user["id"], limit, page) - pastes = [dict(entry) for entry in pastes] - - return UJSONResponse({"pastes": jsonable_encoder(pastes)}) - - -desc = f"""Edit a paste. -You must be the author of the paste (IE, the paste must be created under your account). - -* Requires authentication - -This endpoint falls under the `postpastes` ratelimit bucket. -The `postpastes` bucket has a default ratelimit of {__config['ratelimits']['postpastes']}, and a ratelimit of {__config['ratelimits']['authed_postpastes']} when signed in -""" - - -@router.patch( - "/paste/{paste_id}", - tags=["pastes"], - responses={ - 204: {}, - 401: {"model": errors.Unauthorized}, - 403: {"model": errors.Forbidden}, - 404: {"model": errors.NotFound}, - }, - name="Edit paste", - description=desc, -) -@limit("postpastes") -async def edit_paste( - request: MystbinRequest, - paste_id: str, - payload: payloads.PastePatch, -) -> UJSONResponse | Response: - author = request.state.user - if not author: - return UJSONResponse({"error": "Unathorized", "notice": "You must be signed in to use this route"}, status_code=401) - - paste = await request.app.state.db.edit_paste( - paste_id, - author=author["id"], - new_expires=payload.new_expires, - new_password=payload.new_password, - files=payload.new_files, - ) - if paste == 404: - return UJSONResponse( - {"error": "Paste was not found or you are not it's author."}, - status_code=404, - ) - - return Response(status_code=204) - - -desc = f"""Deletes a paste. -You must be the author of the paste (IE, the paste must be created under your account). - -* Requires authentication. - -This endpoint falls under the `deletepaste` ratelimit bucket. -The `deletepaste` bucket has a default ratelimit of {__config['ratelimits']['deletepaste']}, and a ratelimit of {__config['ratelimits']['authed_deletepaste']} when signed in -""" - - -@router.delete( - "/paste/{paste_id}", - tags=["pastes"], - responses={ - 200: {"content": {"application/json": {"example": {"deleted": "SomePasteID"}}}}, - 401: {"model": errors.Unauthorized}, - }, - status_code=200, - name="Delete paste", - description=desc, -) -@limit("deletepaste") -async def delete_paste(request: MystbinRequest, paste_id: str) -> UJSONResponse | dict[str, str]: - user = request.state.user - if not user: - return UJSONResponse({"error": "Unathorized", "notice": "You must be signed in to use this route"}, status_code=401) - - if not user["admin"]: - is_owner: bool = await request.app.state.db.ensure_author(paste_id, user["id"]) - if not is_owner: - return UJSONResponse({"error": "Unauthorized", "notice": f"You do not own paste '{paste_id}'"}, status_code=401) - - deleted: Record = await request.app.state.db.delete_paste(paste_id, user["id"], admin=False) - - return UJSONResponse({"deleted": deleted["id"]}, status_code=200) - - -desc = f"""Deletes pastes. -You must be the author of the pastes (IE, the pastes must be created under your account). - -* Requires authentication. - -This endpoint falls under the `deletepaste` ratelimit bucket. -The `deletepaste` bucket has a default ratelimit of {__config['ratelimits']['deletepaste']}, and a ratelimit of {__config['ratelimits']['authed_deletepaste']} when signed in -""" - - -@router.delete( - "/paste", - tags=["pastes"], - responses={ - 200: { - "content": { - "application/json": { - "example": { - "succeeded": ["SomePasteID"], - "failed": ["OtherPasteID"], - } - } - } - }, - 401: {"model": errors.Unauthorized}, - 403: {"model": errors.Forbidden}, - }, - status_code=200, - name="Bulk delete pastes", - description=desc, -) -@limit("deletepaste") -async def delete_pastes( - request: MystbinRequest, - payload: payloads.PasteDelete, -) -> UJSONResponse | dict[str, list[str]]: - # We will filter out the pastes that are authorized and unauthorized, and return a clear response - response = {"succeeded": [], "failed": []} - - author: Record = request.state.user - if not author: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - for paste in payload.pastes: - if await request.app.state.db.ensure_author(paste, author["id"]): - response["succeeded"].append(paste) - else: - response["failed"].append(paste) - - for paste in response["succeeded"]: - await request.app.state.db.delete_paste(paste, author["id"], admin=False) - - return UJSONResponse(response, status_code=200) - - -desc = f""" -A compatibility endpoint to maintain hastbin compatibility. Depreciated in favour of /paste -This endpoint does not allow for syntax highlighting, multi-file, password protection, expiry, etc. Use the /paste endpoint for these features - -This endpoint falls under the `postpastes` ratelimit bucket. -The `postpastes` bucket has a default ratelimit of {__config['ratelimits']['postpastes']}, and a ratelimit of {__config['ratelimits']['authed_postpastes']} when signed in -""" - - -@router.post( - "/documents", - tags=["pastes"], - deprecated=True, - responses={ - 200: {"application/json": {"example": {"key": "string"}, "description": "A key containing the slug for your paste"}}, - 400: {"application/json": {"example": {"error": "You have provided an invalid paste body"}}}, - }, - name="Hastebin create paste", - description=desc, -) -@limit("postpastes") -async def compat_create_paste(request: MystbinRequest): - content = await request.body() - limit = request.app.config["paste"]["character_limit"] - if len(content) > limit: - return UJSONResponse({"error": f"body: file size exceeds character limit of {limit}"}, status_code=400) - - paste: Record = await request.app.state.db.put_paste( - paste_id=generate_paste_id(), - pages=[payloads.PasteFile(filename="file.txt", content=content.decode("utf8"))], - origin_ip=respect_dnt(request), - ) - return UJSONResponse({"key": paste["id"]}) diff --git a/mystbin/backend/routers/user.py b/mystbin/backend/routers/user.py deleted file mode 100644 index fa8b98f5..00000000 --- a/mystbin/backend/routers/user.py +++ /dev/null @@ -1,152 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -from fastapi import APIRouter -from fastapi.responses import Response, UJSONResponse -from models import errors, payloads, responses - -from mystbin_models import MystbinRequest -from utils.ratelimits import limit - - -router = APIRouter() - - -@router.get( - "/users/@me", - tags=["users"], - response_model=responses.User, - responses={ - 200: {"model": responses.User}, - 401: {"model": errors.Unauthorized}, - 403: {"model": errors.Forbidden}, - }, - name="Get current user", -) -@limit("self") -async def get_self( - request: MystbinRequest, -) -> UJSONResponse | dict[str, str | int | bool]: - """Gets the User object of the currently logged in user. - * Requires authentication. - """ - - user = request.state.user - if not user: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - data = responses.User(**user).dict() - return UJSONResponse(data) - - -@router.post( - "/users/regenerate", - tags=["users"], - response_model=responses.TokenResponse, - responses={ - 200: {"model": responses.TokenResponse}, - 401: {"model": errors.Unauthorized}, - 403: {"model": errors.Forbidden}, - }, - name="Regenerate your token", -) -@limit("tokengen") -async def regen_token(request: MystbinRequest) -> UJSONResponse | dict[str, str]: - """Regens the user's token. - * Requires authentication. - """ - if not request.state.user: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - token: str | None = await request.app.state.db.regen_token(userid=request.state.user["id"]) - if not token: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - return UJSONResponse({"token": token}) - - -@router.put( - "/users/bookmarks", - tags=["users"], - responses={ - 201: {"description": "Created bookmark"}, - 400: {"model": errors.BadRequest}, - 401: {"model": errors.Unauthorized}, - 403: {"model": errors.Forbidden}, - }, -) -@limit("bookmarks") -async def create_bookmark(request: MystbinRequest, bookmark: payloads.BookmarkPutDelete) -> Response: - """Creates a bookmark on the authorized user's account - * Requires authentication. - """ - if not request.state.user: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - try: - await request.app.state.db.create_bookmark(request.state.user["id"], bookmark.paste_id) - return Response(status_code=201) - except ValueError as e: - return UJSONResponse({"error": e.args[0]}, status_code=400) - - -@router.delete( - "/users/bookmarks", - tags=["users"], - responses={ - 204: {"description": "Deleted bookmark"}, - 400: {"model": errors.BadRequest}, - 401: {"model": errors.Unauthorized}, - 403: {"model": errors.Forbidden}, - }, -) -@limit("bookmarks") -async def delete_bookmark(request: MystbinRequest, bookmark: payloads.BookmarkPutDelete) -> Response: - """Deletes a bookmark on the authorized user's account - * Requires authentication. - """ - if not request.state.user: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - if not await request.app.state.db.delete_bookmark(request.state.user["id"], bookmark.paste_id): - return UJSONResponse({"error": "Bookmark does not exist"}, status_code=400) - else: - return Response(status_code=204) - - -@router.get( - "/users/bookmarks", - tags=["users"], - response_model=responses.Bookmarks, - responses={ - 200: {"model": responses.Bookmarks}, - 400: {"model": errors.BadRequest}, - 401: {"model": errors.Unauthorized}, - 403: {"model": errors.Forbidden}, - }, -) -@limit("bookmarks") -async def get_bookmarks(request: MystbinRequest): - """Fetches all of the authorized users bookmarks - * Requires authentication - """ - if not request.state.user: - return UJSONResponse({"error": "Unauthorized"}, status_code=401) - - data = await request.app.state.db.get_bookmarks(request.state.user["id"]) - return UJSONResponse({"bookmarks": data}) diff --git a/mystbin/backend/utils/cli.py b/mystbin/backend/utils/cli.py deleted file mode 100644 index fc6c80ee..00000000 --- a/mystbin/backend/utils/cli.py +++ /dev/null @@ -1,245 +0,0 @@ -from __future__ import annotations - -import argparse -import asyncio -import shlex -from typing import TYPE_CHECKING - -import aioconsole -import tabulate - - -if TYPE_CHECKING: - from utils.db import Database - - -def find_disallowed_args(ns: argparse.Namespace, args: list[str], allowed_values: tuple = (None, False)) -> list[str]: - return [arg for arg in args if getattr(ns, arg) not in allowed_values] - - -class Interrupt(Exception): - pass - - -class CLIHandler: - def __init__(self, db: Database) -> None: - self.db: Database = db - self.parser = argparse.ArgumentParser( - prog="Mystbin", - description="the Mystbin backend command-line tool", - exit_on_error=False, - usage="", - add_help=False, - ) - self.parser.add_argument("--version", "-v", action="version", version="%(prog)s V3") - - self.subparsers = self.parser.add_subparsers(dest="command") - self.subparsers.add_parser("help", help="send this menu") - - self.subparser_paste = self.subparsers.add_parser( - "paste", help="subcommand to manage pastes. use 'paste -h' to see more info" - ) - self.subparser_paste.add_argument("--delete", "-d", help="delete the paste", metavar="PASTE_ID") - self.subparser_paste.add_argument( - "--set-password", "-sp", help="set the password for this paste", metavar=("PASTE_ID", "PASSWORD"), nargs=2 - ) - self.subparser_paste.add_argument( - "--remove-password", "-rp", help="remove the password for this paste", metavar="PASTE_ID" - ) - self.subparser_paste.add_argument( - "--list", - "-l", - help="list all pastes in the order they were created (most recent first). 50 per page", - metavar="PAGE", - ) - - self.subparser_admin = self.subparsers.add_parser( - "admin", help="subcommand to manage administrator accounts. use 'admin -h' to see more info" - ) - self.subparser_admin.add_argument("--add", "-a", help="give the target user admin privileges", metavar="USERID") - self.subparser_admin.add_argument( - "--remove", "-r", help="remove admin privileges from the target user", metavar="USERID" - ) - self.subparser_admin.add_argument("--list", "-l", help="list the users with admin privileges", action="store_true") - - self.subparser_users = self.subparsers.add_parser( - "users", help="subcommand to manage user accounts. use 'users -h' to see more info" - ) - self.subparser_users.add_argument("--list", "-l", help="list all users. 50 per page", metavar="PAGE") - - async def parse_cli(self) -> None: - await asyncio.sleep(1) # wait until app startup and until the prints are done - print("Entering the command line tool. Type 'help' for more information") - try: - while True: - data = await aioconsole.ainput(prompt="> ") - await self.parse_once(data) - print("\n") - except KeyboardInterrupt: - return - - async def parse_once(self, inp: str) -> None: - args = shlex.split(inp) - try: - ns: argparse.Namespace = self.parser.parse_args(args) - except SystemExit: - return - except argparse.ArgumentError as e: - print(e.message) - else: - print(ns) - if not ns.command: - return - - cb = getattr(self, f"command_{ns.command}", None) - if cb: - try: - await cb(ns) - except Interrupt: - pass - - # subcommands - - async def command_help(self, namespace: argparse.Namespace) -> None: - self.parser.print_help() - - async def command_paste(self, namespace: argparse.Namespace) -> None: - if namespace.list: - try: - page = max(int(namespace.list) - 1, 0) - except: - print(f"Paste: Expected a page number, got '{namespace.list}'") - return - - paste_info = await self.db.get_all_pastes(page, 50) - if not paste_info: - print(f"Paste: Page {page} not found") - return - - tabled = tabulate.tabulate( - [list(x.values()) for x in paste_info], - headers=["id", "author id", "created at", "views", "expires", "origin ip", "has password"], - tablefmt="psql", - ) - await aioconsole.aprint(tabled) - - print(f"Paste: Showing {len(paste_info)} pastes (page {page+1})") - - elif namespace.delete: - if bad_args := find_disallowed_args(namespace, ["remove_password", "list", "set_password"]): - print( - f"Paste: The following args cannot be used with the `delete` argument: {','.join(bad_args).replace('_', '-')}" - ) - return - - resp = await self.db.delete_paste(namespace.delete, admin=True) - if resp: - print(f"Deleted paste '{namespace.delete}'") - else: - print("Failed to delete paste (paste not found)") - - elif namespace.set_password: - if bad_args := find_disallowed_args(namespace, ["remove_password", "list", "delete"]): - print(f"Paste: The following args cannot be used with the `set-password` argument: {','.join(bad_args)}") - return - - pid = namespace.set_password[0] - password = namespace.set_password[1] - - resp = await self.db.set_paste_password(pid, password) - if resp: - print(f"Updated password for paste '{pid}'") - else: - print(f"Paste '{pid}' not found") - - elif namespace.remove_password: - if bad_args := find_disallowed_args(namespace, ["set_password", "list", "delete"]): - print(f"Paste: The following args cannot be used with the `remove-password` argument: {','.join(bad_args)}") - return - - resp = await self.db.set_paste_password(namespace.remove_password, None) - if resp: - print(f"Removed password for paste '{namespace.remove_password}'") - else: - print(f"Paste '{namespace.remove_password}' not found") - - else: - print("Paste: One argument must be passed") - self.subparser_paste.print_help() - - async def command_admin(self, namespace: argparse.Namespace) -> None: - def _try_userid(uid: str) -> int: - try: - return int(uid) - except: - print(f"Admin: Expected a user id, got '{uid}'") - raise Interrupt - - if namespace.add: - if bad_args := find_disallowed_args(namespace, ["remove", "list"]): - print(f"Admin: The following args cannot be used with the `add` argument: {','.join(bad_args)}") - return - - if await self.db.toggle_admin(_try_userid(namespace.add), True): - print(f"{namespace.add} is now an admin") - else: - print("Admin: User not found") - - elif namespace.remove: - if bad_args := find_disallowed_args(namespace, ["add", "list"]): - print(f"Admin: The following args cannot be used with the `remove` argument: {','.join(bad_args)}") - return - - if await self.db.toggle_admin(_try_userid(namespace.add), False): - print(f"{namespace.add} is now not an admin") - else: - print("Admin: User not found") - - elif namespace.list: - users = await self.db.list_admin() - resp = tabulate.tabulate( - [list(x.values()) for x in users], - headers=["User ID", "Username", "Discord ID", "Github ID", "Google ID"], - tablefmt="psql", - ) - await aioconsole.aprint(resp) - - async def command_users(self, namespace: argparse.Namespace) -> None: - if namespace.list: - try: - page = int(namespace.list) - except: - print(f"Users: expected an int, got {namespace.list!r}") - return - - users: dict = (await self.db.get_admin_userlist(page))["users"] # type: ignore - if not users: - print("Users: No users found") - return - - for user in users: - user["authorizations"] = ", ".join(user["authorizations"]) - - fmt = tabulate.tabulate([list(x.values()) for x in users], headers=list(users[0].keys()), tablefmt="psql") - await aioconsole.aprint(fmt) - - -if __name__ == "__main__": - import asyncio - import os - import sys - - sys.path.append(os.path.dirname(os.path.dirname(__file__))) - os.chdir(os.path.dirname(os.path.dirname(__file__))) - - async def main(): - from app import MystbinApp - - app = MystbinApp() - c = CLIHandler(app.state.db) - await c.parse_cli() - - try: - asyncio.run(main()) - except KeyboardInterrupt: - pass diff --git a/mystbin/backend/utils/db.py b/mystbin/backend/utils/db.py deleted file mode 100644 index ab1ad1b6..00000000 --- a/mystbin/backend/utils/db.py +++ /dev/null @@ -1,979 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -from __future__ import annotations - -import asyncio -import datetime -import difflib -import functools -import os -import pathlib -from typing import Any, Literal, cast - -import asyncpg -from asyncpg import Record -from fastapi import Request, Response -from models import payloads - -from . import tokens - - -EPOCH = 1587304800000 # 2020-04-20T00:00:00.0 * 1000 (Milliseconds) - - -def _recursive_hook(d: dict): - for a, b in d.items(): - if isinstance(b, asyncpg.Record): - b = dict(b) - - if isinstance(b, dict): - d[a] = _recursive_hook(b) - elif isinstance(b, datetime.datetime): - d[a] = b.isoformat() - - return d - - -def wrapped_hook_callback(func): - @functools.wraps(func) - async def wraps(*args, **kwargs): - resp = await func(*args, **kwargs) - if isinstance(resp, asyncpg.Record): - resp = dict(resp) - - if isinstance(resp, dict): - return _recursive_hook(resp) - elif isinstance(resp, list): - r = [] - for i in resp: - if isinstance(i, dict): - r.append(_recursive_hook(i)) - else: - r.append(i) - resp = r - - return resp - - return wraps - - -class Database: - """Small Database style object for MystBin usage. - This will be passed across the backend for database usage. - """ - - timeout = 30 - - def __init__(self, config: dict[str, dict[str, str | int]]): - self._pool: asyncpg.Pool | None = None - self._config = config["database"] - if "ISDOCKER" in os.environ: - self._db_schema = pathlib.Path("/etc/schema.sql") - else: - self._db_schema = pathlib.Path("../database/schema.sql") - - self.ban_cache = None - - @property - def pool(self) -> asyncpg.Pool: - """Property for easier access to attr.""" - assert self._pool is not None - - return self._pool - - async def __ainit__(self): - await asyncio.sleep(5) - self._pool = await asyncpg.create_pool( - self._config["dsn"], max_inactive_connection_lifetime=0, max_size=3, min_size=0 - ) - # with open(self._db_schema) as schema: - # await self._pool.execute(schema.read()) - return self - - async def _do_query(self, query: str, *args, conn: asyncpg.Connection | None = None) -> list[Any]: - if self._pool is None: - await self.__ainit__() - - _conn = conn or await cast(asyncpg.Pool, self._pool).acquire() - try: - response = await _conn.fetch(query, *args, timeout=self.timeout) - except asyncio.TimeoutError: - return [] - else: - return response - finally: - if not conn: - await self.pool.release(_conn) - - @wrapped_hook_callback - async def get_all_pastes(self, page: int, count: int, reverse=False) -> list[dict[str, Any]]: - """ - Gets the most recent pastes (20) of them, or oldest if reverse is True - - Parameters - ----------- - page: :class:`int` - The offset of pastes - count: :class:`int` - The amount of pastes to return - reverse: :class:`bool` - whether to search for oldest pastes first - """ - query = f""" - SELECT id, author_id, created_at, views, expires, origin_ip, (password is not null) as has_password - FROM pastes - ORDER BY created_at {'ASC' if reverse else 'DESC'} - LIMIT $1 - OFFSET $2 - """ - results = await self._do_query(query, count, page * count) - if not results: - return [] - return [dict(x) for x in results] - - # for anyone who wonders why this doesnt have a wrapped hook on it, it's because the endpoints for this particular - # db call have to validate the data themselves, and then manually call the hook, so theres no point repeating the - # process twice - async def get_paste(self, paste_id: str, password: str | None = None) -> dict[str, Any] | None: - """Get the specified paste. - Parameters - ------------ - paste_id: :class:`str` - The paste ID we are attempting to get. - password: Optional[:class:`str`] - The passed password we'll attempt to use to decrypt the paste. - - Returns - --------- - Optional[:class:`asyncpg.Record`] - The paste that was retrieved. - """ - async with self.pool.acquire() as conn: - query = """ - UPDATE pastes SET views = views + 1 WHERE id = $1 - RETURNING *, - CASE WHEN password IS NOT NULL THEN true - ELSE false END AS has_password, - CASE WHEN password = CRYPT($2, password) THEN true - ELSE false END AS password_ok - """ - - resp = await self._do_query(query, paste_id, password or "", conn=conn) - - if resp: - query = """ - SELECT * FROM files WHERE parent_id = $1 - """ - contents: list[asyncpg.Record] = await self._do_query(query, paste_id, conn=conn) - resp = dict(resp[0]) - resp["files"] = [{a: b and str(b) for a, b in x.items()} for x in contents] - return resp - else: - return None - - @wrapped_hook_callback - async def get_paste_compat(self, paste_id: str) -> dict[str, str] | None: - if not self._pool: - await self.__ainit__() - - async with self.pool.acquire() as conn: - query = """ - UPDATE pastes SET views = views + 1 WHERE id = $1 - RETURNING *, - CASE WHEN password IS NOT NULL THEN true - ELSE false END AS has_password - """ - - resp = await self._do_query(query, paste_id, conn=conn) - - if resp: - query = """ - SELECT content, syntax FROM files WHERE parent_id = $1 LIMIT 1 - """ - contents: list[asyncpg.Record] = await self._do_query(query, paste_id, conn=conn) - ret = { - "key": paste_id, - "data": contents[0]["content"], - "has_password": resp[0]["has_password"], - } - - return ret - else: - return None - - @wrapped_hook_callback - async def put_paste( - self, - *, - paste_id: str, - origin_ip: str | None, - pages: list[payloads.RichPasteFile] | list[payloads.PasteFile], - expires: datetime.datetime | None = None, - author: int | None = None, - password: str | None = None, - ) -> dict[str, str | int | None | list[dict[str, str | int]]]: - """Puts the specified paste. - Parameters - ----------- - paste_id: :class:`str: - The paste ID we are storing. - origin_ip: :class:`str` - The ip the paste came from - pages: List[Dict[:class:`str`, :class:`str`]] - The paste content. A list of dictionaries containing `content`, `filename, and `syntax` (optional) keys. - expires: Optional[:class:`datetime.datetime`] - The expiry time of this paste, if present. - author: Optional[:class:`int`] - The ID of the author, if present. - password: Optioanl[:class:`str`] - The password used to encrypt the paste, if present. - - Returns - --------- - Dict[str, Optional[Union[str, int, datetime.datetime]]] - """ - if not self._pool: - await self.__ainit__() - - async with self.pool.acquire() as conn: - query = """ - INSERT INTO pastes (id, author_id, expires, password, origin_ip) - VALUES ($1, $2, $3, (SELECT crypt($4, gen_salt('bf')) WHERE $4 is not null), $5) - RETURNING id, author_id, created_at, expires, origin_ip - """ - - resp: list[asyncpg.Record] = await self._do_query( - query, paste_id, author, expires, password, origin_ip, conn=conn - ) - - resp = resp[0] - to_insert = [] - for page in pages: - to_insert.append( - ( - resp["id"], # type: ignore # TODO?? - page.content, - page.filename, - page.content.count("\n") + 1, # add an extra for line 1 - getattr(page, "attachment", None), - ) - ) - - files_query = """ - INSERT INTO files (parent_id, content, filename, loc, attachment) - VALUES ($1, $2, $3, $4, $5) - RETURNING index, filename, loc, charcount, content, attachment - """ - inserted = [] - async with conn.transaction(): - for insert in to_insert: - row = await conn.fetchrow(files_query, *insert) - inserted.append(row) - - formatted_resp = dict(resp) - del formatted_resp["origin_ip"] - formatted_resp["files"] = [dict(file) for file in inserted] - - return formatted_resp - - @wrapped_hook_callback - async def edit_paste( - self, - paste_id: str, - *, - author: int, - new_expires: datetime.datetime | None = None, - new_password: str | None = None, - files: list[payloads.PasteFile] | None = None, - ) -> Literal[404] | None: - """Puts the specified paste. - Parameters - ----------- - paste_id: :class:`str: - The paste ID we are storing. - pages: List[Dict[:class:`str`, :class:`str`] - The paste content. - author: Optional[:class:`int`] - The ID of the author, if present. - password: Optioanl[:class:`str`] - The password used to encrypt the paste, if present. - - Returns - --------- - :class:`asyncpg.Record` - The paste record that was created. - """ - if not self._pool: - await self.__ainit__() - - async with self.pool.acquire() as conn: - conn: asyncpg.Connection - - query = """ - UPDATE pastes - SET last_edited = NOW() AT TIME ZONE 'UTC', - password = (SELECT crypt($3, gen_salt('bf')) WHERE $3 is not null), - expires = $4 - WHERE id = $1 AND author_id = $2 - RETURNING * - """ - - resp = await self._do_query(query, paste_id, author, new_password, new_expires, conn=conn) - if not resp: - return 404 - - if files: - qs = [] - for file in files: - qs.append( - ( - file.content, - file.content.count("\n"), - len(file.content), - paste_id, - ) - ) - - query = """ - UPDATE files - SET content = $1, loc = $2, charcount = $3 - WHERE parent_id = $4 - RETURNING content, loc, charcount - """ - await conn.executemany(query, qs) - - @wrapped_hook_callback - async def set_paste_password(self, paste_id: str, password: str | None) -> asyncpg.Record | None: - """Sets a password for the specified paste. - This is for use by the cli module. - - Parameters - ------------ - paste_id: :class:`str` - The id of the paste to update the password for - password: :class:`str` - The password to use - - Returns - --------- - Optional[:class:`asyncpg.Record`] - The updated paste - """ - query = """ - UPDATE pastes - SET password = $2 - WHERE id = $1 - RETURNING * - """ - resp = await self._do_query(query, paste_id, password) - if resp: - return resp[0] - - return None - - @wrapped_hook_callback - async def get_all_user_pastes(self, author_id: int | None, limit: int, page: int) -> list[asyncpg.Record]: - """Get all pastes for an author and/or with a limit. - Parameters - ------------ - author_id: Optional[:class:`int`] - The paste author id to query against. - limit: Optional[:class:`int`] - The limit to the amount of pastes to return. Defaults to ALL. - - Returns - --------- - Optional[List[:class:`asyncpg.Record`]] - The potential list of pastes. - """ - query = """ - SELECT id, author_id, created_at, views, expires, - CASE WHEN password IS NOT NULL THEN true ELSE false END AS has_password - FROM pastes - WHERE author_id = $1 - ORDER BY created_at DESC - LIMIT $2 - OFFSET $3 - """ - - assert page >= 1 and limit >= 1, ValueError("limit and page cannot be smaller than 1") - response = await self._do_query(query, author_id, limit, page - 1) - if not response: - return [] - - return response - - @wrapped_hook_callback - async def get_paste_count(self) -> asyncpg.Record: - """Get total paste count from Database. - - Returns - --------- - :class:`asyncpg.Record` - The record containing total paste count. - """ - query = """SELECT count(*) FROM pastes""" - - return (await self._do_query(query))[0]["count"] - - @wrapped_hook_callback - async def delete_paste(self, paste_id: str, author_id: int | None = None, *, admin: bool = False) -> str | None: - """Delete a paste, with an admin override. - - Parameters - ------------ - paste_id: :class:`str` - The paste ID to delete. - author_id: Optional[:class:`int`] - The author ID the paste belongs to. - admin: :class:`bool` - Admin override. Defaults to False. - - Returns - --------- - :class:`asyncpg.Record` - The paste that was deleted. - """ - query = """ - DELETE FROM pastes CASCADE - WHERE (id = $1 AND author_id = $2) - OR (id = $1 AND $3) - RETURNING id; - """ - - response = await self._do_query(query, paste_id, author_id, admin) - - return response[0] if response else None - - @wrapped_hook_callback - async def get_user(self, *, user_id: int | None = None, token: str | None = None) -> asyncpg.Record | int | None: - """Returns a User on successful query. - - Parameters - ------------ - user_id: :class:`int` - The user id to query against. - token: :class:`str` - The authorization token to query against once decoded. - - Returns - --------- - Optional[Union[:class:`asyncpg.Record`, :class:`int`]] - Will return a record of the User on success, or a 40x status code depending on failure. - """ - - if not user_id and not token: - raise ValueError("Expected either id or token") - - if user_id and token: - raise ValueError("Expected id or token, not both") - - if token: - user_id = tokens.get_user_id(token) - if not user_id: - return 400 - - query = """ - SELECT * FROM users WHERE id = $1 - """ - - data = await self._do_query(query, user_id) - if not data: - return None - - data = data[0] - if token and data["token"] != token: - return 401 - - return data - - @wrapped_hook_callback - async def new_user( - self, - emails: list[str], - username: str, - discord_id: str | None = None, - github_id: str | None = None, - google_id: str | None = None, - ) -> asyncpg.Record: - """Creates a new User record. - - Parameters - ------------ - emails: List[:class:`str`] - The email addresses the user has registered with. - discord_id: Optional[:class:`str`] - The Discord ID of the registering user. - github_id: Optional[:class:`str`] - The GitHub ID of the registering user. - google_id: Optional[:class:`str`] - The Google ID of the registering user. - - Returns - --------- - :class:`asyncpg.Record` - The record created for the registering User. - """ - - userid = int((datetime.datetime.utcnow().timestamp() * 1000) - EPOCH) - token = tokens.generate(userid) - - query = """ - INSERT INTO users - VALUES ($1, $2, $3, $4, $5, $6, false, DEFAULT, false, $7) - RETURNING *; - """ - - data = await self._do_query( - query, - userid, - token, - emails, - discord_id and str(discord_id), - github_id and str(github_id), - google_id and str(google_id) or None, - username, - ) - return data[0] - - async def update_user( - self, - user_id: int, - emails: list[str] | None = None, - discord_id: str | None = None, - github_id: str | None = None, - google_id: str | None = None, - ) -> str | None: - """Updates an existing user account. - - Parameters - ------------ - user_id: :class:`int` - The ID of the user to edit. - emails: Optional[List[:class:`str`]] - The email to add to the User's list of emails. - discord_id: Optional[:class:`str`] - The user's Discord ID. - github_id: Optional[:class:`str`] - The user's GitHub ID. - google_id: Optional[:class:`str`] - The user's Google ID. - - Returns - --------- - Optional[:class:`str`] - Returns the updated user's token. - """ - - query = """ - UPDATE users SET - discord_id = COALESCE($1, discord_id), - github_id = COALESCE($2, github_id), - google_id = COALESCE($3, google_id) - WHERE id = $4 - RETURNING token, emails; - """ - - data = await self._do_query( - query, - discord_id and str(discord_id), - github_id and str(github_id), - google_id and str(google_id), - user_id, - ) - if not data: - return None - - token, _emails = data[0] - - if not emails: - return token - - new_emails = set(_emails) - new_emails.update(emails) - new_emails = list(new_emails) - if new_emails == emails: - return token - - query = """ - UPDATE users SET emails = $1 WHERE id = $2 - """ - - await self._do_query(query, new_emails, user_id) - return token - - async def unlink_account(self, user_id: int, account: str) -> bool: - """Unlinks an account. Doesnt do sanity checks.""" - if account == "google": - query = "UPDATE users SET google_id = null WHERE id = $1 AND google_id is not null RETURNING id" - elif account == "github": - query = "UPDATE users SET github_id = null WHERE id = $1 AND users.github_id is not null RETURNING id" - elif account == "discord": - query = "UPDATE users SET discord_id = null WHERE id = $1 AND discord_id is not null RETURNING id" - else: - raise ValueError(f"Expected account to be one of google, github, or discord. Not '{account}'") - - return bool(await self._do_query(query, user_id)) - - async def check_email(self, emails: str | list[str]) -> int | None: - """Quick check to query an email.""" - query = """ - SELECT id FROM users WHERE $1 && emails - """ - if isinstance(emails, str): - emails = [emails] - - data = await self._do_query(query, emails) - if data: - return data[0]["id"] - - async def toggle_admin(self, userid: int, admin: bool) -> bool: - """Quick query to toggle admin privileges.""" - query = """ - UPDATE users SET admin = $1 WHERE id = $2 RETURNING id - """ - - return bool(await self._do_query(query, admin, userid)) - - async def list_admin(self) -> list[asyncpg.Record]: - query = """ - SELECT id, username, discord_id, github_id, google_id FROM users WHERE admin = true - """ - resp = await self._do_query(query) - return resp or [] - - @wrapped_hook_callback - async def ban_user(self, userid: int | None = None, ip: str | None = None, reason: str | None = None) -> bool: - """ - Bans a user. - Returns False if the user/ip doesnt exist, or is already banned, otherwise returns True - """ - try: - query = """ - INSERT INTO bans VALUES ($1, $2, $3) - """ - assert userid or ip - await self._do_query(query, ip, userid, reason) - except asyncpg.UniqueViolationError: - return False - else: - return True - - async def unban_user(self, userid: int | None = None, ip: str | None = None) -> bool: - """ - Unbans a user. - Returns True if the user/ip was successfully unbanned, otherwise False - """ - assert userid or ip - - if userid and ip: - query = "DELETE FROM bans WHERE userid = $1 OR ip = $2 RETURNING *;" - return len(await self._do_query(query, userid, ip)) > 0 - elif userid: - query = "DELETE FROM bans WHERE userid = $1 RETURNING *;" - return len(await self._do_query(query, userid)) > 0 - else: - query = "DELETE FROM bans WHERE ip = $1 RETURNING *;" - return len(await self._do_query(query, ip)) > 0 - - @wrapped_hook_callback - async def get_bans(self, page: int = 1): - """ - Lists the bans. Pages by 40 - """ - query = """ - SELECT * FROM bans LIMIT 40 OFFSET $1 * 20 - """ - data = await self._do_query(query, page - 1) - return data - - async def switch_theme(self, userid: int, theme: str) -> None: - """Quick query to set theme choices.""" - query = """ - UPDATE users SET theme = $1 WHERE id = $2 - """ - - await self._do_query(query, theme, userid) - - @wrapped_hook_callback - async def toggle_subscription(self, userid: int, state: bool): - query = "UPDATE users SET subscriber = $1 WHERE id = $2 AND subscriber != $1 RETURNING id;" - val = await self._do_query(query, state, userid) - return len(val) > 0 - - @wrapped_hook_callback - async def regen_token(self, *, userid: int, token: str | None = None) -> str | None: - """Generates a new token for the given user id or token. - Returns the new token, or None if the user does not exist. - """ - if not self._pool: - await self.__ainit__() - - if not userid and not token: - raise ValueError("Expected either userid or token argument") - - async with self.pool.acquire() as conn: - if token: - query = """ - SELECT id from users WHERE token = $1 - """ - data = await self._do_query(query, token, conn=conn) - if not data: - return None - - userid = data[0]["id"] - - new_token = tokens.generate(userid) - query = """ - UPDATE users SET token = $1 WHERE id = $2 RETURNING id - """ - data = await self._do_query(query, new_token, userid, conn=conn) - if not data: - return None - - return new_token - - @wrapped_hook_callback - async def get_bookmarks(self, userid: int) -> list[dict[str, Any]]: - """ - Gets a list of bookmarks from the db - """ - query = """ - SELECT paste as id, pastes.created_at, pastes.expires, pastes.views - FROM bookmarks - INNER JOIN pastes - ON pastes.id = bookmarks.paste - WHERE userid = $1 - ORDER BY created_at - """ - - data = await self._do_query(query, userid) - return [dict(x) for x in data] - - async def create_bookmark(self, userid: int, paste_id: str) -> None: # doesnt return anything, no need for a hook - """creates a bookmark for a user""" - query = """ - INSERT INTO bookmarks (userid, paste) VALUES ($1, $2) - """ - - try: - await self._do_query(query, userid, paste_id) - except asyncpg.UniqueViolationError: - raise ValueError("This paste is already bookmarked") - except asyncpg.ForeignKeyViolationError: - raise ValueError("This paste does not exist") # its an auth'ed endpoint, so the user has to exist - - async def delete_bookmark(self, userid: int, paste_id: str) -> bool: - """deletes a users bookmark""" - query = """ - DELETE FROM bookmarks WHERE userid = $1 AND paste = $2 RETURNING paste - """ - data = await self._do_query(query, userid, paste_id) - return bool(data) - - @wrapped_hook_callback - async def ensure_authorization(self, token: str) -> bool: - """Quick query against a passed token.""" - if not token: - return False - - query = """ - SELECT id FROM users WHERE token = $1 - """ - - data = await self._do_query(query, token) - if not data: - return False - - return data[0]["id"] - - @wrapped_hook_callback - async def ensure_admin(self, token: str) -> bool: - """Quick query against a token to return if admin or not.""" - if not token: - return False - - query = """ - SELECT admin FROM users WHERE token = $1 - """ - - data = await self._do_query(query, token) - if not data: - return False - - return data[0]["admin"] - - @wrapped_hook_callback - async def ensure_author(self, paste_id: str, author_id: int) -> bool: - """Quick query to ensure a paste is owned by the passed author ID.""" - - query = """ - SELECT id - FROM pastes - WHERE id = $1 - AND author_id = $2; - """ - - response = await self._do_query(query, paste_id, author_id) - if response: - return True - return False - - @wrapped_hook_callback - async def get_admin_userlist(self, page: int) -> dict[str, int | dict[str, int | bool | None]]: - query = """ - SELECT - id, - username, - github_id, - discord_id, - google_id, - admin, - theme, - subscriber, - (SELECT COUNT(*) FROM pastes WHERE author_id = users.id) AS paste_count - FROM users LIMIT 20 OFFSET $1 * 20; - """ - if not self._pool: - await self.__ainit__() - - async with self.pool.acquire() as conn: - users = await self._do_query(query, page - 1, conn=conn) - records: list[Record] = await self._do_query("SELECT COUNT(*) AS count FROM users", conn=conn) - pageinfo: int = records[0]["count"] - - users = [ - { - "id": x["id"], - "username": x["username"], - "admin": x["admin"], - "theme": x["theme"], - "subscriber": x["subscriber"], - "paste_count": x["paste_count"], - "last_seen": None, # TODO need something to track last seen timestamps - "authorizations": [ - x - for x in ( - "github" if x["github_id"] else None, - "discord" if x["discord_id"] else None, - "google" if x["google_id"] else None, - ) - if x is not None - ], - } - for x in users - ] - return {"users": users, "user_count": pageinfo, "page": page} # type: ignore # that's - - async def get_admin_usercount(self) -> int: - query = "SELECT COUNT(id) AS count FROM users" - data = await self._do_query(query) - return data[0]["count"] - - @wrapped_hook_callback - async def search_bans( - self, *, ip: str | None = None, userid: int | None = None, search: str | None = None - ) -> str | list[dict[str, Any]] | None: - assert any((ip, userid, search)) - if not self.ban_cache: - if ip and userid: - r = await self._do_query("SELECT reason FROM bans WHERE ip = $1 OR userid = $2", ip, userid) - return r[0]["reason"] if r else None - - if ip: # quick path: select directly from the db - r = await self._do_query("SELECT reason FROM bans WHERE ip = $1", ip) - return r[0]["reason"] if r else None - - elif userid: # quick path: select directly from the db - r = await self._do_query("SELECT reason FROM bans WHERE id = $1", ip) - return r[0]["reason"] if r else None - - # long path, sequence search - self.ban_cache = await self._do_query("SELECT * FROM bans") - - if ip and userid: - v = [x for x in self.ban_cache if x["ip"] == ip or x["userid"] == userid] - return v[0]["reason"] if v else None - - if ip: - v = [x for x in self.ban_cache if x["ip"] == ip] - return v[0]["reason"] if v else None - - elif userid: - v = [x for x in self.ban_cache if x["ip"] == ip] - return v[0]["reason"] if v else None - - # long path - # in reality this should only ever be called by the admin ban search endpoint - assert search - matcher = difflib.SequenceMatcher() - matcher.set_seq1(search) - close = [] - - if "." in search: # a really stupid way to detect a possible ip, but hey, it works - for ban in self.ban_cache: - if not ban["ip"]: - continue - - if ban["ip"] == search: - return ban["reason"] - - matcher.set_seq2(ban["ip"]) - if matcher.quick_ratio() > 0.7: - close.append(dict(ban)) - - for ban in self.ban_cache: - if not ban["id"]: - continue - - if ban["id"] == search: - return ban["reason"] - - matcher.set_seq2(ban["id"]) - if matcher.quick_ratio() > 0.7: - close.append(dict(ban)) - continue - - return close - - async def put_log(self, request: Request, response: Response) -> None: - query = """ - INSERT INTO logs VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - """ - try: - body = request._body - except: - body = None - - try: - resp = str(response.body) - except AttributeError: - resp = None - await self._do_query( - query, - request.headers.get("X-Forwarded-For", request.client.host), - request.state.user and request.state.user["id"], - datetime.datetime.utcnow(), - request.headers.get("CF-RAY"), - request.headers.get("CF-IPCOUNTRY"), - f"{request.method.upper()} {request.url.include_query_params()}", - body, - response.status_code, - resp, - ) diff --git a/mystbin/backend/utils/embed.py b/mystbin/backend/utils/embed.py deleted file mode 100644 index f672fce0..00000000 --- a/mystbin/backend/utils/embed.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Copyright(C) 2020 PythonistaGuild - -This file is part of MystBin. - -MystBin is free software: you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -MystBin is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY -without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with MystBin. If not, see . -""" -from __future__ import annotations - -import datetime - - -class Embed: - def __init__( - self, - *, - title: str | None = None, - description: str | None = None, - colour: int = 0, - author: dict[str, str] | None = None, - timestamp: datetime.datetime | None = None, - footer: dict[str, str] | None = None, - ) -> None: - self.title = title - self.description = description - self.colour = colour - self.author = author - self.timestamp = timestamp - self.footer = footer - - def to_dict(self) -> dict[str, str | int | dict[str, str]]: - final = {} - final["type"] = "rich" - final["color"] = self.colour - if self.title: - final["title"] = self.title - if self.description: - final["description"] = self.description - if self.author: - final["author"] = self.author - if self.timestamp: - final["timestamp"] = ( - self.timestamp.astimezone(tz=datetime.timezone.utc).isoformat() - if self.timestamp.tzinfo - else self.timestamp.replace(tzinfo=datetime.timezone.utc).isoformat() - ) - if self.footer: - final["footer"] = self.footer - - return final diff --git a/mystbin/backend/utils/ratelimits.py b/mystbin/backend/utils/ratelimits.py deleted file mode 100644 index 613d4da6..00000000 --- a/mystbin/backend/utils/ratelimits.py +++ /dev/null @@ -1,418 +0,0 @@ -from __future__ import annotations - -import asyncio -import time -from typing import TYPE_CHECKING, Any, Callable, Coroutine - -from starlette.requests import Request -from starlette.responses import Response, StreamingResponse -from starlette.routing import Match - -from . import tokens - - -if TYPE_CHECKING: - from app import MystbinApp - - from mystbin_models import MystbinRequest - - -class IPBanned(Exception): - _resp = Response(status_code=423, content="You have been banned from this service for abuse.", media_type="text/plain") - - def __init__(self, reason: str | None) -> None: - self.reason = reason - if reason is not None: - self.resp = Response( - status_code=423, content=f"You have been banned from this service: {reason}", media_type="text/plain" - ) - else: - self.resp = self._resp - - super().__init__(reason) - - -class NoRedisConnection(Exception): - pass - - -time_units = { - "second": 1, - "minute": 60, - "hour": 60 * 60, - "day": 60 * 60 * 24, - "week": 60 * 60 * 24 * 7, - "month": 60 * 60 * 24 * 30, - "year": 60 * 60 * 24 * 365, -} - - -class BaseLimitBucket: - strategy: str - - def __init__(self, app: MystbinApp, key: str, count: int, per: int) -> None: - self.count = count - self.per = per - - async def _clear_dead_keys(self) -> None: - raise NotImplementedError - - async def hit(self) -> tuple[bool, int]: - raise NotImplementedError - - async def is_limited(self) -> bool: - raise NotImplementedError - - @property - def reset_at(self): - raise NotImplementedError - - -class InMemoryLimitBucket(BaseLimitBucket): - """ - The in-memory bucket uses the "leaky bucket" method. - As such, there is no reset time. - """ - - __slots__ = ("count", "per", "hits") - - strategy: str = "leakybucket" - - def __init__(self, app: MystbinApp, key: str, count: int, per: int) -> None: - self.count = count - self.per = per - self.hits: list[int | float] = [] - - async def _clear_dead_keys(self) -> None: - dead_t = time.time() - self.per - for key in self.hits: - if key <= dead_t: - self.hits.remove(key) - - async def hit(self) -> tuple[bool, int]: - await self._clear_dead_keys() - self.hits.append(time.time()) - return len(self.hits) >= self.count, len(self.hits) - - async def is_limited(self) -> bool: - return len(self.hits) >= self.count - - @property - def reset_at(self): - return 0 - - -class RedisLimitBucket(BaseLimitBucket): - """ - The redis bucket uses the "window" method. - """ - - __slots__ = ("key", "count", "per", "app", "reset") - - strategy: str = "window" - - def __init__(self, app: MystbinApp, key: str, count: int, per: int) -> None: - self.app = app - self.key = key - self.count = count - self.per = per - self.reset: int | None = None - - async def _clear_dead_keys(self) -> None: - pass # we don't actually need this, keys will be deleted on their own by redis - - async def hit(self) -> tuple[bool, int]: - if not self.app.redis: - raise NoRedisConnection() - - value: int | None = await self.app.redis.get(self.key) - - if value is None: - val = await self.app.redis.incr(self.key) - await self.app.redis.expire(self.key, self.per) - self.reset = round(time.time()) + self.per - - else: - val = await self.app.redis.incr(self.key) - - return val >= self.count, val - - async def is_limited(self) -> bool: - if not self.app.redis: - raise NoRedisConnection() - - t = await self.app.redis.get(self.key) - return t >= self.count if t is not None else False - - @property - def reset_at(self): - return self.reset - - -_CT = Callable[..., Coroutine[Any, Any, Response]] - - -class Limiter: - def startup(self, app: MystbinApp) -> None: - self.app = app - self._keys: dict[str, BaseLimitBucket] = {} - self._zone_cache: dict[str, tuple[int, int]] = {} - self._endpoints: dict[str, str | None] = {} - - stragegy = app.config["redis"]["use-redis"] - if not stragegy: - self.bucket_cls = InMemoryLimitBucket - else: - self.bucket_cls = RedisLimitBucket - - async def get_key(self, zone: str, request: MystbinRequest) -> str: - zone = await ratelimit_zone_key(zone, request) - key = ratelimit_id_key(request) - return f"{zone}%{key}" - - async def _transform_and_log(self, request: Request, response: Response) -> Response: - if isinstance(response, StreamingResponse): - resp_ = Response( - status_code=response.status_code, - background=response.background, - media_type=response.media_type, - headers=response.headers, - ) - body = b"" - async for chunk in response.body_iterator: - if not isinstance(chunk, bytes): - chunk = chunk.encode(response.charset) - - body += chunk - - resp_.body = body - response = resp_ - - await self.app.state.db.put_log(request, response) - return response - - async def get_bucket(self, zone: str, key: str, request: MystbinRequest) -> BaseLimitBucket: - if key in self._keys: - return self._keys[key] - - lims = self._zone_cache.get(zone) - if not lims: - lims = parse_ratelimit(await get_zone(zone, request)) - - bucket = self.bucket_cls(self.app, key, lims[0], lims[1]) - self._keys[key] = bucket - - return bucket - - async def middleware(self, request: MystbinRequest, call_next: _CT) -> Response: - zone: str | None = None - request.state.user = None - - try: - await _fetch_user(request) - except IPBanned as e: - return e.resp # don't log attempts from banned ips (maybe we should?) - - for route in self.app.routes: - match, _ = route.matches(request.scope) - if match == Match.FULL and hasattr(route, "endpoint"): - handler = route.endpoint # type: ignore - zone = getattr(handler, "__zone__", None) - - if not await _ignores_ratelimits(request): - key = await self.get_key("global", request) - bucket = await self.get_bucket("global", key, request) - - is_limited, keys_used = await bucket.hit() - headers = { - "X-Global-Ratelimit-Used": str(keys_used), - "X-Global-Ratelimit-Reset": str(bucket.reset_at), - "X-Global-Ratelimit-Max": str(bucket.count), - "X-Global-Ratelimit-Available": str(bucket.count - keys_used), - "X-Ratelimit-Used": "0", - "X-Ratelimit-Reset": "0", - "X-Ratelimit-Max": "1", - "X-Ratelimit-Available": "1", - "X-Ratelimit-Strategy": self.bucket_cls.strategy, - } - - if is_limited: - return Response(status_code=429, headers=headers) - - if zone is not None: - key = await self.get_key(zone, request) - bucket = await self.get_bucket(zone, key, request) - - is_limited, keys_used = await bucket.hit() - headers.update( - { - "X-Ratelimit-Used": str(keys_used), - "X-Ratelimit-Reset": str(bucket.reset_at), - "X-Ratelimit-Max": str(bucket.count), - "X-Ratelimit-Available": str(bucket.count - keys_used), - } - ) - - if is_limited: - return Response(status_code=429, headers=headers) - - try: - resp = await call_next(request) - resp.headers.update(headers) - except: - resp = Response(status_code=500, headers=headers, content="An error occurred while processing the request") - raise - finally: - resp = await self._transform_and_log(request, resp) - - return resp - - else: - headers = { - "X-Global-Ratelimit-Used": "0", - "X-Global-Ratelimit-Reset": "0", - "X-Global-Ratelimit-Max": "1", - "X-Global-Ratelimit-Available": "1", - "X-Ratelimit-Used": "0", - "X-Ratelimit-Reset": "0", - "X-Ratelimit-Max": "1", - "X-Ratelimit-Available": "1", - "X-Ratelimit-Strategy": "ignore", - } - try: - resp = await call_next(request) - resp.headers.update(headers) - except: - resp = Response(status_code=500, headers=headers, content="An error occurred while processing the request") - raise - finally: - resp = await self._transform_and_log(request, resp) - - return resp - - -def parse_ratelimit(limit: str) -> tuple[int, int]: - _per, units = limit.split("/", 1) - - per: int = int(_per) - transformed_units: int = time_units[units] - return per, transformed_units - - -async def _set_redis_ip_key(request: MystbinRequest, key: str, value: str) -> None: - redis = request.app.redis - if not redis: - return - - await redis.set(key, value, ex=120) - - -async def _get_redis_ip_key(request: MystbinRequest, key: str) -> str | None: - redis = request.app.redis - if not redis: - return - - bans: bytes | None = await redis.get(key) # speed up ban checking by redis caching - return None if bans is None else bans.decode() - - -async def _fetch_user(request: MystbinRequest): - host = request.headers.get("X-Forwarded-For") or request.client.host - - auth = request.headers.get("Authorization", None) - - if request.app.redis: - bans = await _get_redis_ip_key(request, f"ban-ip-{host}") # speed up ban checking by redis caching - if bans: - raise IPBanned(bans) - elif bans == "": - asyncio.create_task(_set_redis_ip_key(request, f"ban-ip-{host}", "")) - if not auth: - request.state.user = None - return # quick path: no db requests - - if not auth: - query = "SELECT * FROM bans WHERE ip = $1" - bans = await request.app.state.db._do_query(query, host) - if bans: - ban = bans[0] - if request.app.redis: - asyncio.create_task(_set_redis_ip_key(request, f"ban-ip-{host}", ban["reason"])) - - raise IPBanned(ban["reason"]) - - asyncio.create_task(_set_redis_ip_key(request, f"ban-ip-{host}", "")) - return - - query = """ - SELECT users.*, bans.ip as _is_ip_banned , bans.userid as _is_user_banned, bans.reason as _ban_reason - FROM users - FULL OUTER JOIN bans - ON ip = $2 - OR userid = users.id - WHERE token = $1; - """ - - user = await request.app.state.db._do_query(query, auth.replace("Bearer ", ""), host) - if not user: - return - - user = user[0] - if user["_is_ip_banned"]: - if request.app.redis: - asyncio.create_task(_set_redis_ip_key(request, f"ban-ip-{host}", user["_ban_reason"])) - - raise IPBanned(user["_ban_reason"]) - - elif user["_is_user_banned"]: - raise IPBanned(user["_ban_reason"]) - - request.state.user = user - - -async def _ignores_ratelimits(request: MystbinRequest): - if request.state.user and request.state.user["admin"]: - return True - - return False - - -async def ratelimit_zone_key(zone: str, request: MystbinRequest) -> str: - _zone = zone - user = request.state.user - - if user: - _zone = ("authed_" if not user["subscriber"] else "premium_") + zone - - return _zone - - -async def get_zone(zone: str, request: MystbinRequest) -> str: - zone = await ratelimit_zone_key(zone, request) - try: - return request.app.config["ratelimits"][zone] - except: - return request.app.config["ratelimits"][zone.replace("authed_", "").replace("premium_", "")] - - -def ratelimit_id_key(request: Request) -> str: - auth = request.headers.get("Authorization", None) - if not auth: - return request.headers.get("X-Forwarded-For", None) or request.client.host - - userid = tokens.get_user_id(auth.replace("Bearer ", "")) - if not userid: # must be a fake token, so just ignore it and go by ip - return request.headers.get("X-Forwarded-For", None) or request.client.host - - return str(userid) - - -def limit(zone: str | None = None) -> Callable[[Any], Callable[[Any], Any]]: - def wrapped(cb: Callable[[Any], Any]) -> Callable[[Any], Any]: - cb.__zone__ = zone - return cb - - return wrapped - - -limiter = Limiter() diff --git a/mystbin/backend/utils/tokens.py b/mystbin/backend/utils/tokens.py deleted file mode 100644 index 42473b83..00000000 --- a/mystbin/backend/utils/tokens.py +++ /dev/null @@ -1,23 +0,0 @@ -import binascii - -import jwt -from Crypto.Random import get_random_bytes - - -JWT_OPTIONS = {"verify_signature": False} - - -def generate(userid: int) -> str: - """Generate a token.""" - payload = {"id": userid} - key = binascii.hexlify(get_random_bytes(64)).decode() - return jwt.encode(payload, key, algorithm="HS512") - - -def get_user_id(token: str) -> int | None: - """Get user ID from a token.""" - try: - payload = jwt.decode(token, algorithms=["HS512"], options=JWT_OPTIONS) - return payload["id"] - except jwt.DecodeError: - return None diff --git a/mystbin/backend/utils/webhooks.py b/mystbin/backend/utils/webhooks.py deleted file mode 100644 index 42b55336..00000000 --- a/mystbin/backend/utils/webhooks.py +++ /dev/null @@ -1,33 +0,0 @@ -from datetime import datetime - -import aiohttp -from yarl import URL - - -class WebhookAdapter: - def __init__(self) -> None: - self.session: aiohttp.ClientSession | None = None - - async def _ensure_session(self) -> None: - if not self.session: - self.session = aiohttp.ClientSession() - - async def send_request(self, payload: dict[str, str | int | dict]) -> int: - ... - - async def assemble_payload(self, event: str, url: URL, data: dict) -> dict: - ... - - async def event_new_paste( - self, paste_id: str, paste_owner: str | None, paste_filecount: int, paste_expiry: datetime | None - ) -> None: - ... - - async def event_delete_paste(self, paste_id: str, paste_owner: str | None) -> None: - ... - - async def event_edit_paste(self, paste_id: str, paste_owner: str | None) -> None: - ... - - async def event_newuser_authorized(self, user_id: int, service: str) -> None: - ... diff --git a/mystbin/backend/utils/words.txt b/mystbin/backend/utils/words.txt deleted file mode 100644 index fc3787de..00000000 --- a/mystbin/backend/utils/words.txt +++ /dev/null @@ -1,9886 +0,0 @@ -the -of -and -to -a -in -for -is -on -that -by -this -with -i -you -it -not -or -be -are -from -at -as -your -all -have -new -more -an -was -we -will -home -can -us -about -if -page -my -has -search -free -but -our -one -other -do -no -information -time -they -site -he -up -may -what -which -their -news -out -use -any -there -see -only -so -his -when -contact -here -business -who -web -also -now -help -get -pm -view -online -c -e -first -am -been -would -how -were -me -s -services -some -these -click -its -like -service -x -than -find -price -date -back -top -people -had -list -name -just -over -state -year -day -into -email -two -health -n -world -re -next -used -go -b -work -last -most -products -music -buy -data -make -them -should -product -system -post -her -city -t -add -policy -number -such -please -available -copyright -support -message -after -best -software -then -jan -good -video -well -d -where -info -rights -public -books -high -school -through -m -each -links -she -review -years -order -very -privacy -book -items -company -r -read -group -need -many -user -said -de -does -set -under -general -research -university -january -mail -full -map -reviews -program -life -know -games -way -days -management -p -part -could -great -united -hotel -real -f -item -international -center -ebay -must -store -travel -comments -made -development -report -off -member -details -line -terms -before -hotels -did -send -right -type -because -local -those -using -results -office -education -national -car -design -take -posted -internet -address -community -within -states -area -want -phone -dvd -shipping -reserved -subject -between -forum -family -l -long -based -w -code -show -o -even -black -check -special -prices -website -index -being -women -much -sign -file -link -open -today -technology -south -case -project -same -pages -uk -version -section -own -found -sports -house -related -security -both -g -county -american -photo -game -members -power -while -care -network -down -computer -systems -three -total -place -end -following -download -h -him -without -per -access -think -north -resources -current -posts -big -media -law -control -water -history -pictures -size -art -personal -since -including -guide -shop -directory -board -location -change -white -text -small -rating -rate -government -children -during -usa -return -students -v -shopping -account -times -sites -level -digital -profile -previous -form -events -love -old -john -main -call -hours -image -department -title -description -non -k -y -insurance -another -why -shall -property -class -cd -still -money -quality -every -listing -content -country -private -little -visit -save -tools -low -reply -customer -december -compare -movies -include -college -value -article -york -man -card -jobs -provide -j -food -source -author -different -press -u -learn -sale -around -print -course -job -canada -process -teen -room -stock -training -too -credit -point -join -science -men -categories -advanced -west -sales -look -english -left -team -estate -box -conditions -select -windows -photos -gay -thread -week -category -note -live -large -gallery -table -register -however -june -october -november -market -library -really -action -start -series -model -features -air -industry -plan -human -provided -tv -yes -required -second -hot -accessories -cost -movie -forums -march -la -september -better -say -questions -july -yahoo -going -medical -test -friend -come -dec -server -pc -study -application -cart -staff -articles -san -feedback -again -play -looking -issues -april -never -users -complete -street -topic -comment -financial -things -working -against -standard -tax -person -below -mobile -less -got -blog -party -payment -equipment -login -student -let -programs -offers -legal -above -recent -park -stores -side -act -problem -red -give -memory -performance -social -q -august -quote -language -story -sell -options -experience -rates -create -key -body -young -america -important -field -few -east -paper -single -ii -age -activities -club -example -girls -additional -password -z -latest -something -road -gift -question -changes -night -ca -hard -texas -oct -pay -four -poker -status -browse -issue -range -building -seller -court -february -always -result -audio -light -write -war -nov -offer -blue -groups -al -easy -given -files -event -release -analysis -request -fax -china -making -picture -needs -possible -might -professional -yet -month -major -star -areas -future -space -committee -hand -sun -cards -problems -london -washington -meeting -rss -become -interest -id -child -keep -enter -california -share -similar -garden -schools -million -added -reference -companies -listed -baby -learning -energy -run -delivery -net -popular -term -film -stories -put -computers -journal -reports -co -try -welcome -central -images -president -notice -original -head -radio -until -cell -color -self -council -away -includes -track -australia -discussion -archive -once -others -entertainment -agreement -format -least -society -months -log -safety -friends -sure -faq -trade -edition -cars -messages -marketing -tell -further -updated -association -able -having -provides -david -fun -already -green -studies -close -common -drive -specific -several -gold -feb -living -sep -collection -called -short -arts -lot -ask -display -limited -powered -solutions -means -director -daily -beach -past -natural -whether -due -et -electronics -five -upon -period -planning -database -says -official -weather -mar -land -average -done -technical -window -france -pro -region -island -record -direct -microsoft -conference -environment -records -st -district -calendar -costs -style -url -front -statement -update -parts -aug -ever -downloads -early -miles -sound -resource -present -applications -either -ago -document -word -works -material -bill -apr -written -talk -federal -hosting -rules -final -adult -tickets -thing -centre -requirements -via -cheap -kids -finance -true -minutes -else -mark -third -rock -gifts -europe -reading -topics -bad -individual -tips -plus -auto -cover -usually -edit -together -videos -percent -fast -function -fact -unit -getting -global -tech -meet -far -economic -en -player -projects -lyrics -often -subscribe -submit -germany -amount -watch -included -feel -though -bank -risk -thanks -everything -deals -various -words -linux -jul -production -commercial -james -weight -town -heart -advertising -received -choose -treatment -newsletter -archives -points -knowledge -magazine -error -camera -jun -girl -currently -construction -toys -registered -clear -golf -receive -domain -methods -chapter -makes -protection -policies -loan -wide -beauty -manager -india -position -taken -sort -listings -models -michael -known -half -cases -step -engineering -florida -simple -quick -none -wireless -license -paul -friday -lake -whole -annual -published -later -basic -sony -shows -corporate -google -church -method -purchase -customers -active -response -practice -hardware -figure -materials -fire -holiday -chat -enough -designed -along -among -death -writing -speed -html -countries -loss -face -brand -discount -higher -effects -created -remember -standards -oil -bit -yellow -political -increase -advertise -kingdom -base -near -environmental -thought -stuff -french -storage -oh -japan -doing -loans -shoes -entry -stay -nature -orders -availability -africa -summary -turn -mean -growth -notes -agency -king -monday -european -activity -copy -although -drug -pics -western -income -force -cash -employment -overall -bay -river -commission -ad -package -contents -seen -players -engine -port -album -regional -stop -supplies -started -administration -bar -institute -views -plans -double -dog -build -screen -exchange -types -soon -sponsored -lines -electronic -continue -across -benefits -needed -season -apply -someone -held -ny -anything -printer -condition -effective -believe -organization -effect -asked -eur -mind -sunday -selection -casino -pdf -lost -tour -menu -volume -cross -anyone -mortgage -hope -silver -corporation -wish -inside -solution -mature -role -rather -weeks -addition -came -supply -nothing -certain -usr -executive -running -lower -necessary -union -jewelry -according -dc -clothing -mon -com -particular -fine -names -robert -homepage -hour -gas -skills -six -bush -islands -advice -career -military -rental -decision -leave -british -teens -pre -huge -sat -woman -facilities -zip -bid -kind -sellers -middle -move -cable -opportunities -taking -values -division -coming -tuesday -object -lesbian -appropriate -machine -logo -length -actually -nice -score -statistics -client -ok -returns -capital -follow -sample -investment -sent -shown -saturday -christmas -england -culture -band -flash -ms -lead -george -choice -went -starting -registration -fri -thursday -courses -consumer -hi -airport -foreign -artist -outside -furniture -levels -channel -letter -mode -phones -ideas -wednesday -structure -fund -summer -allow -degree -contract -button -releases -wed -homes -super -male -matter -custom -virginia -almost -took -located -multiple -asian -distribution -editor -inn -industrial -cause -potential -song -cnet -ltd -los -hp -focus -late -fall -featured -idea -rooms -female -responsible -inc -communications -win -associated -thomas -primary -cancer -numbers -reason -tool -browser -spring -foundation -answer -voice -eg -friendly -schedule -documents -communication -purpose -feature -bed -comes -police -everyone -independent -ip -approach -cameras -brown -physical -operating -hill -maps -medicine -deal -hold -ratings -chicago -forms -glass -happy -tue -smith -wanted -developed -thank -safe -unique -survey -prior -telephone -sport -ready -feed -animal -sources -mexico -population -pa -regular -secure -navigation -operations -therefore -simply -evidence -station -christian -round -paypal -favorite -understand -option -master -valley -recently -probably -thu -rentals -sea -built -publications -blood -cut -worldwide -improve -connection -publisher -hall -larger -anti -networks -earth -parents -nokia -impact -transfer -introduction -kitchen -strong -tel -carolina -wedding -properties -hospital -ground -overview -ship -accommodation -owners -disease -tx -excellent -paid -italy -perfect -hair -opportunity -kit -classic -basis -command -cities -william -express -award -distance -tree -peter -assessment -ensure -thus -wall -ie -involved -el -extra -especially -interface -partners -budget -rated -guides -success -maximum -ma -operation -existing -quite -selected -boy -amazon -patients -restaurants -beautiful -warning -wine -locations -horse -vote -forward -flowers -stars -significant -lists -technologies -owner -retail -animals -useful -directly -manufacturer -ways -est -son -providing -rule -mac -housing -takes -iii -gmt -bring -catalog -searches -max -trying -mother -authority -considered -told -xml -traffic -programme -joined -input -strategy -feet -agent -valid -bin -modern -senior -ireland -teaching -door -grand -testing -trial -charge -units -instead -canadian -cool -normal -wrote -enterprise -ships -entire -educational -md -leading -metal -positive -fl -fitness -chinese -opinion -mb -asia -football -abstract -uses -output -funds -mr -greater -likely -develop -employees -artists -alternative -processing -responsibility -resolution -java -guest -seems -publication -pass -relations -trust -van -contains -session -multi -photography -republic -fees -components -vacation -century -academic -assistance -completed -skin -graphics -indian -prev -ads -mary -il -expected -ring -grade -dating -pacific -mountain -organizations -pop -filter -mailing -vehicle -longer -consider -int -northern -behind -panel -floor -german -buying -match -proposed -default -require -iraq -boys -outdoor -deep -morning -otherwise -allows -rest -protein -plant -reported -hit -transportation -mm -pool -mini -politics -partner -disclaimer -authors -boards -faculty -parties -fish -membership -mission -eye -string -sense -modified -pack -released -stage -internal -goods -recommended -born -unless -richard -detailed -japanese -race -approved -background -target -except -character -usb -maintenance -ability -maybe -functions -ed -moving -brands -places -php -pretty -trademarks -phentermine -spain -southern -yourself -etc -winter -battery -youth -pressure -submitted -boston -debt -keywords -medium -television -interested -core -break -purposes -throughout -sets -dance -wood -msn -itself -defined -papers -playing -awards -fee -studio -reader -virtual -device -established -answers -rent -las -remote -dark -programming -external -apple -le -regarding -instructions -min -offered -theory -enjoy -remove -aid -surface -minimum -visual -host -variety -teachers -isbn -martin -manual -block -subjects -agents -increased -repair -fair -civil -steel -understanding -songs -fixed -wrong -beginning -hands -associates -finally -az -updates -desktop -classes -paris -ohio -gets -sector -capacity -requires -jersey -un -fat -fully -father -electric -saw -instruments -quotes -officer -driver -businesses -dead -respect -unknown -specified -restaurant -mike -trip -pst -worth -mi -procedures -poor -teacher -eyes -relationship -workers -farm -georgia -peace -traditional -campus -tom -showing -creative -coast -benefit -progress -funding -devices -lord -grant -sub -agree -fiction -hear -sometimes -watches -careers -beyond -goes -families -led -museum -themselves -fan -transport -interesting -blogs -wife -evaluation -accepted -former -implementation -ten -hits -zone -complex -th -cat -galleries -references -die -presented -jack -flat -flow -agencies -literature -respective -parent -spanish -michigan -columbia -setting -dr -scale -stand -economy -highest -helpful -monthly -critical -frame -musical -definition -secretary -angeles -networking -path -australian -employee -chief -gives -kb -bottom -magazines -packages -detail -francisco -laws -changed -pet -heard -begin -individuals -colorado -royal -clean -switch -russian -largest -african -guy -titles -relevant -guidelines -justice -connect -bible -dev -cup -basket -applied -weekly -vol -installation -described -demand -pp -suite -vegas -na -square -chris -attention -advance -skip -diet -army -auction -gear -lee -os -difference -allowed -correct -charles -nation -selling -lots -piece -sheet -firm -seven -older -illinois -regulations -elements -species -jump -cells -module -resort -facility -random -pricing -dvds -certificate -minister -motion -looks -fashion -directions -visitors -documentation -monitor -trading -forest -calls -whose -coverage -couple -giving -chance -vision -ball -ending -clients -actions -listen -discuss -accept -automotive -naked -goal -successful -sold -wind -communities -clinical -situation -sciences -markets -lowest -highly -publishing -appear -emergency -developing -lives -currency -leather -determine -temperature -palm -announcements -patient -actual -historical -stone -bob -commerce -ringtones -perhaps -persons -difficult -scientific -satellite -fit -tests -village -accounts -amateur -ex -met -pain -xbox -particularly -factors -coffee -www -settings -buyer -cultural -steve -easily -oral -ford -poster -edge -functional -root -au -fi -closed -holidays -ice -pink -zealand -balance -monitoring -graduate -replies -shot -nc -architecture -initial -label -thinking -scott -llc -sec -recommend -canon -league -waste -minute -bus -provider -optional -dictionary -cold -accounting -manufacturing -sections -chair -fishing -effort -phase -fields -bag -fantasy -po -letters -motor -va -professor -context -install -shirt -apparel -generally -continued -foot -mass -crime -count -breast -techniques -ibm -rd -johnson -sc -quickly -dollars -websites -religion -claim -driving -permission -surgery -patch -heat -wild -measures -generation -kansas -miss -chemical -doctor -task -reduce -brought -himself -nor -component -enable -exercise -bug -santa -mid -guarantee -leader -diamond -israel -se -processes -soft -servers -alone -meetings -seconds -jones -arizona -keyword -interests -flight -congress -fuel -username -walk -produced -italian -paperback -classifieds -wait -supported -pocket -saint -rose -freedom -argument -competition -creating -jim -drugs -joint -premium -providers -fresh -characters -attorney -upgrade -di -factor -growing -thousands -km -stream -apartments -pick -hearing -eastern -auctions -therapy -entries -dates -generated -signed -upper -administrative -serious -prime -samsung -limit -began -louis -steps -errors -shops -del -efforts -informed -ga -ac -thoughts -creek -ft -worked -quantity -urban -practices -sorted -reporting -essential -myself -tours -platform -load -affiliate -labor -immediately -admin -nursing -defense -machines -designated -tags -heavy -covered -recovery -joe -guys -integrated -configuration -merchant -comprehensive -expert -universal -protect -drop -solid -cds -presentation -languages -became -orange -compliance -vehicles -prevent -theme -rich -im -campaign -marine -improvement -vs -guitar -finding -pennsylvania -examples -ipod -saying -spirit -ar -claims -challenge -motorola -acceptance -strategies -mo -seem -affairs -touch -intended -towards -sa -goals -hire -election -suggest -branch -charges -serve -affiliates -reasons -magic -mount -smart -talking -gave -ones -latin -multimedia -xp -avoid -certified -manage -corner -rank -computing -oregon -element -birth -virus -abuse -interactive -requests -separate -quarter -procedure -leadership -tables -define -racing -religious -facts -breakfast -kong -column -plants -faith -chain -developer -identify -avenue -missing -died -approximately -domestic -sitemap -recommendations -moved -houston -reach -comparison -mental -viewed -moment -extended -sequence -inch -attack -sorry -centers -opening -damage -lab -reserve -recipes -cvs -gamma -plastic -produce -snow -placed -truth -counter -failure -follows -eu -weekend -dollar -camp -ontario -automatically -des -minnesota -films -bridge -native -fill -williams -movement -printing -baseball -owned -approval -draft -chart -played -contacts -cc -jesus -readers -clubs -lcd -wa -jackson -equal -adventure -matching -offering -shirts -profit -leaders -posters -institutions -assistant -variable -ave -dj -advertisement -expect -parking -headlines -yesterday -compared -determined -wholesale -workshop -russia -gone -codes -kinds -extension -seattle -statements -golden -completely -teams -fort -cm -wi -lighting -senate -forces -funny -brother -gene -turned -portable -tried -electrical -applicable -disc -returned -pattern -ct -boat -named -theatre -laser -earlier -manufacturers -sponsor -classical -icon -warranty -dedicated -indiana -direction -harry -basketball -objects -ends -delete -evening -assembly -nuclear -taxes -mouse -signal -criminal -issued -brain -wisconsin -powerful -dream -obtained -false -da -cast -flower -felt -personnel -passed -supplied -identified -falls -pic -soul -aids -opinions -promote -stated -stats -hawaii -professionals -appears -carry -flag -decided -nj -covers -hr -em -advantage -hello -designs -maintain -tourism -priority -newsletters -adults -clips -savings -iv -graphic -atom -payments -rw -estimated -binding -brief -ended -winning -eight -anonymous -iron -straight -script -served -wants -miscellaneous -prepared -void -dining -alert -integration -atlanta -dakota -tag -interview -mix -framework -disk -installed -queen -vhs -credits -clearly -fix -handle -sweet -desk -criteria -pubmed -dave -massachusetts -diego -hong -vice -associate -ne -truck -behavior -enlarge -ray -frequently -revenue -measure -changing -votes -du -duty -looked -discussions -bear -gain -festival -laboratory -ocean -flights -experts -signs -lack -depth -iowa -whatever -logged -laptop -vintage -train -exactly -dry -explore -maryland -spa -concept -nearly -eligible -checkout -reality -forgot -handling -origin -knew -gaming -feeds -billion -destination -scotland -faster -intelligence -dallas -bought -con -ups -nations -route -followed -specifications -broken -tripadvisor -frank -alaska -zoom -blow -battle -residential -anime -speak -decisions -industries -protocol -query -clip -partnership -editorial -nt -expression -es -equity -provisions -speech -wire -principles -suggestions -rural -shared -sounds -replacement -tape -strategic -judge -spam -economics -acid -bytes -cent -forced -compatible -fight -apartment -height -null -zero -speaker -filed -gb -netherlands -obtain -bc -consulting -recreation -offices -designer -remain -managed -pr -failed -marriage -roll -korea -banks -fr -participants -secret -bath -aa -kelly -leads -negative -austin -favorites -toronto -theater -springs -missouri -andrew -var -perform -healthy -translation -estimates -font -assets -injury -mt -joseph -ministry -drivers -lawyer -figures -married -protected -proposal -sharing -philadelphia -portal -waiting -birthday -beta -fail -gratis -banking -officials -brian -toward -won -slightly -assist -conduct -contained -lingerie -legislation -calling -parameters -jazz -serving -bags -profiles -miami -comics -matters -houses -doc -postal -relationships -tennessee -wear -controls -breaking -combined -ultimate -wales -representative -frequency -introduced -minor -finish -departments -residents -noted -displayed -mom -reduced -physics -rare -spent -performed -extreme -samples -davis -daniel -bars -reviewed -row -oz -forecast -removed -helps -singles -administrator -cycle -amounts -contain -accuracy -dual -rise -usd -sleep -mg -bird -pharmacy -brazil -creation -static -scene -hunter -addresses -lady -crystal -famous -writer -chairman -violence -fans -oklahoma -speakers -drink -academy -dynamic -gender -eat -permanent -agriculture -dell -cleaning -constitutes -portfolio -practical -delivered -collectibles -infrastructure -exclusive -seat -concerns -colour -vendor -originally -intel -utilities -philosophy -regulation -officers -reduction -aim -bids -referred -supports -nutrition -recording -regions -junior -toll -les -cape -ann -rings -meaning -tip -secondary -wonderful -mine -ladies -henry -ticket -announced -guess -agreed -prevention -whom -ski -soccer -math -import -posting -presence -instant -mentioned -automatic -healthcare -viewing -maintained -ch -increasing -majority -connected -christ -dan -dogs -sd -directors -aspects -austria -ahead -moon -participation -scheme -utility -preview -fly -manner -matrix -containing -combination -devel -amendment -despite -strength -guaranteed -turkey -libraries -proper -distributed -degrees -singapore -enterprises -delta -fear -seeking -inches -phoenix -rs -convention -shares -principal -daughter -standing -comfort -colors -wars -cisco -ordering -kept -alpha -appeal -cruise -bonus -certification -previously -hey -bookmark -buildings -specials -beat -disney -household -batteries -adobe -smoking -bbc -becomes -drives -arms -alabama -tea -improved -trees -avg -achieve -positions -dress -subscription -dealer -contemporary -sky -utah -nearby -rom -carried -happen -exposure -panasonic -hide -permalink -signature -gambling -refer -miller -provision -outdoors -clothes -caused -luxury -babes -frames -certainly -indeed -newspaper -toy -circuit -layer -printed -slow -removal -easier -src -liability -trademark -hip -printers -faqs -nine -adding -kentucky -mostly -eric -spot -taylor -trackback -prints -spend -factory -interior -revised -grow -americans -optical -promotion -relative -amazing -clock -dot -hiv -identity -suites -conversion -feeling -hidden -reasonable -victoria -serial -relief -revision -broadband -influence -ratio -pda -importance -rain -onto -dsl -planet -webmaster -copies -recipe -zum -permit -seeing -proof -dna -diff -tennis -bass -prescription -bedroom -empty -instance -hole -pets -ride -licensed -orlando -specifically -tim -bureau -maine -sql -represent -conservation -pair -ideal -specs -recorded -don -pieces -finished -parks -dinner -lawyers -sydney -stress -cream -ss -runs -trends -yeah -discover -ap -patterns -boxes -louisiana -hills -javascript -fourth -nm -advisor -mn -marketplace -nd -evil -aware -wilson -shape -evolution -irish -certificates -objectives -stations -suggested -gps -op -remains -acc -greatest -firms -concerned -euro -operator -structures -generic -encyclopedia -usage -cap -ink -charts -continuing -mixed -census -interracial -peak -tn -competitive -exist -wheel -transit -suppliers -salt -compact -poetry -lights -tracking -angel -bell -keeping -preparation -attempt -receiving -matches -accordance -width -noise -engines -forget -array -discussed -accurate -stephen -elizabeth -climate -reservations -pin -playstation -alcohol -greek -instruction -managing -annotation -sister -raw -differences -walking -explain -smaller -newest -establish -gnu -happened -expressed -jeff -extent -sharp -lesbians -ben -lane -paragraph -kill -mathematics -aol -compensation -ce -export -managers -aircraft -modules -sweden -conflict -conducted -versions -employer -occur -percentage -knows -mississippi -describe -concern -backup -requested -citizens -connecticut -heritage -personals -immediate -holding -trouble -spread -coach -kevin -agricultural -expand -supporting -audience -assigned -jordan -collections -ages -participate -plug -specialist -cook -affect -virgin -experienced -investigation -raised -hat -institution -directed -dealers -searching -sporting -helping -perl -affected -lib -bike -totally -plate -expenses -indicate -blonde -ab -proceedings -favourite -transmission -anderson -utc -characteristics -der -lose -organic -seek -experiences -albums -cheats -extremely -verzeichnis -contracts -guests -hosted -diseases -concerning -developers -equivalent -chemistry -tony -neighborhood -nevada -kits -thailand -variables -agenda -anyway -continues -tracks -advisory -cam -curriculum -logic -template -prince -circle -soil -grants -anywhere -psychology -responses -atlantic -wet -circumstances -edward -investor -identification -ram -leaving -wildlife -appliances -matt -elementary -cooking -speaking -sponsors -fox -unlimited -respond -sizes -plain -exit -entered -iran -arm -keys -launch -wave -checking -costa -belgium -printable -holy -acts -guidance -mesh -trail -enforcement -symbol -crafts -highway -buddy -hardcover -observed -dean -setup -poll -booking -glossary -fiscal -celebrity -styles -denver -unix -filled -bond -channels -ericsson -appendix -notify -blues -chocolate -pub -portion -scope -hampshire -supplier -cables -cotton -bluetooth -controlled -requirement -authorities -biology -dental -killed -border -ancient -debate -representatives -starts -pregnancy -causes -arkansas -biography -leisure -attractions -learned -transactions -notebook -explorer -historic -attached -opened -tm -husband -disabled -authorized -crazy -upcoming -britain -concert -retirement -scores -financing -efficiency -sp -comedy -adopted -efficient -weblog -linear -commitment -specialty -bears -jean -hop -carrier -edited -constant -visa -mouth -jewish -meter -linked -portland -interviews -concepts -nh -gun -reflect -pure -deliver -wonder -lessons -fruit -begins -qualified -reform -lens -alerts -treated -discovery -draw -mysql -classified -relating -assume -confidence -alliance -fm -confirm -warm -neither -lewis -howard -offline -leaves -engineer -lifestyle -consistent -replace -clearance -connections -inventory -converter -organisation -babe -checks -reached -becoming -safari -objective -indicated -sugar -crew -legs -sam -stick -securities -allen -pdt -relation -enabled -genre -slide -montana -volunteer -tested -rear -democratic -enhance -switzerland -exact -bound -parameter -adapter -processor -node -formal -dimensions -contribute -lock -hockey -storm -micro -colleges -laptops -mile -showed -challenges -editors -mens -threads -bowl -supreme -brothers -recognition -presents -ref -tank -submission -dolls -estimate -encourage -navy -kid -regulatory -inspection -consumers -cancel -limits -territory -transaction -manchester -weapons -paint -delay -pilot -outlet -contributions -continuous -db -czech -resulting -cambridge -initiative -novel -pan -execution -disability -increases -ultra -winner -idaho -contractor -ph -episode -examination -potter -dish -plays -bulletin -ia -pt -indicates -modify -oxford -adam -truly -epinions -painting -committed -extensive -affordable -universe -candidate -databases -patent -slot -psp -outstanding -ha -eating -perspective -planned -watching -lodge -messenger -mirror -tournament -consideration -ds -discounts -sterling -sessions -kernel -stocks -buyers -journals -gray -catalogue -ea -jennifer -antonio -charged -broad -taiwan -und -chosen -demo -greece -lg -swiss -sarah -clark -labour -hate -terminal -publishers -nights -behalf -caribbean -liquid -rice -nebraska -loop -salary -reservation -foods -gourmet -guard -properly -orleans -saving -nfl -remaining -empire -resume -twenty -newly -raise -prepare -avatar -gary -depending -illegal -expansion -vary -hundreds -rome -arab -lincoln -helped -premier -tomorrow -purchased -milk -decide -consent -drama -visiting -performing -downtown -keyboard -contest -collected -nw -bands -boot -suitable -ff -absolutely -millions -lunch -audit -push -chamber -guinea -findings -muscle -featuring -iso -implement -clicking -scheduled -polls -typical -tower -yours -sum -misc -calculator -significantly -chicken -temporary -attend -shower -alan -sending -jason -tonight -dear -sufficient -holdem -shell -province -catholic -oak -vat -awareness -vancouver -governor -beer -seemed -contribution -measurement -swimming -spyware -formula -constitution -packaging -solar -jose -catch -jane -pakistan -ps -reliable -consultation -northwest -sir -doubt -earn -finder -unable -periods -classroom -tasks -democracy -attacks -kim -wallpaper -merchandise -const -resistance -doors -symptoms -resorts -biggest -memorial -visitor -twin -forth -insert -baltimore -gateway -ky -dont -alumni -drawing -candidates -charlotte -ordered -biological -fighting -transition -happens -preferences -spy -romance -instrument -bruce -split -themes -powers -heaven -br -bits -pregnant -twice -classification -focused -egypt -physician -hollywood -bargain -wikipedia -cellular -norway -vermont -asking -blocks -normally -lo -spiritual -hunting -diabetes -suit -ml -shift -chip -res -sit -bodies -photographs -cutting -wow -simon -writers -marks -flexible -loved -favourites -mapping -numerous -relatively -birds -satisfaction -represents -char -indexed -pittsburgh -superior -preferred -saved -paying -cartoon -shots -intellectual -moore -granted -choices -carbon -spending -comfortable -magnetic -interaction -listening -effectively -registry -crisis -outlook -massive -denmark -employed -bright -treat -header -cs -poverty -formed -piano -echo -que -grid -sheets -patrick -experimental -puerto -revolution -consolidation -displays -plasma -allowing -earnings -voip -mystery -landscape -dependent -mechanical -journey -delaware -bidding -consultants -risks -banner -applicant -charter -fig -barbara -cooperation -counties -acquisition -ports -implemented -sf -directories -recognized -dreams -blogger -notification -kg -licensing -stands -teach -occurred -textbooks -rapid -pull -hairy -diversity -cleveland -ut -reverse -deposit -seminar -investments -latina -nasa -wheels -specify -accessibility -dutch -sensitive -templates -formats -tab -depends -boots -holds -router -concrete -si -editing -poland -folder -womens -css -completion -upload -pulse -universities -technique -contractors -voting -courts -notices -subscriptions -calculate -mc -detroit -alexander -broadcast -converted -metro -toshiba -anniversary -improvements -strip -specification -pearl -accident -nick -accessible -accessory -resident -plot -qty -possibly -airline -typically -representation -regard -pump -exists -arrangements -smooth -conferences -uniprotkb -strike -consumption -birmingham -flashing -lp -narrow -afternoon -threat -surveys -sitting -putting -consultant -controller -ownership -committees -legislative -researchers -vietnam -trailer -anne -castle -gardens -missed -malaysia -unsubscribe -antique -labels -willing -bio -molecular -acting -heads -stored -exam -logos -residence -attorneys -antiques -density -hundred -ryan -operators -strange -sustainable -philippines -statistical -beds -mention -innovation -pcs -employers -grey -parallel -honda -amended -operate -bills -bold -bathroom -stable -opera -definitions -von -doctors -lesson -cinema -asset -ag -scan -elections -drinking -reaction -blank -enhanced -entitled -severe -generate -stainless -newspapers -hospitals -vi -deluxe -humor -aged -monitors -exception -lived -duration -bulk -successfully -indonesia -pursuant -sci -fabric -edt -visits -primarily -tight -domains -capabilities -pmid -contrast -recommendation -flying -recruitment -sin -berlin -cute -organized -ba -para -siemens -adoption -improving -cr -expensive -meant -capture -pounds -buffalo -organisations -plane -pg -explained -seed -programmes -desire -expertise -mechanism -camping -ee -jewellery -meets -welfare -peer -caught -eventually -marked -driven -measured -medline -bottle -agreements -considering -innovative -marshall -massage -rubber -conclusion -closing -tampa -thousand -meat -legend -grace -susan -ing -ks -adams -python -monster -alex -bang -villa -bone -columns -disorders -bugs -collaboration -hamilton -detection -ftp -cookies -inner -formation -tutorial -med -engineers -entity -cruises -gate -holder -proposals -moderator -sw -tutorials -settlement -portugal -lawrence -roman -duties -valuable -tone -collectables -ethics -forever -dragon -busy -captain -fantastic -imagine -brings -heating -leg -neck -hd -wing -governments -purchasing -scripts -abc -stereo -appointed -taste -dealing -commit -tiny -operational -rail -airlines -liberal -livecam -jay -trips -gap -sides -tube -turns -corresponding -descriptions -cache -belt -jacket -determination -animation -oracle -er -matthew -lease -productions -aviation -hobbies -proud -excess -disaster -console -commands -jr -telecommunications -instructor -giant -achieved -injuries -shipped -seats -approaches -biz -alarm -voltage -anthony -nintendo -usual -loading -stamps -appeared -franklin -angle -rob -vinyl -highlights -mining -designers -melbourne -ongoing -worst -imaging -betting -scientists -liberty -wyoming -blackjack -argentina -era -convert -possibility -analyst -commissioner -dangerous -garage -exciting -reliability -thongs -gcc -unfortunately -respectively -volunteers -attachment -ringtone -finland -morgan -derived -pleasure -honor -asp -oriented -eagle -desktops -pants -columbus -nurse -prayer -appointment -workshops -hurricane -quiet -luck -postage -producer -represented -mortgages -dial -responsibilities -cheese -comic -carefully -jet -productivity -investors -crown -par -underground -diagnosis -maker -crack -principle -picks -vacations -gang -semester -calculated -fetish -applies -casinos -appearance -smoke -apache -filters -incorporated -nv -craft -cake -notebooks -apart -fellow -blind -lounge -mad -algorithm -semi -coins -andy -gross -strongly -cafe -valentine -hilton -ken -proteins -horror -su -exp -familiar -capable -douglas -debian -till -involving -pen -investing -christopher -admission -epson -shoe -elected -carrying -victory -sand -madison -terrorism -joy -editions -cpu -mainly -ethnic -ran -parliament -actor -finds -seal -situations -fifth -allocated -citizen -vertical -corrections -structural -municipal -describes -prize -sr -occurs -jon -absolute -disabilities -consists -anytime -substance -prohibited -addressed -lies -pipe -soldiers -nr -guardian -lecture -simulation -layout -initiatives -ill -concentration -classics -lbs -lay -interpretation -horses -lol -dirty -deck -wayne -donate -taught -bankruptcy -mp -worker -optimization -alive -temple -substances -prove -discovered -wings -breaks -genetic -restrictions -participating -waters -promise -thin -exhibition -prefer -ridge -cabinet -modem -harris -mph -bringing -sick -dose -evaluate -tiffany -tropical -collect -bet -composition -toyota -streets -nationwide -vector -definitely -shaved -turning -buffer -purple -existence -commentary -larry -limousines -developments -def -immigration -destinations -lets -mutual -pipeline -necessarily -syntax -li -attribute -prison -skill -chairs -nl -everyday -apparently -surrounding -mountains -moves -popularity -inquiry -ethernet -checked -exhibit -throw -trend -sierra -visible -cats -desert -postposted -ya -oldest -rhode -nba -coordinator -obviously -mercury -steven -handbook -greg -navigate -worse -summit -victims -epa -spaces -fundamental -burning -escape -coupons -somewhat -receiver -substantial -tr -progressive -cialis -bb -boats -glance -scottish -championship -arcade -richmond -sacramento -impossible -ron -russell -tells -obvious -fiber -depression -graph -covering -platinum -judgment -bedrooms -talks -filing -foster -modeling -passing -awarded -testimonials -trials -tissue -nz -memorabilia -clinton -masters -bonds -cartridge -alberta -explanation -folk -org -commons -cincinnati -subsection -fraud -electricity -permitted -spectrum -arrival -okay -pottery -emphasis -roger -aspect -workplace -awesome -mexican -confirmed -counts -priced -wallpapers -hist -crash -lift -desired -inter -closer -assumes -heights -shadow -riding -infection -firefox -lisa -expense -grove -eligibility -venture -clinic -korean -healing -princess -mall -entering -packet -spray -studios -involvement -dad -buttons -placement -observations -vbulletin -funded -thompson -winners -extend -roads -subsequent -pat -dublin -rolling -fell -motorcycle -yard -disclosure -establishment -memories -nelson -te -arrived -creates -faces -tourist -av -mayor -murder -sean -adequate -senator -yield -presentations -grades -cartoons -pour -digest -reg -lodging -tion -dust -hence -wiki -entirely -replaced -radar -rescue -undergraduate -losses -combat -reducing -stopped -occupation -lakes -donations -associations -citysearch -closely -radiation -diary -seriously -kings -shooting -kent -adds -nsw -ear -flags -pci -baker -launched -elsewhere -pollution -conservative -guestbook -shock -effectiveness -walls -abroad -ebony -tie -ward -drawn -arthur -ian -visited -roof -walker -demonstrate -atmosphere -suggests -kiss -beast -ra -operated -experiment -targets -overseas -purchases -dodge -counsel -federation -pizza -invited -yards -assignment -chemicals -gordon -mod -farmers -rc -queries -bmw -rush -ukraine -absence -nearest -cluster -vendors -mpeg -whereas -yoga -serves -woods -surprise -lamp -rico -partial -shoppers -phil -everybody -couples -nashville -ranking -jokes -cst -http -ceo -simpson -twiki -sublime -counseling -palace -acceptable -satisfied -glad -wins -measurements -verify -globe -trusted -copper -milwaukee -rack -medication -warehouse -shareware -ec -rep -dicke -kerry -receipt -supposed -ordinary -nobody -ghost -violation -configure -stability -mit -applying -southwest -boss -pride -institutional -expectations -independence -knowing -reporter -metabolism -keith -champion -cloudy -linda -ross -personally -chile -anna -plenty -solo -sentence -throat -ignore -maria -uniform -excellence -wealth -tall -rm -somewhere -vacuum -dancing -attributes -recognize -brass -writes -plaza -pdas -outcomes -survival -quest -publish -sri -screening -toe -thumbnail -trans -jonathan -whenever -nova -lifetime -api -pioneer -booty -forgotten -acrobat -plates -acres -venue -athletic -thermal -essays -behaviour -vital -telling -fairly -coastal -config -cf -charity -intelligent -edinburgh -vt -excel -modes -obligation -campbell -wake -stupid -harbor -hungary -traveler -urw -segment -realize -regardless -lan -enemy -puzzle -rising -aluminum -wells -wishlist -opens -insight -sms -restricted -republican -secrets -lucky -latter -merchants -thick -trailers -repeat -syndrome -philips -attendance -penalty -drum -glasses -enables -nec -iraqi -builder -vista -jessica -chips -terry -flood -foto -ease -arguments -amsterdam -arena -adventures -pupils -stewart -announcement -tabs -outcome -appreciate -expanded -casual -grown -polish -lovely -extras -gm -centres -jerry -clause -smile -lands -ri -troops -indoor -bulgaria -armed -broker -charger -regularly -believed -pine -cooling -tend -gulf -rt -rick -trucks -cp -mechanisms -divorce -laura -shopper -tokyo -partly -nikon -customize -tradition -candy -pills -tiger -donald -folks -sensor -exposed -telecom -hunt -angels -deputy -indicators -sealed -thai -emissions -physicians -loaded -fred -complaint -scenes -experiments -afghanistan -dd -boost -spanking -scholarship -governance -mill -founded -supplements -chronic -icons -moral -den -catering -aud -finger -keeps -pound -locate -camcorder -pl -trained -burn -implementing -roses -labs -ourselves -bread -tobacco -wooden -motors -tough -roberts -incident -gonna -dynamics -lie -crm -rf -conversation -decrease -chest -pension -billy -revenues -emerging -worship -capability -ak -fe -craig -herself -producing -churches -precision -damages -reserves -contributed -solve -shorts -reproduction -minority -td -diverse -amp -ingredients -sb -ah -johnny -sole -franchise -recorder -complaints -facing -sm -nancy -promotions -tones -passion -rehabilitation -maintaining -sight -laid -clay -defence -patches -weak -refund -usc -towns -environments -trembl -divided -blvd -reception -amd -wise -emails -cyprus -wv -odds -correctly -insider -seminars -consequences -makers -hearts -geography -appearing -integrity -worry -ns -discrimination -eve -carter -legacy -marc -pleased -danger -vitamin -widely -processed -phrase -genuine -raising -implications -functionality -paradise -hybrid -reads -roles -intermediate -emotional -sons -leaf -pad -glory -platforms -ja -bigger -billing -diesel -versus -combine -overnight -geographic -exceed -bs -rod -saudi -fault -cuba -hrs -preliminary -districts -introduce -silk -promotional -kate -chevrolet -babies -bi -karen -compiled -romantic -revealed -specialists -generator -albert -examine -jimmy -graham -suspension -bristol -margaret -compaq -sad -correction -wolf -slowly -authentication -communicate -rugby -supplement -showtimes -cal -portions -infant -promoting -sectors -samuel -fluid -grounds -fits -kick -regards -meal -ta -hurt -machinery -bandwidth -unlike -equation -baskets -probability -pot -dimension -wright -img -barry -proven -schedules -admissions -cached -warren -slip -studied -reviewer -involves -quarterly -rpm -profits -devil -grass -comply -marie -florist -illustrated -cherry -continental -alternate -deutsch -achievement -limitations -kenya -webcam -cuts -funeral -nutten -earrings -enjoyed -automated -chapters -pee -charlie -quebec -passenger -convenient -dennis -mars -francis -tvs -sized -manga -noticed -socket -silent -literary -egg -mhz -signals -caps -orientation -pill -theft -childhood -swing -symbols -lat -meta -humans -analog -facial -choosing -talent -dated -flexibility -seeker -wisdom -shoot -boundary -mint -packard -offset -payday -philip -elite -gi -spin -holders -believes -swedish -poems -deadline -jurisdiction -robot -displaying -witness -collins -equipped -stages -encouraged -sur -winds -powder -broadway -acquired -assess -wash -cartridges -stones -entrance -gnome -roots -declaration -losing -attempts -gadgets -noble -glasgow -automation -impacts -rev -gospel -advantages -shore -loves -induced -ll -knight -preparing -loose -aims -recipient -linking -extensions -appeals -cl -earned -illness -islamic -athletics -southeast -ieee -ho -alternatives -pending -parker -determining -lebanon -corp -personalized -kennedy -gt -sh -conditioning -teenage -soap -ae -triple -cooper -nyc -vincent -jam -secured -unusual -answered -partnerships -destruction -slots -increasingly -migration -disorder -routine -toolbar -basically -rocks -conventional -titans -applicants -wearing -axis -sought -genes -mounted -habitat -firewall -median -guns -scanner -herein -occupational -animated -judicial -rio -hs -adjustment -hero -integer -treatments -bachelor -attitude -camcorders -engaged -falling -basics -montreal -carpet -rv -struct -lenses -binary -genetics -attended -difficulty -punk -collective -coalition -pi -dropped -enrollment -duke -walter -ai -pace -besides -wage -producers -ot -collector -arc -hosts -interfaces -advertisers -moments -atlas -strings -dawn -representing -observation -feels -torture -carl -deleted -coat -mitchell -mrs -rica -restoration -convenience -returning -ralph -opposition -container -yr -defendant -warner -confirmation -app -embedded -inkjet -supervisor -wizard -corps -actors -liver -peripherals -liable -brochure -morris -bestsellers -petition -eminem -recall -antenna -picked -assumed -departure -minneapolis -belief -killing -bikini -memphis -shoulder -decor -lookup -texts -harvard -brokers -roy -ion -diameter -ottawa -doll -ic -podcast -seasons -peru -interactions -refine -bidder -singer -evans -herald -literacy -fails -aging -nike -intervention -fed -plugin -attraction -diving -invite -modification -alice -latinas -suppose -customized -reed -involve -moderate -terror -younger -thirty -mice -opposite -understood -rapidly -dealtime -ban -temp -intro -mercedes -zus -assurance -clerk -happening -vast -mills -outline -amendments -tramadol -holland -receives -jeans -metropolitan -compilation -verification -fonts -ent -odd -wrap -refers -mood -favor -veterans -quiz -mx -sigma -gr -attractive -xhtml -occasion -recordings -jefferson -victim -demands -sleeping -careful -ext -beam -gardening -obligations -arrive -orchestra -sunset -tracked -moreover -minimal -polyphonic -lottery -tops -framed -aside -outsourcing -licence -adjustable -allocation -michelle -essay -discipline -amy -ts -demonstrated -dialogue -identifying -alphabetical -camps -declared -dispatched -aaron -handheld -trace -disposal -shut -florists -packs -ge -installing -switches -romania -voluntary -ncaa -thou -consult -phd -greatly -blogging -mask -cycling -midnight -ng -commonly -pe -photographer -inform -turkish -coal -cry -messaging -pentium -quantum -murray -intent -tt -zoo -largely -pleasant -announce -constructed -additions -requiring -spoke -aka -arrow -engagement -sampling -rough -weird -tee -refinance -lion -inspired -holes -weddings -blade -suddenly -oxygen -cookie -meals -canyon -goto -meters -merely -calendars -arrangement -conclusions -passes -bibliography -pointer -compatibility -stretch -durham -furthermore -permits -cooperative -muslim -xl -neil -sleeve -netscape -cleaner -cricket -beef -feeding -stroke -township -rankings -measuring -cad -hats -robin -robinson -jacksonville -strap -headquarters -sharon -crowd -tcp -transfers -surf -olympic -transformation -remained -attachments -dv -dir -entities -customs -administrators -personality -rainbow -hook -roulette -decline -gloves -israeli -medicare -cord -skiing -cloud -facilitate -subscriber -valve -val -hewlett -explains -proceed -flickr -feelings -knife -jamaica -priorities -shelf -bookstore -timing -liked -parenting -adopt -denied -fotos -incredible -britney -freeware -donation -outer -crop -deaths -rivers -commonwealth -pharmaceutical -manhattan -tales -katrina -workforce -islam -nodes -tu -fy -thumbs -seeds -cited -lite -ghz -hub -targeted -organizational -skype -realized -twelve -founder -decade -gamecube -rr -dispute -portuguese -tired -titten -adverse -everywhere -excerpt -eng -steam -discharge -ef -drinks -ace -voices -acute -halloween -climbing -stood -sing -tons -perfume -carol -honest -albany -hazardous -restore -stack -methodology -somebody -sue -ep -housewares -reputation -resistant -democrats -recycling -hang -gbp -curve -creator -amber -qualifications -museums -coding -slideshow -tracker -variation -passage -transferred -trunk -hiking -lb -pierre -jelsoft -headset -photograph -oakland -colombia -waves -camel -distributor -lamps -underlying -hood -wrestling -suicide -archived -photoshop -jp -chi -bt -arabia -gathering -projection -juice -chase -mathematical -logical -sauce -fame -extract -specialized -diagnostic -panama -indianapolis -af -payable -corporations -courtesy -criticism -automobile -confidential -rfc -statutory -accommodations -athens -northeast -downloaded -judges -sl -seo -retired -isp -remarks -detected -decades -paintings -walked -arising -nissan -bracelet -ins -eggs -juvenile -injection -yorkshire -populations -protective -afraid -acoustic -railway -cassette -initially -indicator -pointed -hb -jpg -causing -mistake -norton -locked -eliminate -tc -fusion -mineral -sunglasses -ruby -steering -beads -fortune -preference -canvas -threshold -parish -claimed -screens -cemetery -planner -croatia -flows -stadium -venezuela -exploration -mins -fewer -sequences -coupon -nurses -ssl -stem -proxy -astronomy -lanka -opt -edwards -drew -contests -flu -translate -announces -mlb -costume -tagged -berkeley -voted -killer -bikes -gates -adjusted -rap -tune -bishop -pulled -corn -gp -shaped -compression -seasonal -establishing -farmer -counters -puts -constitutional -grew -perfectly -tin -slave -instantly -cultures -norfolk -coaching -examined -trek -encoding -litigation -submissions -oem -heroes -painted -lycos -ir -zdnet -broadcasting -horizontal -artwork -cosmetic -resulted -portrait -terrorist -informational -ethical -carriers -ecommerce -mobility -floral -builders -ties -struggle -schemes -suffering -neutral -fisher -rat -spears -prospective -bedding -ultimately -joining -heading -equally -artificial -bearing -spectacular -coordination -connector -brad -combo -seniors -worlds -guilty -affiliated -activation -naturally -haven -tablet -jury -dos -tail -subscribers -charm -lawn -violent -mitsubishi -underwear -basin -soup -potentially -ranch -constraints -crossing -inclusive -dimensional -cottage -drunk -considerable -crimes -resolved -mozilla -byte -toner -nose -latex -branches -anymore -oclc -delhi -holdings -alien -locator -selecting -processors -pantyhose -plc -broke -nepal -zimbabwe -difficulties -juan -complexity -msg -constantly -browsing -resolve -barcelona -presidential -documentary -cod -territories -melissa -moscow -thesis -thru -jews -nylon -palestinian -discs -rocky -bargains -frequent -trim -nigeria -ceiling -pixels -ensuring -hispanic -cv -cb -legislature -hospitality -gen -anybody -procurement -diamonds -espn -fleet -untitled -bunch -totals -marriott -singing -theoretical -afford -exercises -starring -referral -nhl -surveillance -optimal -quit -distinct -protocols -lung -highlight -substitute -inclusion -hopefully -brilliant -turner -sucking -cents -reuters -ti -fc -gel -todd -spoken -omega -evaluated -stayed -civic -assignments -fw -manuals -doug -sees -termination -watched -saver -thereof -grill -households -gs -redeem -rogers -grain -aaa -authentic -regime -wanna -wishes -bull -montgomery -architectural -louisville -depend -differ -macintosh -movements -ranging -monica -repairs -breath -amenities -virtually -cole -mart -candle -hanging -colored -authorization -tale -verified -lynn -formerly -projector -bp -situated -comparative -std -seeks -herbal -loving -strictly -routing -docs -stanley -psychological -surprised -retailer -vitamins -elegant -gains -renewal -vid -genealogy -opposed -deemed -scoring -expenditure -brooklyn -liverpool -sisters -critics -connectivity -spots -oo -algorithms -hacker -madrid -similarly -margin -coin -solely -fake -salon -collaborative -norman -fda -excluding -turbo -headed -voters -cure -madonna -commander -arch -ni -murphy -thinks -thats -suggestion -hdtv -soldier -phillips -asin -aimed -justin -bomb -harm -interval -mirrors -spotlight -tricks -reset -brush -investigate -thy -expansys -panels -repeated -assault -connecting -spare -logistics -deer -kodak -tongue -bowling -tri -danish -pal -monkey -proportion -filename -skirt -florence -invest -honey -um -analyses -drawings -significance -scenario -ye -fs -lovers -atomic -approx -symposium -arabic -gauge -essentials -junction -protecting -nn -faced -mat -rachel -solving -transmitted -weekends -screenshots -produces -oven -ted -intensive -chains -kingston -sixth -engage -deviant -noon -switching -quoted -adapters -correspondence -farms -imports -supervision -cheat -bronze -expenditures -sandy -separation -testimony -suspect -celebrities -macro -sender -mandatory -boundaries -crucial -syndication -gym -celebration -kde -adjacent -filtering -tuition -spouse -exotic -viewer -signup -threats -luxembourg -puzzles -reaching -vb -damaged -cams -receptor -laugh -joel -surgical -destroy -citation -pitch -autos -yo -premises -perry -proved -offensive -imperial -dozen -benjamin -deployment -teeth -cloth -studying -colleagues -stamp -lotus -salmon -olympus -separated -proc -cargo -tan -directive -fx -salem -mate -dl -starter -upgrades -likes -butter -pepper -weapon -luggage -burden -chef -tapes -zones -races -isle -stylish -slim -maple -luke -grocery -offshore -governing -retailers -depot -kenneth -comp -alt -pie -blend -harrison -ls -julie -occasionally -cbs -attending -emission -pete -spec -finest -realty -janet -bow -penn -recruiting -apparent -instructional -phpbb -autumn -traveling -probe -midi -permissions -biotechnology -toilet -ranked -jackets -routes -packed -excited -outreach -helen -mounting -recover -tied -lopez -balanced -prescribed -catherine -timely -talked -debug -delayed -chuck -reproduced -hon -dale -explicit -calculation -villas -ebook -consolidated -exclude -peeing -occasions -brooks -equations -newton -oils -sept -exceptional -anxiety -bingo -whilst -spatial -respondents -unto -lt -ceramic -prompt -precious -minds -annually -considerations -scanners -atm -xanax -eq -pays -fingers -sunny -ebooks -delivers -je -queensland -necklace -musicians -leeds -composite -unavailable -cedar -arranged -lang -theaters -advocacy -raleigh -stud -fold -essentially -designing -threaded -uv -qualify -blair -hopes -assessments -cms -mason -diagram -burns -pumps -footwear -sg -vic -beijing -peoples -victor -mario -pos -attach -licenses -utils -removing -advised -brunswick -spider -phys -ranges -pairs -sensitivity -trails -preservation -hudson -isolated -calgary -interim -assisted -divine -streaming -approve -chose -compound -intensity -technological -syndicate -abortion -dialog -venues -blast -wellness -calcium -newport -antivirus -addressing -pole -discounted -indians -shield -harvest -membrane -prague -previews -bangladesh -constitute -locally -concluded -pickup -desperate -mothers -nascar -iceland -demonstration -governmental -manufactured -candles -graduation -mega -bend -sailing -variations -moms -sacred -addiction -morocco -chrome -tommy -springfield -refused -brake -exterior -greeting -ecology -oliver -congo -glen -botswana -nav -delays -synthesis -olive -undefined -unemployment -cyber -verizon -scored -enhancement -newcastle -clone -velocity -lambda -relay -composed -tears -performances -oasis -baseline -cab -angry -fa -societies -silicon -brazilian -identical -petroleum -compete -ist -norwegian -lover -belong -honolulu -beatles -lips -retention -exchanges -pond -rolls -thomson -barnes -soundtrack -wondering -malta -daddy -lc -ferry -rabbit -profession -seating -dam -cnn -separately -physiology -lil -collecting -das -exports -omaha -tire -participant -scholarships -recreational -dominican -chad -electron -loads -friendship -heather -passport -motel -unions -treasury -warrant -sys -solaris -frozen -occupied -josh -royalty -scales -rally -observer -sunshine -strain -drag -ceremony -somehow -arrested -expanding -provincial -investigations -icq -ripe -yamaha -rely -medications -hebrew -gained -rochester -dying -laundry -stuck -solomon -placing -stops -homework -adjust -assessed -advertiser -enabling -encryption -filling -downloadable -sophisticated -imposed -silence -scsi -focuses -soviet -possession -cu -laboratories -treaty -vocal -trainer -organ -stronger -volumes -advances -vegetables -lemon -toxic -dns -thumbnails -darkness -pty -ws -nuts -nail -bizrate -vienna -implied -span -stanford -sox -stockings -joke -respondent -packing -statute -rejected -satisfy -destroyed -shelter -chapel -gamespot -manufacture -layers -wordpress -guided -vulnerability -accountability -celebrate -accredited -appliance -compressed -bahamas -powell -mixture -bench -univ -tub -rider -scheduling -radius -perspectives -mortality -logging -hampton -christians -borders -therapeutic -pads -butts -inns -bobby -impressive -sheep -accordingly -architect -railroad -lectures -challenging -wines -nursery -harder -cups -ash -microwave -cheapest -accidents -travesti -relocation -stuart -contributors -salvador -ali -salad -np -monroe -tender -violations -foam -temperatures -paste -clouds -competitions -discretion -tft -tanzania -preserve -jvc -poem -unsigned -staying -cosmetics -easter -theories -repository -praise -jeremy -venice -concentrations -estonia -christianity -veteran -streams -landing -signing -executed -katie -negotiations -realistic -dt -cgi -showcase -integral -asks -relax -namibia -generating -christina -congressional -synopsis -hardly -prairie -reunion -composer -bean -sword -absent -photographic -sells -ecuador -hoping -accessed -spirits -modifications -coral -pixel -float -colin -bias -imported -paths -bubble -por -acquire -contrary -millennium -tribune -vessel -acids -focusing -viruses -cheaper -admitted -dairy -admit -mem -fancy -equality -samoa -gc -achieving -tap -stickers -fisheries -exceptions -reactions -leasing -lauren -beliefs -ci -macromedia -companion -squad -analyze -ashley -scroll -relate -divisions -swim -wages -additionally -suffer -forests -fellowship -nano -invalid -concerts -martial -males -victorian -retain -colours -execute -tunnel -genres -cambodia -patents -copyrights -yn -chaos -lithuania -mastercard -wheat -chronicles -obtaining -beaver -updating -distribute -readings -decorative -kijiji -confused -compiler -enlargement -eagles -bases -vii -accused -bee -campaigns -unity -loud -conjunction -bride -rats -defines -airports -instances -indigenous -begun -cfr -brunette -packets -anchor -socks -validation -parade -corruption -stat -trigger -incentives -cholesterol -gathered -essex -slovenia -notified -differential -beaches -folders -dramatic -surfaces -terrible -routers -cruz -pendant -dresses -baptist -scientist -starsmerchant -hiring -clocks -arthritis -bios -females -wallace -nevertheless -reflects -taxation -fever -pmc -cuisine -surely -practitioners -transcript -myspace -theorem -inflation -thee -nb -ruth -pray -stylus -compounds -pope -drums -contracting -arnold -structured -reasonably -jeep -chicks -bare -hung -cattle -mba -radical -graduates -rover -recommends -controlling -treasure -reload -distributors -flame -levitra -tanks -assuming -monetary -elderly -pit -arlington -mono -particles -floating -extraordinary -tile -indicating -bolivia -spell -hottest -stevens -coordinate -kuwait -exclusively -emily -alleged -limitation -widescreen -compile -webster -struck -rx -illustration -plymouth -warnings -construct -apps -inquiries -bridal -annex -mag -gsm -inspiration -tribal -curious -affecting -freight -rebate -meetup -eclipse -sudan -ddr -downloading -rec -shuttle -aggregate -stunning -cycles -affects -forecasts -detect -actively -ciao -ampland -knee -prep -pb -complicated -chem -fastest -butler -shopzilla -injured -decorating -payroll -cookbook -expressions -ton -courier -uploaded -shakespeare -hints -collapse -americas -connectors -unlikely -oe -gif -pros -conflicts -techno -beverage -tribute -wired -elvis -immune -latvia -travelers -forestry -barriers -cant -jd -rarely -gpl -infected -offerings -martha -genesis -barrier -argue -incorrect -trains -metals -bicycle -furnishings -letting -arise -guatemala -celtic -thereby -irc -jamie -particle -perception -minerals -advise -humidity -bottles -boxing -wy -dm -bangkok -renaissance -pathology -sara -bra -ordinance -hughes -photographers -infections -jeffrey -chess -operates -brisbane -configured -survive -oscar -festivals -menus -joan -possibilities -duck -reveal -canal -amino -phi -contributing -herbs -clinics -mls -cow -manitoba -analytical -missions -watson -lying -costumes -strict -dive -saddam -circulation -drill -offense -bryan -cet -protest -assumption -jerusalem -hobby -tries -invention -nickname -fiji -technician -inline -executives -enquiries -washing -audi -staffing -cognitive -exploring -trick -enquiry -closure -raid -ppc -timber -volt -intense -div -playlist -registrar -showers -supporters -ruling -steady -dirt -statutes -withdrawal -myers -drops -predicted -wider -saskatchewan -jc -cancellation -plugins -enrolled -sensors -screw -ministers -publicly -hourly -blame -geneva -freebsd -veterinary -acer -prostores -reseller -dist -handed -suffered -intake -informal -relevance -incentive -butterfly -tucson -mechanics -heavily -swingers -fifty -headers -mistakes -numerical -ons -geek -uncle -defining -counting -reflection -sink -accompanied -assure -invitation -devoted -princeton -jacob -sodium -randy -spirituality -hormone -meanwhile -proprietary -timothy -childrens -brick -grip -naval -thumbzilla -medieval -porcelain -avi -bridges -pichunter -captured -watt -thehun -decent -casting -dayton -translated -shortly -cameron -columnists -pins -carlos -reno -donna -andreas -warrior -diploma -cabin -innocent -scanning -ide -consensus -polo -valium -copying -rpg -delivering -cordless -patricia -horn -eddie -uganda -fired -journalism -pd -prot -trivia -adidas -perth -frog -grammar -intention -syria -disagree -klein -harvey -tires -logs -undertaken -tgp -hazard -retro -leo -statewide -semiconductor -gregory -episodes -boolean -circular -anger -diy -mainland -illustrations -suits -chances -interact -snap -happiness -arg -substantially -bizarre -glenn -ur -auckland -olympics -fruits -identifier -geo -ribbon -calculations -doe -jpeg -conducting -startup -suzuki -trinidad -ati -kissing -wal -handy -swap -exempt -crops -reduces -accomplished -calculators -geometry -impression -abs -slovakia -flip -guild -correlation -gorgeous -capitol -sim -dishes -rna -barbados -chrysler -nervous -refuse -extends -fragrance -mcdonald -replica -plumbing -brussels -tribe -neighbors -trades -superb -buzz -transparent -nuke -rid -trinity -charleston -handled -legends -boom -calm -champions -floors -selections -projectors -inappropriate -exhaust -comparing -shanghai -speaks -burton -vocational -davidson -copied -scotia -farming -gibson -pharmacies -fork -troy -ln -roller -introducing -batch -organize -appreciated -alter -nicole -latino -ghana -edges -uc -mixing -handles -skilled -fitted -albuquerque -harmony -distinguished -asthma -projected -assumptions -shareholders -twins -developmental -rip -zope -regulated -triangle -amend -anticipated -oriental -reward -windsor -zambia -completing -gmbh -buf -ld -hydrogen -webshots -sprint -comparable -chick -advocate -sims -confusion -copyrighted -tray -inputs -warranties -genome -escorts -documented -thong -medal -paperbacks -coaches -vessels -harbour -walks -sol -keyboards -sage -knives -eco -vulnerable -arrange -artistic -bat -honors -booth -indie -reflected -unified -bones -breed -detector -ignored -polar -fallen -precise -sussex -respiratory -notifications -msgid -mainstream -invoice -evaluating -lip -subcommittee -sap -gather -suse -maternity -backed -alfred -colonial -mf -carey -motels -forming -embassy -cave -journalists -danny -rebecca -slight -proceeds -indirect -amongst -wool -foundations -msgstr -arrest -volleyball -mw -adipex -horizon -nu -deeply -toolbox -ict -marina -liabilities -prizes -bosnia -browsers -decreased -patio -dp -tolerance -surfing -creativity -lloyd -describing -optics -pursue -lightning -overcome -eyed -ou -quotations -grab -inspector -attract -brighton -beans -bookmarks -ellis -disable -snake -succeed -leonard -lending -oops -reminder -xi -searched -behavioral -riverside -bathrooms -plains -sku -ht -raymond -insights -abilities -initiated -sullivan -za -midwest -karaoke -trap -lonely -fool -ve -nonprofit -lancaster -suspended -hereby -observe -julia -containers -attitudes -karl -berry -collar -simultaneously -racial -integrate -bermuda -amanda -sociology -mobiles -screenshot -exhibitions -kelkoo -confident -retrieved -exhibits -officially -consortium -dies -terrace -bacteria -pts -replied -seafood -novels -rh -rrp -recipients -ought -delicious -traditions -fg -jail -safely -finite -kidney -periodically -fixes -sends -durable -mazda -allied -throws -moisture -hungarian -roster -referring -symantec -spencer -wichita -nasdaq -uruguay -ooo -hz -transform -timer -tablets -tuning -gotten -educators -tyler -futures -vegetable -verse -highs -humanities -independently -wanting -custody -scratch -launches -ipaq -alignment -henderson -bk -britannica -comm -ellen -competitors -nhs -rocket -aye -bullet -towers -racks -lace -nasty -visibility -latitude -consciousness -ste -tumor -ugly -deposits -beverly -mistress -encounter -trustees -watts -duncan -reprints -hart -bernard -resolutions -ment -accessing -forty -tubes -attempted -col -midlands -priest -floyd -ronald -analysts -queue -dx -sk -trance -locale -nicholas -biol -yu -bundle -hammer -invasion -witnesses -runner -rows -administered -notion -sq -skins -mailed -oc -fujitsu -spelling -arctic -exams -rewards -beneath -strengthen -defend -aj -frederick -medicaid -treo -infrared -seventh -gods -une -welsh -belly -aggressive -tex -advertisements -quarters -stolen -cia -soonest -haiti -disturbed -determines -sculpture -poly -ears -dod -wp -fist -naturals -neo -motivation -lenders -pharmacology -fitting -fixtures -bloggers -mere -agrees -passengers -quantities -petersburg -consistently -powerpoint -cons -surplus -elder -sonic -obituaries -cheers -dig -taxi -punishment -appreciation -subsequently -om -belarus -nat -zoning -gravity -providence -thumb -restriction -incorporate -backgrounds -treasurer -guitars -essence -flooring -lightweight -ethiopia -tp -mighty -athletes -humanity -transcription -jm -holmes -complications -scholars -dpi -scripting -gis -remembered -galaxy -chester -snapshot -caring -loc -worn -synthetic -shaw -vp -segments -testament -expo -dominant -twist -specifics -itunes -stomach -partially -buried -cn -newbie -minimize -darwin -ranks -wilderness -debut -generations -tournaments -bradley -deny -anatomy -bali -judy -sponsorship -headphones -fraction -trio -proceeding -cube -defects -volkswagen -uncertainty -breakdown -milton -marker -reconstruction -subsidiary -strengths -clarity -rugs -sandra -adelaide -encouraging -furnished -monaco -settled -folding -emirates -terrorists -airfare -comparisons -beneficial -distributions -vaccine -belize -fate -viewpicture -promised -volvo -penny -robust -bookings -threatened -minolta -republicans -discusses -gui -porter -gras -jungle -ver -rn -responded -rim -abstracts -zen -ivory -alpine -dis -prediction -pharmaceuticals -andale -fabulous -remix -alias -thesaurus -individually -battlefield -literally -newer -kay -ecological -spice -oval -implies -cg -soma -ser -cooler -appraisal -consisting -maritime -periodic -submitting -overhead -ascii -prospect -shipment -breeding -citations -geographical -donor -mozambique -tension -href -benz -trash -shapes -wifi -tier -fwd -earl -manor -envelope -diane -homeland -disclaimers -championships -excluded -andrea -breeds -rapids -disco -sheffield -bailey -aus -endif -finishing -emotions -wellington -incoming -prospects -lexmark -cleaners -bulgarian -hwy -eternal -cashiers -guam -cite -aboriginal -remarkable -rotation -nam -preventing -productive -boulevard -eugene -ix -gdp -pig -metric -compliant -minus -penalties -bennett -imagination -hotmail -refurbished -joshua -armenia -varied -grande -closest -activated -actress -mess -conferencing -assign -armstrong -politicians -trackbacks -lit -accommodate -tigers -aurora -una -slides -milan -premiere -lender -villages -shade -chorus -christine -rhythm -digit -argued -dietary -symphony -clarke -sudden -accepting -precipitation -marilyn -lions -findlaw -ada -pools -tb -lyric -claire -isolation -speeds -sustained -matched -approximate -rope -carroll -rational -programmer -fighters -chambers -dump -greetings -inherited -warming -incomplete -vocals -chronicle -fountain -chubby -grave -legitimate -biographies -burner -yrs -foo -investigator -gba -plaintiff -finnish -gentle -bm -prisoners -deeper -muslims -hose -mediterranean -nightlife -footage -howto -worthy -reveals -architects -saints -entrepreneur -carries -sig -freelance -duo -excessive -devon -screensaver -helena -saves -regarded -valuation -unexpected -cigarette -fog -characteristic -marion -lobby -egyptian -tunisia -metallica -outlined -consequently -headline -treating -punch -appointments -str -gotta -cowboy -narrative -bahrain -enormous -karma -consist -betty -queens -academics -pubs -quantitative -lucas -screensavers -subdivision -tribes -vip -defeat -clicks -distinction -honduras -naughty -hazards -insured -harper -livestock -mardi -exemption -tenant -sustainability -cabinets -tattoo -shake -algebra -shadows -holly -formatting -silly -nutritional -yea -mercy -hartford -freely -marcus -sunrise -wrapping -mild -fur -nicaragua -weblogs -timeline -tar -belongs -rj -readily -affiliation -soc -fence -nudist -infinite -diana -ensures -relatives -lindsay -clan -legally -shame -satisfactory -revolutionary -bracelets -sync -civilian -telephony -mesa -fatal -remedy -realtors -breathing -briefly -thickness -adjustments -graphical -genius -discussing -aerospace -fighter -meaningful -flesh -retreat -adapted -barely -wherever -estates -rug -democrat -borough -maintains -failing -shortcuts -ka -retained -voyeurweb -pamela -andrews -marble -extending -jesse -specifies -hull -logitech -surrey -briefing -belkin -dem -accreditation -wav -blackberry -highland -meditation -modular -microphone -macedonia -combining -brandon -instrumental -giants -organizing -shed -balloon -moderators -winston -memo -ham -solved -tide -kazakhstan -hawaiian -standings -partition -invisible -gratuit -consoles -funk -fbi -qatar -magnet -translations -porsche -cayman -jaguar -reel -sheer -commodity -posing -kilometers -rp -bind -thanksgiving -rand -hopkins -urgent -guarantees -infants -gothic -cylinder -witch -buck -indication -eh -congratulations -tba -cohen -sie -usgs -puppy -kathy -acre -graphs -surround -cigarettes -revenge -expires -enemies -lows -controllers -aqua -chen -emma -consultancy -finances -accepts -enjoying -conventions -eva -patrol -smell -pest -hc -italiano -coordinates -rca -fp -carnival -roughly -sticker -promises -responding -reef -physically -divide -stakeholders -hydrocodone -gst -consecutive -cornell -satin -bon -deserve -attempting -mailto -promo -jj -representations -chan -worried -tunes -garbage -competing -combines -mas -beth -bradford -len -phrases -kai -peninsula -chelsea -boring -reynolds -dom -jill -accurately -speeches -reaches -schema -considers -sofa -catalogs -ministries -vacancies -quizzes -parliamentary -obj -prefix -lucia -savannah -barrel -typing -nerve -dans -planets -deficit -boulder -pointing -renew -coupled -viii -myanmar -metadata -harold -circuits -floppy -texture -handbags -jar -ev -somerset -incurred -acknowledge -thoroughly -antigua -nottingham -thunder -tent -caution -identifies -questionnaire -qualification -locks -modelling -namely -miniature -dept -hack -dare -euros -interstate -pirates -aerial -hawk -consequence -rebel -systematic -perceived -origins -hired -makeup -textile -lamb -madagascar -nathan -tobago -presenting -cos -troubleshooting -uzbekistan -indexes -pac -rl -erp -centuries -gl -magnitude -ui -richardson -hindu -dh -fragrances -vocabulary -licking -earthquake -vpn -fundraising -fcc -markers -weights -albania -geological -assessing -lasting -wicked -eds -introduces -kills -roommate -webcams -pushed -webmasters -ro -df -computational -acdbentity -participated -junk -handhelds -wax -lucy -answering -hans -impressed -slope -reggae -failures -poet -conspiracy -surname -theology -nails -evident -whats -rides -rehab -epic -saturn -organizer -nut -allergy -sake -twisted -combinations -preceding -merit -enzyme -cumulative -zshops -planes -edmonton -tackle -disks -condo -pokemon -amplifier -ambien -arbitrary -prominent -retrieve -lexington -vernon -sans -worldcat -titanium -irs -fairy -builds -contacted -shaft -lean -bye -cdt -recorders -occasional -leslie -casio -deutsche -ana -postings -innovations -kitty -postcards -dude -drain -monte -fires -algeria -blessed -luis -reviewing -cardiff -cornwall -favors -potato -panic -explicitly -sticks -leone -ez -citizenship -excuse -reforms -basement -onion -strand -pf -sandwich -uw -lawsuit -alto -informative -girlfriend -bloomberg -cheque -hierarchy -influenced -banners -reject -eau -abandoned -bd -circles -italic -beats -merry -mil -scuba -gore -complement -cult -dash -passive -mauritius -valued -cage -checklist -requesting -courage -verde -lauderdale -scenarios -gazette -hitachi -divx -extraction -batman -elevation -hearings -coleman -hugh -lap -utilization -beverages -calibration -jake -eval -efficiently -anaheim -ping -textbook -dried -entertaining -prerequisite -luther -frontier -settle -stopping -refugees -knights -hypothesis -palmer -medicines -flux -derby -sao -peaceful -altered -pontiac -regression -doctrine -scenic -trainers -muze -enhancements -renewable -intersection -passwords -sewing -consistency -collectors -conclude -recognised -munich -oman -celebs -gmc -propose -hh -azerbaijan -lighter -rage -adsl -uh -prix -astrology -advisors -pavilion -tactics -trusts -occurring -supplemental -travelling -talented -annie -pillow -induction -derek -precisely -shorter -harley -spreading -provinces -relying -finals -paraguay -steal -parcel -refined -fd -bo -fifteen -widespread -incidence -fears -predict -boutique -acrylic -rolled -tuner -avon -incidents -peterson -rays -asn -shannon -toddler -enhancing -flavor -alike -walt -homeless -horrible -hungry -metallic -acne -blocked -interference -warriors -palestine -listprice -libs -undo -cadillac -atmospheric -malawi -wm -pk -sagem -knowledgestorm -dana -halo -ppm -curtis -parental -referenced -strikes -lesser -publicity -marathon -ant -proposition -gays -pressing -gasoline -apt -dressed -scout -belfast -exec -dealt -niagara -inf -eos -warcraft -charms -catalyst -trader -bucks -allowance -vcr -denial -uri -designation -thrown -prepaid -raises -gem -duplicate -electro -criterion -badge -wrist -civilization -analyzed -vietnamese -heath -tremendous -ballot -lexus -varying -remedies -validity -trustee -maui -weighted -angola -performs -plastics -realm -corrected -jenny -helmet -salaries -postcard -elephant -yemen -encountered -tsunami -scholar -nickel -internationally -surrounded -psi -buses -expedia -geology -pct -wb -creatures -coating -commented -wallet -cleared -smilies -vids -accomplish -boating -drainage -shakira -corners -broader -vegetarian -rouge -yeast -yale -newfoundland -sn -qld -pas -clearing -investigated -dk -ambassador -coated -intend -stephanie -contacting -vegetation -doom -findarticles -louise -kenny -specially -owen -routines -hitting -yukon -beings -bite -issn -aquatic -reliance -habits -striking -myth -infectious -podcasts -singh -gig -gilbert -sas -ferrari -continuity -brook -fu -outputs -phenomenon -ensemble -insulin -assured -biblical -weed -conscious -accent -mysimon -eleven -wives -ambient -utilize -mileage -oecd -prostate -adaptor -auburn -unlock -hyundai -pledge -vampire -angela -relates -nitrogen -xerox -dice -merger -softball -referrals -quad -dock -differently -firewire -mods -nextel -framing -organised -musician -blocking -rwanda -sorts -integrating -vsnet -limiting -dispatch -revisions -papua -restored -hint -armor -riders -chargers -remark -dozens -varies -msie -reasoning -wn -liz -rendered -picking -charitable -guards -annotated -ccd -sv -convinced -openings -buys -burlington -replacing -researcher -watershed -councils -occupations -acknowledged -kruger -pockets -granny -pork -zu -equilibrium -viral -inquire -pipes -characterized -laden -aruba -cottages -realtor -merge -privilege -edgar -develops -qualifying -chassis -dubai -estimation -barn -pushing -llp -fleece -pediatric -boc -fare -dg -asus -pierce -allan -dressing -techrepublic -sperm -vg -bald -filme -craps -fuji -frost -leon -institutes -mold -dame -fo -sally -yacht -tracy -prefers -drilling -brochures -herb -tmp -alot -ate -breach -whale -traveller -appropriations -suspected -tomatoes -benchmark -beginners -instructors -highlighted -bedford -stationery -idle -mustang -unauthorized -clusters -antibody -competent -momentum -fin -wiring -io -pastor -mud -calvin -uni -shark -contributor -demonstrates -phases -grateful -emerald -gradually -laughing -grows -cliff -desirable -tract -ul -ballet -ol -journalist -abraham -js -bumper -afterwards -webpage -religions -garlic -hostels -shine -senegal -explosion -pn -banned -wendy -briefs -signatures -diffs -cove -mumbai -ozone -disciplines -casa -mu -daughters -conversations -radios -tariff -nvidia -opponent -pasta -simplified -muscles -serum -wrapped -swift -motherboard -runtime -inbox -focal -bibliographic -eden -distant -incl -champagne -ala -decimal -hq -deviation -superintendent -propecia -dip -nbc -samba -hostel -housewives -employ -mongolia -penguin -magical -influences -inspections -irrigation -miracle -manually -reprint -reid -wt -hydraulic -centered -robertson -flex -yearly -wound -belle -rosa -conviction -hash -omissions -writings -hamburg -lazy -mv -mpg -retrieval -qualities -cindy -fathers -carb -charging -cas -marvel -lined -cio -dow -prototype -importantly -rb -petite -apparatus -upc -terrain -dui -pens -explaining -yen -strips -gossip -rangers -nomination -empirical -mh -rotary -worm -dependence -discrete -beginner -boxed -lid -polyester -cubic -deaf -commitments -suggesting -sapphire -kinase -skirts -mats -remainder -crawford -labeled -privileges -televisions -specializing -marking -commodities -pvc -serbia -sheriff -griffin -declined -guyana -spies -blah -mime -neighbor -motorcycles -elect -highways -thinkpad -concentrate -intimate -reproductive -preston -deadly -feof -bunny -chevy -molecules -rounds -longest -refrigerator -tions -intervals -sentences -dentists -usda -exclusion -workstation -holocaust -keen -flyer -peas -dosage -receivers -urls -customise -disposition -variance -navigator -investigators -cameroon -baking -marijuana -adaptive -computed -needle -baths -enb -gg -cathedral -brakes -og -nirvana -ko -fairfield -owns -til -invision -sticky -destiny -generous -madness -emacs -climb -blowing -fascinating -landscapes -heated -lafayette -jackie -wto -computation -hay -cardiovascular -ww -sparc -cardiac -salvation -dover -adrian -predictions -accompanying -vatican -brutal -learners -gd -selective -arbitration -configuring -token -editorials -zinc -sacrifice -seekers -guru -isa -removable -convergence -yields -gibraltar -levy -suited -numeric -anthropology -skating -kinda -aberdeen -emperor -grad -malpractice -dylan -bras -belts -blacks -educated -rebates -reporters -burke -proudly -pix -necessity -rendering -mic -inserted -pulling -basename -kyle -obesity -curves -suburban -touring -clara -vertex -bw -hepatitis -nationally -tomato -andorra -waterproof -expired -mj -travels -flush -waiver -pale -specialties -hayes -humanitarian -invitations -functioning -delight -survivor -garcia -cingular -economies -alexandria -bacterial -moses -counted -undertake -declare -continuously -johns -valves -gaps -impaired -achievements -donors -tear -jewel -teddy -lf -convertible -ata -teaches -ventures -nil -bufing -stranger -tragedy -julian -nest -pam -dryer -painful -velvet -tribunal -ruled -nato -pensions -prayers -funky -secretariat -nowhere -cop -paragraphs -gale -joins -adolescent -nominations -wesley -dim -lately -cancelled -scary -mattress -mpegs -brunei -likewise -banana -introductory -slovak -cakes -stan -reservoir -occurrence -idol -mixer -remind -wc -worcester -sbjct -demographic -charming -mai -tooth -disciplinary -annoying -respected -stays -disclose -affair -drove -washer -upset -restrict -springer -beside -mines -portraits -rebound -logan -mentor -interpreted -evaluations -fought -baghdad -elimination -metres -hypothetical -immigrants -complimentary -helicopter -pencil -freeze -hk -performer -abu -titled -commissions -sphere -powerseller -moss -ratios -concord -graduated -endorsed -ty -surprising -walnut -lance -ladder -italia -unnecessary -dramatically -liberia -sherman -cork -maximize -cj -hansen -senators -workout -mali -yugoslavia -bleeding -characterization -colon -likelihood -lanes -purse -fundamentals -contamination -mtv -endangered -compromise -optimize -stating -dome -caroline -leu -expiration -namespace -align -peripheral -bless -engaging -negotiation -crest -opponents -triumph -nominated -confidentiality -electoral -changelog -welding -deferred -alternatively -heel -alloy -condos -plots -polished -yang -gently -greensboro -tulsa -locking -casey -controversial -draws -fridge -blanket -bloom -qc -simpsons -lou -elliott -recovered -fraser -justify -upgrading -blades -pgp -loops -surge -frontpage -trauma -aw -tahoe -advert -possess -demanding -defensive -sip -flashers -subaru -forbidden -tf -vanilla -programmers -pj -monitored -installations -deutschland -picnic -souls -arrivals -spank -cw -practitioner -motivated -wr -dumb -smithsonian -hollow -vault -securely -examining -fioricet -groove -revelation -rg -pursuit -delegation -wires -bl -dictionaries -mails -backing -greenhouse -sleeps -vc -blake -transparency -dee -travis -wx -endless -figured -orbit -currencies -niger -bacon -survivors -positioning -heater -colony -cannon -circus -promoted -forbes -mae -moldova -mel -descending -paxil -spine -trout -enclosed -feat -temporarily -ntsc -cooked -thriller -transmit -apnic -fatty -gerald -pressed -frequencies -scanned -reflections -hunger -mariah -sic -municipality -usps -joyce -detective -surgeon -cement -experiencing -fireplace -endorsement -bg -planners -disputes -textiles -missile -intranet -closes -seq -psychiatry -persistent -deborah -conf -marco -assists -summaries -glow -gabriel -auditor -wma -aquarium -violin -prophet -cir -bracket -looksmart -isaac -oxide -oaks -magnificent -erik -colleague -naples -promptly -modems -adaptation -hu -harmful -paintball -prozac -enclosure -acm -dividend -newark -kw -paso -glucose -phantom -norm -playback -supervisors -westminster -turtle -ips -distances -absorption -treasures -dsc -warned -neural -ware -fossil -mia -hometown -badly -transcripts -apollo -wan -disappointed -persian -continually -communist -collectible -handmade -greene -entrepreneurs -robots -grenada -creations -jade -scoop -acquisitions -foul -keno -gtk -earning -mailman -sanyo -nested -biodiversity -excitement -somalia -movers -verbal -blink -presently -seas -carlo -workflow -mysterious -novelty -bryant -tiles -voyuer -librarian -subsidiaries -switched -stockholm -tamil -garmin -ru -pose -fuzzy -indonesian -grams -therapist -richards -mrna -budgets -toolkit -promising -relaxation -goat -render -carmen -ira -sen -thereafter -hardwood -erotica -temporal -sail -forge -commissioners -dense -dts -brave -forwarding -qt -awful -nightmare -airplane -reductions -southampton -istanbul -impose -organisms -sega -telescope -viewers -asbestos -portsmouth -cdna -meyer -enters -pod -savage -advancement -wu -harassment -willow -resumes -bolt -gage -throwing -existed -generators -lu -wagon -barbie -dat -favour -soa -knock -urge -smtp -generates -potatoes -thorough -replication -inexpensive -kurt -receptors -peers -roland -optimum -neon -interventions -quilt -huntington -creature -ours -mounts -syracuse -internship -lone -refresh -aluminium -snowboard -webcast -michel -evanescence -subtle -coordinated -notre -shipments -maldives -stripes -firmware -antarctica -cope -shepherd -lm -canberra -cradle -chancellor -mambo -lime -kirk -flour -controversy -legendary -bool -sympathy -choir -avoiding -beautifully -blond -expects -cho -jumping -fabrics -antibodies -polymer -hygiene -wit -poultry -virtue -burst -examinations -surgeons -bouquet -immunology -promotes -mandate -wiley -departmental -bbs -spas -ind -corpus -johnston -terminology -gentleman -fibre -reproduce -convicted -shades -jets -indices -roommates -adware -qui -intl -threatening -spokesman -zoloft -activists -frankfurt -prisoner -daisy -halifax -encourages -ultram -cursor -assembled -earliest -donated -stuffed -restructuring -insects -terminals -crude -morrison -maiden -simulations -cz -sufficiently -examines -viking -myrtle -bored -cleanup -yarn -knit -conditional -mug -crossword -bother -budapest -conceptual -knitting -attacked -hl -bhutan -liechtenstein -mating -compute -redhead -arrives -translator -automobiles -tractor -allah -continent -ob -unwrap -fares -longitude -resist -challenged -telecharger -hoped -pike -safer -insertion -instrumentation -ids -hugo -wagner -constraint -groundwater -touched -strengthening -cologne -gzip -wishing -ranger -smallest -insulation -newman -marsh -ricky -ctrl -scared -theta -infringement -bent -laos -subjective -monsters -asylum -lightbox -robbie -stake -cocktail -outlets -swaziland -varieties -arbor -mediawiki -configurations -poison \ No newline at end of file diff --git a/mystbin/database/schema.sql b/mystbin/database/schema.sql deleted file mode 100644 index 116abf1a..00000000 --- a/mystbin/database/schema.sql +++ /dev/null @@ -1,76 +0,0 @@ -CREATE EXTENSION IF NOT EXISTS pgcrypto; -CREATE EXTENSION IF NOT EXISTS pg_trgm; - -CREATE TABLE IF NOT EXISTS users ( - id BIGINT PRIMARY KEY, - token TEXT NOT NULL, - emails TEXT[] NOT NULL DEFAULT '{}', - discord_id TEXT, - github_id TEXT, - google_id TEXT, - admin BOOLEAN NOT NULL DEFAULT false, - theme TEXT NOT NULL DEFAULT 'dark', - subscriber BOOLEAN DEFAULT false, - username TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS pastes ( - id TEXT PRIMARY KEY, - author_id BIGINT REFERENCES users(id), - created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'), - expires TIMESTAMP WITH TIME ZONE, - last_edited TIMESTAMP WITH TIME ZONE, - password TEXT, - views INTEGER DEFAULT 0, - origin_ip TEXT -); - -CREATE TABLE IF NOT EXISTS files ( - parent_id TEXT REFERENCES pastes(id) ON DELETE CASCADE, - content TEXT NOT NULL, - filename TEXT, - loc INTEGER NOT NULL, - charcount INTEGER GENERATED ALWAYS AS (LENGTH(content)) STORED, - index SERIAL NOT NULL, - attachment TEXT, - PRIMARY KEY (parent_id, index) -); - -CREATE TABLE IF NOT EXISTS bookmarks ( - userid bigint not null references users(id) ON DELETE CASCADE, - paste text not null references pastes(id) ON DELETE CASCADE, - PRIMARY KEY (userid, paste), - created_at timestamp without time zone not null default (now() at time zone 'utc') -); - -CREATE TABLE IF NOT EXISTS bans ( - ip TEXT UNIQUE, - userid BIGINT UNIQUE, - reason TEXT -); - -CREATE TABLE IF NOT EXISTS logs ( - ip TEXT NOT NULL, - userid BIGINT REFERENCES users(id), - accessed TIMESTAMP, - cf_ray TEXT, - cf_country TEXT, - web_route TEXT NOT NULL, - body TEXT, - response_code INTEGER NOT NULL, - response TEXT -); - -CREATE OR REPLACE FUNCTION deleteOldPastes() RETURNS TRIGGER AS $$ -BEGIN - DELETE FROM pastes CASCADE WHERE expires IS NOT NULL AND expires < now() AT TIME ZONE 'utc'; - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - -DROP TRIGGER IF EXISTS oldPastesExpiry on public.pastes; -CREATE TRIGGER oldPastesExpiry - AFTER INSERT OR UPDATE - ON pastes - FOR STATEMENT - EXECUTE PROCEDURE deleteOldPastes(); diff --git a/mystbin/fallback/fallback.py b/mystbin/fallback/fallback.py deleted file mode 100644 index bf63ad2a..00000000 --- a/mystbin/fallback/fallback.py +++ /dev/null @@ -1,49 +0,0 @@ -import os -import json -from aiohttp import web - -app = web.Application() -app.config = {} -router = web.RouteTableDef() - -def load_config(): - if os.path.exists("./config.json"): - with open("./config.json", "r") as __f: - config = json.load(__f) - - elif os.path.exists("../../config.json"): - with open("../../config.json", "r") as __f: - config = json.load(__f) - - else: - raise RuntimeError("Cannot find config file") - - app.config = config - -load_config() - - -@router.post("/reload") -async def _router_reload(request: web.Request) -> web.Response: # type: ignore - if request.remote == "127.0.0.1" and "X-Forwarded-For" not in request.headers: # systemd reloads will only ever come from localhost using curl - load_config() - return web.Response(body="Reloaded", status=200) - - raise web.HTTPPermanentRedirect("/") - - -@router.get("/") -@router.get("/{pth:.*}") -@router.post("/") -@router.post("/{pth:.*}") -@router.patch("/") -@router.patch("/{pth:.*}") -async def fallback_route(request: web.Request) -> web.Response | web.FileResponse: - if app.config.get("maintenance", False): # type: ignore - return web.FileResponse("maintenance.html") - - return web.FileResponse("service_failure.html") - -app.add_routes(router) - -web.run_app(app, port=app.config["site"]["fallback_port"]) # type: ignore \ No newline at end of file diff --git a/mystbin/fallback/maintenance.html b/mystbin/fallback/maintenance.html deleted file mode 100644 index 422706bc..00000000 --- a/mystbin/fallback/maintenance.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - MystBin - Maintenance - - - - -
-

MystBin

-

Scheduled Maintenance

- We will be back shortly! - -
- - - - \ No newline at end of file diff --git a/mystbin/fallback/requirements.txt b/mystbin/fallback/requirements.txt deleted file mode 100644 index ce235718..00000000 --- a/mystbin/fallback/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -aiohttp \ No newline at end of file diff --git a/mystbin/fallback/service_failure.html b/mystbin/fallback/service_failure.html deleted file mode 100644 index e81d8a58..00000000 --- a/mystbin/fallback/service_failure.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - MystBin - Maintenance - - - - -
-

MystBin

-

Whoops!

- We dropped the pastes! - Join our discord for updates - -
- - - - \ No newline at end of file diff --git a/mystbin/frontend/Dockerfile b/mystbin/frontend/Dockerfile deleted file mode 100644 index 02ada436..00000000 --- a/mystbin/frontend/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM node:latest - -RUN mkdir -p /app -WORKDIR /app - -COPY . /app - -RUN yarn install --production - -ENV NODE_OPTIONS="--openssl-legacy-provider" -CMD ["yarn", "run", "launch"] diff --git a/mystbin/frontend/components/Base.tsx b/mystbin/frontend/components/Base.tsx deleted file mode 100644 index 4c17cfe4..00000000 --- a/mystbin/frontend/components/Base.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { PropsWithChildren } from "react"; -import OptsBar from "./OptsBar"; -import styles from "../styles/Base.module.css"; -import LogoMain from "../public/LogoMain"; -import GitHubIcon from "@material-ui/icons/GitHub"; -import DiscordColorIcon from "../icons/DiscordColour"; -import TipModal from "./TipModal"; -import Head from "next/head"; - -export default function Base(props: PropsWithChildren<{ className: string }>) { - const { children, className } = props; - - return ( -
- - - -
{children}
- -
-
- - - Copyright © 2020-current PythonistaGuild - -
- - - - - Terms and Conditions - / - Privacy Policy - / - Contact Us - -
-
- ); -} diff --git a/mystbin/frontend/components/EditorTabs.tsx b/mystbin/frontend/components/EditorTabs.tsx deleted file mode 100644 index 26e832e5..00000000 --- a/mystbin/frontend/components/EditorTabs.tsx +++ /dev/null @@ -1,358 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import MonacoEditor from "./MonacoEditor"; -import styles from "../styles/EditorTabs.module.css"; -import {Dropdown, Toast, ToastHeader} from "react-bootstrap"; -import PasswordModal from "./PasswordModal"; -import Tab from "./Tab"; -import NewTabButton from "./NewTabButton"; -import pasteDispatcher from "../dispatchers/PasteDispatcher"; -import getLanguage from "../stores/languageStore"; -import config from "../config.json"; -import { Button } from "@material-ui/core"; -import DropdownItem from "react-bootstrap/DropdownItem"; -import React from "react"; -import SettingsIcon from "@material-ui/icons/Settings"; -import AddBoxIcon from '@material-ui/icons/AddBox'; -import LibraryAddCheckIcon from '@material-ui/icons/LibraryAddCheck'; -import {ImageRounded} from "@material-ui/icons"; - -const languages = { - py: "python", - python: "python", - pyi: "python", - js: "javascript", - javascript: "javascript", - jsx: "javascript", - ts: "typescript", - typescript: "typescript", - tsx: "typescript", - html: "html", - swift: "swift", - json: "json", - rs: "rust", - rust: "rust", - ex: "elixir", - elixir: "elixir", - md: "markdown", - markdown: "markdown", - go: "go", - cpp: "cpp", - c: "cpp", - h: "cpp", - cs: "csharp", - css: "css", - hs: "haskell", - perl: "perl", - pl: "perl", - pm: "perl", - bash: "bash", - zsh: "bash", - sh: "bash", - sql: "sql", - nginx: "nginx", - ini: "ini", - toml: "toml", - xml: "xml", - yml: "yml", - yaml: "yml", -}; - -interface TabInfo { - initialData?: any; - hasPassword?: boolean; - pid?: string; -} - -export default function EditorTabs({ - initialData = null, - hasPassword = false, - pid = null, -}: TabInfo) { - const [value, setValue] = useState([{ title: "file.txt", content: "", image: "" },]); - const [currTab, setCurrTab] = useState(0); - const [charCountToast, setCharCountToast] = useState(false); - const [passwordModal, setPasswordModal] = useState(!!hasPassword); - const [shake, setShake] = useState(false); - const [loading, setLoading] = useState(false); - const [lang, setLang] = useState([]); - const id = pid; - const [initialState, setInitialState] = useState(false); - const [langDropDown, setLangDropDown] = useState(false); - const [dropLang, setDropLang] = useState(null); - const [image, setImage] = useState(null) - const [showImage, setShowImage] = useState(null) - - const tabRef = useRef(); - const imageRef = useRef(); - const DnDRef = useRef(); - - async function handleDnD(e, index) { - e.preventDefault() - - if (!!id) { - return; - } - - if(e.dataTransfer && e.dataTransfer.files.length != 0) { - let file = e.dataTransfer.files[0] - - if (file.size / 1024 / 1024 > 4) { - alert('You can only upload files 4Mb in size or less.'); - return; - - } - - let data = await file.text(); - let name = file.name; - - let newValue = [...value]; - newValue[index]['title'] = name; - newValue[index]['content'] = data; - - setValue(newValue); - } - - } - - - function handleSetImage(e) { - let file = e.currentTarget.files[0] - let allowed = ['image/gif', 'image/jpeg', 'image/png']; - - if (!!file && !allowed.includes(file['type'])) { - alert('Only images are currently supported.') - } - else if (file.size / 1024 / 1024 > 4) { - alert('You can only upload files 4Mb in size or less.') - } - else { - setImage(file) - } - } - - useEffect( () => { - let newValue = [...value]; - newValue[currTab]['image'] = image - - setValue(newValue) - }, [image]) - - pasteDispatcher.dispatch({ paste: value }); - const maxCharCount = config["paste"]["character_limit"]; - - useEffect( () => { - if (initialData !== null && !initialState) { - setValue(initialData); - setInitialState(true); - } - }, []) - - useEffect(() => { - if (sessionStorage.getItem("pasteCopy") !== null) { - setValue(JSON.parse(sessionStorage.getItem("pasteCopy"))); - } - }, []); - - useEffect(() => { - if (!value[currTab]) { - let tabNumber = currTab.valueOf(); - if (currTab > 1) { - tabNumber = currTab - 1; - } else { - tabNumber = 0; - } - - setCurrTab(tabNumber); - } - }, [value]); - - useEffect(() => { - let newLang = [...lang]; - newLang.splice(currTab, 1, dropLang); - setLang(newLang); - }, [langDropDown]); - - useEffect(() => { - let initialLangs = []; - value.map(function (v) { - let filetype = v["title"].split(".").pop(); - let val = getLanguage(filetype); - initialLangs.push(val); - setLang(initialLangs); - }); - }, [value]); - - const handlePasswordAttempt = async (attempt: string) => { - setLoading(true); - const response = await fetch( - config["site"]["backend_site"] + "/paste/" + id + "?password=" + attempt, - { headers: { Accept: "application/json" } } - ); - const paste = await response.json(); - let actualData = []; - - try { - if (response.status === 200) { - - for (let file of paste["files"]) { - actualData.push({ - title: file["filename"], - content: file["content"], - image: file['attachment'] - }); - } - setValue(actualData); - setPasswordModal(false); - } else { - throw () => {}; - } - } catch { - setShake(true); - setTimeout(function () { - setShake(false); - }, 500); - } - setLoading(false); - }; - - return ( - <> -
- {value.map((v, i) => ( -
setShowImage(-1)} style={{ display: showImage === i ? "flex" : "none",}}> - - Open Original -
- ))} -
- - - -
-
- {value.map((v, i) => ( - 1 && !initialData} - filename={v.title} - onFocus={() => setCurrTab(i)} - onChange={(filename) => { - let newValue = [...value]; - newValue[i].title = filename; - setValue(newValue); - }} - onDelete={() => { - let newValue = [...value]; - newValue.splice(i, 1); - setValue(newValue); - }} - > - (handleSetImage(e))} /> - {!pid && value[i]['image'] === null || value[i]['image'] === "" ? -
{ - e.preventDefault(); - // @ts-ignore - imageRef.current.click(); - - }} > - -
: null} - - { value[i]['image'] !== 'None' && value[i]['image'] !== null && value[i]['image'] !== undefined && value[i]['image'] !== "" ? -
setShowImage(i)} - > - -
- : null } - - {!!pid ? ( -
-
setLangDropDown(!langDropDown)} - > - -
- {langDropDown && i === currTab ? ( -
- - {Object.keys(languages).map((v, index) => { - return ( - { - e.preventDefault(); - }} - onClick={() => { - setLangDropDown(false); - setDropLang(getLanguage(v)); - }} - > - {v} - - ); - })} - -
- ) : null} -
- ) : null} -
- ))} - { - let newValue = [...value]; - newValue.push({ title: "file.txt", content: "", image: "" }); - setValue(newValue); - setCurrTab(value.length); - }} - enabled={value.length <= 4 && !id} - /> -
- - {value.map((v, i, arr) => ( -
-
e.preventDefault()} - onDrop={async (e) => await handleDnD(e, i)} - > - { - if (newVal.length > maxCharCount) { - setCharCountToast(true); - newVal = newVal.slice(0, maxCharCount); - } - let newValue = [...value]; - newValue[i]["content"] = newVal; - setValue(newValue); - return `${newVal}`; - }} - value={v.content} - theme={"mystBinDark"} - readOnly={!!id} - /> -
-
- ))} -
- - ); -} diff --git a/mystbin/frontend/components/ExpiryModal.tsx b/mystbin/frontend/components/ExpiryModal.tsx deleted file mode 100644 index c9301b2a..00000000 --- a/mystbin/frontend/components/ExpiryModal.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { useState } from "react"; -import { Button, Form, InputGroup, Modal } from "react-bootstrap"; -import styles from "../styles/OptsBar.module.css"; - -export default function ExpiryModal({ - initialValue, - onHide, - onSubmit, -}: { - onHide: () => void; - initialValue: number[]; - onSubmit: (arg0: number[]) => void; -}) { - const [expiryValue, setExpiryValue] = useState(initialValue); - let days = [-1]; - let hours = [-1]; - const minutes = [-1, 0, 5, 15, 30, 45]; - - for (let i = 0; i <= 31; i++) { - days.push(i); - } - - for (let i = 0; i <= 23; i++) { - hours.push(i); - } - - const handleExpirySubmit = (e) => { - e.preventDefault(); - onHide(); - onSubmit(expiryValue); - }; - - let optionMap = { - Days: [0, days], - Hours: [1, hours], - Mins: [2, minutes], - }; - - return ( - - - - Set Paste Expiry - - - -
- {Object.keys(optionMap).map((optName) => { - let [index, options]: [number, number[]] = optionMap[optName]; - - return ( - <> - - {optName} - - { - const oldExpiry = expiryValue; - oldExpiry[index] = parseInt(e.target.value); - - setExpiryValue(oldExpiry); - }} - > - {options.map((v) => { - let chosen = expiryValue[index]; - return ( - - ); - })} - - - ); - })} -
-
- - - -
- ); -} diff --git a/mystbin/frontend/components/LoginModal.tsx b/mystbin/frontend/components/LoginModal.tsx deleted file mode 100644 index 699e1107..00000000 --- a/mystbin/frontend/components/LoginModal.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Modal } from "react-bootstrap"; -import styles from "../styles/Login.module.css"; -import LogoMain from "../public/LogoMain"; -import DiscordColorIcon from "../icons/DiscordColour"; -import GitHubIcon from "@material-ui/icons/GitHub"; -import GoogleIcon from "../icons/GoogleIcon"; -import { useRouter } from "next/router"; -import { useState } from "react"; -import Popout from "react-popout"; -import cookieCutter from "cookie-cutter"; -import config from "../config.json"; - -export default function LoginModal({ onHide }: { onHide: () => void }) { - const router = useRouter(); - const [window, setWindow] = useState(null); - - return ( - <> - {window ? ( - { - setWindow(null); - router.reload(); - }} - /> - ) : null} - - - - -
- Login -
-
- -
{ - setWindow( - `https://discord.com/api/oauth2/authorize?client_id=${config["apps"]["discord_application_id"]}&redirect_uri=${config["site"]["frontend_site"]}/discord_auth&response_type=code&scope=identify%20email` - ); - }} - > - -
-
{ - setWindow( - `https://github.com/login/oauth/authorize?client_id=${config["apps"]["github_application_id"]}&redirect_uri=${config["site"]["frontend_site"]}/github_auth&scope=read:user%20user:email` - ); - }} - > - -
-
{ - setWindow( - `https://accounts.google.com/o/oauth2/v2/auth?client_id=${config["apps"]["google_application_id"]}&redirect_uri=${config["site"]["frontend_site"]}/google_auth&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email` - ); - }} - > - -
-
- - Via Discord - Via GitHub - Via Google - - - - By logging in via one of the provided services above, you agree to - our Terms and Conditions,{" "} - Privacy Policy - and consent to our Cookie Policy. - - -
- - ); -} diff --git a/mystbin/frontend/components/MonacoEditor.tsx b/mystbin/frontend/components/MonacoEditor.tsx deleted file mode 100644 index 04279db8..00000000 --- a/mystbin/frontend/components/MonacoEditor.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { - monaco, - ControlledEditor, - ControlledEditorOnChange, -} from "@monaco-editor/react"; -import { PropsWithoutRef } from "react"; - - -monaco.init().then((monaco) => { - // MystBin Dark - monaco.editor.defineTheme("mystBinDark", { - base: "vs-dark", - inherit: false, - rules: [ - { token: "", foreground: "D4D4D4", background: "1E1E1E" }, - { token: "invalid", foreground: "f44747" }, - { token: "emphasis", fontStyle: "italic" }, - { token: "strong", fontStyle: "bold" }, - - { token: "variable", foreground: "74B0DF" }, - { token: "variable.predefined", foreground: "4864AA" }, - { token: "variable.parameter", foreground: "9CDCFE" }, - { token: "constant", foreground: "569CD6" }, - { token: "comment", foreground: "608B4E" }, - { token: "number", foreground: "B5CEA8" }, - { token: "number.hex", foreground: "5BB498" }, - { token: "regexp", foreground: "B46695" }, - { token: "annotation", foreground: "cc6666" }, - { token: "type", foreground: "3DC9B0" }, - - { token: "delimiter", foreground: "DCDCDC" }, - { token: "delimiter.html", foreground: "808080" }, - { token: "delimiter.xml", foreground: "808080" }, - - { token: "tag", foreground: "569CD6" }, - { token: "tag.id.pug", foreground: "4F76AC" }, - { token: "tag.class.pug", foreground: "4F76AC" }, - { token: "meta.scss", foreground: "A79873" }, - { token: "meta.tag", foreground: "CE9178" }, - { token: "metatag", foreground: "DD6A6F" }, - { token: "metatag.content.html", foreground: "9CDCFE" }, - { token: "metatag.html", foreground: "569CD6" }, - { token: "metatag.xml", foreground: "569CD6" }, - { token: "metatag.php", fontStyle: "bold" }, - - { token: "key", foreground: "9CDCFE" }, - { token: "string.key.json", foreground: "9CDCFE" }, - { token: "string.value.json", foreground: "CE9178" }, - - { token: "attribute.name", foreground: "9CDCFE" }, - { token: "attribute.value", foreground: "CE9178" }, - { token: "attribute.value.number.css", foreground: "B5CEA8" }, - { token: "attribute.value.unit.css", foreground: "B5CEA8" }, - { token: "attribute.value.hex.css", foreground: "D4D4D4" }, - - { token: "string", foreground: "CE9178" }, - { token: "string.sql", foreground: "FF0000" }, - - { token: "keyword", foreground: "569CD6" }, - { token: "keyword.flow", foreground: "C586C0" }, - { token: "keyword.json", foreground: "CE9178" }, - { token: "keyword.flow.scss", foreground: "569CD6" }, - - { token: "operator.scss", foreground: "909090" }, - { token: "operator.sql", foreground: "778899" }, - { token: "operator.swift", foreground: "909090" }, - { token: "predefined.sql", foreground: "FF00FF" }, - ], - colors: { - "editor.background": "#2B2B2B", - }, - }); -}); - -export default function MonacoEditor( - props: PropsWithoutRef<{ - value: string; - language: string; - onChange: ControlledEditorOnChange; - theme: string; - readOnly: boolean; - }> -) { - let { value, onChange, theme, language, readOnly } = props; - - return ( - - ); -} diff --git a/mystbin/frontend/components/NewTabButton.tsx b/mystbin/frontend/components/NewTabButton.tsx deleted file mode 100644 index a15474f8..00000000 --- a/mystbin/frontend/components/NewTabButton.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import styles from "../styles/NewTabButton.module.css"; - -export default function NewTabButton({ enabled, onClick }) { - return ( - enabled && ( - - ) - ); -} diff --git a/mystbin/frontend/components/OptsBar.tsx b/mystbin/frontend/components/OptsBar.tsx deleted file mode 100644 index 6719213f..00000000 --- a/mystbin/frontend/components/OptsBar.tsx +++ /dev/null @@ -1,397 +0,0 @@ -import { OverlayTrigger, Popover, Spinner, Toast } from "react-bootstrap"; -import PopoverBody from "react-bootstrap/PopoverBody"; -import PopoverHeader from "react-bootstrap/PopoverHeader"; -import { useEffect, useRef, useState } from "react"; -import EnhancedEncryptionIcon from "@material-ui/icons/EnhancedEncryption"; -import HourglassFullIcon from "@material-ui/icons/HourglassFull"; -import SaveIcon from "@material-ui/icons/Save"; -import EditIcon from "@material-ui/icons/Edit"; -import FavoriteIcon from "@material-ui/icons/Favorite"; -import styles from "../styles/OptsBar.module.css"; -import { useHotkeys } from "react-hotkeys-hook"; -import ExpiryModal from "./ExpiryModal"; -import DashboardIcon from "@material-ui/icons/Dashboard"; -import FiberNewIcon from "@material-ui/icons/FiberNew"; -import BrushIcon from "@material-ui/icons/Brush"; -import { useRouter } from "next/router"; -import pasteStore from "../stores/PasteStore"; -import LoginModal from "./LoginModal"; -import cookieCutter from "cookie-cutter"; -import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward"; -import ArrowDownwardIcon from "@material-ui/icons/ArrowDownward"; -import { Slide } from "@material-ui/core"; -import config from "../config.json"; -import { useMediaQuery } from "react-responsive"; -import SetPasswordModal from "./SetPasswordModal"; -import axios from "axios"; - -export default function OptsBar() { - const [currentModal, setCurrentModal] = useState(null); - const [expiryValue, setExpiryValue] = useState([-1, -1, -1]); - const router = useRouter(); - const [saveSuccessToast, setSaveSuccessToast] = useState(null); - const [saveBlankToast, setSaveBlankToast] = useState(false); - const [copyBadPasteToast, setCopyBadPasteToast] = useState(false); - const [saving, setSaving] = useState(false); - const [optsVisible, setOptsVisible] = useState( - !useMediaQuery({ query: `(max-width: 768px)` }) - ); - - const [uploaded, setUploaded] = useState(false) - - - const personal = [ - { - title: "Dashboard", - content: - "Login into your account via Discord, Google or GitHub and view your saved pastes and bookmarks or manage your preferences.", - icon: , - callback: () => { - const loginState = cookieCutter.get("auth"); - - if (!!loginState) { - router.push("/dashboard"); - return; - } - setCurrentModal( - { - setCurrentModal(null); - }} - /> - ); - }, - }, - - { - title: "Change Theme", - content: - "Change the look and feel of MystBin. Saves to your account preferences.", - icon: , - }, - - { - title: "Bookmark Paste", - content: "Bookmark this paste to your favourites for later viewing.", - icon: , - callback: () => { - let pasteId = window.location.pathname.replace("/", ""); - if (!pasteId) { - alert("Not a valid paste"); - return; - } - let auth = cookieCutter.get("auth"); - if (!auth) { - alert("You are not logged in"); // TODO: make this pretty popup - return; - } - fetch(config["site"]["backend_site"] + "/users/bookmarks", { - method: "PUT", - headers: { - Authorization: `Bearer ${auth}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ paste_id: pasteId }), - }).then((value) => { - if (value.status !== 201) { - console.error(value.json()["error"]); - } else { - alert("Bookmark Saved!"); //TODO make this a pretty popup - } - }); - }, - }, - ]; - - const actions = [ - { - title: "New Paste", - content: "Create a new paste to share.", - icon: , - callback: () => { - router.push("/").then(() => { - router.reload(); - }); - - sessionStorage.removeItem("pasteCopy"); - }, - }, - - { - title: "Save Paste", - content: "Save this paste and all its files.", - icon: , - callback: () => { - let paste = pasteStore.getPaste(); - if (window.location.pathname !== "/") { - return; - } - - if (!paste) { - return; - } - - if (paste.length === 1 && paste[0]["content"] === "") { - return setSaveBlankToast(true); - } - - setSaving(true); - let files = []; - let FD = new FormData(); - - for (let file of paste) { - if (file['image'] === null || file['image'] == undefined || file['image'] === '') { - files.push({ filename: file["title"], content: file["content"] }); - } - else { - files.push({ filename: file["title"], content: file["content"], attachment: file['image'].name }); - FD.append('images', file['image'], file['image'].name) - } - } - - let data = { "files": files, "password": paste.password } - FD.append('data', JSON.stringify(data)) - - const headers = { - "Content-Type": "multipart/form-data", - }; - - if (!!cookieCutter.get("auth")) { - headers["Authorization"] = cookieCutter.get("auth"); - } - - axios( { - url: config["site"]["backend_site"] + "/rich-paste", - method: "POST", - headers: headers, - data: FD, - }) - .then((r) => { - if (r.status === 201) { - return r.data; - } - setSaving(false); - setUploaded(false); - - console.error(r.status); - console.log(r.data) - }) - .then((d) => { - setUploaded(true); - - if (d && d.id) { - - let path = `/${d.id}`; - let full = window.location.origin + path; - navigator.clipboard.writeText(full).then(() => { - setSaving(false); - setSaveSuccessToast(d.id); - setTimeout(() => { - router.push(path); - }, 3000); - }); - } - }); - }, - hotKey: "ctrl+s", - }, - { - title: "Edit Paste", - content: "Copy and Edit this paste as a new paste.", - icon: , - callback: () => { - let paste = pasteStore.getPaste(); - - if (router.route == "/") { - setCopyBadPasteToast(true); - return; - } - - sessionStorage.setItem("pasteCopy", JSON.stringify(paste)); - - router.push("/").then(() => { - router.reload(); - }); - }, - hotKey: "ctrl+e", - }, - ]; - const opts = [ - { - title: "Create Password", - content: "Create a password for this paste and all its files.", - optional: true, - icon: , - callback: () => { - if (window.location.pathname !== "/") { - return; - } - - setCurrentModal( - { - setCurrentModal(null); - }} - /> - ); - }, - }, - { - title: "Create Expiry", - content: "Create a expiry date for this paste and all its files.", - optional: true, - icon: , - callback: () => { - if (window.location.pathname !== "/") { - return; - } - - setCurrentModal( - { - setCurrentModal(null); - }} - onSubmit={setExpiryValue} - /> - ); - }, - }, - ]; - function optionTitle() { - return (optsVisible ? "Hide" : "Show") + " options"; - } - function optionDescription() { - return (optsVisible ? "Hide" : "Show") + " the options"; - } - - const collapse = [ - { - title: optionTitle(), - content: optionDescription(), - icon: optsVisible ? : , - callback: () => setOptsVisible(!optsVisible), - }, - ]; - - actions.forEach(({ callback, hotKey }) => { - useHotkeys(hotKey, callback); - }); - - return ( - <> - {currentModal && currentModal} -
- {optsVisible ? ( -
- ) : ( -
-
- {collapse.map(OptsButton)} -
-
- )} - -
- {personal.map(OptsButton)} -
- {actions.map(OptsButton)} -
- {opts.map(OptsButton)} - {collapse.map(OptsButton)} -
-
-
- - setSaveSuccessToast(null)} - show={saveSuccessToast} - delay={5000} - autohide - > - - Save Successful! - {saveSuccessToast} - - Your paste URL has been copied to clipboard. - - - setSaveBlankToast(false)} - show={saveBlankToast} - delay={5000} - autohide - > - - Save Error! - - - Your paste is empty, cannot save an empty paste. - - - setCopyBadPasteToast(false)} - show={copyBadPasteToast} - delay={5000} - autohide - > - - Copy Error! - - Cannot copy a paste that is not already saved - - - - ); -} - -function OptsButton(obj: { - title: string; - content: string; - icon: JSX.Element; - callback: () => any; - hotKey?: string; - optional?: boolean; -}): JSX.Element { - return ( - - - {obj.title} - {obj.hotKey && ( - - {obj.hotKey} - - )} - - - {obj.content} - {obj.optional !== undefined && ( - {obj.optional ? "Optional" : "Required"} - )} - - - } - > -
obj.callback()}> - {obj.icon} -
-
- ); -} diff --git a/mystbin/frontend/components/PasswordInput.tsx b/mystbin/frontend/components/PasswordInput.tsx deleted file mode 100644 index 2e1951c8..00000000 --- a/mystbin/frontend/components/PasswordInput.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Form, InputGroup } from "react-bootstrap"; -import styles from "../styles/PasswordInput.module.css"; -import VisibilityOffIcon from "@material-ui/icons/VisibilityOff"; -import VisibilityIcon from "@material-ui/icons/Visibility"; -import React, { useState } from "react"; - -export default function PasswordInput({ - value, - onChange, - onSubmit, -}: { - value: string; - onChange: (arg0: string) => void; - onSubmit: (arg0: string) => void; -}) { - const [passwordHide, setPasswordHide] = useState(true); - - return ( - - { - onChange(event.currentTarget.value); - }} - onKeyDown={(e) => { - if (e.key === "Enter") { - e.preventDefault(); - onSubmit(value); - } - }} - /> - - setPasswordHide(!passwordHide)} - > - {passwordHide ? : } - - - - ); -} diff --git a/mystbin/frontend/components/PasswordModal.tsx b/mystbin/frontend/components/PasswordModal.tsx deleted file mode 100644 index 9fc93ce1..00000000 --- a/mystbin/frontend/components/PasswordModal.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import LockIcon from "@material-ui/icons/Lock"; -import { Button, Modal, Spinner } from "react-bootstrap"; -import Link from "next/link"; -import styles from "../styles/PasswordModal.module.css"; -import { useEffect, useState } from "react"; -import PasswordInput from "./PasswordInput"; - -export default function PasswordModal({ - show, - shake, - loading, - onAttempt, -}: { - show: boolean; - shake: boolean; - loading: boolean; - onAttempt: (arg0: string) => void; -}) { - const [passwordAttempt, setPasswordAttempt] = useState(""); - - return ( - - - - Password Protected - - - - - This paste is password protected. Please enter the password to continue. - - - - Return to home - - - - ); -} diff --git a/mystbin/frontend/components/PrettySeconds.tsx b/mystbin/frontend/components/PrettySeconds.tsx deleted file mode 100644 index 85e6f8fc..00000000 --- a/mystbin/frontend/components/PrettySeconds.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useEffect } from "react"; - -export default function PrettySeconds({ seconds }) { - let numyears = Math.floor(seconds / 31536000); - let numdays = Math.floor((seconds % 31536000) / 86400); - let numhours = Math.floor(((seconds % 31536000) % 86400) / 3600); - let numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60); - let numseconds = (((seconds % 31536000) % 86400) % 3600) % 60; - - let pretty = ""; - if (numyears > 0) { - pretty += `${numyears.toFixed()} years, `; - } - - pretty += `${numdays.toFixed()} days, ${numhours.toFixed()} hours, ${numminutes.toFixed()} minutes, ${numseconds.toFixed()} seconds.`; - return {pretty}; -} diff --git a/mystbin/frontend/components/SetPasswordModal.tsx b/mystbin/frontend/components/SetPasswordModal.tsx deleted file mode 100644 index f98ea346..00000000 --- a/mystbin/frontend/components/SetPasswordModal.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useState } from "react"; -import { Button, Form, InputGroup, Modal } from "react-bootstrap"; -import styles from "../styles/PasswordModal.module.css"; -import PasswordInput from "./PasswordInput"; -import pasteStore from "../stores/PasteStore"; -import pasteDispatcher from "../dispatchers/PasteDispatcher"; - -export default function SetPasswordModal({ onHide }: { onHide: () => void }) { - const [passwordValue, setPasswordValue] = useState(""); - const [passwordTwoValue, setPasswordTwoValue] = useState(""); - const [passwordsMatch, setPasswordsMatch] = useState(true); - - const handlePasswordSubmit = (e) => { - e.preventDefault(); - - if (passwordValue != passwordTwoValue) { - setPasswordsMatch(false); - return; - } - - onHide(); - - const paste = pasteStore.getPaste(); - paste.password = passwordValue; - - pasteDispatcher.dispatch({ paste: paste }); - }; - - return ( - - - - Set the paste Password - - - -
- {passwordsMatch ? null : ( - - Your passwords do not match! Please make sure the passwords are - the same. -
-
- )} - Enter your password: - -
- Re-enter your password: - {}} - /> - -
- - - -
- ); -} diff --git a/mystbin/frontend/components/Sleeper.tsx b/mystbin/frontend/components/Sleeper.tsx deleted file mode 100644 index f9a6d212..00000000 --- a/mystbin/frontend/components/Sleeper.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useRouter } from "next/router"; -import { useEffect } from "react"; - -export default function SleeperPush({ ms, route }) { - const router = useRouter(); - - useEffect(() => { - async function sleep() { - const sleeper = new Promise((resolve) => setTimeout(resolve, ms)); - await sleeper; - - await router.push(route); - } - - sleep(); - }, []); - - return
; -} diff --git a/mystbin/frontend/components/Tab.tsx b/mystbin/frontend/components/Tab.tsx deleted file mode 100644 index 8432fe23..00000000 --- a/mystbin/frontend/components/Tab.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useState } from "react"; -import styles from "../styles/Tab.module.css"; -import CloseIcon from "@material-ui/icons/Close"; -import ArrowDownwardIcon from "@material-ui/icons/ArrowDownward"; - -export default function Tab({ - current, - children, - onFocus, - onChange, - onDelete, - filename, - deletable, - editable, -}: { - current: boolean; - onFocus: () => void; - onChange: (arg0: string) => void; - onDelete: () => void; - filename: string; - deletable: boolean; - editable: boolean; - children: any; -}) { - const spanRef = React.createRef(); - - return ( -
-
{ - const button = e.currentTarget; - - if (e.key == "Enter") { - button.blur(); // Lose focus... - } - }} - onBlur={() => { - let _filename = spanRef.current.textContent; - - if (_filename === "") { - _filename = filename; - } - - onChange(_filename); - }} - > - { - if (e.key === "Enter") { - e.preventDefault(); - e.currentTarget.blur(); - } - }} - > - {filename} - -
- {deletable && ( - - )} - {children} -
- ); -} diff --git a/mystbin/frontend/components/TipModal.tsx b/mystbin/frontend/components/TipModal.tsx deleted file mode 100644 index 15534c9c..00000000 --- a/mystbin/frontend/components/TipModal.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import styles from "../styles/TipModal.module.css"; -import { Button } from "react-bootstrap"; -import { useEffect, useState } from "react"; -import cookieCutter from "cookie-cutter"; - -export default function TipModal() { - const [showTip, setShowTip] = useState(true); - const [tab, setTab] = useState(0); - - useEffect(() => { - const showCookie = cookieCutter.get("showTips"); - - if (showCookie === "false") { - setShowTip(false); - } - }); - - function handleClick() { - setShowTip(false); - - const date = new Date(); - date.setDate(date.getDate() + 14); - cookieCutter.set("showTips", false, { expires: date }); - } - - function handleChange() { - if (tab + 1 === tabs.length) { - setTab(0); - } - - else { - setTab(tab + 1) - } - } - - const tabs = [ - 'You can change the filename and extension? Changing the extension lets you change the language your paste will be highlighted in. For example, for Python scripts, change the extension to .py', - 'You can now Drag and Drop named files into the editor to copy it\'s contents, name and file extension.', - 'You can now add images to your paste files. Click on + symbol on your file tab to add an image.', - ] - - return ( - <> - {showTip ? ( -
- Welcome to the new MystBin! -
-
- - Did you know:
{tabs[tab]} -
-
-
-
- - -
-
- ) : null} - - ); -} diff --git a/mystbin/frontend/dispatchers/PasteDispatcher.tsx b/mystbin/frontend/dispatchers/PasteDispatcher.tsx deleted file mode 100644 index 4a2f66ef..00000000 --- a/mystbin/frontend/dispatchers/PasteDispatcher.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import { Dispatcher } from "flux"; - -const pasteDispatcher = new Dispatcher(); -export default pasteDispatcher; diff --git a/mystbin/frontend/fonts/WhitneySemiboldItalic.woff b/mystbin/frontend/fonts/WhitneySemiboldItalic.woff deleted file mode 100644 index f8bafb8996615fd49130af1462365d41242e4209..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16308 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 U8!W66LkJxJ1fn+a diff --git a/mystbin/frontend/fonts/whitneybold.woff b/mystbin/frontend/fonts/whitneybold.woff deleted file mode 100644 index f7376b96beb9f20a373f439bcb6070573b53575d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15748 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 diff --git a/mystbin/frontend/fonts/whitneybook.woff b/mystbin/frontend/fonts/whitneybook.woff deleted file mode 100644 index 3a8f72e0b456df2036b479633acfa55982abf8df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15372 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 diff --git a/mystbin/frontend/fonts/whitneylight.woff b/mystbin/frontend/fonts/whitneylight.woff deleted file mode 100644 index 5d655b17a0cc2ba0b6925a570330203fec7970bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15176 zcmV-OJGaDlPew*hR8&s@06Rzk3IG5A0BFnr00000000000000000000000000000( zMn)h2008s=04Rw707KOc- z003Y!N;o28ZDDW#04aa~00Pqh00csc;=a~sWnp9h05;SB001!n001`UX$mQ5Xk}pl z05=E#0018V001NhECc9hZFG1505>QA00L(K00Y$S#W|yGVR&!=06CBV000I6000I6 z_D}$BVQpmq06CZd00B(^00L@BZ)2ozZ*z1206R1Q000vJ001EWegG7B0nNGxSXIZ? zKfF+9_Gt82;>LpRZ2_Yw7VOvyC@OYA5L7xSAfl+KAX02&L7Iwa>|L-6*synFiP0oR zjnP!SF?H>eIlS*T2aQ)v^4t{f`~4q$JbO=FGqcvL-}1Lewrjg5`TD=d6+RB)Wr zfbh_;$zW3kY{12+42GSJQKyVix2#d4oTyc??7J1q)di31Z7cNXsmT=ay{82{+7xMua- zqXL`Pn>uxB^T{s?n+Hyad}S5SUj~jC5H(?P@WfdEi14V8@Tl;~aS_3jCY2V1#svFM zmNQtNqy1whhxOp;I01d$&9H0@r1C5~xI6_ls22Ri% zT0l!^1{U1|Jv&zTgL=VGN7~e+Ym;2!db;flvs8a2N;UAp#;H3MN1_ zOoSMiB#$^2rodE~25~SQX248{hXj}fi7*?IAQ@6173RQPm?srs0dQCdi(oM5u`LkY%#~a$qwQ!zWTX9!O!&RwRH{b(!A8x`esbU|)hwu^HfxGZ2+=KhD7B<2*SYg9$7DBGg zBAdmq+-8YQn$1$1bejyDOju@uH1hpMpF#x%aY*%?Pl$3S|aB89ykq6v~{p)|a)1vTdMj zFDN?*%KAgu9Z>c(l%r5C6v|~mxvzn^0vQA32GABj`v4698V__i(0!l)D4RgJ0jw#o zDZusv`w*BJ$`6L}lc9VPl+T0mx1jvjP@xV~=n54UL4`B$Rs(n|0^Yg+Z#{&HwV`4M zs2Bwm*F(j#Q1L6MR2C}LfJ&{PQa`BV1(m{}(jhQWF!cpfI+#9#%Jrf0C8%5iRUDv7 z7*t7zDr=$2k5JVfs`^9K-B9&ss8$E6#X`0HQ0)=CT?XFn0B=Xa+uPvnOHjQzRPP4W z*Fp6!KrIJq0;m^3{RC_s!FD{aRqAHK+U>Pvm?}82{j)=&F`RAXQ)*O zwa!AVe?jdEP`ftN?f|vDp!Nc&y#?%?!7c&p(!lOruqyyN3)CqOby`B5&QNDC)JcRo zi=oaosB;+V+<-a{pl*Grn*w!rf#wWaJZMKi4+niJ=&PY#Rj9WT>X(7~1nPT3{aetW zA2cX{246$Nw$N}AG&}_jEnwdU?0bRzP_Xw0`)IJA1@?=plrW3R|3T=I$Z8)@zfVNwpT}^1W4cc9S_9LPF7HEGLIy8U| zqoG3%bhri`DRgWI9o?bhIOsSJIz9rI0noWOblw1+zl1LBpiBH5lh|w$so2)+rVgPi zEg59vQj#k?)H})`>Y|uL9V{|;PzTV}q7T_HhX@S^s2+5ceA_sOn6M^#;XoBf(MU0v z?4et6Fu}SsbsrH`X&%N9EKm0-q72=F9waz}M2HbY_{g`xB)GJTs7sdy5UfhWL<|w- zX@G)dXq50EyOYUv!`C{pwMOMt zEEmN5#1Z11;hP({nx%{;2c{RyJe1X&3hn=%5=D#YcZQ$Qaw->te#bX2+$f zaKLv+{`rk}avw9_ERxVNws+JpCPFci3_BH@&x_f)fFd4PkYvoYR z>i6l}TIU`c0*;Jej{dzocs*ACt_!N#=lsL{XYMe3OT0ydBQ{mNy1IpR*otxNa2+W&c+8Rx649}KYhlNQ zE90WaBT*GA3t-kh*YKLku}3ScDk>u|@7V-v3*}=`nF~3D=+IkG(ZG>qjv?6br_a!i zqboBtLo_~A=PoCrdL&82S|pw(idyn*E18=>u#z`%6O3f1kOx1I2MtMfI¬Oc#ta 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 C0jtpf diff --git a/mystbin/frontend/fonts/whitneylightitalic.woff b/mystbin/frontend/fonts/whitneylightitalic.woff deleted file mode 100644 index c9eee63c4cf63b913f10d2305eebfa64512b0567..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15612 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 DL9|M9 diff --git a/mystbin/frontend/fonts/whitneymediumitalic.woff b/mystbin/frontend/fonts/whitneymediumitalic.woff deleted file mode 100644 index 55702381e829e1687b19fcef17f3c201fd7cdfe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15528 zcmZu&V~nOvuzknYj&0l4j&0kvZDR*Jwr$(CJ+oumzVqE5_t&jXRnk=_^*rfRPCA_` zH#t#JIb~%z0Dz|y01fyNYmEQw|0hDCq69zw2LOOl8UPR#WxD)#C?+qb1OU*Y001yB z003s=x#M-RoDx0LPd?!v><U2><}HxXVM)Z)4#8laK4)4+i{y z^+_IJYhd#e=Q;rZfHZ!v4gzK$HuiQ-&OiSW_k%(GQG#zr0E#=%R0o8%>c|~CpMS^9 z@SS>s(BcWFy1daqs3DjaJ*1(a2&ix*fyk-I1x<1(!4z~U($IFD{xz-X zahy-*a}PqX!@za-SjF?V*W-OagPEJDnW>qpTenlKKQwA@fB$bx&NX_TQS$O3eSEtf z3#GR(FYq^yfDlr30s1bg6MT&m7n;X*)weLz#V{G5e|kx@B30w66;x`aFhMn3sz@$_ zt2rs(n?Z|p;pLhTxth4t*Jm!h&2$1eo~-{sm`nQpK_wCb`U${I`wT||wMWCIYGIy) zkTS!l?UC_%6SMo23VwfjlZS1voW9n{WT;lHuRXv#rJI<%SWDXl{?Upe8DU}JiJKkW<_G9(#ca)#ICZ)ilrO->cT#NqCyCCwtSbV|h@-UVIV)77YfzVj10=%UNaJhu5zlqIZ zW&RnNLr>*$Hu@*)LsaFmHU>1$!e8X^Is+h1AhU9rodc?8;c)t(+Jz8Kpt^F|9|F2( z5#9y>7?45w46p%t46vC79IgJ;(ExS~r~-YKXdpKXn0JO;D~=<+y6ikd(p{) z@Lz-gp#3P2f%>V?gow}vi0}*gT*3k*!h$S=0yKkyJR|}{ll~;Bl%M)+lL2iE2uDIp z_Tm9xwTNgE*Ds*Sy^8AKWoTn@Q0Kdln?cAokgC0!9AI*V&y~R}qXBY+=&A!;s|36j zFxEkr^BM5{j2v*s|6nsiVCEa>`bk@%Q;NcLg0SszBX^lkt=#^hZH9K( zfl2-buK3eej;sy`O6^vg4y?-Edj_mr3WVC9+wWO=!y1<@z~iqCvi|D?klQU_1~LWY zi2{q-R|^7*;O_#8264esi@)22u-gT2r-$3tLbyg15|Ju>AymeJMIFf7GbnvwR>lHP zV#=GBlW*fv#sf+c%5$Vs?uJVGhEh2bLM1QVgH|d2o%AYi!l|r*prY$v$Ba?X=Z;j) z=3kzecV<$m3RCV9X4#Op4qxgDT;39P1||PVuviMtI{Z6^Q+}VWa|dM`A@n(M_w%>= zH>dY3C?5tWzI6yfq<{seB!J?+cOAGg%)bdj-46Kt`rm+asRpX$y1*2jyc6_dCIqXR z5G{DqumCN8sSBhfLh$;2?vr+D7m_8u|9VRLhmvtq$5?%XF}R@gPSpGX6bm7A7;H9JFfn4b5RtDM27g3wA##=lDqT0w^fFh2jD=m` zUf#SLG2JfkNG-1op4Rkt6!olPQ1b5V9n0z?NQZT;7f={Zob)F8G9?6S>*7=pEMdI869vjo^0M4ET0?c(cF_g5FCr<4M`!*m6>1`aV|hRmh`+1 zp^vU;pgK80-v`StG5q-i{6frA14820n01#mhEpMl&0kiv(Ozk$sR)u{k{i%zVOj`s zAS^pK_YWhvWf}Xu?oFqd8BMh1w8}qg*b0Ap$)F^shvKW$<7uR156d$19xDu$S$s#H zb8C6}73mBK)LKZ;1`k9VHaAFEhzSPFFp=qsuTgyAj1rqBd@G7?ZY#yU8F81|yy0^u z)}(won8c&WKiNfm$6IQZyq>+Qeftl;jZGILbX8^)v%R#~X4R}mjOaqEqw zuO5LhHw)&iC~+)fJ}h2YEt4g9)6*cZ90QZ+9B1zd%@q?`S@pVC-ji}UI-3q3wO9EH zrqu<6OD;%cYFqeWkyHIra9~YC5=InF*kvPOPv7(tzG@=&SQ~srW!zqLsuS1Hl^%p_ zN7|B5!=l)niUBsAFa_N^;ENV7XEtAa0%O2p46fcgrgqkinLV`OCA7(j| z*}Pc1PI6Y_GDTkC*TpA!&X2gI{w#$>wO)EGD$R?O&2PsbQDmJvYj}GJh=hOPgYQ?a zTEY===L%2mSG+uO(5+^c?F^%xRqSeW^LHjLFb^0a&H9U{%<)9f1-E*GXkpPVho`WQ z<&)j5=&}4ASz5_;=i67kJuh1Ftg~&O&;Y|thOqQ{i$Uw}&`H z0ZX#OzvUaeV>)citNpF~TKr6@AaXepCJdW1$b}KvoHfQfu$FWQG_9#d?uP9Wi+-%h zK42MnP%1`VH8IOrrL4wh-ZY`g*2GK+-HPLy#N7D#h38v(y9Xuac~r-36&dyNXHvcL zdQe zmdfzApLLcm78+(~^wJU}-a#lqb?t8vQcOeliVqT*~ z;Z+N+$vX_6k*fTWIM*-Xyi1f8afGmLsDg_9@c_*VNBzi2OtVP1$?152g0`l>+4*{K zW|WC~eREmPdd{C4v+i&ag3p?rmC!{Hh3WKYP2RE6s9rv)z5tvla;8=!e<@mh2Y0#*czxx%%3%xrGcLkKea?BYU&=-R;;;QC{WIHkHbozfH~y&uqfo~V@@m@*@}7;H}E+GmWP2Q%^54!Yx$HUo*{FYt z?K&^WYCO4+Zjmwe_>4yrxP*GIj=h`;YN#@0weDMC`d(Ba3@AoEF?!GI{Q9`mx^6cl zexsU0)VOb+8)XBhrVr#PCs~0K#Es$(x)w_#O2b0*?I4_-5_&kPvr^1orG7e!GHJCo zt8cEEP<95y!%_M&uwQ}zSLKHCLaJI&dpKpK0>n(MfXKl>iE98(V^zE5(Lh&Qukop@}qlU}~<3}`{$JNwrhKTf#_I0=7Ac{g7 zHbn!8$}M9SQ9FBx0D_8Ze0+>j^*8_;aZ1r9>Coc)H<}%nOs>>}mp?%nzA?&z5NYh~ z_xu@L&-+6iP6%hhUMmh;bz$1JwBSgyy3T`QNQF7gTjW~0y*Ev{5k0C-9plr`nkJ0L zFL*l|tO(EYS}fS|VLrHyX5v@H_or|_AXxifC=4@-q8Zz)2kbFaVp-oko@k8s55`6C8HJ~73tIf8c$ zZhkyjc*G|=R)bbGt+BV9C3%S<&Tu+<E08c@p{ zFzGJ%9rmTzX2w7|_Yw=rYr(uAMR~wJ4-~E5GuXbk;F$rQNFkU7!E#hNluE2;?kzID z9#XLi)|KU)2t>xs#O|z%m@_RrK<}AKE1yD)QZrCJ8Qvs$LHdVG`5+@UK6KtKT?}zi z*Nreop#bmm9(awCy?{aq=(G?$E?Jo-{?GIq-8@SmkMCCeW?`#j3LWXz85`@Dx*;VL zmOP^}4N;&0mIdV+D^f~01>!dezang}4X9OQ*TE?j5tjryd%lW95W6tr9v>3^9(oaz zGaD-zo?(`EoGZuZ(Y*Y( zbKpSi6O*75F+YkrQjSIMu@0h26eayJ!FkT~lYqNoa>}z*O19bVpGzusXEUiGWYP-l zWYT)eG6E%U9owmZh>F6@MU2{5%$2aKw5s&}Api7ZiId)1jI%^2gu+?L1`)0z(OT5nebHiV+aq|^MWM0k;#O}lB&=|bW zo1!<$ne+}zG-f)gQH5AhJXxN8Of~_qih_<%)gU=%)v!BxM`C# zPo@`>Ms9G~f{8{463Zh-*FJDN9bX^SJi~0FHfiJ0QI8^|i71>4B#c49^iWzC zn#$-`>9t(@-XoPW$vPctdwiy0x!`h}`R)?v7|iR;_B>_<{chPywTA;Ee-^#F9d&hy zm_7H!TJV^U&Lo=#E>?=~cyN!A&NN_tC@m{q-`O4Qk?|E(qY zH2*E<7{8u|mpF2I0P_j{nyt&+n5Q8(mcU!#{PS+OiY zu?JFSy1y`RU3&b|9T@1H%n~{1a_@XSzGi=u?)C>@vBXIDTEb%ebaTE#+w0=imF_s^<%O`6hT!CeKNWeC=fNuyvQjWA)QUooF2Iyncir+av^#Qe_g z*nElJm2fok5~K?=uQXqzo6_0*ti09Q(6V9A_WW=V*qzyS6L3Ww*1#zz!A@0b(E74s z=gQ@!-*C`UNYf+MSeXQoBKAp6JtHnlH8S4t$T(j%*KSrZWF2W^bQF92|o1- zY_l^iS3KJ-iE^V4azjdK*sAyu`Q)FYTkC(*xfN$*-M$KMzg?%|80c-gqaAlvFSKLm zWhA6D`dtm+aX=${Z%bc42hI5Ym%dRBNyTaGiiV%Q;hol#}S9~2&#^94O_ zUHRzn_RZjMcxTeuewjirx1U`lNMKXpNUIKt)QGPS#m~qD9H6o0n?-n?g66lq#77U{ zOQ2I}PK-dkFxUMdW|#gtU`mR=OGk-ZIe=zuKPM&;bfTh)f**wHNE0IrwlNjXC&3Q0=vvqtUv@7;Ayd&`qk_fi5{|h@uf!9OeO6bHwlaa&a z$7$B`y89UBt3OQWi7r9eo!@nlgqy`d>r^o8cI7?{gGmoP z?J}(o13Q!^)_jK^UGs0`h02w~Z9(CYlr_@)g07vKzsK$bNAByjTTpf#pZV{7D1eKB zqdJV@~w4uo>yfCg597yiCa8V zEG(S8%J~AOA~8&4C~=6Mr8gWu$?>P*M0H0w&m8qG`uNQu$uu`3VRD0dcB}gx6)mLjg%>F$ma#cpedVbsR#s#iDVfgD>oKv0gf>b?= z2bA9U8fCZNP%O?6E=}w zwMgOX(~hS#*n_H)8BP_yNp&;VZWL^dLQt8L1;&0AEkNJ351jiYbSMnE<6j#D2gW`m z2(Btl&Q&D-DT73H3oi}$Bi;cS7$bukZZ3=b_Ul3t87IP?X(!{tqBBVPdj1;uLOex} zC?ASqEoFRwLwqGidgZ82N6fWZaR43kwHL|3m9r~D zc4o|2)S)j81BY?2@Zhj>_31D)Z;z)<+UeNat%WB!{WiA(l)elgUc z&p~@&nZ#zeV#*A3+c%P&G{(sn{=iSscc$mr2BNFyP*C*xN5mptR9m75eOAUl+A~CO zjM2jXwC~U3k5-lr&#g2DRlC{jMpH;cUUhQR`3p}%C60gxc;<;iKM49G90oRay6k6h zOpMHBbFnbc&`QCMf^C%9kB+PtJ43dvS5n=WW{&+j|DM1($Wk6W(k87YIUxDg6x4sP z#mjDog#UrfpnSS0rumKrRmhVecKU?!<;a5ESsKrm5%z8s?M|7i4KQR@f-@mE{Z(mv zQ^1%1>lP)=(cgfWwOT}nN#@(F*rG^!$-+3+SW}59t5Z{hCT;m6>r{VEMMKm4h_mhQ zaV4X$G8MIw#$7$rBXWS`x=o-OdRZ^Jj zfKz|w73=FY$B1C46|1Qde2r{-XIg*0=4?$p#HSYgJr2bv=xNraWc6zBit_wc8VDbe zLi^_g_8APvhLT;LN^l0FwOa$l--rY^r!)kmj*p#}a}-|*NpC<4+pmDSB`m{|n-Z#P zPK%$Zd=bfNl9EDpB%|_VVz)W|r*203saY)~AYFMtlyyLd;({_VUo`9HS%87&e=EQG%4NGWb=XNL)YL}(pl%P*YoI z*8y?td?XF~i8_6r!$&idg?XzwX4K$AiP{j7Xl`5G|0rS@HC?Xz$*=yrfil$hSE^Xv zVBr#a-JAo6ns_L$TtVHCpj$ax&*QG(>2*|SYmv-HlYN#82g^lCgbL-Js**-0gwa#U z;}3p-kLJ>mp0lGXbn(Tr6Um%xs+MFKe7=z7!#o}xY$hb%r)UjU|9Ukj|{F_D$t10 zi$Y%jP-$Vg4_S}Kz|nb`4vKmN1Vj&b4{q(MYeO2ajgk2!y{Oj4=7` zpjIbjRII8cceN%873z_VM91l=)Zmo&^~W^_6>Imz>bKl3xHA&tvBrjpt);MMBGtoj zGFqc0c^muBPES_(lh33!QEQ{qx};t8;ol@oq%+()`6NBXOWq$$Inoind+=NLGZYHD z(rr00zbaX#z9fYv^zN;>ZIW)3m*IG^+=Cjv0VtmqaNtg6JrP2yi_Eqe?M_d2W1p

rXSk$0$w40y#H!TA1FHc1IWNDxLJCQS@@$6{we7)Oj1?Vmw^O_`@# z2p=bS_AKdyR`i!OlFW%NbJ?a+Bl^_=8sHKkT}v>TR=s9rIBpS^Qk}cdvK8}1bU203 zr@>#ZzO~=GZ=yENkh)#gw-u~m9Z7eriZ%kXC#^D`vCO80V^s4KW?2-@Rd=pQ=IsgW zCeS!v@hbaoeTMbGHZdInw_YGlvjj!q2>8xNYA~~g4s?vIHj7gq7c?ZVm$g5mzt9Em zc{&xQr$6<8=w$bs9E;zi!r9u-)W$O>zE?2$r)ksVLl>xgk}JavL^LcdiaqC-)bF3m z=QTfGWV#NeoVrK_Pvtf3FlAf5740zT&uJ$f1Uk!emu8XS&j5VBcgB9MfIn$1kOucN zSo3vK29>N0E0b%g&_+wYiDOsRhba52lY+X==z+>-S(x3qG`P$vGpj{SUdAh$Fd;8V%`);|Vrn9Pq za>i57rkw3^tD_~@XzYh=l&-*H$1{E4lvp_MQ4mpwpVt|*s( zF^|Uvb0zBdG9MlJpX9w-zgJmZuy=5JSW}C1i4s?&sX{nuaB+oEWiX<+4Z&RKcQTqt zQ_@J+qj#wKUT9B+Y(%gu!YvBXv#7e0CYqtm;_Zg4>vMdg8pb3Uqf)8%@JP$T!<(Fg zfdV3VLzPgTF`~!b^fL?XD`#6ueYpl8Vae(pjq4dpbSlaqQM!%ec9q`t3jMsM3{Gz9 z@q_fBI)0v+ybZqer!j)r?YsMl!6a0(Mr2^`lVyL^NdilawBQbJQ)>jD6wq9eXYIBb zL}ij<_uV&^((In@2v+t-gP0;x(>o7iBT_TY&&9JF>cD&GCY-+IeGq$1>7`AK7uSDq zAtgH#wG+^JOW4 zeLfNL!SB;7{_PmDhuIakn(>5m-;&iNNq+=ioK(1S)1652F}v}-K#escJ*JsD75O+e z7|WI3Kh|k+Pwb9@j7k?PMEwZQuV??saBkQt8^iv%t**l95e^B*jp!WDKa5A(ui;FR z56=~6Pykt;&x=&uA8_GD%7Me3lwW2PPmx$G9qxKu`C%Q{lCkYF&#-(Kz%tAdSRq1M zRO-Iq6dU~cr)}KDm+<<8&$S0)?IjN55kup=qKt>dN7z=$TXSA7v}lwmizx66eTrj{ zZC8BIT*I&GyEx|TobKQ$Cvn*gpP=fBiha|yMGfq|h-QAiHj0n4c|UvEu6qv?g>_jQ zw#<4}?71fDCgGTvZM(Q|W;#8CH6<&S)XcD9id9F??VGK9D|J%`YbUPf)EO^vn70u2 z7IRL`1&)HFG;7=SB2ZKLAOG)-$Xw~Mj5y9vW`o_?qJZJ8J2G1YBVQzgJa4LfMyb7p z{j^VEKYG5v$Cr5!t8(?@TZgo~EWDOATeyr=$xLRVA*2GpRFSoThzfquz*Zm;;C*jRbU1mOMwVG2^O(wNoL6%$xc zo7n;Nzn#rY342IZO2m=O>rCH z#OtBnf!ma_9JjeWMhg{XIblQkbMlw2kdYOasd6+tW;uUvFJIX~8t>ED0=y62*#hU5 zR--U8*C7Yh0Bi7 z%p!_^Y`(UdFec#-f%)1ivzPnG4sd4g35!u;sP4|{)EO^rU~;>T0^3# zx%cp1A?Yp6f=I-wY(j$R$h|OH2MM7{5O{8jf6~k7K@V9oAD7-6ZSeKObV@Lw?3v+j@Z%AR76fXUb*f;|@ zAthmUL+Q%}E7pu|dQw~F)MRE?v|0URW2LprP<^U;%*bSTl$?9YaRi*N78%Yqm^fd- zV7@g@C$AL0kHIU@%~RzNgr}kg@?gn1VcAK1c6)R=Ds^k@i`wC{t+KcB>2Sh$5|mbKL~$bYF#isfO0`_O$g=5-)pEI9r^L4F4GxRVabDcrsD~h%k^xt$V#*2ir4e$Y^%hs=L-ZJ9*f8Lq;{8BG}dve=no#V>*fm- zGPz7Plgs)8rs;G#@5rXdJvxncqkBYAGVP3do#WCO>vDC2@4&FkSHo0u@3PH?n}62o z&=s%ObAa_NqQ&|sT?;IP3Ajs;t6-15b1lUvr0YoR$n?aS1@)rAVxJgZ6v4b`~XuNtuVz(N96j?5Erdlru56XE>CzB1}8_-XLtAQ%mu&h;Xz z_76MA!9kNmodg`RG13;EyK(X!U}&nsTB1m*;?`f1RHPafl;h$ms->CY9;nL75+*9j zDl#sti^@_qE{iI1K5#6H;zlwoOVUoXjf;|2HZ%*eUbxPS5@tHiNyQmgo+o7~+ixco zIbYxy=5c+Y7?!EKq8Ju`@8@IKhK2Vx?E+c1>xWRxx-5zkx1DmlXrJ@ij?|BBxoI3z zxVMbTW^`P~(D5GPrr_VE2fA;oa=fo0_}lvkdtTS*yC1?qnqL>LzivvkHcfLzc#oju zJmwc%J%^pLA3M=I4@Bs_hGO8k_f%>;wiM7W95xxrJ9kxEI?Pk5TsLfMGBz))+*a*6 zVX>>~*EO*#YqvRbURI?u8j}{h6KNZJX&R;)R(~nUFfVV&a$XW6yexK1zf5v(KL^x& zShiz+12*gr=>hIQTd6+gn{K!xI~Mc^GV2G9W26>JB*t4j5ur5AR_mh#$6JTX&SQs; z9_`V6p~#6aQQ>q3N~uiHZlg#CvZ0j&pGqcyB5cml0TmoBEla~9EhmAi=IpTh+AHhU zEO+-q&(HfGQx&L}ssHfpwsUr#ckdrQz45P<3CmRR=SEZ*{btaw)na%WN9(2KM9#YD zXURx_-dr)Nfo3w|nNbl`c@%FyadElvHg6xZRJcoAyZ1a+?^};Of+hQ+XJUM#)ey`grXcIxd0TDDNEBiVSjo!dNG zUpJ=gUJmVUpI?H$O8Kw+CclNgC8ca8bZBduhc*4?+qclHCz`tIS2|j}u5Y`XUib^L z%E=OR$`|mKDg<>|%O$4nDnzO*7j!R`I-V+&*iZd!mucThfzOE4a^zb)ohv*aC2$uB zw~7bO7LagNdiaWfz#c6^Q08*;+l7RJrMMZ@)8-0OL;KTtdj8Y3kBcXK>u(hKN5MmqV>s$?0GbM_w7fAsn`|Nr zuPayrQB^Xl+IvjYRZ_Z*)s6Lfr_rN~?3o^rb6X|6Y-(ef)079|4IzMV0v49)f$^X* z6IYV?J37W+&^xMce>jZQ`=u*kG-`E}iC#=En~{2S)j$|o5nGe1ZgJ>Yup<14*_=Mt z2bCYjl=STs-}s57p``NprwK^n05dL+UT!B0Y2wvtc%HPz1!v-XP-$?_NS(%c7F*ne2yJ@CI_!s=y6wr@RTG3a)p;I>!a9re(?6v9YhZx}+q6 zo_}BFDBLlinMFO%CahDoB5W-~{J7~)cc4prHyf2F{%6udJj`Xj$y8${fmfXmGz=JP zuPzVExd;NE{%y48i>(RxniBUN?@Md%%TJD;{z_w7&*=&v_bMm#c1xz6Xb9gAOU5sS z`_Uv(Cf1N^pGl6$3refDkh9l#OH3^@o=xSj$9~p|L$=0s)O#G}oLN8{{H_m%a4$i& z@GeGpkb|>KZ=!m7{Z}ao&qOBYa8WPtXVj|fKyf`rM#(2B5qDJ9mf_G%be5nk!hnx^ zZ&}bI996gu&}4NkZG?BC#(*rzF(_pgUYI#I?3;g8o;geKKZYG7cf{C0qeg@PZo%j$ z4jbg*_4YNmg+|i&V{^GSOOgQ$r!#_Mi|76Ka;q-rv)rbIOe^%Y+v|TzQSOgex?Qnq zR_>2Rj|+a>koh)Pll~pA)BYw0;D#HDfCqnFL|{`m{Y1aij}+MflFTAn2wN3aJ+IY! zxL&E`jav);lUX`%uQ``J^hLPug^25mO?FgZ14N*pN_`Yp*XprNh8m8Zm^p$W$n5h@dFDr#{&dwo?%612hDBXKX;k_7*S4PhEm+H2Ff| zPwx)YWMWa8CpRm074XB=`)7G1?w(kfTy>CXV(?!0i{t6o)c-EN=A$ZxpMp40d!?W=IB({w)E4Fr!f>m%}sDL47T z)5P{i$1QG-?-<3?aJGDRG5r=4o7A{hdR&mRIk)8i0j>ly!(H$Q1bIFe2CHwt}}){D<%L zqCj;?KhDTLqdtw#Ln5l?t(L{U(6m3{&$h(;1}Ff|g?LEEw5yL))8W){s>$_w%zXc< zd1)y^QUAvcSASyAw0M>olY-OJJW;u3CGVV-p=FBtep-+}F+HupA*6?fW2`gH$-azv zJUm{8>%sqzHlzs-%-^TwUQ*}^taDzwWDprTfMK8bhCt<)NOw@Tk+C)8W(1G)24py+ zDN1Y*W8A-KJhN%gJCZw;RB#Cfp$#0qqF$amU|*U(3l5_!f=IY!#u^;1`)Qb)^7dXN z0r*9!=yQ`6M{b+<0Nnjz;M@mapf3NG^!w0V?Oa)*A7<>m`2B*Bh6g;beEq|Z`iq_t z4(_nBwbWDCq1O^08TGye78fM(W|hEoyhSLf>`fZ8zcNEF+3(5fKD9ZI>aAo*&?nQT zUw64yW~}&68nc~5?gAS$$$qU^+8~WZ{>Qz&!oVrIMw{Nm1fm-hIAJ6If*-Yrc0+~f z_-8nsYSV!4z3FGWK}&XF=hosgi4#zf$3`aCvR;AFl zmTYpC%n|wtrz06~0w7MKh(_Y}Z#e9CjwRfaJ)GQ=fH!%_j-y`cf-^;@4cSM$w>M1P zG6{D$d3dV8Q^y@HW65vaWqvp^ddvJu@CpnfUgpprh$YDtl7SnPTHyu50~6SU8>HE7 z_&MBgYYlS-ai}4EIECa6md8rAIZ-meqZ_^VmpF)*TV?$7HAhDfm&4WIW4)UmB)v4A z!Ml$zUc3xmnh>5;MRsu3JxWEU#?8-hH9XKJ&fqxRpOjH~<-wKdXsn8X_edS-5<6Y5 zcc895Uu!*m!elVxbB0=9=^gPJ=TA_IX=VHV;hH`6MZG9)t`0{xId%C5cBOE%?Rpg| zGQ6zs@vhfh>uAr)wd3U685BElWGj*W+E|dM)8wuO(Mo@*1^*NXzitZw5)GArY6OA7 z-Mb0|W$oa!)`E7oHJN52dujrN_9{CtaneWMHJI7}9j-^qy}?nL5(jxi@D(>GtCS|*eJXlrXdoE1A62iN2Y#v1Nc?v2B(>Btb@uha%IX^ zbQ#~-XuV#R@@uhZ3-_I0J9{0V15`9lq{V$N--={mB z2sf3Lj$a6Sen9WBj$c0O?Xq&b%Tb#a8PTJRdz7M`z2PdNna09(50P1>#cM&{X6E1g zZwMED=C4BEVNc+yopl#}$3nus7!P%OPv@5`bI5J)IcNtYOnj*-U;gJuq2DrXb!#hr zoF5&!R6}D+9>-HtykVI=%ioE95V_j%Q$KfMBYk6iz;1!#@W%eDO*az*bB{hCP>6Eu zh~dp|$PWh1^vDHP8rE6>78Jm@4I}e2WCsBHnbNEN=J>J8-|pX)D zR{~Y2f|mXbsjYt1KN^A}OpF}3lpldquX`LDBaJdR^5?nR?RtO9YdVRn z1f+D9=b%GrwS-+Enq%DY5@e&e!E1m!|;+}lC;`z{fVQUy)3u4BKKSwBaPV~LfU>T z%!eHl*cIc+Kg6-BufcKNBIK8 zEq1?E4#Z(IFsV}vF6NOIC!BKrDtyb}z2h^5U};1mGYvgMD3F3(Sm7W|jaV)_Dqt&+ zZ<#Nmc6NXW4$h5qs#+o^tEX!pfx|B2?{-Y3M>S1WnCVS9B9NcTMSdbR-s)D%5?us7 z*kcK9W0J~Mxj9B`l0E-SKakIe2RkP=9K?16gj+pc7p64z{N`+$cG{RDP&>KSLP^K_ zWoX0fyIobd;)MhN_zZ3zq4O~6OoA-*eI#(UHsTjc{0jIsxHnk!UbUw2l*^gw@H5n% zAF+;mNsJEjGx##OM;vj+j?IWYh?{DY&Yd;gVy#d1o!0uBT}Q?qL07TnST^QmBj{fT zt~SMN$oK$=;=PR>O7rV`V9dJMe4DJaKuKC#?P3usoi;bvp(h}|!TR5FlLr8v06%qX zGc72EEr>CE`%R`m*_ci9SC@-CLO*m(xWfH}ltA=M=4MeyedMC|YMLpQ<{tA6>}~4J zh3g<>Na)yLcVyu14v-TCb@va!4-5t8p1`;t-4`nD&gjByQVido1_1x;6VRFSfu>|1 zjK;i6yfY;I%J#%8D~fMFNX{7c?kmaoPQX1>uQVB4lNoMOqV5{`pD}clWC_JlvNV#m zrJdo}Hdn9?x3;iXmTQX2l8J(Nzt~=v*<`D3r<|xbpIEAAq0TOK5ayzZ7+a2P5hzkpKVy diff --git a/mystbin/frontend/fonts/whitneysemibold.woff b/mystbin/frontend/fonts/whitneysemibold.woff deleted file mode 100644 index 3aeae062103be7a23d9e505c17f0b2bccc27bfa0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15724 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 a1ESn4z>rm zK-OTw$yjxE74mTCQC~4=w zJ=zJRxq+}_i${urx8k7fOq2>b{3O=j^h!&fV%GBi&DT9!Dt>X&u{zf%R)y*Ji-jF) z9em$I-YHxvSu_kAQJ`tKYc6lRwENyB;>4&7#r_EV_P^|6@8Lq4$I~^05XE?FIX`%2@5!RqUJT0ujfkt!pXB3;FJ9EW}ct`NrdR%G^~5= z4}~@Uneg6!iP)$!@i7>5SqI8Bc>nXY+Qh$Y-!Lmr_7~1U({omMG7~QyR2SVN@MXvO z>YD4ViymR6-utQETA|6BsyX!E&UdEBZ0d9LJh2e?N>vH9Z&c%+D6r`>JwdNT$=|o^ z4;;dM>-YUW7b%g1Ssdt+_Yunsu7;Rv+R<4-Q z=%{RgyopVLLU_vskG(rpKG<3pa@(YgScVEB*4W~OWsUpw^Y&SLH@6zwAaV=c{bd8( z|2QHwg!S3EzR>$p#G&oIvgM!mZ(!B5HTi2P-_zfU^wb8WU1SuHNPnHwWxFKTgE~d*XT|Nb%BI5Lc*^k5BbWXRY&bMP9OKrkECdkyq$<+|N7L zF=|ETU3mhwgwvOd7l-gC0}6~jM!cBy<*-92$iVHb*{3Vx)%A~|ncd6}(c;bNo}CO` ztoo?q&&Gfc$@2Fh0|0SN?=U zPxG7f5QtVSuj8CkTtRdb&(+?#UpPnju8k*ey4Q^Rxfo#`%$?iuRakOe+Kz(vMys`$ z`%QeT@w6iZ+oQKJFT4j z^2s0N-M{Gg^7=$baH!w&1T`@H%#o)Y~{+D6_)^GMHK0mj4$>c)??Wc$iMW&O7Igl=ns@UZUj72 z*qslE_`ag@wZ%gC9nmG2iN{)EaipW|N<-Vc1j6FtD9TV!;}PJlU%?5w$Yp{jq=UjU$8a4ra0l~W}4^x{lr;uf}d3D<(~Wp)qeEj z?r{tK+6UJO$pjmRKO{pyNb8!+wyc9tLb0w;ExvwcV|ALdCdMjLOpUT9|ALIf#c~B& z`VLlVIWLJPsWI1!UbB`YMbiT`dYdY<;k@w_ASG&w@tnszn3FseFy1|+Io(Bn2Lb9A zoFHoyJ4M?6MPQ)-V>JXi(!41&a`u_o$vrj!=OCic(5kaZRk7nA(ja-6Ph3bXR%$I} z10hVbyhF9~;tl~>jfKc_!0qn11;h`+zyHG%T{BXsU(cUUd@PlkGA`VhzDdT5_hccN z6qBfT1p7MmQ8fu|rs4}hlNPhPu0-6QjsG3%^@-{(VFy=e zn~OX&0}_{YNQ;xqQEMXsvq8=xf!*aHn_Xv`8_PS>A0yKPMwsXeaWv3e;bLgJT(RL* zk;-RAAUkn&M`*Ju+3Rk8wuw*V%YnYX&tNEJaP`{;K|mBiKpy|IHM1FEnN1<=k=p=w z4Lc+~zUFd$eQ#v?E2gz2nxJwBB`Zd?o5i8dI4R<67?TN>{Ni-~F8@@-T?(ja(ZA`( z)4QMoW@K~6j9;oaDhx|xk1vIU_pt_NFf5+o6|nAa0tU#To7c+L15(a{L2QO8ELHJ{8jsw&7e*5|d0}EK58i!Dh2)1IM}-og3bH0es#qL~ zy``o}QmY?4fx{PD^{wqESQ3%;!2kKVF{;J;VE;sa{~~Y^*x%2DAOZ%A!6sxO4upgZ z`v(dSO9($E6ezsQiMCMsWe;CF#fu~VlZgqI=SL`x0khqYNfuC$A!;PT|6+zYNbuJZ QJSqHl@i7G^XcEZ(0PNtntN;K2 diff --git a/mystbin/frontend/icons/DiscordColour.tsx b/mystbin/frontend/icons/DiscordColour.tsx deleted file mode 100644 index 582885e1..00000000 --- a/mystbin/frontend/icons/DiscordColour.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import * as React from "react"; - -function DiscordColorIcon(props: React.SVGProps) { - return ( - - - - - - ); -} - -export default DiscordColorIcon; diff --git a/mystbin/frontend/icons/GoogleIcon.tsx b/mystbin/frontend/icons/GoogleIcon.tsx deleted file mode 100644 index 9d23c111..00000000 --- a/mystbin/frontend/icons/GoogleIcon.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import * as React from "react"; - -function GoogleIcon(props) { - return ( - - - - - - - - - - ); -} - -export default GoogleIcon; diff --git a/mystbin/frontend/icons/LoginIcon.tsx b/mystbin/frontend/icons/LoginIcon.tsx deleted file mode 100644 index 6042929e..00000000 --- a/mystbin/frontend/icons/LoginIcon.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import SvgIcon from "@material-ui/core/SvgIcon"; - -export default function LoginIcon(props) { - return ( - - - - - - - - - ); -} diff --git a/mystbin/frontend/icons/PatreonFirey.tsx b/mystbin/frontend/icons/PatreonFirey.tsx deleted file mode 100644 index 14a3a5a7..00000000 --- a/mystbin/frontend/icons/PatreonFirey.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import * as React from "react"; - -function PatreonFireyIcon(props: React.SVGProps) { - return ( - - - - - - ); -} - -export default PatreonFireyIcon; diff --git a/mystbin/frontend/next-env.d.ts b/mystbin/frontend/next-env.d.ts deleted file mode 100644 index 9bc3dd46..00000000 --- a/mystbin/frontend/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/mystbin/frontend/package.json b/mystbin/frontend/package.json deleted file mode 100644 index c179b270..00000000 --- a/mystbin/frontend/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "mystbin", - "version": "3", - "private": true, - "scripts": { - "dev": "next dev --port 4340", - "dev-ssl": "NODE_OPTIONS=--openssl-legacy-provider next dev --port 4340", - "build": "next build", - "start": "next start", - "launch": "next build && next start" - }, - "dependencies": { - "@emotion/react": "^11.10.4", - "@emotion/styled": "^11.10.4", - "@fortawesome/fontawesome-svg-core": "^6.2.0", - "@material-ui/core": "^4.11.0", - "@material-ui/icons": "^4.9.1", - "@material-ui/x-grid": "^4.0.0-alpha.37", - "@monaco-editor/react": "^3.7.0", - "@mui/icons-material": "^5.10.3", - "@mui/material": "^5.10.3", - "@types/crypto-js": "^4.0.1", - "@types/node": "*", - "@types/react": "18.0.1", - "axios": "^0.27.2", - "boostrap": "^2.0.0", - "bootstrap": "^5.2.0", - "cookie-cutter": "^0.2.0", - "cookies": "^0.8.0", - "crypto-js": "^4.0.0", - "file-loader": "^6.2.0", - "flux": "^4.0.1", - "http-proxy": "^1.18.1", - "install": "^0.13.0", - "monaco-editor": "^0.34.0", - "monaco-editor-webpack-plugin": "^7.0.1", - "mui-datatables": "^4.2.2", - "next": "^11.0.0", - "react": "17.0.1", - "react-bootstrap": "^2.5.0", - "react-dom": "17.0.1", - "react-dropdown": "^1.10.0", - "react-hotkeys-hook": "^3.0.1", - "react-loader-spinner": "^5.1.7-beta.1", - "react-popout": "^1.0.3", - "react-responsive": "^9.0.0-beta.10", - "react-tabs": "^3.1.1" - }, - "devDependencies": { - "@types/node": "^14.14.7", - "@types/react": "18.0.1", - "eslint": "^7.13.0", - "prettier": "^2.2.0", - "typescript": "^4.0.5" - } -} diff --git a/mystbin/frontend/pages/[pid].tsx b/mystbin/frontend/pages/[pid].tsx deleted file mode 100644 index c239508c..00000000 --- a/mystbin/frontend/pages/[pid].tsx +++ /dev/null @@ -1,89 +0,0 @@ -import dynamic from "next/dynamic"; -import Base from "../components/Base"; -import styles from "../styles/Home.module.css"; -import {PropsWithoutRef} from "react"; -import {GetServerSideProps} from "next"; -import Head from "next/head"; - -import config from "../config.json"; - -const PostMonacoEditor = dynamic(() => import("../components/EditorTabs"), { - ssr: false, -}); - -export default function Pastey(props: PropsWithoutRef<{ paste }>) { - const { paste } = props; - let initialData = []; - - if (paste["status"] === 401) { - initialData.push({ - title: "PASSWORD PROTECTED", - content: "This is a password protected paste.", - }); - } else { - for (let file of paste["files"]) { - initialData.push({ title: file["filename"], content: file["content"], image: file['attachment'] }); - } - } - - return ( -

- - MystBin - {paste["pid"]} - - - - - - - - -
- ); -} - -export const getServerSideProps: GetServerSideProps = async ({ query }) => { - let { pid } = query; - pid = pid.toString().split(".", 1)[0]; - let response = await fetch( - `${config["site"]["backend_site"]}/paste/${pid}` - ); - - if (response.status === 404) { - return { - notFound: true, - }; - } - - let paste = await response.json(); - paste["status"] = response.status; - paste["pid"] = pid; - - return { - props: { paste }, - }; -}; diff --git a/mystbin/frontend/pages/_app.tsx b/mystbin/frontend/pages/_app.tsx deleted file mode 100644 index da8fe233..00000000 --- a/mystbin/frontend/pages/_app.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import "../styles/globals.css"; -import "bootstrap/dist/css/bootstrap.min.css"; -import Script from "next/script"; - - -function MyApp({ Component, pageProps }) { - return(<> - + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+ + Delete File +
+ +
+
+ +
+
+
+ Save Paste + +
+ +
Add extra file...
+
+
+ + + + + \ No newline at end of file diff --git a/web/password.html b/web/password.html new file mode 100644 index 00000000..44abd7cf --- /dev/null +++ b/web/password.html @@ -0,0 +1,83 @@ + + + + + + MystBin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + MystBin + +
+ + +
+
+ +
+
+
+

Password Protected!

+ This paste has been password protected. Please enter the password below to continue. + + + Submit +
+
+
+ + + + + \ No newline at end of file diff --git a/web/paste.html b/web/paste.html new file mode 100644 index 00000000..f7f24ddf --- /dev/null +++ b/web/paste.html @@ -0,0 +1,74 @@ + + + + + + MystBin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + MystBin + +
+ + +
+
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/web/static/images/logo.svg b/web/static/images/logo.svg new file mode 100644 index 00000000..fc90865d --- /dev/null +++ b/web/static/images/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/static/images/vsc.svg b/web/static/images/vsc.svg new file mode 100644 index 00000000..97306b16 --- /dev/null +++ b/web/static/images/vsc.svg @@ -0,0 +1 @@ + diff --git a/web/static/packages/highlight-ln.min.js b/web/static/packages/highlight-ln.min.js new file mode 100644 index 00000000..a5f9f204 --- /dev/null +++ b/web/static/packages/highlight-ln.min.js @@ -0,0 +1 @@ +!function(r,o){"use strict";var e,i="hljs-ln",l="hljs-ln-line",h="hljs-ln-code",s="hljs-ln-numbers",c="hljs-ln-n",m="data-line-number",a=/\r\n|\r|\n/g;function u(e){for(var n=e.toString(),t=e.anchorNode;"TD"!==t.nodeName;)t=t.parentNode;for(var r=e.focusNode;"TD"!==r.nodeName;)r=r.parentNode;var o=parseInt(t.dataset.lineNumber),a=parseInt(r.dataset.lineNumber);if(o==a)return n;var i,l=t.textContent,s=r.textContent;for(a
{6}',[l,s,c,m,h,o+n.startFrom,0{1}',[i,r])}return e}(e.innerHTML,o)}function v(e){var n=e.className;if(/hljs-/.test(n)){for(var t=g(e.innerHTML),r=0,o="";r{1}\n',[n,0{ +throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{ +const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i) +})),t}class t{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function n(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope +;class o{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const n=e.split(".") +;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") +}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)} +closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const r=(e={})=>{const t={children:[]} +;return Object.assign(t,e),t};class a{constructor(){ +this.rootNode=r(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t=r({scope:e}) +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,t){const n=e.root +;t&&(n.scope="language:"+t),this.add(n)}toHTML(){ +return new o(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function l(e){ +return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")} +function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")} +function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{ +const t=e[e.length-1] +;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} +})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n +;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break} +s+=i.substring(0,e.index), +i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0], +"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)} +const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",_="\\b(0b[01]+)",O={ +begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t, +contains:[]},n);s.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const o=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return s.contains.push({begin:h(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s +},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({ +__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{ +scope:"number",begin:_,relevance:0},BINARY_NUMBER_RE:_,COMMENT:N, +C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number", +begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0}, +NUMBER_MODE:{scope:"number",begin:w,relevance:0},NUMBER_RE:w, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t, +end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function L(e,t){ +Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function P(e,t){ +void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] +})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={ +relevance:0,contains:[Object.assign(n,{endsParent:!0})] +},e.relevance=0,delete n.beforeMatch +},H=["of","and","for","in","not","or","if","then","parent","list","value"],C="keyword" +;function $(e,t,n=C){const i=Object.create(null) +;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,$(e[n],t,n))})),i;function s(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){ +return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const z={},W=e=>{ +console.error(e)},X=(e,...t)=>{console.log("WARN: "+e,...t)},G=(e,t)=>{ +z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0) +},K=Error();function F(e,t,{key:n}){let i=0;const s=e[n],o={},r={} +;for(let e=1;e<=t.length;e++)r[e+i]=s[e],o[e+i]=!0,i+=p(t[e-1]) +;e[n]=r,e[n]._emit=o,e[n]._multi=!0}function Z(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw W("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +K +;if("object"!=typeof e.beginScope||null===e.beginScope)throw W("beginScope must be object"), +K;F(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw W("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +K +;if("object"!=typeof e.endScope||null===e.endScope)throw W("endScope must be object"), +K;F(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function V(e){ +function t(t,n){ +return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) +}class n{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const t=this.matcherRe.exec(e);if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=i(e.classNameAliases||{}),function n(o,r){const a=o +;if(o.isCompiled)return a +;[I,B,Z,D].forEach((e=>e(o,r))),e.compilerExtensions.forEach((e=>e(o,r))), +o.__beforeBegin=null,[T,L,P].forEach((e=>e(o,r))),o.isCompiled=!0;let c=null +;return"object"==typeof o.keywords&&o.keywords.$pattern&&(o.keywords=Object.assign({},o.keywords), +c=o.keywords.$pattern, +delete o.keywords.$pattern),c=c||/\w+/,o.keywords&&(o.keywords=$(o.keywords,e.case_insensitive)), +a.keywordPatternRe=t(c,!0), +r&&(o.begin||(o.begin=/\B|\b/),a.beginRe=t(a.begin),o.end||o.endsWithParent||(o.end=/\B|\b/), +o.end&&(a.endRe=t(a.end)), +a.terminatorEnd=l(a.end)||"",o.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+r.terminatorEnd)), +o.illegal&&(a.illegalRe=t(o.illegal)), +o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?i(e,{ +starts:e.starts?i(e.starts):null +}):Object.isFrozen(e)?i(e):e))("self"===e?o:e)))),o.contains.forEach((e=>{n(e,a) +})),o.starts&&n(o.starts,r),a.matcher=(e=>{const t=new s +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){ +return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{ +constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} +const Y=n,Q=i,ee=Symbol("nomatch"),te=n=>{ +const i=Object.create(null),s=Object.create(null),o=[];let r=!0 +;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:c};function b(e){ +return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s="" +;"object"==typeof t?(i=e, +n=t.ignoreIllegals,s=t.language):(G("10.7.0","highlight(lang, code, ...args) has been deprecated."), +G("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +s=e,i=t),void 0===n&&(n=!0);const o={code:i,language:s};N("before:highlight",o) +;const r=o.result?o.result:E(o.language,o.code,n) +;return r.code=o.code,N("after:highlight",r),r}function E(e,n,s,o){ +const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R) +;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n="" +;for(;t;){n+=R.substring(e,t.index) +;const s=_.case_insensitive?t[0].toLowerCase():t[0],o=(i=s,N.keywords[i]);if(o){ +const[e,i]=o +;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{ +const n=_.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0] +;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i +;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{ +if(""===R)return;let e=null;if("string"==typeof N.subLanguage){ +if(!i[N.subLanguage])return void M.addText(R) +;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top +}else e=x(R,N.subLanguage.length?N.subLanguage:null) +;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language) +})():l(),R=""}function u(e,t){ +""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1 +;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue} +const i=_.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}} +function h(e,t){ +return e.scope&&"string"==typeof e.scope&&M.openNode(_.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(u(R,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{ +value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t) +;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e) +;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return f(e.parent,n,i)}function b(e){ +return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){ +const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return ee;const o=N +;N.endScope&&N.endScope._wrap?(g(), +u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(), +d(N.endScope,e)):o.skip?R+=t:(o.returnEnd||o.excludeEnd||(R+=t), +g(),o.excludeEnd&&(R=t));do{ +N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent +}while(N!==s.parent);return s.starts&&h(s.starts,e),o.returnEnd?0:t.length} +let w={};function y(i,o){const a=o&&o[0];if(R+=i,null==a)return g(),0 +;if("begin"===w.type&&"end"===o.type&&w.index===o.index&&""===a){ +if(R+=n.slice(o.index,o.index+1),!r){const t=Error(`0 width match regex (${e})`) +;throw t.languageName=e,t.badRule=w.rule,t}return 1} +if(w=o,"begin"===o.type)return(e=>{ +const n=e[0],i=e.rule,s=new t(i),o=[i.__beforeBegin,i["on:begin"]] +;for(const t of o)if(t&&(t(e,s),s.isMatchIgnored))return b(n) +;return i.skip?R+=n:(i.excludeBegin&&(R+=n), +g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(o) +;if("illegal"===o.type&&!s){ +const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"")+'"') +;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e} +if("illegal"===o.type&&""===a)return 1 +;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches") +;return R+=a,a.length}const _=O(e) +;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[] +;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope) +;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{ +if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){ +I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A +;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e) +;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e, +value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){ +if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n), +illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A, +context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{ +language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N} +;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{ +const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)} +;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1))) +;s.unshift(n);const o=s.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1 +;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r +;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{ +let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" +;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1]) +;return t||(X(a.replace("{}",n[1])), +X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} +return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return +;if(N("before:highlightElement",{el:e,language:n +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML) +;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i) +;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n +;e.classList.add("hljs"),e.classList.add("language-"+i) +})(e,n,o.language),e.result={language:o.language,re:o.relevance, +relevance:o.relevance},o.secondBest&&(e.secondBest={ +language:o.secondBest.language,relevance:o.secondBest.relevance +}),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0 +}function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]} +function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +s[e.toLowerCase()]=t}))}function k(e){const t=O(e) +;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{ +e[n]&&e[n](t)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_, +highlightElement:w, +highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"), +G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)}, +initHighlighting:()=>{ +_(),G("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +_(),G("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){ +if(W("Language definition for '{}' could not be registered.".replace("{}",e)), +!r)throw t;W(t),s=l} +s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete i[e] +;for(const t of Object.keys(s))s[t]===e&&delete s[t]}, +listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v, +autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)}, +removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{ +r=!1},n.safeMode=()=>{r=!0},n.versionString="11.9.0",n.regex={concat:h, +lookahead:g,either:f,optional:d,anyNumberOfTimes:u} +;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n +},ne=te({});return ne.newInstance=()=>te({}),ne}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `accesslog` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n=e.regex,a=["GET","POST","HEAD","PUT","DELETE","CONNECT","OPTIONS","PATCH","TRACE"] +;return{name:"Apache Access Log",contains:[{className:"number", +begin:/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?\b/,relevance:5},{ +className:"number",begin:/\b\d+\b/,relevance:0},{className:"string", +begin:n.concat(/"/,n.either(...a)),end:/"/,keywords:a,illegal:/\n/,relevance:5, +contains:[{begin:/HTTP\/[12]\.\d'/,relevance:5}]},{className:"string", +begin:/\[\d[^\]\n]{8,}\]/,illegal:/\n/,relevance:1},{className:"string", +begin:/\[/,end:/\]/,illegal:/\n/,relevance:0},{className:"string", +begin:/"Mozilla\/\d\.\d \(/,end:/"/,illegal:/\n/,relevance:3},{ +className:"string",begin:/"/,end:/"/,illegal:/\n/,relevance:0}]}}})() +;hljs.registerLanguage("accesslog",e)})();/*! `bash` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const s=e.regex,t={},n={begin:/\$\{/, +end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]};Object.assign(t,{ +className:"variable",variants:[{ +begin:s.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},n]});const a={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE] +},i=e.inherit(e.COMMENT(),{match:[/(^|\s)/,/#.*$/],scope:{2:"comment"}}),c={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},o={className:"string",begin:/"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,t,a]};a.contains.push(o);const r={begin:/\$?\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] +},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),m={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, +keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"], +literal:["true","false"], +built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] +},contains:[l,e.SHEBANG(),m,r,i,c,{match:/(\/[a-z._-]+)+/},o,{match:/\\"/},{ +className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}})() +;hljs.registerLanguage("bash",e)})();/*! `c` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),s="decltype\\(auto\\)",a="[a-zA-Z_]\\w*::",r="("+s+"|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",i={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},l={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(l,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0 +},g=n.optional(a)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},m=[c,i,t,e.C_BLOCK_COMMENT_MODE,o,l],_={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:m.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:s,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,l,o,i,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,l,o,i] +}]},i,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:l,keywords:u}}}})();hljs.registerLanguage("c",e)})();/*! `clojure` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const t="a-zA-Z_\\-!.?+*=<>&'",n="[#]?["+t+"]["+t+"0-9/;:$#]*",a="def defonce defprotocol defstruct defmulti defmethod defn- defn defmacro deftype defrecord",r={ +$pattern:n, +built_in:a+" cond apply if-not if-let if not not= =|0 <|0 >|0 <=|0 >=|0 ==|0 +|0 /|0 *|0 -|0 rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy first rest cons cast coll last butlast sigs reify second ffirst fnext nfirst nnext meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize" +},s={begin:n,relevance:0},o={scope:"number",relevance:0,variants:[{ +match:/[-+]?0[xX][0-9a-fA-F]+N?/},{match:/[-+]?0[0-7]+N?/},{ +match:/[-+]?[1-9][0-9]?[rR][0-9a-zA-Z]+N?/},{match:/[-+]?[0-9]+\/[0-9]+N?/},{ +match:/[-+]?[0-9]+((\.[0-9]*([eE][+-]?[0-9]+)?M?)|([eE][+-]?[0-9]+M?|M))/},{ +match:/[-+]?([1-9][0-9]*|0)N?/}]},c={scope:"character",variants:[{ +match:/\\o[0-3]?[0-7]{1,2}/},{match:/\\u[0-9a-fA-F]{4}/},{ +match:/\\(newline|space|tab|formfeed|backspace|return)/},{match:/\\\S/, +relevance:0}]},i={scope:"regex",begin:/#"/,end:/"/,contains:[e.BACKSLASH_ESCAPE] +},d=e.inherit(e.QUOTE_STRING_MODE,{illegal:null}),l={scope:"punctuation", +match:/,/,relevance:0},m=e.COMMENT(";","$",{relevance:0}),p={ +className:"literal",begin:/\b(true|false|nil)\b/},u={ +begin:"\\[|(#::?"+n+")?\\{",end:"[\\]\\}]",relevance:0},f={className:"symbol", +begin:"[:]{1,2}"+n},h={begin:"\\(",end:"\\)"},y={endsWithParent:!0,relevance:0 +},g={keywords:r,className:"name",begin:n,relevance:0,starts:y +},b=[l,h,c,i,d,m,f,u,o,p,s],v={beginKeywords:a,keywords:{$pattern:n,keyword:a}, +end:'(\\[|#|\\d|"|:|\\{|\\)|\\(|$)',contains:[{className:"title",begin:n, +relevance:0,excludeEnd:!0,endsParent:!0}].concat(b)} +;return h.contains=[v,g,y],y.contains=b,u.contains=b,{name:"Clojure", +aliases:["clj","edn"],illegal:/\S/,contains:[l,h,c,i,d,m,f,u,o,p]}}})() +;hljs.registerLanguage("clojure",e)})();/*! `cmake` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>({name:"CMake",aliases:["cmake.in"], +case_insensitive:!0,keywords:{ +keyword:"break cmake_host_system_information cmake_minimum_required cmake_parse_arguments cmake_policy configure_file continue elseif else endforeach endfunction endif endmacro endwhile execute_process file find_file find_library find_package find_path find_program foreach function get_cmake_property get_directory_property get_filename_component get_property if include include_guard list macro mark_as_advanced math message option return separate_arguments set_directory_properties set_property set site_name string unset variable_watch while add_compile_definitions add_compile_options add_custom_command add_custom_target add_definitions add_dependencies add_executable add_library add_link_options add_subdirectory add_test aux_source_directory build_command create_test_sourcelist define_property enable_language enable_testing export fltk_wrap_ui get_source_file_property get_target_property get_test_property include_directories include_external_msproject include_regular_expression install link_directories link_libraries load_cache project qt_wrap_cpp qt_wrap_ui remove_definitions set_source_files_properties set_target_properties set_tests_properties source_group target_compile_definitions target_compile_features target_compile_options target_include_directories target_link_directories target_link_libraries target_link_options target_sources try_compile try_run ctest_build ctest_configure ctest_coverage ctest_empty_binary_directory ctest_memcheck ctest_read_custom_files ctest_run_script ctest_sleep ctest_start ctest_submit ctest_test ctest_update ctest_upload build_name exec_program export_library_dependencies install_files install_programs install_targets load_command make_directory output_required_files remove subdir_depends subdirs use_mangled_mesa utility_source variable_requires write_file qt5_use_modules qt5_use_package qt5_wrap_cpp on off true false and or not command policy target test exists is_newer_than is_directory is_symlink is_absolute matches less greater equal less_equal greater_equal strless strgreater strequal strless_equal strgreater_equal version_less version_greater version_equal version_less_equal version_greater_equal in_list defined" +},contains:[{className:"variable",begin:/\$\{/,end:/\}/ +},e.COMMENT(/#\[\[/,/]]/),e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE] +})})();hljs.registerLanguage("cmake",e)})();/*! `cpp` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const t=e.regex,a=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),n="decltype\\(auto\\)",r="[a-zA-Z_]\\w*::",i="(?!struct)("+n+"|"+t.optional(r)+"[a-zA-Z_]\\w*"+t.optional("<[^<>]+>")+")",s={ +className:"type",begin:"\\b[a-z\\d_]*_t\\b"},c={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ +className:"number",variants:[{ +begin:"[+-]?(?:(?:[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?|\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)" +},{ +begin:"[+-]?\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)" +}],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(c,{className:"string"}),{ +className:"string",begin:/<.*?>/},a,e.C_BLOCK_COMMENT_MODE]},u={ +className:"title",begin:t.optional(r)+e.IDENT_RE,relevance:0 +},d=t.optional(r)+e.IDENT_RE+"\\s*\\(",p={ +type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], +keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], +literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], +_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] +},_={className:"function.dispatch",relevance:0,keywords:{ +_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] +}, +begin:t.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\s*\(/)) +},m=[_,l,s,a,e.C_BLOCK_COMMENT_MODE,o,c],f={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:p,contains:m.concat([{begin:/\(/,end:/\)/,keywords:p, +contains:m.concat(["self"]),relevance:0}]),relevance:0},g={className:"function", +begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:p,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:n,keywords:p,relevance:0},{ +begin:d,returnBegin:!0,contains:[u],relevance:0},{begin:/::/,relevance:0},{ +begin:/:/,endsWithParent:!0,contains:[c,o]},{relevance:0,match:/,/},{ +className:"params",begin:/\(/,end:/\)/,keywords:p,relevance:0, +contains:[a,e.C_BLOCK_COMMENT_MODE,c,o,s,{begin:/\(/,end:/\)/,keywords:p, +relevance:0,contains:["self",a,e.C_BLOCK_COMMENT_MODE,c,o,s]}] +},s,a,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C++", +aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:p,illegal:"",keywords:p,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:p},{ +match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], +className:{1:"keyword",3:"title.class"}}])}}})();hljs.registerLanguage("cpp",e) +})();/*! `csharp` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n={ +keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), +built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], +literal:["default","false","null","true"]},a=e.inherit(e.TITLE_MODE,{ +begin:"[a-zA-Z](\\.?\\w)*"}),i={className:"number",variants:[{ +begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] +},t=e.inherit(s,{illegal:/\n/}),r={className:"subst",begin:/\{/,end:/\}/, +keywords:n},l=e.inherit(r,{illegal:/\n/}),c={className:"string",begin:/\$"/, +end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ +},e.BACKSLASH_ESCAPE,l]},o={className:"string",begin:/\$@"/,end:'"',contains:[{ +begin:/\{\{/},{begin:/\}\}/},{begin:'""'},r]},d=e.inherit(o,{illegal:/\n/, +contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},l]}) +;r.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.C_BLOCK_COMMENT_MODE], +l.contains=[d,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.inherit(e.C_BLOCK_COMMENT_MODE,{ +illegal:/\n/})];const g={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},a] +},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={ +begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], +keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, +contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ +begin:"\x3c!--|--\x3e"},{begin:""}]}] +}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", +end:"$",keywords:{ +keyword:"if else elif endif define undef warning error line region endregion pragma checksum" +}},g,i,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, +illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" +},a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", +relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[a,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", +begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ +className:"string",begin:/"/,end:/"/}]},{ +beginKeywords:"new return throw await else",relevance:0},{className:"function", +begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", +relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +contains:[e.TITLE_MODE,E],relevance:0},{match:/\(\)/},{className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, +contains:[g,i,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}})() +;hljs.registerLanguage("csharp",e)})();/*! `css` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video","defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],r=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),o=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),t=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),i=["align-content","align-items","align-self","alignment-baseline","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","cx","cy","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","empty-cells","enable-background","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","flood-color","flood-opacity","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","kerning","justify-content","left","letter-spacing","lighting-color","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","marker","marker-end","marker-mid","marker-start","mask","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","r","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","speak","speak-as","src","tab-size","table-layout","text-anchor","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vector-effect","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index"].sort().reverse() +;return n=>{const a=n.regex,l=(e=>({IMPORTANT:{scope:"meta",begin:"!important"}, +BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number", +begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{ +className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{ +scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}))(n),s=[n.APOS_STRING_MODE,n.QUOTE_STRING_MODE];return{name:"CSS", +case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"}, +classNameAliases:{keyframePosition:"selector-tag"},contains:[l.BLOCK_COMMENT,{ +begin:/-(webkit|moz|ms|o)-(?=[a-z])/},l.CSS_NUMBER_MODE,{ +className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{ +className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},l.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+o.join("|")+")"},{begin:":(:)?("+t.join("|")+")"}]},l.CSS_VARIABLE,{ +className:"attribute",begin:"\\b("+i.join("|")+")\\b"},{begin:/:/,end:/[;}{]/, +contains:[l.BLOCK_COMMENT,l.HEXCOLOR,l.IMPORTANT,l.CSS_NUMBER_MODE,...s,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...s,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},l.FUNCTION_DISPATCH]},{begin:a.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:r.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...s,l.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+e.join("|")+")\\b"}]}}})() +;hljs.registerLanguage("css",e)})();/*! `dart` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n={className:"subst",variants:[{ +begin:"\\$[A-Za-z0-9_]+"}]},a={className:"subst",variants:[{begin:/\$\{/, +end:/\}/}],keywords:"true false null this is new super"},t={className:"string", +variants:[{begin:"r'''",end:"'''"},{begin:'r"""',end:'"""'},{begin:"r'",end:"'", +illegal:"\\n"},{begin:'r"',end:'"',illegal:"\\n"},{begin:"'''",end:"'''", +contains:[e.BACKSLASH_ESCAPE,n,a]},{begin:'"""',end:'"""', +contains:[e.BACKSLASH_ESCAPE,n,a]},{begin:"'",end:"'",illegal:"\\n", +contains:[e.BACKSLASH_ESCAPE,n,a]},{begin:'"',end:'"',illegal:"\\n", +contains:[e.BACKSLASH_ESCAPE,n,a]}]};a.contains=[e.C_NUMBER_MODE,t] +;const i=["Comparable","DateTime","Duration","Function","Iterable","Iterator","List","Map","Match","Object","Pattern","RegExp","Set","Stopwatch","String","StringBuffer","StringSink","Symbol","Type","Uri","bool","double","int","num","Element","ElementList"],r=i.map((e=>e+"?")) +;return{name:"Dart",keywords:{ +keyword:["abstract","as","assert","async","await","base","break","case","catch","class","const","continue","covariant","default","deferred","do","dynamic","else","enum","export","extends","extension","external","factory","false","final","finally","for","Function","get","hide","if","implements","import","in","interface","is","late","library","mixin","new","null","on","operator","part","required","rethrow","return","sealed","set","show","static","super","switch","sync","this","throw","true","try","typedef","var","void","when","while","with","yield"], +built_in:i.concat(r).concat(["Never","Null","dynamic","print","document","querySelector","querySelectorAll","window"]), +$pattern:/[A-Za-z][A-Za-z0-9_]*\??/}, +contains:[t,e.COMMENT(/\/\*\*(?!\/)/,/\*\//,{subLanguage:"markdown",relevance:0 +}),e.COMMENT(/\/{3,} ?/,/$/,{contains:[{subLanguage:"markdown",begin:".", +end:"$",relevance:0}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{ +className:"class",beginKeywords:"class interface",end:/\{/,excludeEnd:!0, +contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE] +},e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"},{begin:"=>"}]}}})() +;hljs.registerLanguage("dart",e)})();/*! `diff` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=e.regex;return{name:"Diff", +aliases:["patch"],contains:[{className:"meta",relevance:10, +match:a.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:a.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}}})();hljs.registerLanguage("diff",e)})();/*! `dockerfile` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>({name:"Dockerfile",aliases:["docker"], +case_insensitive:!0, +keywords:["from","maintainer","expose","env","arg","user","onbuild","stopsignal"], +contains:[e.HASH_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +beginKeywords:"run cmd entrypoint volume add copy workdir label healthcheck shell", +starts:{end:/[^\\]$/,subLanguage:"bash"}}],illegal:"{var e=(()=>{"use strict";return e=>{const n={ +keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], +type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], +literal:["true","false","iota","nil"], +built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] +};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{var e=(()=>{"use strict";return e=>({name:"Gradle",case_insensitive:!0, +keywords:["task","project","allprojects","subprojects","artifacts","buildscript","configurations","dependencies","repositories","sourceSets","description","delete","from","into","include","exclude","source","classpath","destinationDir","includes","options","sourceCompatibility","targetCompatibility","group","flatDir","doLast","doFirst","flatten","todir","fromdir","ant","def","abstract","break","case","catch","continue","default","do","else","extends","final","finally","for","if","implements","instanceof","native","new","private","protected","public","return","static","switch","synchronized","throw","throws","transient","try","volatile","while","strictfp","package","import","false","null","super","this","true","antlrtask","checkstyle","codenarc","copy","boolean","byte","char","class","double","float","int","interface","long","short","void","compile","runTime","file","fileTree","abs","any","append","asList","asWritable","call","collect","compareTo","count","div","dump","each","eachByte","eachFile","eachLine","every","find","findAll","flatten","getAt","getErr","getIn","getOut","getText","grep","immutable","inject","inspect","intersect","invokeMethods","isCase","join","leftShift","minus","multiply","newInputStream","newOutputStream","newPrintWriter","newReader","newWriter","next","plus","pop","power","previous","print","println","push","putAt","read","readBytes","readLines","reverse","reverseEach","round","size","sort","splitEachLine","step","subMap","times","toInteger","toList","tokenize","upto","waitForOrKill","withPrintWriter","withReader","withStream","withWriter","withWriterAppend","write","writeLine"], +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,e.REGEXP_MODE] +})})();hljs.registerLanguage("gradle",e)})();/*! `graphql` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=e.regex;return{name:"GraphQL", +aliases:["gql"],case_insensitive:!0,disableAutodetect:!1,keywords:{ +keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], +literal:["true","false","null"]}, +contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", +begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, +end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ +scope:"symbol",begin:a.concat(/[_A-Za-z][_0-9A-Za-z]*/,a.lookahead(/\s*:/)), +relevance:0}],illegal:[/[;<']/,/BEGIN/]}}})();hljs.registerLanguage("graphql",e) +})();/*! `haskell` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n="([0-9]_*)+",a="([0-9a-fA-F]_*)+",i="([!#$%&*+.\\/<=>?@\\\\^~-]|(?!([(),;\\[\\]`|{}]|[_:\"']))(\\p{S}|\\p{P}))",s={ +variants:[e.COMMENT("--+","$"),e.COMMENT(/\{-/,/-\}/,{contains:["self"]})]},l={ +className:"meta",begin:/\{-#/,end:/#-\}/},t={className:"meta",begin:"^#",end:"$" +},c={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},r={begin:"\\(", +end:"\\)",illegal:'"',contains:[l,t,{className:"type", +begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TITLE_MODE,{ +begin:"[_a-z][\\w']*"}),s]},o={className:"number",relevance:0,variants:[{ +match:`\\b(${n})(\\.(${n}))?([eE][+-]?(${n}))?\\b`},{ +match:`\\b0[xX]_*(${a})(\\.(${a}))?([pP][+-]?(${n}))?\\b`},{ +match:"\\b0[oO](([0-7]_*)+)\\b"},{match:"\\b0[bB](([01]_*)+)\\b"}]};return{ +name:"Haskell",aliases:["hs"], +keywords:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec", +unicodeRegex:!0,contains:[{beginKeywords:"module",end:"where", +keywords:"module where",contains:[r,s],illegal:"\\W\\.|;"},{ +begin:"\\bimport\\b",end:"$",keywords:"import qualified as hiding", +contains:[r,s],illegal:"\\W\\.|;"},{className:"class", +begin:"^(\\s*)?(class|instance)\\b",end:"where", +keywords:"class family instance where",contains:[c,r,s]},{className:"class", +begin:"\\b(data|(new)?type)\\b",end:"$", +keywords:"data family type newtype deriving",contains:[l,c,r,{begin:/\{/, +end:/\}/,contains:r.contains},s]},{beginKeywords:"default",end:"$", +contains:[c,r,s]},{beginKeywords:"infix infixl infixr",end:"$", +contains:[e.C_NUMBER_MODE,s]},{begin:"\\bforeign\\b",end:"$", +keywords:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe", +contains:[c,e.QUOTE_STRING_MODE,s]},{className:"meta", +begin:"#!\\/usr\\/bin\\/env runhaskell",end:"$"},l,t,{scope:"string", +begin:/'(?=\\?.')/,end:/'/,contains:[{scope:"char.escape",match:/\\./}] +},e.QUOTE_STRING_MODE,o,c,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),{ +begin:`(?!-)${i}--+|--+(?!-)${i}`},s,{begin:"->|<-"}]}}})() +;hljs.registerLanguage("haskell",e)})();/*! `ini` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,a={className:"number", +relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:e.NUMBER_RE}] +},s=e.COMMENT();s.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const i={ +className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/ +}]},t={className:"literal",begin:/\bon|off|true|false|yes|no\b/},r={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''", +end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"' +},{begin:"'",end:"'"}]},l={begin:/\[/,end:/\]/,contains:[s,t,i,r,a,"self"], +relevance:0},c=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ +name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[s,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n.concat(c,"(\\s*\\.\\s*",c,")*",n.lookahead(/\s*=\s*[^#\s]/)), +className:"attr",starts:{end:/$/,contains:[s,l,t,i,r,a]}}]}}})() +;hljs.registerLanguage("ini",e)})();/*! `java` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;var e="[0-9](_*[0-9])*",a=`\\.(${e})`,n="[0-9a-fA-F](_*[0-9a-fA-F])*",s={ +className:"number",variants:[{ +begin:`(\\b(${e})((${a})|\\.)?|(${a}))[eE][+-]?(${e})[fFdD]?\\b`},{ +begin:`\\b(${e})((${a})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${a})[fFdD]?\\b` +},{begin:`\\b(${e})[fFdD]\\b`},{ +begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?(${e})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function t(e,a,n){return-1===n?"":e.replace(a,(s=>t(e,a,n-1)))} +return e=>{ +const a=e.regex,n="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",i=n+t("(?:<"+n+"~~~(?:\\s*,\\s*"+n+"~~~)*>)?",/~~~/g,2),r={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},l={className:"meta",begin:"@"+n,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},c={className:"params",begin:/\(/, +end:/\)/,keywords:r,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:r,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,n],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[a.concat(/(?!else)/,n),/\s+/,n,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,n],className:{1:"keyword", +3:"title.class"},contains:[c,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+i+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:r,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:r,relevance:0, +contains:[l,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,s,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},s,l]}}})() +;hljs.registerLanguage("java",e)})();/*! `javascript` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s) +;return o=>{const l=o.regex,b=e,d={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="",$={ +match:[/const|var|let/,/\s+/,b,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,N,_,f,v,{match:/\$\d+/},A,k,{ +className:"attr",begin:b+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[v,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:d.begin, +"on:begin":d.isTrulyOpeningTag,end:d.end}],subLanguage:"xml",contains:[{ +begin:d.begin,end:d.end,skip:!0,contains:["self"]}]}]},I,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:b, +className:"title.function"})]},{match:/\.\.\./,relevance:0},C,{match:"\\$"+b, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},x,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},O,M,{match:/\$[(.]/}]}}})() +;hljs.registerLanguage("javascript",e)})();/*! `json` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],n={ +scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",keywords:{ +literal:a},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/, +relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,n,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}}})();hljs.registerLanguage("json",e)})();/*! `kotlin` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;var e="[0-9](_*[0-9])*",n=`\\.(${e})`,a="[0-9a-fA-F](_*[0-9a-fA-F])*",i={ +className:"number",variants:[{ +begin:`(\\b(${e})((${n})|\\.)?|(${n}))[eE][+-]?(${e})[fFdD]?\\b`},{ +begin:`\\b(${e})((${n})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${n})[fFdD]?\\b` +},{begin:`\\b(${e})[fFdD]\\b`},{ +begin:`\\b0[xX]((${a})\\.?|(${a})?\\.(${a}))[pP][+-]?(${e})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${a})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};return e=>{const n={ +keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", +built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", +literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" +},s={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},t={ +className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", +variants:[{begin:'"""',end:'"""(?=[^"])',contains:[t,s]},{begin:"'",end:"'", +illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, +contains:[e.BACKSLASH_ESCAPE,t,s]}]};s.contains.push(r);const l={ +className:"meta", +begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" +},c={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, +end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] +},o=i,b=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),E={ +variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, +contains:[]}]},d=E;return d.variants[1].contains=[E],E.variants[1].contains=[d], +{name:"Kotlin",aliases:["kt","kts"],keywords:n, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", +begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,b,{className:"keyword", +begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", +begin:/@\w+/}]}},a,l,c,{className:"function",beginKeywords:"fun",end:"[(]|$", +returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, +keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, +endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, +endsWithParent:!0,contains:[E,e.C_LINE_COMMENT_MODE,b],relevance:0 +},e.C_LINE_COMMENT_MODE,b,l,c,r,e.C_NUMBER_MODE]},b]},{ +begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ +3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, +illegal:"extends implements",contains:[{ +beginKeywords:"public protected internal private constructor" +},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, +excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, +excludeBegin:!0,returnEnd:!0},l,c]},r,{className:"meta",begin:"^#!/usr/bin/env", +end:"$",illegal:"\n"},o]}}})();hljs.registerLanguage("kotlin",e)})();/*! `latex` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n=[{begin:/\^{6}[0-9a-f]{6}/},{ +begin:/\^{5}[0-9a-f]{5}/},{begin:/\^{4}[0-9a-f]{4}/},{begin:/\^{3}[0-9a-f]{3}/ +},{begin:/\^{2}[0-9a-f]{2}/},{begin:/\^{2}[\u0000-\u007f]/}],a=[{ +className:"keyword",begin:/\\/,relevance:0,contains:[{endsParent:!0, +begin:e.regex.either(...["(?:NeedsTeXFormat|RequirePackage|GetIdInfo)","Provides(?:Expl)?(?:Package|Class|File)","(?:DeclareOption|ProcessOptions)","(?:documentclass|usepackage|input|include)","makeat(?:letter|other)","ExplSyntax(?:On|Off)","(?:new|renew|provide)?command","(?:re)newenvironment","(?:New|Renew|Provide|Declare)(?:Expandable)?DocumentCommand","(?:New|Renew|Provide|Declare)DocumentEnvironment","(?:(?:e|g|x)?def|let)","(?:begin|end)","(?:part|chapter|(?:sub){0,2}section|(?:sub)?paragraph)","caption","(?:label|(?:eq|page|name)?ref|(?:paren|foot|super)?cite)","(?:alpha|beta|[Gg]amma|[Dd]elta|(?:var)?epsilon|zeta|eta|[Tt]heta|vartheta)","(?:iota|(?:var)?kappa|[Ll]ambda|mu|nu|[Xx]i|[Pp]i|varpi|(?:var)rho)","(?:[Ss]igma|varsigma|tau|[Uu]psilon|[Pp]hi|varphi|chi|[Pp]si|[Oo]mega)","(?:frac|sum|prod|lim|infty|times|sqrt|leq|geq|left|right|middle|[bB]igg?)","(?:[lr]angle|q?quad|[lcvdi]?dots|d?dot|hat|tilde|bar)"].map((e=>e+"(?![a-zA-Z@:_])"))) +},{endsParent:!0, +begin:RegExp(["(?:__)?[a-zA-Z]{2,}_[a-zA-Z](?:_?[a-zA-Z])+:[a-zA-Z]*","[lgc]__?[a-zA-Z](?:_?[a-zA-Z])*_[a-zA-Z]{2,}","[qs]__?[a-zA-Z](?:_?[a-zA-Z])+","use(?:_i)?:[a-zA-Z]*","(?:else|fi|or):","(?:if|cs|exp):w","(?:hbox|vbox):n","::[a-zA-Z]_unbraced","::[a-zA-Z:]"].map((e=>e+"(?![a-zA-Z:_])")).join("|")) +},{endsParent:!0,variants:n},{endsParent:!0,relevance:0,variants:[{ +begin:/[a-zA-Z@]+/},{begin:/[^a-zA-Z@]?/}]}]},{className:"params",relevance:0, +begin:/#+\d?/},{variants:n},{className:"built_in",relevance:0,begin:/[$&^_]/},{ +className:"meta",begin:/% ?!(T[eE]X|tex|BIB|bib)/,end:"$",relevance:10 +},e.COMMENT("%","$",{relevance:0})],i={begin:/\{/,end:/\}/,relevance:0, +contains:["self",...a]},t=e.inherit(i,{relevance:0,endsParent:!0, +contains:[i,...a]}),r={begin:/\[/,end:/\]/,endsParent:!0,relevance:0, +contains:[i,...a]},s={begin:/\s+/,relevance:0},c=[t],l=[r],o=(e,n)=>({ +contains:[s],starts:{relevance:0,contains:e,starts:n}}),d=(e,n)=>({ +begin:"\\\\"+e+"(?![a-zA-Z@:_])",keywords:{$pattern:/\\[a-zA-Z]+/,keyword:"\\"+e +},relevance:0,contains:[s],starts:n}),g=(n,a)=>e.inherit({ +begin:"\\\\begin(?=[ \t]*(\\r?\\n[ \t]*)?\\{"+n+"\\})",keywords:{ +$pattern:/\\[a-zA-Z]+/,keyword:"\\begin"},relevance:0 +},o(c,a)),m=(n="string")=>e.END_SAME_AS_BEGIN({className:n,begin:/(.|\r?\n)/, +end:/(.|\r?\n)/,excludeBegin:!0,excludeEnd:!0,endsParent:!0}),b=e=>({ +className:"string",end:"(?=\\\\end\\{"+e+"\\})"}),p=(e="string")=>({relevance:0, +begin:/\{/,starts:{endsParent:!0,contains:[{className:e,end:/(?=\})/, +endsParent:!0,contains:[{begin:/\{/,end:/\}/,relevance:0,contains:["self"]}]}]} +});return{name:"LaTeX",aliases:["tex"], +contains:[...["verb","lstinline"].map((e=>d(e,{contains:[m()]}))),d("mint",o(c,{ +contains:[m()]})),d("mintinline",o(c,{contains:[p(),m()]})),d("url",{ +contains:[p("link"),p("link")]}),d("hyperref",{contains:[p("link")] +}),d("href",o(l,{contains:[p("link")] +})),...[].concat(...["","\\*"].map((e=>[g("verbatim"+e,b("verbatim"+e)),g("filecontents"+e,o(c,b("filecontents"+e))),...["","B","L"].map((n=>g(n+"Verbatim"+e,o(l,b(n+"Verbatim"+e)))))]))),g("minted",o(l,o(c,b("minted")))),...a] +}}})();hljs.registerLanguage("latex",e)})();/*! `lisp` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n="[a-zA-Z_\\-+\\*\\/<=>&#][a-zA-Z0-9_\\-+*\\/<=>&#!]*",a="\\|[^]*?\\|",i="(-|\\+)?\\d+(\\.\\d+|\\/\\d+)?((d|e|f|l|s|D|E|F|L|S)(\\+|-)?\\d+)?",s={ +className:"literal",begin:"\\b(t{1}|nil)\\b"},l={className:"number",variants:[{ +begin:i,relevance:0},{begin:"#(b|B)[0-1]+(/[0-1]+)?"},{ +begin:"#(o|O)[0-7]+(/[0-7]+)?"},{begin:"#(x|X)[0-9a-fA-F]+(/[0-9a-fA-F]+)?"},{ +begin:"#(c|C)\\("+i+" +"+i,end:"\\)"}]},b=e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null}),g=e.COMMENT(";","$",{relevance:0}),r={begin:"\\*",end:"\\*"},t={ +className:"symbol",begin:"[:&]"+n},c={begin:n,relevance:0},d={begin:a},o={ +contains:[l,b,r,t,{begin:"\\(",end:"\\)",contains:["self",s,b,l,c]},c], +variants:[{begin:"['`]\\(",end:"\\)"},{begin:"\\(quote ",end:"\\)",keywords:{ +name:"quote"}},{begin:"'"+a}]},v={variants:[{begin:"'"+n},{ +begin:"#'"+n+"(::"+n+")*"}]},m={begin:"\\(\\s*",end:"\\)"},u={endsWithParent:!0, +relevance:0};return m.contains=[{className:"name",variants:[{begin:n,relevance:0 +},{begin:a}]},u],u.contains=[o,v,m,s,l,b,g,r,t,d,c],{name:"Lisp",illegal:/\S/, +contains:[l,e.SHEBANG(),s,b,g,o,v,m,c]}}})();hljs.registerLanguage("lisp",e) +})();/*! `lua` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const t="\\[=*\\[",a="\\]=*\\]",n={ +begin:t,end:a,contains:["self"] +},o=[e.COMMENT("--(?!"+t+")","$"),e.COMMENT("--"+t,a,{contains:[n],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:o.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:o}].concat(o) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:t,end:a,contains:[n],relevance:5}])}}})();hljs.registerLanguage("lua",e) +})();/*! `makefile` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const i={className:"variable", +variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/, +end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/, +relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[] +}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c) +;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g) +})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})() +;hljs.registerLanguage("markdown",e)})();/*! `nginx` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,a={ +className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{\w+\}/},{ +begin:n.concat(/[$@]/,e.UNDERSCORE_IDENT_RE)}]},s={endsWithParent:!0,keywords:{ +$pattern:/[a-z_]{2,}|\/dev\/poll/, +literal:["on","off","yes","no","true","false","none","blocked","debug","info","notice","warn","error","crit","select","break","last","permanent","redirect","kqueue","rtsig","epoll","poll","/dev/poll"] +},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string", +contains:[e.BACKSLASH_ESCAPE,a],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/ +}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[a] +},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,a],variants:[{begin:"\\s\\^", +end:"\\s|\\{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|\\{|;",returnEnd:!0},{ +begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number", +begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{ +className:"number",begin:"\\b\\d+[kKmMgGdshdwy]?\\b",relevance:0},a]};return{ +name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{ +beginKeywords:"upstream location",end:/;|\{/,contains:s.contains,keywords:{ +section:"upstream location"}},{className:"section", +begin:n.concat(e.UNDERSCORE_IDENT_RE+n.lookahead(/\s+\{/)),relevance:0},{ +begin:n.lookahead(e.UNDERSCORE_IDENT_RE+"\\s"),end:";|\\{",contains:[{ +className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:s}],relevance:0}], +illegal:"[^\\s\\}\\{]"}}})();hljs.registerLanguage("nginx",e)})();/*! `objectivec` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={ +$pattern:n,keyword:["@interface","@class","@protocol","@implementation"]} +;return{name:"Objective-C", +aliases:["mm","objc","obj-c","obj-c++","objective-c++"],keywords:{ +"variable.language":["this","super"],$pattern:n, +keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], +literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], +built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], +type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] +},illegal:"/,end:/$/,illegal:"\\n" +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", +begin:"("+_.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:_, +contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, +relevance:0}]}}})();hljs.registerLanguage("objectivec",e)})();/*! `perl` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n=e.regex,t=/[dualxmsipngr]{0,12}/,s={$pattern:/[\w.]+/, +keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot class close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl field fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map method mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" +},r={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:s},a={begin:/->\{/, +end:/\}/},i={scope:"attr",match:/\s+:\s*\w+(\s*\(.*?\))?/},c={scope:"variable", +variants:[{begin:/\$\d/},{ +begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") +},{begin:/[$%@][^\s\w{=]|\$=/,relevance:0}],contains:[i]},o={className:"number", +variants:[{match:/0?\.[0-9][0-9_]+\b/},{ +match:/\bv?(0|[1-9][0-9_]*(\.[0-9_]+)?|[1-9][0-9_]*)\b/},{ +match:/\b0[0-7][0-7_]*\b/},{match:/\b0x[0-9a-fA-F][0-9a-fA-F_]*\b/},{ +match:/\b0b[0-1][0-1_]*\b/}],relevance:0 +},l=[e.BACKSLASH_ESCAPE,r,c],g=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],d=(e,s,r="\\1")=>{ +const a="\\1"===r?r:n.concat(r,s) +;return n.concat(n.concat("(?:",e,")"),s,/(?:\\.|[^\\\/])*?/,a,/(?:\\.|[^\\\/])*?/,r,t) +},m=(e,s,r)=>n.concat(n.concat("(?:",e,")"),s,/(?:\\.|[^\\\/])*?/,r,t),p=[c,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ +endsWithParent:!0}),a,{className:"string",contains:l,variants:[{ +begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", +end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ +begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", +relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", +contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ +begin:"-?\\w+\\s*=>",relevance:0}]},o,{ +begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", +keywords:"split return print reverse grep",relevance:0, +contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ +begin:d("s|tr|y",n.either(...g,{capture:!0}))},{begin:d("s|tr|y","\\(","\\)")},{ +begin:d("s|tr|y","\\[","\\]")},{begin:d("s|tr|y","\\{","\\}")}],relevance:2},{ +className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ +begin:m("(?:m|qr)?",/\//,/\//)},{begin:m("m|qr",n.either(...g,{capture:!0 +}),/\1/)},{begin:m("m|qr",/\(/,/\)/)},{begin:m("m|qr",/\[/,/\]/)},{ +begin:m("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub method", +end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE,i] +},{className:"class",beginKeywords:"class",end:"[;{]",excludeEnd:!0,relevance:5, +contains:[e.TITLE_MODE,i,o]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$", +end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$", +className:"comment"}]}];return r.contains=p,a.contains=p,{name:"Perl", +aliases:["pl","pm"],keywords:s,contains:p}}})();hljs.registerLanguage("perl",e) +})();/*! `pgsql` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var E=(()=>{"use strict";return E=>{ +const T=E.COMMENT("--","$"),N="\\$([a-zA-Z_]?|[a-zA-Z_][a-zA-Z_0-9]*)\\$",A="BIGINT INT8 BIGSERIAL SERIAL8 BIT VARYING VARBIT BOOLEAN BOOL BOX BYTEA CHARACTER CHAR VARCHAR CIDR CIRCLE DATE DOUBLE PRECISION FLOAT8 FLOAT INET INTEGER INT INT4 INTERVAL JSON JSONB LINE LSEG|10 MACADDR MACADDR8 MONEY NUMERIC DEC DECIMAL PATH POINT POLYGON REAL FLOAT4 SMALLINT INT2 SMALLSERIAL|10 SERIAL2|10 SERIAL|10 SERIAL4|10 TEXT TIME ZONE TIMETZ|10 TIMESTAMP TIMESTAMPTZ|10 TSQUERY|10 TSVECTOR|10 TXID_SNAPSHOT|10 UUID XML NATIONAL NCHAR INT4RANGE|10 INT8RANGE|10 NUMRANGE|10 TSRANGE|10 TSTZRANGE|10 DATERANGE|10 ANYELEMENT ANYARRAY ANYNONARRAY ANYENUM ANYRANGE CSTRING INTERNAL RECORD PG_DDL_COMMAND VOID UNKNOWN OPAQUE REFCURSOR NAME OID REGPROC|10 REGPROCEDURE|10 REGOPER|10 REGOPERATOR|10 REGCLASS|10 REGTYPE|10 REGROLE|10 REGNAMESPACE|10 REGCONFIG|10 REGDICTIONARY|10 ",R=A.trim().split(" ").map((E=>E.split("|")[0])).join("|"),I="ARRAY_AGG AVG BIT_AND BIT_OR BOOL_AND BOOL_OR COUNT EVERY JSON_AGG JSONB_AGG JSON_OBJECT_AGG JSONB_OBJECT_AGG MAX MIN MODE STRING_AGG SUM XMLAGG CORR COVAR_POP COVAR_SAMP REGR_AVGX REGR_AVGY REGR_COUNT REGR_INTERCEPT REGR_R2 REGR_SLOPE REGR_SXX REGR_SXY REGR_SYY STDDEV STDDEV_POP STDDEV_SAMP VARIANCE VAR_POP VAR_SAMP PERCENTILE_CONT PERCENTILE_DISC ROW_NUMBER RANK DENSE_RANK PERCENT_RANK CUME_DIST NTILE LAG LEAD FIRST_VALUE LAST_VALUE NTH_VALUE NUM_NONNULLS NUM_NULLS ABS CBRT CEIL CEILING DEGREES DIV EXP FLOOR LN LOG MOD PI POWER RADIANS ROUND SCALE SIGN SQRT TRUNC WIDTH_BUCKET RANDOM SETSEED ACOS ACOSD ASIN ASIND ATAN ATAND ATAN2 ATAN2D COS COSD COT COTD SIN SIND TAN TAND BIT_LENGTH CHAR_LENGTH CHARACTER_LENGTH LOWER OCTET_LENGTH OVERLAY POSITION SUBSTRING TREAT TRIM UPPER ASCII BTRIM CHR CONCAT CONCAT_WS CONVERT CONVERT_FROM CONVERT_TO DECODE ENCODE INITCAP LEFT LENGTH LPAD LTRIM MD5 PARSE_IDENT PG_CLIENT_ENCODING QUOTE_IDENT|10 QUOTE_LITERAL|10 QUOTE_NULLABLE|10 REGEXP_MATCH REGEXP_MATCHES REGEXP_REPLACE REGEXP_SPLIT_TO_ARRAY REGEXP_SPLIT_TO_TABLE REPEAT REPLACE REVERSE RIGHT RPAD RTRIM SPLIT_PART STRPOS SUBSTR TO_ASCII TO_HEX TRANSLATE OCTET_LENGTH GET_BIT GET_BYTE SET_BIT SET_BYTE TO_CHAR TO_DATE TO_NUMBER TO_TIMESTAMP AGE CLOCK_TIMESTAMP|10 DATE_PART DATE_TRUNC ISFINITE JUSTIFY_DAYS JUSTIFY_HOURS JUSTIFY_INTERVAL MAKE_DATE MAKE_INTERVAL|10 MAKE_TIME MAKE_TIMESTAMP|10 MAKE_TIMESTAMPTZ|10 NOW STATEMENT_TIMESTAMP|10 TIMEOFDAY TRANSACTION_TIMESTAMP|10 ENUM_FIRST ENUM_LAST ENUM_RANGE AREA CENTER DIAMETER HEIGHT ISCLOSED ISOPEN NPOINTS PCLOSE POPEN RADIUS WIDTH BOX BOUND_BOX CIRCLE LINE LSEG PATH POLYGON ABBREV BROADCAST HOST HOSTMASK MASKLEN NETMASK NETWORK SET_MASKLEN TEXT INET_SAME_FAMILY INET_MERGE MACADDR8_SET7BIT ARRAY_TO_TSVECTOR GET_CURRENT_TS_CONFIG NUMNODE PLAINTO_TSQUERY PHRASETO_TSQUERY WEBSEARCH_TO_TSQUERY QUERYTREE SETWEIGHT STRIP TO_TSQUERY TO_TSVECTOR JSON_TO_TSVECTOR JSONB_TO_TSVECTOR TS_DELETE TS_FILTER TS_HEADLINE TS_RANK TS_RANK_CD TS_REWRITE TSQUERY_PHRASE TSVECTOR_TO_ARRAY TSVECTOR_UPDATE_TRIGGER TSVECTOR_UPDATE_TRIGGER_COLUMN XMLCOMMENT XMLCONCAT XMLELEMENT XMLFOREST XMLPI XMLROOT XMLEXISTS XML_IS_WELL_FORMED XML_IS_WELL_FORMED_DOCUMENT XML_IS_WELL_FORMED_CONTENT XPATH XPATH_EXISTS XMLTABLE XMLNAMESPACES TABLE_TO_XML TABLE_TO_XMLSCHEMA TABLE_TO_XML_AND_XMLSCHEMA QUERY_TO_XML QUERY_TO_XMLSCHEMA QUERY_TO_XML_AND_XMLSCHEMA CURSOR_TO_XML CURSOR_TO_XMLSCHEMA SCHEMA_TO_XML SCHEMA_TO_XMLSCHEMA SCHEMA_TO_XML_AND_XMLSCHEMA DATABASE_TO_XML DATABASE_TO_XMLSCHEMA DATABASE_TO_XML_AND_XMLSCHEMA XMLATTRIBUTES TO_JSON TO_JSONB ARRAY_TO_JSON ROW_TO_JSON JSON_BUILD_ARRAY JSONB_BUILD_ARRAY JSON_BUILD_OBJECT JSONB_BUILD_OBJECT JSON_OBJECT JSONB_OBJECT JSON_ARRAY_LENGTH JSONB_ARRAY_LENGTH JSON_EACH JSONB_EACH JSON_EACH_TEXT JSONB_EACH_TEXT JSON_EXTRACT_PATH JSONB_EXTRACT_PATH JSON_OBJECT_KEYS JSONB_OBJECT_KEYS JSON_POPULATE_RECORD JSONB_POPULATE_RECORD JSON_POPULATE_RECORDSET JSONB_POPULATE_RECORDSET JSON_ARRAY_ELEMENTS JSONB_ARRAY_ELEMENTS JSON_ARRAY_ELEMENTS_TEXT JSONB_ARRAY_ELEMENTS_TEXT JSON_TYPEOF JSONB_TYPEOF JSON_TO_RECORD JSONB_TO_RECORD JSON_TO_RECORDSET JSONB_TO_RECORDSET JSON_STRIP_NULLS JSONB_STRIP_NULLS JSONB_SET JSONB_INSERT JSONB_PRETTY CURRVAL LASTVAL NEXTVAL SETVAL COALESCE NULLIF GREATEST LEAST ARRAY_APPEND ARRAY_CAT ARRAY_NDIMS ARRAY_DIMS ARRAY_FILL ARRAY_LENGTH ARRAY_LOWER ARRAY_POSITION ARRAY_POSITIONS ARRAY_PREPEND ARRAY_REMOVE ARRAY_REPLACE ARRAY_TO_STRING ARRAY_UPPER CARDINALITY STRING_TO_ARRAY UNNEST ISEMPTY LOWER_INC UPPER_INC LOWER_INF UPPER_INF RANGE_MERGE GENERATE_SERIES GENERATE_SUBSCRIPTS CURRENT_DATABASE CURRENT_QUERY CURRENT_SCHEMA|10 CURRENT_SCHEMAS|10 INET_CLIENT_ADDR INET_CLIENT_PORT INET_SERVER_ADDR INET_SERVER_PORT ROW_SECURITY_ACTIVE FORMAT_TYPE TO_REGCLASS TO_REGPROC TO_REGPROCEDURE TO_REGOPER TO_REGOPERATOR TO_REGTYPE TO_REGNAMESPACE TO_REGROLE COL_DESCRIPTION OBJ_DESCRIPTION SHOBJ_DESCRIPTION TXID_CURRENT TXID_CURRENT_IF_ASSIGNED TXID_CURRENT_SNAPSHOT TXID_SNAPSHOT_XIP TXID_SNAPSHOT_XMAX TXID_SNAPSHOT_XMIN TXID_VISIBLE_IN_SNAPSHOT TXID_STATUS CURRENT_SETTING SET_CONFIG BRIN_SUMMARIZE_NEW_VALUES BRIN_SUMMARIZE_RANGE BRIN_DESUMMARIZE_RANGE GIN_CLEAN_PENDING_LIST SUPPRESS_REDUNDANT_UPDATES_TRIGGER LO_FROM_BYTEA LO_PUT LO_GET LO_CREAT LO_CREATE LO_UNLINK LO_IMPORT LO_EXPORT LOREAD LOWRITE GROUPING CAST".split(" ").map((E=>E.split("|")[0])).join("|") +;return{name:"PostgreSQL",aliases:["postgres","postgresql"],supersetOf:"sql", +case_insensitive:!0,keywords:{ +keyword:"ABORT ALTER ANALYZE BEGIN CALL CHECKPOINT|10 CLOSE CLUSTER COMMENT COMMIT COPY CREATE DEALLOCATE DECLARE DELETE DISCARD DO DROP END EXECUTE EXPLAIN FETCH GRANT IMPORT INSERT LISTEN LOAD LOCK MOVE NOTIFY PREPARE REASSIGN|10 REFRESH REINDEX RELEASE RESET REVOKE ROLLBACK SAVEPOINT SECURITY SELECT SET SHOW START TRUNCATE UNLISTEN|10 UPDATE VACUUM|10 VALUES AGGREGATE COLLATION CONVERSION|10 DATABASE DEFAULT PRIVILEGES DOMAIN TRIGGER EXTENSION FOREIGN WRAPPER|10 TABLE FUNCTION GROUP LANGUAGE LARGE OBJECT MATERIALIZED VIEW OPERATOR CLASS FAMILY POLICY PUBLICATION|10 ROLE RULE SCHEMA SEQUENCE SERVER STATISTICS SUBSCRIPTION SYSTEM TABLESPACE CONFIGURATION DICTIONARY PARSER TEMPLATE TYPE USER MAPPING PREPARED ACCESS METHOD CAST AS TRANSFORM TRANSACTION OWNED TO INTO SESSION AUTHORIZATION INDEX PROCEDURE ASSERTION ALL ANALYSE AND ANY ARRAY ASC ASYMMETRIC|10 BOTH CASE CHECK COLLATE COLUMN CONCURRENTLY|10 CONSTRAINT CROSS DEFERRABLE RANGE DESC DISTINCT ELSE EXCEPT FOR FREEZE|10 FROM FULL HAVING ILIKE IN INITIALLY INNER INTERSECT IS ISNULL JOIN LATERAL LEADING LIKE LIMIT NATURAL NOT NOTNULL NULL OFFSET ON ONLY OR ORDER OUTER OVERLAPS PLACING PRIMARY REFERENCES RETURNING SIMILAR SOME SYMMETRIC TABLESAMPLE THEN TRAILING UNION UNIQUE USING VARIADIC|10 VERBOSE WHEN WHERE WINDOW WITH BY RETURNS INOUT OUT SETOF|10 IF STRICT CURRENT CONTINUE OWNER LOCATION OVER PARTITION WITHIN BETWEEN ESCAPE EXTERNAL INVOKER DEFINER WORK RENAME VERSION CONNECTION CONNECT TABLES TEMP TEMPORARY FUNCTIONS SEQUENCES TYPES SCHEMAS OPTION CASCADE RESTRICT ADD ADMIN EXISTS VALID VALIDATE ENABLE DISABLE REPLICA|10 ALWAYS PASSING COLUMNS PATH REF VALUE OVERRIDING IMMUTABLE STABLE VOLATILE BEFORE AFTER EACH ROW PROCEDURAL ROUTINE NO HANDLER VALIDATOR OPTIONS STORAGE OIDS|10 WITHOUT INHERIT DEPENDS CALLED INPUT LEAKPROOF|10 COST ROWS NOWAIT SEARCH UNTIL ENCRYPTED|10 PASSWORD CONFLICT|10 INSTEAD INHERITS CHARACTERISTICS WRITE CURSOR ALSO STATEMENT SHARE EXCLUSIVE INLINE ISOLATION REPEATABLE READ COMMITTED SERIALIZABLE UNCOMMITTED LOCAL GLOBAL SQL PROCEDURES RECURSIVE SNAPSHOT ROLLUP CUBE TRUSTED|10 INCLUDE FOLLOWING PRECEDING UNBOUNDED RANGE GROUPS UNENCRYPTED|10 SYSID FORMAT DELIMITER HEADER QUOTE ENCODING FILTER OFF FORCE_QUOTE FORCE_NOT_NULL FORCE_NULL COSTS BUFFERS TIMING SUMMARY DISABLE_PAGE_SKIPPING RESTART CYCLE GENERATED IDENTITY DEFERRED IMMEDIATE LEVEL LOGGED UNLOGGED OF NOTHING NONE EXCLUDE ATTRIBUTE USAGE ROUTINES TRUE FALSE NAN INFINITY ALIAS BEGIN CONSTANT DECLARE END EXCEPTION RETURN PERFORM|10 RAISE GET DIAGNOSTICS STACKED|10 FOREACH LOOP ELSIF EXIT WHILE REVERSE SLICE DEBUG LOG INFO NOTICE WARNING ASSERT OPEN SUPERUSER NOSUPERUSER CREATEDB NOCREATEDB CREATEROLE NOCREATEROLE INHERIT NOINHERIT LOGIN NOLOGIN REPLICATION NOREPLICATION BYPASSRLS NOBYPASSRLS ", +built_in:"CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURRENT_CATALOG|10 CURRENT_DATE LOCALTIME LOCALTIMESTAMP CURRENT_ROLE|10 CURRENT_SCHEMA|10 SESSION_USER PUBLIC FOUND NEW OLD TG_NAME|10 TG_WHEN|10 TG_LEVEL|10 TG_OP|10 TG_RELID|10 TG_RELNAME|10 TG_TABLE_NAME|10 TG_TABLE_SCHEMA|10 TG_NARGS|10 TG_ARGV|10 TG_EVENT|10 TG_TAG|10 ROW_COUNT RESULT_OID|10 PG_CONTEXT|10 RETURNED_SQLSTATE COLUMN_NAME CONSTRAINT_NAME PG_DATATYPE_NAME|10 MESSAGE_TEXT TABLE_NAME SCHEMA_NAME PG_EXCEPTION_DETAIL|10 PG_EXCEPTION_HINT|10 PG_EXCEPTION_CONTEXT|10 SQLSTATE SQLERRM|10 SUCCESSFUL_COMPLETION WARNING DYNAMIC_RESULT_SETS_RETURNED IMPLICIT_ZERO_BIT_PADDING NULL_VALUE_ELIMINATED_IN_SET_FUNCTION PRIVILEGE_NOT_GRANTED PRIVILEGE_NOT_REVOKED STRING_DATA_RIGHT_TRUNCATION DEPRECATED_FEATURE NO_DATA NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED SQL_STATEMENT_NOT_YET_COMPLETE CONNECTION_EXCEPTION CONNECTION_DOES_NOT_EXIST CONNECTION_FAILURE SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION TRANSACTION_RESOLUTION_UNKNOWN PROTOCOL_VIOLATION TRIGGERED_ACTION_EXCEPTION FEATURE_NOT_SUPPORTED INVALID_TRANSACTION_INITIATION LOCATOR_EXCEPTION INVALID_LOCATOR_SPECIFICATION INVALID_GRANTOR INVALID_GRANT_OPERATION INVALID_ROLE_SPECIFICATION DIAGNOSTICS_EXCEPTION STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER CASE_NOT_FOUND CARDINALITY_VIOLATION DATA_EXCEPTION ARRAY_SUBSCRIPT_ERROR CHARACTER_NOT_IN_REPERTOIRE DATETIME_FIELD_OVERFLOW DIVISION_BY_ZERO ERROR_IN_ASSIGNMENT ESCAPE_CHARACTER_CONFLICT INDICATOR_OVERFLOW INTERVAL_FIELD_OVERFLOW INVALID_ARGUMENT_FOR_LOGARITHM INVALID_ARGUMENT_FOR_NTILE_FUNCTION INVALID_ARGUMENT_FOR_NTH_VALUE_FUNCTION INVALID_ARGUMENT_FOR_POWER_FUNCTION INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION INVALID_CHARACTER_VALUE_FOR_CAST INVALID_DATETIME_FORMAT INVALID_ESCAPE_CHARACTER INVALID_ESCAPE_OCTET INVALID_ESCAPE_SEQUENCE NONSTANDARD_USE_OF_ESCAPE_CHARACTER INVALID_INDICATOR_PARAMETER_VALUE INVALID_PARAMETER_VALUE INVALID_REGULAR_EXPRESSION INVALID_ROW_COUNT_IN_LIMIT_CLAUSE INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE INVALID_TABLESAMPLE_ARGUMENT INVALID_TABLESAMPLE_REPEAT INVALID_TIME_ZONE_DISPLACEMENT_VALUE INVALID_USE_OF_ESCAPE_CHARACTER MOST_SPECIFIC_TYPE_MISMATCH NULL_VALUE_NOT_ALLOWED NULL_VALUE_NO_INDICATOR_PARAMETER NUMERIC_VALUE_OUT_OF_RANGE SEQUENCE_GENERATOR_LIMIT_EXCEEDED STRING_DATA_LENGTH_MISMATCH STRING_DATA_RIGHT_TRUNCATION SUBSTRING_ERROR TRIM_ERROR UNTERMINATED_C_STRING ZERO_LENGTH_CHARACTER_STRING FLOATING_POINT_EXCEPTION INVALID_TEXT_REPRESENTATION INVALID_BINARY_REPRESENTATION BAD_COPY_FILE_FORMAT UNTRANSLATABLE_CHARACTER NOT_AN_XML_DOCUMENT INVALID_XML_DOCUMENT INVALID_XML_CONTENT INVALID_XML_COMMENT INVALID_XML_PROCESSING_INSTRUCTION INTEGRITY_CONSTRAINT_VIOLATION RESTRICT_VIOLATION NOT_NULL_VIOLATION FOREIGN_KEY_VIOLATION UNIQUE_VIOLATION CHECK_VIOLATION EXCLUSION_VIOLATION INVALID_CURSOR_STATE INVALID_TRANSACTION_STATE ACTIVE_SQL_TRANSACTION BRANCH_TRANSACTION_ALREADY_ACTIVE HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION READ_ONLY_SQL_TRANSACTION SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED NO_ACTIVE_SQL_TRANSACTION IN_FAILED_SQL_TRANSACTION IDLE_IN_TRANSACTION_SESSION_TIMEOUT INVALID_SQL_STATEMENT_NAME TRIGGERED_DATA_CHANGE_VIOLATION INVALID_AUTHORIZATION_SPECIFICATION INVALID_PASSWORD DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST DEPENDENT_OBJECTS_STILL_EXIST INVALID_TRANSACTION_TERMINATION SQL_ROUTINE_EXCEPTION FUNCTION_EXECUTED_NO_RETURN_STATEMENT MODIFYING_SQL_DATA_NOT_PERMITTED PROHIBITED_SQL_STATEMENT_ATTEMPTED READING_SQL_DATA_NOT_PERMITTED INVALID_CURSOR_NAME EXTERNAL_ROUTINE_EXCEPTION CONTAINING_SQL_NOT_PERMITTED MODIFYING_SQL_DATA_NOT_PERMITTED PROHIBITED_SQL_STATEMENT_ATTEMPTED READING_SQL_DATA_NOT_PERMITTED EXTERNAL_ROUTINE_INVOCATION_EXCEPTION INVALID_SQLSTATE_RETURNED NULL_VALUE_NOT_ALLOWED TRIGGER_PROTOCOL_VIOLATED SRF_PROTOCOL_VIOLATED EVENT_TRIGGER_PROTOCOL_VIOLATED SAVEPOINT_EXCEPTION INVALID_SAVEPOINT_SPECIFICATION INVALID_CATALOG_NAME INVALID_SCHEMA_NAME TRANSACTION_ROLLBACK TRANSACTION_INTEGRITY_CONSTRAINT_VIOLATION SERIALIZATION_FAILURE STATEMENT_COMPLETION_UNKNOWN DEADLOCK_DETECTED SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION SYNTAX_ERROR INSUFFICIENT_PRIVILEGE CANNOT_COERCE GROUPING_ERROR WINDOWING_ERROR INVALID_RECURSION INVALID_FOREIGN_KEY INVALID_NAME NAME_TOO_LONG RESERVED_NAME DATATYPE_MISMATCH INDETERMINATE_DATATYPE COLLATION_MISMATCH INDETERMINATE_COLLATION WRONG_OBJECT_TYPE GENERATED_ALWAYS UNDEFINED_COLUMN UNDEFINED_FUNCTION UNDEFINED_TABLE UNDEFINED_PARAMETER UNDEFINED_OBJECT DUPLICATE_COLUMN DUPLICATE_CURSOR DUPLICATE_DATABASE DUPLICATE_FUNCTION DUPLICATE_PREPARED_STATEMENT DUPLICATE_SCHEMA DUPLICATE_TABLE DUPLICATE_ALIAS DUPLICATE_OBJECT AMBIGUOUS_COLUMN AMBIGUOUS_FUNCTION AMBIGUOUS_PARAMETER AMBIGUOUS_ALIAS INVALID_COLUMN_REFERENCE INVALID_COLUMN_DEFINITION INVALID_CURSOR_DEFINITION INVALID_DATABASE_DEFINITION INVALID_FUNCTION_DEFINITION INVALID_PREPARED_STATEMENT_DEFINITION INVALID_SCHEMA_DEFINITION INVALID_TABLE_DEFINITION INVALID_OBJECT_DEFINITION WITH_CHECK_OPTION_VIOLATION INSUFFICIENT_RESOURCES DISK_FULL OUT_OF_MEMORY TOO_MANY_CONNECTIONS CONFIGURATION_LIMIT_EXCEEDED PROGRAM_LIMIT_EXCEEDED STATEMENT_TOO_COMPLEX TOO_MANY_COLUMNS TOO_MANY_ARGUMENTS OBJECT_NOT_IN_PREREQUISITE_STATE OBJECT_IN_USE CANT_CHANGE_RUNTIME_PARAM LOCK_NOT_AVAILABLE OPERATOR_INTERVENTION QUERY_CANCELED ADMIN_SHUTDOWN CRASH_SHUTDOWN CANNOT_CONNECT_NOW DATABASE_DROPPED SYSTEM_ERROR IO_ERROR UNDEFINED_FILE DUPLICATE_FILE SNAPSHOT_TOO_OLD CONFIG_FILE_ERROR LOCK_FILE_EXISTS FDW_ERROR FDW_COLUMN_NAME_NOT_FOUND FDW_DYNAMIC_PARAMETER_VALUE_NEEDED FDW_FUNCTION_SEQUENCE_ERROR FDW_INCONSISTENT_DESCRIPTOR_INFORMATION FDW_INVALID_ATTRIBUTE_VALUE FDW_INVALID_COLUMN_NAME FDW_INVALID_COLUMN_NUMBER FDW_INVALID_DATA_TYPE FDW_INVALID_DATA_TYPE_DESCRIPTORS FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER FDW_INVALID_HANDLE FDW_INVALID_OPTION_INDEX FDW_INVALID_OPTION_NAME FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH FDW_INVALID_STRING_FORMAT FDW_INVALID_USE_OF_NULL_POINTER FDW_TOO_MANY_HANDLES FDW_OUT_OF_MEMORY FDW_NO_SCHEMAS FDW_OPTION_NAME_NOT_FOUND FDW_REPLY_HANDLE FDW_SCHEMA_NOT_FOUND FDW_TABLE_NOT_FOUND FDW_UNABLE_TO_CREATE_EXECUTION FDW_UNABLE_TO_CREATE_REPLY FDW_UNABLE_TO_ESTABLISH_CONNECTION PLPGSQL_ERROR RAISE_EXCEPTION NO_DATA_FOUND TOO_MANY_ROWS ASSERT_FAILURE INTERNAL_ERROR DATA_CORRUPTED INDEX_CORRUPTED " +},illegal:/:==|\W\s*\(\*|(^|\s)\$[a-z]|\{\{|[a-z]:\s*$|\.\.\.|TO:|DO:/, +contains:[{className:"keyword",variants:[{begin:/\bTEXT\s*SEARCH\b/},{ +begin:/\b(PRIMARY|FOREIGN|FOR(\s+NO)?)\s+KEY\b/},{ +begin:/\bPARALLEL\s+(UNSAFE|RESTRICTED|SAFE)\b/},{ +begin:/\bSTORAGE\s+(PLAIN|EXTERNAL|EXTENDED|MAIN)\b/},{ +begin:/\bMATCH\s+(FULL|PARTIAL|SIMPLE)\b/},{begin:/\bNULLS\s+(FIRST|LAST)\b/},{ +begin:/\bEVENT\s+TRIGGER\b/},{begin:/\b(MAPPING|OR)\s+REPLACE\b/},{ +begin:/\b(FROM|TO)\s+(PROGRAM|STDIN|STDOUT)\b/},{ +begin:/\b(SHARE|EXCLUSIVE)\s+MODE\b/},{ +begin:/\b(LEFT|RIGHT)\s+(OUTER\s+)?JOIN\b/},{ +begin:/\b(FETCH|MOVE)\s+(NEXT|PRIOR|FIRST|LAST|ABSOLUTE|RELATIVE|FORWARD|BACKWARD)\b/ +},{begin:/\bPRESERVE\s+ROWS\b/},{begin:/\bDISCARD\s+PLANS\b/},{ +begin:/\bREFERENCING\s+(OLD|NEW)\b/},{begin:/\bSKIP\s+LOCKED\b/},{ +begin:/\bGROUPING\s+SETS\b/},{ +begin:/\b(BINARY|INSENSITIVE|SCROLL|NO\s+SCROLL)\s+(CURSOR|FOR)\b/},{ +begin:/\b(WITH|WITHOUT)\s+HOLD\b/},{ +begin:/\bWITH\s+(CASCADED|LOCAL)\s+CHECK\s+OPTION\b/},{ +begin:/\bEXCLUDE\s+(TIES|NO\s+OTHERS)\b/},{ +begin:/\bFORMAT\s+(TEXT|XML|JSON|YAML)\b/},{ +begin:/\bSET\s+((SESSION|LOCAL)\s+)?NAMES\b/},{begin:/\bIS\s+(NOT\s+)?UNKNOWN\b/ +},{begin:/\bSECURITY\s+LABEL\b/},{begin:/\bSTANDALONE\s+(YES|NO|NO\s+VALUE)\b/ +},{begin:/\bWITH\s+(NO\s+)?DATA\b/},{begin:/\b(FOREIGN|SET)\s+DATA\b/},{ +begin:/\bSET\s+(CATALOG|CONSTRAINTS)\b/},{begin:/\b(WITH|FOR)\s+ORDINALITY\b/},{ +begin:/\bIS\s+(NOT\s+)?DOCUMENT\b/},{ +begin:/\bXML\s+OPTION\s+(DOCUMENT|CONTENT)\b/},{ +begin:/\b(STRIP|PRESERVE)\s+WHITESPACE\b/},{ +begin:/\bNO\s+(ACTION|MAXVALUE|MINVALUE)\b/},{ +begin:/\bPARTITION\s+BY\s+(RANGE|LIST|HASH)\b/},{begin:/\bAT\s+TIME\s+ZONE\b/},{ +begin:/\bGRANTED\s+BY\b/},{begin:/\bRETURN\s+(QUERY|NEXT)\b/},{ +begin:/\b(ATTACH|DETACH)\s+PARTITION\b/},{ +begin:/\bFORCE\s+ROW\s+LEVEL\s+SECURITY\b/},{ +begin:/\b(INCLUDING|EXCLUDING)\s+(COMMENTS|CONSTRAINTS|DEFAULTS|IDENTITY|INDEXES|STATISTICS|STORAGE|ALL)\b/ +},{begin:/\bAS\s+(ASSIGNMENT|IMPLICIT|PERMISSIVE|RESTRICTIVE|ENUM|RANGE)\b/}]},{ +begin:/\b(FORMAT|FAMILY|VERSION)\s*\(/},{begin:/\bINCLUDE\s*\(/, +keywords:"INCLUDE"},{begin:/\bRANGE(?!\s*(BETWEEN|UNBOUNDED|CURRENT|[-0-9]+))/ +},{ +begin:/\b(VERSION|OWNER|TEMPLATE|TABLESPACE|CONNECTION\s+LIMIT|PROCEDURE|RESTRICT|JOIN|PARSER|COPY|START|END|COLLATION|INPUT|ANALYZE|STORAGE|LIKE|DEFAULT|DELIMITER|ENCODING|COLUMN|CONSTRAINT|TABLE|SCHEMA)\s*=/ +},{begin:/\b(PG_\w+?|HAS_[A-Z_]+_PRIVILEGE)\b/,relevance:10},{ +begin:/\bEXTRACT\s*\(/,end:/\bFROM\b/,returnEnd:!0,keywords:{ +type:"CENTURY DAY DECADE DOW DOY EPOCH HOUR ISODOW ISOYEAR MICROSECONDS MILLENNIUM MILLISECONDS MINUTE MONTH QUARTER SECOND TIMEZONE TIMEZONE_HOUR TIMEZONE_MINUTE WEEK YEAR" +}},{begin:/\b(XMLELEMENT|XMLPI)\s*\(\s*NAME/,keywords:{keyword:"NAME"}},{ +begin:/\b(XMLPARSE|XMLSERIALIZE)\s*\(\s*(DOCUMENT|CONTENT)/,keywords:{ +keyword:"DOCUMENT CONTENT"}},{beginKeywords:"CACHE INCREMENT MAXVALUE MINVALUE", +end:E.C_NUMBER_RE,returnEnd:!0,keywords:"BY CACHE INCREMENT MAXVALUE MINVALUE" +},{className:"type",begin:/\b(WITH|WITHOUT)\s+TIME\s+ZONE\b/},{className:"type", +begin:/\bINTERVAL\s+(YEAR|MONTH|DAY|HOUR|MINUTE|SECOND)(\s+TO\s+(MONTH|HOUR|MINUTE|SECOND))?\b/ +},{ +begin:/\bRETURNS\s+(LANGUAGE_HANDLER|TRIGGER|EVENT_TRIGGER|FDW_HANDLER|INDEX_AM_HANDLER|TSM_HANDLER)\b/, +keywords:{keyword:"RETURNS", +type:"LANGUAGE_HANDLER TRIGGER EVENT_TRIGGER FDW_HANDLER INDEX_AM_HANDLER TSM_HANDLER" +}},{begin:"\\b("+I+")\\s*\\("},{begin:"\\.("+R+")\\b"},{ +begin:"\\b("+R+")\\s+PATH\\b",keywords:{keyword:"PATH", +type:A.replace("PATH ","")}},{className:"type",begin:"\\b("+R+")\\b"},{ +className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{ +className:"string",begin:"(e|E|u&|U&)'",end:"'",contains:[{begin:"\\\\."}], +relevance:10},E.END_SAME_AS_BEGIN({begin:N,end:N,contains:[{ +subLanguage:["pgsql","perl","python","tcl","r","lua","java","php","ruby","bash","scheme","xml","json"], +endsWithParent:!0}]}),{begin:'"',end:'"',contains:[{begin:'""'}] +},E.C_NUMBER_MODE,E.C_BLOCK_COMMENT_MODE,T,{className:"meta",variants:[{ +begin:"%(ROW)?TYPE",relevance:10},{begin:"\\$\\d+"},{begin:"^#\\w",end:"$"}]},{ +className:"symbol",begin:"<<\\s*[a-zA-Z_][a-zA-Z_0-9$]*\\s*>>",relevance:10}]}} +})();hljs.registerLanguage("pgsql",E)})();/*! `php` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const t=e.regex,a=/(?![A-Za-z0-9])(?![$])/,r=t.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,a),n=t.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,a),o={ +scope:"variable",match:"\\$+"+r},c={scope:"subst",variants:[{begin:/\$\w+/},{ +begin:/\{\$/,end:/\}/}]},i=e.inherit(e.APOS_STRING_MODE,{illegal:null +}),s="[ \t\n]",l={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(c)}),i,{ +begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/, +contains:e.QUOTE_STRING_MODE.contains.concat(c),"on:begin":(e,t)=>{ +t.data._beginMatch=e[1]||e[2]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}},e.END_SAME_AS_BEGIN({ +begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},d={scope:"number",variants:[{ +begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ +begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ +begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" +}],relevance:0 +},_=["false","null","true"],p=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],E={ +keyword:p,literal:(e=>{const t=[];return e.forEach((e=>{ +t.push(e),e.toLowerCase()===e?t.push(e.toUpperCase()):t.push(e.toLowerCase()) +})),t})(_),built_in:b},u=e=>e.map((e=>e.replace(/\|\d+$/,""))),g={variants:[{ +match:[/new/,t.concat(s,"+"),t.concat("(?!",u(b).join("\\b|"),"\\b)"),n],scope:{ +1:"keyword",4:"title.class"}}]},h=t.concat(r,"\\b(?!\\()"),m={variants:[{ +match:[t.concat(/::/,t.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant" +}},{match:[/::/,/class/],scope:{2:"variable.language"}},{ +match:[n,t.concat(/::/,t.lookahead(/(?!class\b)/)),h],scope:{1:"title.class", +3:"variable.constant"}},{match:[n,t.concat("::",t.lookahead(/(?!class\b)/))], +scope:{1:"title.class"}},{match:[n,/::/,/class/],scope:{1:"title.class", +3:"variable.language"}}]},I={scope:"attr", +match:t.concat(r,t.lookahead(":"),t.lookahead(/(?!::)/))},f={relevance:0, +begin:/\(/,end:/\)/,keywords:E,contains:[I,o,m,e.C_BLOCK_COMMENT_MODE,l,d,g] +},O={relevance:0, +match:[/\b/,t.concat("(?!fn\\b|function\\b|",u(p).join("\\b|"),"|",u(b).join("\\b|"),"\\b)"),r,t.concat(s,"*"),t.lookahead(/(?=\()/)], +scope:{3:"title.function.invoke"},contains:[f]};f.contains.push(O) +;const v=[I,m,e.C_BLOCK_COMMENT_MODE,l,d,g];return{case_insensitive:!1, +keywords:E,contains:[{begin:t.concat(/#\[\s*/,n),beginScope:"meta",end:/]/, +endScope:"meta",keywords:{literal:_,keyword:["new","array"]},contains:[{ +begin:/\[/,end:/]/,keywords:{literal:_,keyword:["new","array"]}, +contains:["self",...v]},...v,{scope:"meta",match:n}] +},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{ +scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, +keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, +contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ +begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ +begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},o,O,m,{ +match:[/const/,/\s/,r],scope:{1:"keyword",3:"variable.constant"}},g,{ +scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, +excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" +},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", +begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:E, +contains:["self",o,m,e.C_BLOCK_COMMENT_MODE,l,d]}]},{scope:"class",variants:[{ +beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", +illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ +beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, +contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ +beginKeywords:"use",relevance:0,end:";",contains:[{ +match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},l,d]} +}})();hljs.registerLanguage("php",e)})();/*! `plaintext` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var t=(()=>{"use strict";return t=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0})})() +;hljs.registerLanguage("plaintext",t)})();/*! `powershell` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n={$pattern:/-?[A-z\.\-]+\b/, +keyword:"if else foreach return do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch hidden static parameter", +built_in:"ac asnp cat cd CFS chdir clc clear clhy cli clp cls clv cnsn compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo|0 epal epcsv epsn erase etsn exsn fc fhx fl ft fw gal gbp gc gcb gci gcm gcs gdr gerr ghy gi gin gjb gl gm gmo gp gps gpv group gsn gsnp gsv gtz gu gv gwmi h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc scb select set shcm si sl sleep sls sort sp spjb spps spsv start stz sujb sv swmi tee trcm type wget where wjb write" +},s={begin:"`[\\s\\S]",relevance:0},i={className:"variable",variants:[{ +begin:/\$\B/},{className:"keyword",begin:/\$this/},{begin:/\$[\w\d][\w\d_:]*/}] +},a={className:"string",variants:[{begin:/"/,end:/"/},{begin:/@"/,end:/^"@/}], +contains:[s,i,{className:"variable",begin:/\$[A-z]/,end:/[^A-z]/}]},t={ +className:"string",variants:[{begin:/'/,end:/'/},{begin:/@'/,end:/^'@/}] +},r=e.inherit(e.COMMENT(null,null),{variants:[{begin:/#/,end:/$/},{begin:/<#/, +end:/#>/}],contains:[{className:"doctag",variants:[{ +begin:/\.(synopsis|description|example|inputs|outputs|notes|link|component|role|functionality)/ +},{ +begin:/\.(parameter|forwardhelptargetname|forwardhelpcategory|remotehelprunspace|externalhelp)\s+\S+/ +}]}]}),c={className:"class",beginKeywords:"class enum",end:/\s*[{]/, +excludeEnd:!0,relevance:0,contains:[e.TITLE_MODE]},l={className:"function", +begin:/function\s+/,end:/\s*\{|$/,excludeEnd:!0,returnBegin:!0,relevance:0, +contains:[{begin:"function",relevance:0,className:"keyword"},{className:"title", +begin:/\w[\w\d]*((-)[\w\d]+)*/,relevance:0},{begin:/\(/,end:/\)/, +className:"params",relevance:0,contains:[i]}]},o={begin:/using\s/,end:/$/, +returnBegin:!0,contains:[a,t,{className:"keyword", +begin:/(using|assembly|command|module|namespace|type)/}]},p={ +className:"function",begin:/\[.*\]\s*[\w]+[ ]??\(/,end:/$/,returnBegin:!0, +relevance:0,contains:[{className:"keyword", +begin:"(".concat(n.keyword.toString().replace(/\s/g,"|"),")\\b"),endsParent:!0, +relevance:0},e.inherit(e.TITLE_MODE,{endsParent:!0})] +},g=[p,r,s,e.NUMBER_MODE,a,t,{className:"built_in",variants:[{ +begin:"(Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Mount|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Build|Complete|Confirm|Deny|Deploy|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where)+(-)[\\w\\d]+" +}]},i,{className:"literal",begin:/\$(null|true|false)\b/},{ +className:"selector-tag",begin:/@\B/,relevance:0}],m={begin:/\[/,end:/\]/, +excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[].concat("self",g,{ +begin:"(string|char|byte|int|long|bool|decimal|single|double|DateTime|xml|array|hashtable|void)", +className:"built_in",relevance:0},{className:"type",begin:/[\.\w\d]+/, +relevance:0})};return p.contains.unshift(m),{name:"PowerShell", +aliases:["pwsh","ps","ps1"],case_insensitive:!0,keywords:n, +contains:g.concat(c,l,o,{variants:[{className:"operator", +begin:"(-and|-as|-band|-bnot|-bor|-bxor|-casesensitive|-ccontains|-ceq|-cge|-cgt|-cle|-clike|-clt|-cmatch|-cne|-cnotcontains|-cnotlike|-cnotmatch|-contains|-creplace|-csplit|-eq|-exact|-f|-file|-ge|-gt|-icontains|-ieq|-ige|-igt|-ile|-ilike|-ilt|-imatch|-in|-ine|-inotcontains|-inotlike|-inotmatch|-ireplace|-is|-isnot|-isplit|-join|-le|-like|-lt|-match|-ne|-not|-notcontains|-notin|-notlike|-notmatch|-or|-regex|-replace|-shl|-shr|-split|-wildcard|-xor)\\b" +},{className:"literal",begin:/(-){1,2}[\w\d-]+/,relevance:0}]},m)}}})() +;hljs.registerLanguage("powershell",e)})();/*! `prolog` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var n=(()=>{"use strict";return n=>{const e={begin:/\(/,end:/\)/, +relevance:0},a={begin:/\[/,end:/\]/},s={className:"comment",begin:/%/,end:/$/, +contains:[n.PHRASAL_WORDS_MODE]},i={className:"string",begin:/`/,end:/`/, +contains:[n.BACKSLASH_ESCAPE]},g=[{begin:/[a-z][A-Za-z0-9_]*/,relevance:0},{ +className:"symbol",variants:[{begin:/[A-Z][a-zA-Z0-9_]*/},{ +begin:/_[A-Za-z0-9_]*/}],relevance:0},e,{begin:/:-/ +},a,s,n.C_BLOCK_COMMENT_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,i,{ +className:"string",begin:/0'(\\'|.)/},{className:"string",begin:/0'\\s/ +},n.C_NUMBER_MODE];return e.contains=g,a.contains=g,{name:"Prolog", +contains:g.concat([{begin:/\.$/}])}}})();hljs.registerLanguage("prolog",n)})();/*! `protobuf` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const s={ +match:[/(message|enum|service)\s+/,e.IDENT_RE],scope:{1:"keyword", +2:"title.class"}};return{name:"Protocol Buffers",aliases:["proto"],keywords:{ +keyword:["package","import","option","optional","required","repeated","group","oneof"], +type:["double","float","int32","int64","uint32","uint64","sint32","sint64","fixed32","fixed64","sfixed32","sfixed64","bool","string","bytes"], +literal:["true","false"]}, +contains:[e.QUOTE_STRING_MODE,e.NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{ +className:"function",beginKeywords:"rpc",end:/[{;]/,excludeEnd:!0, +keywords:"rpc returns"},{begin:/^\s*[A-Z_]+(?=\s*=[^\n]+;$)/}]}}})() +;hljs.registerLanguage("protobuf",e)})();/*! `python` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n=e.regex,a=/[\p{XID_Start}_]\p{XID_Continue}*/u,s=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ +$pattern:/[A-Za-z]\w+|__\w+__/,keyword:s, +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},t={className:"meta",begin:/^(>>>|\.\.\.) /},r={className:"subst",begin:/\{/, +end:/\}/,keywords:i,illegal:/#/},l={begin:/\{\{/,relevance:0},b={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,t],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,t],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,t,l,r]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,t,l,r]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,l,r]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,l,r]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},o="[0-9](_?[0-9])*",c=`(\\b(${o}))?\\.(${o})|\\b(${o})\\.`,d="\\b|"+s.join("|"),g={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${o})|(${c}))[eE][+-]?(${o})[jJ]?(?=${d})`},{begin:`(${c})[jJ]?`},{ +begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${d})`},{ +begin:`\\b0[bB](_?[01])+[lL]?(?=${d})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${d})` +},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${d})`},{begin:`\\b(${o})[jJ](?=${d})` +}]},p={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, +contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ +className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, +end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, +contains:["self",t,g,b,e.HASH_COMMENT_MODE]}]};return r.contains=[b,g,t],{ +name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, +illegal:/(<\/|\?)|=>/,contains:[t,g,{begin:/\bself\b/},{beginKeywords:"if", +relevance:0},{match:/\bor\b/,scope:"keyword"},b,p,e.HASH_COMMENT_MODE,{ +match:[/\bdef/,/\s+/,a],scope:{1:"keyword",3:"title.function"},contains:[m]},{ +variants:[{match:[/\bclass/,/\s+/,a,/\s*/,/\(\s*/,a,/\s*\)/]},{ +match:[/\bclass/,/\s+/,a]}],scope:{1:"keyword",3:"title.class", +6:"title.class.inherited"}},{className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/, +contains:[g,m,b]}]}}})();hljs.registerLanguage("python",e)})();/*! `python-repl` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var a=(()=>{"use strict";return a=>({aliases:["pycon"],contains:[{ +className:"meta.prompt",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"} +},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]})})() +;hljs.registerLanguage("python-repl",a)})();/*! `r` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const a=e.regex,n=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,i=a.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),s=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,t=a.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) +;return{name:"R",keywords:{$pattern:n, +keyword:"function if in break next repeat else for while", +literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", +built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" +},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, +starts:{end:a.lookahead(a.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), +endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ +scope:"variable",variants:[{match:n},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 +}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] +}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], +variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', +relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ +1:"operator",2:"number"},match:[s,i]},{scope:{1:"operator",2:"number"}, +match:[/%[^%]*%/,i]},{scope:{1:"punctuation",2:"number"},match:[t,i]},{scope:{ +2:"number"},match:[/[^a-zA-Z0-9._]|^/,i]}]},{scope:{3:"operator"}, +match:[n,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:s},{ +match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:t},{begin:"`",end:"`", +contains:[{begin:/\\./}]}]}}})();hljs.registerLanguage("r",e)})();/*! `ruby` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n=e.regex,a="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",s=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(s,/(::\w+)*/),t={ +"variable.constant":["__FILE__","__LINE__","__ENCODING__"], +"variable.language":["self","super"], +keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], +built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], +literal:["true","false","nil"]},c={className:"doctag",begin:"@[A-Za-z]+"},r={ +begin:"#<",end:">"},b=[e.COMMENT("#","$",{contains:[c] +}),e.COMMENT("^=begin","^=end",{contains:[c],relevance:10 +}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],l={className:"subst",begin:/#\{/, +end:/\}/,keywords:t},d={className:"string",contains:[e.BACKSLASH_ESCAPE,l], +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ +begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ +begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, +end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ +begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ +begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), +contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[e.BACKSLASH_ESCAPE,l]})]}]},o="[0-9](_?[0-9])*",g={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${o}))?([eE][+-]?(${o})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},_={variants:[{match:/\(\)/},{ +className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, +keywords:t}]},u=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ +match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", +4:"title.class.inherited"},keywords:t},{match:[/(include|extend)\s+/,i],scope:{ +2:"title.class"},keywords:t},{relevance:0,match:[i,/\.new[. (]/],scope:{ +1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},{relevance:0,match:s,scope:"title.class"},{ +match:[/def/,/\s+/,a],scope:{1:"keyword",3:"title.function"},contains:[_]},{ +begin:e.IDENT_RE+"::"},{className:"symbol", +begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[d,{begin:a}],relevance:0},g,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0, +relevance:0,keywords:t},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", +keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,l], +illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ +begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", +end:"\\][a-z]*"}]}].concat(r,b),relevance:0}].concat(r,b) +;l.contains=u,_.contains=u;const m=[{begin:/^\s*=>/,starts:{end:"$",contains:u} +},{className:"meta.prompt", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",keywords:t,contains:u}}];return b.unshift(r),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:t,illegal:/\/\*/, +contains:[e.SHEBANG({binary:"ruby"})].concat(m).concat(b).concat(u)}}})() +;hljs.registerLanguage("ruby",e)})();/*! `rust` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const t=e.regex,n={ +className:"title.function.invoke",relevance:0, +begin:t.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,t.lookahead(/\s*\(/)) +},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],s=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:s, +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},n]}}})() +;hljs.registerLanguage("rust",e)})();/*! `scala` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,a={className:"subst", +variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:/\$\{/,end:/\}/}]},s={ +className:"string",variants:[{begin:'"""',end:'"""'},{begin:'"',end:'"', +illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'[a-z]+"',end:'"', +illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,a]},{className:"string", +begin:'[a-z]+"""',end:'"""',contains:[a],relevance:10}]},i={className:"type", +begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title", +begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/, +relevance:0},c={className:"class",beginKeywords:"class object trait type", +end:/[:={\[\n;]/,excludeEnd:!0, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{ +beginKeywords:"extends with",relevance:10},{begin:/\[/,end:/\]/,excludeBegin:!0, +excludeEnd:!0,relevance:0, +contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,relevance:0, +contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},t]},r={ +className:"function",beginKeywords:"def",end:n.lookahead(/[:={\[(\n;]/), +contains:[t]};return{name:"Scala",keywords:{literal:"true false null", +keyword:"type yield lazy override def with val var sealed abstract private trait object if then forSome for while do throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit export enum given transparent" +},contains:[{begin:["//>",/\s+/,/using/,/\s+/,/\S+/],beginScope:{1:"comment", +3:"keyword",5:"type"},end:/$/,contains:[{className:"string",begin:/\S+/}] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,i,r,c,e.C_NUMBER_MODE,{ +begin:[/^\s*/,"extension",/\s+(?=[[(])/],beginScope:{2:"keyword"}},{ +begin:[/^\s*/,/end/,/\s+/,/(extension\b)?/],beginScope:{2:"keyword",4:"keyword"} +},{match:/\.inline\b/},{begin:/\binline(?=\s)/,keywords:"inline"},{ +begin:[/\(\s*/,/using/,/\s+(?!\))/],beginScope:{2:"keyword"}},{className:"meta", +begin:"@[A-Za-z]+"}]}}})();hljs.registerLanguage("scala",e)})();/*! `scss` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video","defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],r=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),t=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),o=["align-content","align-items","align-self","alignment-baseline","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","cx","cy","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","empty-cells","enable-background","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","flood-color","flood-opacity","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","kerning","justify-content","left","letter-spacing","lighting-color","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","marker","marker-end","marker-mid","marker-start","mask","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","r","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","speak","speak-as","src","tab-size","table-layout","text-anchor","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vector-effect","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index"].sort().reverse() +;return n=>{const a=(e=>({IMPORTANT:{scope:"meta",begin:"!important"}, +BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number", +begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{ +className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{ +scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}))(n),l=t,s=i,d="@[a-z-]+",c={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", +case_insensitive:!0,illegal:"[=/|']", +contains:[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE,a.CSS_NUMBER_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},a.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+e.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+s.join("|")+")"},{className:"selector-pseudo", +begin:":(:)?("+l.join("|")+")"},c,{begin:/\(/,end:/\)/, +contains:[a.CSS_NUMBER_MODE]},a.CSS_VARIABLE,{className:"attribute", +begin:"\\b("+o.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:/:/,end:/[;}{]/,relevance:0, +contains:[a.BLOCK_COMMENT,c,a.HEXCOLOR,a.CSS_NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,a.IMPORTANT,a.FUNCTION_DISPATCH] +},{begin:"@(page|font-face)",keywords:{$pattern:d,keyword:"@page @font-face"}},{ +begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:r.join(" ")},contains:[{begin:d, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},c,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,a.HEXCOLOR,a.CSS_NUMBER_MODE] +},a.FUNCTION_DISPATCH]}}})();hljs.registerLanguage("scss",e)})();/*! `shell` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var s=(()=>{"use strict";return s=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]})})();hljs.registerLanguage("shell",s)})();/*! `sql` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const r=e.regex,t=e.COMMENT("--","$"),n=["true","false","unknown"],a=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],i=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=i,c=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!i.includes(e))),l={ +begin:r.concat(/\b/,r.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} +;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ +$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:r,when:t}={})=>{const n=t +;return r=r||[],e.map((e=>e.match(/\|\d+$/)||r.includes(e)?e:n(e)?e+"|0":e)) +})(c,{when:e=>e.length<3}),literal:n,type:a, +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{begin:r.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, +keyword:c.concat(s),literal:n,type:a}},{className:"type", +begin:r.either("double precision","large object","with timezone","without timezone") +},l,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string", +variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/, +contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{ +className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, +relevance:0}]}}})();hljs.registerLanguage("sql",e)})();/*! `swift` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";function e(e){ +return e?"string"==typeof e?e:e.source:null}function n(e){return t("(?=",e,")")} +function t(...n){return n.map((n=>e(n))).join("")}function a(...n){const t=(e=>{ +const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(n);return"("+(t.capture?"":"?:")+n.map((n=>e(n))).join("|")+")"} +const i=e=>t(/\b/,e,/\w$/.test(e)?/\b/:/\B/),s=["Protocol","Type"].map(i),c=["init","self"].map(i),u=["Any","Self"],r=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],o=["false","nil","true"],l=["assignment","associativity","higherThan","left","lowerThan","none","right"],m=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],p=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],d=a(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),F=a(d,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),b=t(d,F,"*"),h=a(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),f=a(h,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),w=t(h,f,"*"),g=t(/[A-Z]/,f,"*"),y=["attached","autoclosure",t(/convention\(/,a("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",t(/objc\(/,w,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],E=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] +;return e=>{const d={match:/\s+/,relevance:0},h=e.COMMENT("/\\*","\\*/",{ +contains:["self"]}),A=[e.C_LINE_COMMENT_MODE,h],v={match:[/\./,a(...s,...c)], +className:{2:"keyword"}},C={match:t(/\./,a(...r)),relevance:0 +},N=r.filter((e=>"string"==typeof e)).concat(["_|0"]),k={variants:[{ +className:"keyword", +match:a(...r.filter((e=>"string"!=typeof e)).concat(u).map(i),...c)}]},B={ +$pattern:a(/\b\w+/,/#\w+/),keyword:N.concat(m),literal:o},S=[v,C,k],D=[{ +match:t(/\./,a(...p)),relevance:0},{className:"built_in", +match:t(/\b/,a(...p),/(?=\()/)}],_={match:/->/,relevance:0},M=[_,{ +className:"operator",relevance:0,variants:[{match:b},{match:`\\.(\\.|${F})+`}] +}],x="([0-9]_*)+",$="([0-9a-fA-F]_*)+",L={className:"number",relevance:0, +variants:[{match:`\\b(${x})(\\.(${x}))?([eE][+-]?(${x}))?\\b`},{ +match:`\\b0x(${$})(\\.(${$}))?([pP][+-]?(${x}))?\\b`},{match:/\b0o([0-7]_*)+\b/ +},{match:/\b0b([01]_*)+\b/}]},I=(e="")=>({className:"subst",variants:[{ +match:t(/\\/,e,/[0\\tnr"']/)},{match:t(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}] +}),O=(e="")=>({className:"subst",match:t(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/) +}),P=(e="")=>({className:"subst",label:"interpol",begin:t(/\\/,e,/\(/),end:/\)/ +}),T=(e="")=>({begin:t(e,/"""/),end:t(/"""/,e),contains:[I(e),O(e),P(e)] +}),K=(e="")=>({begin:t(e,/"/),end:t(/"/,e),contains:[I(e),P(e)]}),j={ +className:"string", +variants:[T(),T("#"),T("##"),T("###"),K(),K("#"),K("##"),K("###")] +},z=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0, +contains:[e.BACKSLASH_ESCAPE]}],q={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//, +contains:z},U=e=>{const n=t(e,/\//),a=t(/\//,e);return{begin:n,end:a, +contains:[...z,{scope:"comment",begin:`#(?!.*${a})`,end:/$/}]}},Z={ +scope:"regexp",variants:[U("###"),U("##"),U("#"),q]},V={match:t(/`/,w,/`/) +},W=[V,{className:"variable",match:/\$\d+/},{className:"variable", +match:`\\$${f}+`}],G=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{ +contains:[{begin:/\(/,end:/\)/,keywords:E,contains:[...M,L,j]}]}},{ +scope:"keyword",match:t(/@/,a(...y))},{scope:"meta",match:t(/@/,w)}],H={ +match:n(/\b[A-Z]/),relevance:0,contains:[{className:"type", +match:t(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,f,"+") +},{className:"type",match:g,relevance:0},{match:/[?!]+/,relevance:0},{ +match:/\.\.\./,relevance:0},{match:t(/\s+&\s+/,n(g)),relevance:0}]},R={ +begin://,keywords:B,contains:[...A,...S,...G,_,H]};H.contains.push(R) +;const X={begin:/\(/,end:/\)/,relevance:0,keywords:B,contains:["self",{ +match:t(w,/\s*:/),keywords:"_|0",relevance:0 +},...A,Z,...S,...D,...M,L,j,...W,...G,H]},J={begin://, +keywords:"repeat each",contains:[...A,H]},Q={begin:/\(/,end:/\)/,keywords:B, +contains:[{begin:a(n(t(w,/\s*:/)),n(t(w,/\s+/,w,/\s*:/))),end:/:/,relevance:0, +contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:w}] +},...A,...S,...M,L,j,...G,H,X],endsParent:!0,illegal:/["']/},Y={ +match:[/(func|macro)/,/\s+/,a(V.match,w,b)],className:{1:"keyword", +3:"title.function"},contains:[J,Q,d],illegal:[/\[/,/%/]},ee={ +match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, +contains:[J,Q,d],illegal:/\[|%/},ne={match:[/operator/,/\s+/,b],className:{ +1:"keyword",3:"title"}},te={begin:[/precedencegroup/,/\s+/,g],className:{ +1:"keyword",3:"title"},contains:[H],keywords:[...l,...o],end:/}/} +;for(const e of j.variants){const n=e.contains.find((e=>"interpol"===e.label)) +;n.keywords=B;const t=[...S,...D,...M,L,j,...W];n.contains=[...t,{begin:/\(/, +end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:B, +contains:[...A,Y,ee,{beginKeywords:"struct protocol class extension enum actor", +end:"\\{",excludeEnd:!0,keywords:B,contains:[e.inherit(e.TITLE_MODE,{ +className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...S] +},ne,te,{beginKeywords:"import",end:/$/,contains:[...A],relevance:0 +},Z,...S,...D,...M,L,j,...W,...G,H,X]}}})();hljs.registerLanguage("swift",e) +})();/*! `typescript` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s) +;function o(o){const l=o.regex,d=e,b={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="",$={ +match:[/const|var|let/,/\s+/,d,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,p,N,f,_,h,{match:/\$\d+/},y,k,{ +className:"attr",begin:d+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[h,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:b.begin, +"on:begin":b.isTrulyOpeningTag,end:b.end}],subLanguage:"xml",contains:[{ +begin:b.begin,end:b.end,skip:!0,contains:["self"]}]}]},O,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:d, +className:"title.function"})]},{match:/\.\.\./,relevance:0},T,{match:"\\$"+d, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},C,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},x,M,{match:/\$[(.]/}]}}return t=>{ +const s=o(t),r=e,l=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],d={ +beginKeywords:"namespace",end:/\{/,excludeEnd:!0, +contains:[s.exports.CLASS_REFERENCE]},b={beginKeywords:"interface",end:/\{/, +excludeEnd:!0,keywords:{keyword:"interface extends",built_in:l}, +contains:[s.exports.CLASS_REFERENCE]},g={$pattern:e, +keyword:n.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]), +literal:a,built_in:i.concat(l),"variable.language":c},u={className:"meta", +begin:"@"+r},m=(e,n,a)=>{const t=e.contains.findIndex((e=>e.label===n)) +;if(-1===t)throw Error("can not find mode to replace");e.contains.splice(t,1,a)} +;return Object.assign(s.keywords,g), +s.exports.PARAMS_CONTAINS.push(u),s.contains=s.contains.concat([u,d,b]), +m(s,"shebang",t.SHEBANG()),m(s,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),s.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(s,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),s}})() +;hljs.registerLanguage("typescript",e)})();/*! `vbscript` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const t=e.regex,r=["lcase","month","vartype","instrrev","ubound","setlocale","getobject","rgb","getref","string","weekdayname","rnd","dateadd","monthname","now","day","minute","isarray","cbool","round","formatcurrency","conversions","csng","timevalue","second","year","space","abs","clng","timeserial","fixs","len","asc","isempty","maths","dateserial","atn","timer","isobject","filter","weekday","datevalue","ccur","isdate","instr","datediff","formatdatetime","replace","isnull","right","sgn","array","snumeric","log","cdbl","hex","chr","lbound","msgbox","ucase","getlocale","cos","cdate","cbyte","rtrim","join","hour","oct","typename","trim","strcomp","int","createobject","loadpicture","tan","formatnumber","mid","split","cint","sin","datepart","ltrim","sqr","time","derived","eval","date","formatpercent","exp","inputbox","left","ascw","chrw","regexp","cstr","err"] +;return{name:"VBScript",aliases:["vbs"],case_insensitive:!0,keywords:{ +keyword:["call","class","const","dim","do","loop","erase","execute","executeglobal","exit","for","each","next","function","if","then","else","on","error","option","explicit","new","private","property","let","get","public","randomize","redim","rem","select","case","set","stop","sub","while","wend","with","end","to","elseif","is","or","xor","and","not","class_initialize","class_terminate","default","preserve","in","me","byval","byref","step","resume","goto"], +built_in:["server","response","request","scriptengine","scriptenginebuildversion","scriptengineminorversion","scriptenginemajorversion"], +literal:["true","false","null","nothing","empty"]},illegal:"//",contains:[{ +begin:t.concat(t.either(...r),"\\s*\\("),relevance:0,keywords:{built_in:r} +},e.inherit(e.QUOTE_STRING_MODE,{contains:[{begin:'""'}]}),e.COMMENT(/'/,/$/,{ +relevance:0}),e.C_NUMBER_MODE]}}})();hljs.registerLanguage("vbscript",e)})();/*! `wasm` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{e.regex;const a=e.COMMENT(/\(;/,/;\)/) +;return a.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, +keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] +},contains:[e.COMMENT(/;;/,/$/),a,{match:[/(?:offset|align)/,/\s*/,/=/], +className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ +match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ +begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", +3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, +className:"type"},{className:"keyword", +match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ +},{className:"number",relevance:0, +match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ +}]}}})();hljs.registerLanguage("wasm",e)})();/*! `x86asm` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var s=(()=>{"use strict";return s=>({name:"Intel x86 Assembly", +case_insensitive:!0,keywords:{$pattern:"[.%]?"+s.IDENT_RE, +keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63", +built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr", +meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__" +},contains:[s.COMMENT(";","$",{relevance:0}),{className:"number",variants:[{ +begin:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*(\\.[0-9_]*)?(?:[pP](?:[+-]?[0-9_]+)?)?)\\b", +relevance:0},{begin:"\\$[0-9][0-9A-Fa-f]*",relevance:0},{ +begin:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b" +},{ +begin:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b" +}]},s.QUOTE_STRING_MODE,{className:"string",variants:[{begin:"'",end:"[^\\\\]'" +},{begin:"`",end:"[^\\\\]`"}],relevance:0},{className:"symbol",variants:[{ +begin:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{ +begin:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],relevance:0},{className:"subst", +begin:"%[0-9]+",relevance:0},{className:"subst",begin:"%!S+",relevance:0},{ +className:"meta",begin:/^\s*\.[\w_-]+/}]})})();hljs.registerLanguage("x86asm",s) +})();/*! `xml` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{ +className:"meta",begin://,contains:[t,i,l,c]}]}] +},e.COMMENT(//,{relevance:10}),{begin://, +relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, +relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:a.concat(//,/>/,/\s/)))), +end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{ +className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{ +className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}} +})();hljs.registerLanguage("xml",e)})();/*! `yaml` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/, +end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]", +contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{ +begin:/\w[\w :()\./-]*:(?=[ \t]|$)/},{begin:/"\w[\w :()\./-]*":(?=[ \t]|$)/},{ +begin:/'\w[\w :()\./-]*':(?=[ \t]|$)/}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", +begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b] +;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:b}}})();hljs.registerLanguage("yaml",e)})(); \ No newline at end of file diff --git a/web/static/packages/htmx.min.js b/web/static/packages/htmx.min.js new file mode 100644 index 00000000..de5f0f1a --- /dev/null +++ b/web/static/packages/htmx.min.js @@ -0,0 +1 @@ +(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else if(typeof module==="object"&&module.exports){module.exports=t()}else{e.htmx=e.htmx||t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(e,t){var r=dr(e,t||"post");return r.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,logger:null,config:{historyEnabled:true,historyCacheSize:10,refreshOnHistoryMiss:false,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:true,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:true,allowScriptTags:true,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:false,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false,getCacheBusterParam:false,globalViewTransitions:false,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Q.config.wsBinaryType;return t},version:"1.9.12"};var r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R};var w=["get","post","put","delete","patch"];var i=w.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");var S=e("head"),q=e("title"),H=e("svg",true);function e(e,t){return new RegExp("<"+e+"(\\s[^>]*>|>)([\\s\\S]*?)<\\/"+e+">",!!t?"gim":"im")}function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function u(e){return e.parentElement}function re(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function L(e,t,r){var n=te(t,r);var i=te(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function ne(t,r){var n=null;c(t,function(e){return n=L(t,e,r)});if(n!=="unset"){return n}}function h(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function A(e){var t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;var r=t.exec(e);if(r){return r[1].toLowerCase()}else{return""}}function s(e,t){var r=new DOMParser;var n=r.parseFromString(e,"text/html");var i=n.body;while(t>0){t--;i=i.firstChild}if(i==null){i=re().createDocumentFragment()}return i}function N(e){return/",0);var a=i.querySelector("template").content;if(Q.config.allowScriptTags){oe(a.querySelectorAll("script"),function(e){if(Q.config.inlineScriptNonce){e.nonce=Q.config.inlineScriptNonce}e.htmxExecuted=navigator.userAgent.indexOf("Firefox")===-1})}else{oe(a.querySelectorAll("script"),function(e){_(e)})}return a}switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return s(""+n+"
",1);case"col":return s(""+n+"
",2);case"tr":return s(""+n+"
",2);case"td":case"th":return s(""+n+"
",3);case"script":case"style":return s("
"+n+"
",1);default:return s(n,0)}}function ie(e){if(e){e()}}function I(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return I(e,"Function")}function P(e){return I(e,"Object")}function ae(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function M(e){var t=[];if(e){for(var r=0;r=0}function se(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return re().body.contains(e.getRootNode().host)}else{return re().body.contains(e)}}function D(e){return e.trim().split(/\s+/)}function le(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function E(e){try{return JSON.parse(e)}catch(e){b(e);return null}}function U(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function B(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(t){var e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function j(){Q.logger=null}function C(e,t){if(t){return e.querySelector(t)}else{return C(re(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(re(),e)}}function _(e,t){e=p(e);if(t){setTimeout(function(){_(e);e=null},t)}else{e.parentElement.removeChild(e)}}function z(e,t,r){e=p(e);if(r){setTimeout(function(){z(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=p(e);if(r){setTimeout(function(){n(e,t);e=null},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function $(e,t){e=p(e);e.classList.toggle(t)}function W(e,t){e=p(e);oe(e.parentElement.children,function(e){n(e,t)});z(e,t)}function v(e,t){e=p(e);if(e.closest){return e.closest(t)}else{do{if(e==null||h(e,t)){return e}}while(e=e&&u(e));return null}}function g(e,t){return e.substring(0,t.length)===t}function G(e,t){return e.substring(e.length-t.length)===t}function J(e){var t=e.trim();if(g(t,"<")&&G(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function Z(e,t){if(t.indexOf("closest ")===0){return[v(e,J(t.substr(8)))]}else if(t.indexOf("find ")===0){return[C(e,J(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[K(e,J(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[Y(e,J(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return re().querySelectorAll(J(t))}}var K=function(e,t){var r=re().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ue(e,t){if(t){return Z(e,t)[0]}else{return Z(re().body,e)[0]}}function p(e){if(I(e,"String")){return C(e)}else{return e}}function ve(e,t,r){if(k(t)){return{target:re().body,event:e,listener:t}}else{return{target:p(e),event:t,listener:r}}}function de(t,r,n){jr(function(){var e=ve(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=k(r);return e?r:n}function ge(t,r,n){jr(function(){var e=ve(t,r,n);e.target.removeEventListener(e.event,e.listener)});return k(r)?r:n}var pe=re().createElement("output");function me(e,t){var r=ne(e,t);if(r){if(r==="this"){return[xe(e,t)]}else{var n=Z(e,r);if(n.length===0){b('The selector "'+r+'" on '+t+" returned no matches!");return[pe]}else{return n}}}}function xe(e,t){return c(e,function(e){return te(e,t)!=null})}function ye(e){var t=ne(e,"hx-target");if(t){if(t==="this"){return xe(e,"hx-target")}else{return ue(e,t)}}else{var r=ae(e);if(r.boosted){return re().body}else{return e}}}function be(e){var t=Q.config.attributesToSettle;for(var r=0;r0){o=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{o=e}var r=re().querySelectorAll(t);if(r){oe(r,function(e){var t;var r=i.cloneNode(true);t=re().createDocumentFragment();t.appendChild(r);if(!Se(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ce(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Fe(o,e,e,t,a)}oe(a.elts,function(e){ce(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);fe(re().body,"htmx:oobErrorNoTarget",{content:i})}return e}function Ce(e,t,r){var n=ne(e,"hx-select-oob");if(n){var i=n.split(",");for(var a=0;a0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();we(e,i);s.tasks.push(function(){we(e,a)})}}})}function Oe(e){return function(){n(e,Q.config.addedClass);zt(e);Nt(e);qe(e);ce(e,"htmx:load")}}function qe(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function a(e,t,r,n){Te(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;z(i,Q.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Oe(i))}}}function He(e,t){var r=0;while(r-1){var t=e.replace(H,"");var r=t.match(q);if(r){return r[2]}}}function je(e,t,r,n,i,a){i.title=Ve(n);var o=l(n);if(o){Ce(r,o,i);o=Be(r,o,a);Re(o);return Fe(e,r,t,o,i)}}function _e(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=E(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!P(o)){o={value:o}}ce(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Tr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){fe(re().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Qe(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function y(e,t){var r="";while(e.length>0&&!t.test(e[0])){r+=e.shift()}return r}function tt(e){var t;if(e.length>0&&Ze.test(e[0])){e.shift();t=y(e,Ke).trim();e.shift()}else{t=y(e,x)}return t}var rt="input, textarea, select";function nt(e,t,r){var n=[];var i=Ye(t);do{y(i,Je);var a=i.length;var o=y(i,/[,\[\s]/);if(o!==""){if(o==="every"){var s={trigger:"every"};y(i,Je);s.pollInterval=d(y(i,/[,\[\s]/));y(i,Je);var l=et(e,i,"event");if(l){s.eventFilter=l}n.push(s)}else if(o.indexOf("sse:")===0){n.push({trigger:"sse",sseEvent:o.substr(4)})}else{var u={trigger:o};var l=et(e,i,"event");if(l){u.eventFilter=l}while(i.length>0&&i[0]!==","){y(i,Je);var f=i.shift();if(f==="changed"){u.changed=true}else if(f==="once"){u.once=true}else if(f==="consume"){u.consume=true}else if(f==="delay"&&i[0]===":"){i.shift();u.delay=d(y(i,x))}else if(f==="from"&&i[0]===":"){i.shift();if(Ze.test(i[0])){var c=tt(i)}else{var c=y(i,x);if(c==="closest"||c==="find"||c==="next"||c==="previous"){i.shift();var h=tt(i);if(h.length>0){c+=" "+h}}}u.from=c}else if(f==="target"&&i[0]===":"){i.shift();u.target=tt(i)}else if(f==="throttle"&&i[0]===":"){i.shift();u.throttle=d(y(i,x))}else if(f==="queue"&&i[0]===":"){i.shift();u.queue=y(i,x)}else if(f==="root"&&i[0]===":"){i.shift();u[f]=tt(i)}else if(f==="threshold"&&i[0]===":"){i.shift();u[f]=y(i,x)}else{fe(e,"htmx:syntax:error",{token:i.shift()})}}n.push(u)}}if(i.length===a){fe(e,"htmx:syntax:error",{token:i.shift()})}y(i,Je)}while(i[0]===","&&i.shift());if(r){r[t]=n}return n}function it(e){var t=te(e,"hx-trigger");var r=[];if(t){var n=Q.config.triggerSpecsCache;r=n&&n[t]||nt(e,t,n)}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,rt)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function at(e){ae(e).cancelled=true}function ot(e,t,r){var n=ae(e);n.timeout=setTimeout(function(){if(se(e)&&n.cancelled!==true){if(!ct(r,e,Wt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}ot(e,t,r)}},r.pollInterval)}function st(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function lt(t,r,e){if(t.tagName==="A"&&st(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=ee(t,"href")}else{var a=ee(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=ee(t,"action")}e.forEach(function(e){ht(t,function(e,t){if(v(e,Q.config.disableSelector)){m(e);return}he(n,i,e,t)},r,e,true)})}}function ut(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&v(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function ft(e,t){return ae(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function ct(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){fe(re().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function ht(a,o,e,s,l){var u=ae(a);var t;if(s.from){t=Z(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ae(e);t.lastValue=e.value})}oe(t,function(n){var i=function(e){if(!se(a)){n.removeEventListener(s.trigger,i);return}if(ft(a,e)){return}if(l||ut(e,a)){e.preventDefault()}if(ct(s,a,e)){return}var t=ae(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ae(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle>0){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay>0){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{ce(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var vt=false;var dt=null;function gt(){if(!dt){dt=function(){vt=true};window.addEventListener("scroll",dt);setInterval(function(){if(vt){vt=false;oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){pt(e)})}},200)}}function pt(t){if(!o(t,"data-hx-revealed")&&X(t)){t.setAttribute("data-hx-revealed","true");var e=ae(t);if(e.initHash){ce(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ce(t,"revealed")},{once:true})}}}function mt(e,t,r){var n=D(r);for(var i=0;i=0){var t=wt(n);setTimeout(function(){xt(s,r,n+1)},t)}};t.onopen=function(e){n=0};ae(s).webSocket=t;t.addEventListener("message",function(e){if(yt(s)){return}var t=e.data;R(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=M(n.children);for(var a=0;a0){ce(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(ut(e,u)){e.preventDefault()}})}else{fe(u,"htmx:noWebSocketSourceError")}}function wt(e){var t=Q.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(e,t,r){var n=D(r);for(var i=0;i0){setTimeout(i,n)}else{i()}}function Ht(t,i,e){var a=false;oe(w,function(r){if(o(t,"hx-"+r)){var n=te(t,"hx-"+r);a=true;i.path=n;i.verb=r;e.forEach(function(e){Lt(t,e,i,function(e,t){if(v(e,Q.config.disableSelector)){m(e);return}he(r,n,e,t)})})}});return a}function Lt(n,e,t,r){if(e.sseEvent){Rt(n,r,e.sseEvent)}else if(e.trigger==="revealed"){gt();ht(n,r,t,e);pt(n)}else if(e.trigger==="intersect"){var i={};if(e.root){i.root=ue(n,e.root)}if(e.threshold){i.threshold=parseFloat(e.threshold)}var a=new IntersectionObserver(function(e){for(var t=0;t0){t.polling=true;ot(n,r,e)}else{ht(n,r,t,e)}}function At(e){if(!e.htmxExecuted&&Q.config.allowScriptTags&&(e.type==="text/javascript"||e.type==="module"||e.type==="")){var t=re().createElement("script");oe(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}var r=e.parentElement;try{r.insertBefore(t,e)}catch(e){b(e)}finally{if(e.parentElement){e.parentElement.removeChild(e)}}}}function Nt(e){if(h(e,"script")){At(e)}oe(f(e,"script"),function(e){At(e)})}function It(e){var t=e.attributes;if(!t){return false}for(var r=0;r0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Bt(o)}for(var l in r){Ft(e,l,r[l])}}}function jt(e){Ae(e);for(var t=0;tQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(re().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Yt(e){if(!U()){return null}e=B(e);var t=E(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Zt();var r=T(t);var n=Ve(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Ue(t,e,r);nr(r.tasks);Jt=a;ce(re().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{fe(re().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function ar(e){er();e=e||location.pathname+location.search;var t=Yt(e);if(t){var r=l(t.content);var n=Zt();var i=T(n);Ue(n,r,i);nr(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Jt=e;ce(re().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{ir(e)}}}function or(e){var t=me(e,"hx-indicator");if(t==null){t=[e]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Q.config.requestClass)});return t}function sr(e){var t=me(e,"hx-disabled-elt");if(t==null){t=[]}oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function lr(e,t){oe(e,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Q.config.requestClass)}});oe(t,function(e){var t=ae(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function ur(e,t){for(var r=0;r=0}function wr(e,t){var r=t?t:ne(e,"hx-swap");var n={swapStyle:ae(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(e).boosted&&!br(e)){n["show"]="top"}if(r){var i=D(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var v=o.substr("focus-scroll:".length);n["focusScroll"]=v=="true"}else if(a==0){n["swapStyle"]=o}else{b("Unknown modifier in hx-swap: "+o)}}}}return n}function Sr(e){return ne(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function Er(t,r,n){var i=null;R(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Sr(r)){return mr(n)}else{return pr(n)}}}function T(e){return{tasks:[],elts:[e]}}function Cr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ue(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ue(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function Rr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=te(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Tr(e,function(){return Function("return ("+a+")")()},{})}else{s=E(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Rr(u(e),t,r,n)}function Tr(e,t,r){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return r}}function Or(e,t){return Rr(e,"hx-vars",true,t)}function qr(e,t){return Rr(e,"hx-vals",false,t)}function Hr(e){return le(Or(e),qr(e))}function Lr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Ar(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(re().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return t.test(e.getAllResponseHeaders())}function Nr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||I(r,"String")){return he(e,t,null,null,{targetOverride:p(r),returnPromise:true})}else{return he(e,t,p(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:p(r.target),swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return he(e,t,null,null,{returnPromise:true})}}function Ir(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function kr(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=g(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!n){return false}}return ce(e,"htmx:validateUrl",le({url:i,sameHost:n},r))}function he(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=re().body}var M=a.handler||Mr;var X=a.select||null;if(!se(n)){ie(o);return l}var u=a.targetOverride||ye(n);if(u==null||u==pe){fe(n,"htmx:targetError",{target:te(n,"hx-target")});ie(s);return l}var f=ae(n);var c=f.lastButtonClicked;if(c){var h=ee(c,"formaction");if(h!=null){r=h}var v=ee(c,"formmethod");if(v!=null){if(v.toLowerCase()!=="dialog"){t=v}}}var d=ne(n,"hx-confirm");if(e===undefined){var D=function(e){return he(t,r,n,i,a,!!e)};var U={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:d};if(ce(n,"htmx:confirm",U)===false){ie(o);return l}}var g=n;var p=ne(n,"hx-sync");var m=null;var x=false;if(p){var B=p.split(":");var F=B[0].trim();if(F==="this"){g=xe(n,"hx-sync")}else{g=ue(n,F)}p=(B[1]||"drop").trim();f=ae(g);if(p==="drop"&&f.xhr&&f.abortable!==true){ie(o);return l}else if(p==="abort"){if(f.xhr){ie(o);return l}else{x=true}}else if(p==="replace"){ce(g,"htmx:abort")}else if(p.indexOf("queue")===0){var V=p.split(" ");m=(V[1]||"last").trim()}}if(f.xhr){if(f.abortable){ce(g,"htmx:abort")}else{if(m==null){if(i){var y=ae(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){m=y.triggerSpec.queue}}if(m==null){m="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(m==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(m==="all"){f.queuedRequests.push(function(){he(t,r,n,i,a)})}else if(m==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){he(t,r,n,i,a)})}ie(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var j=ne(n,"hx-prompt");if(j){var S=prompt(j);if(S===null||!ce(n,"htmx:prompt",{prompt:S,target:u})){ie(o);w();return l}}if(d&&!e){if(!confirm(d)){ie(o);w();return l}}var E=xr(n,u,S);if(t!=="get"&&!Sr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(a.headers){E=le(E,a.headers)}var _=dr(n,t);var C=_.errors;var R=_.values;if(a.values){R=le(R,a.values)}var z=Hr(n);var $=le(R,z);var T=yr($,n);if(Q.config.getCacheBusterParam&&t==="get"){T["org.htmx.cache-buster"]=ee(u,"id")||"true"}if(r==null||r===""){r=re().location.href}var O=Rr(n,"hx-request");var W=ae(n).boosted;var q=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:T,unfilteredParameters:$,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||O.credentials||Q.config.withCredentials,timeout:a.timeout||O.timeout||Q.config.timeout,path:r,triggeringEvent:i};if(!ce(n,"htmx:configRequest",H)){ie(o);w();return l}r=H.path;t=H.verb;E=H.headers;T=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){ce(n,"htmx:validation:halted",H);ie(o);w();return l}var G=r.split("#");var J=G[0];var L=G[1];var A=r;if(q){A=J;var Z=Object.keys(T).length!==0;if(Z){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=pr(T);if(L){A+="#"+L}}}if(!kr(n,A,H)){fe(n,"htmx:invalidPath",H);ie(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(O.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var K=E[N];Lr(b,N,K)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,select:X,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Ir(n);I.pathInfo.responsePath=Ar(b);M(n,I);lr(k,P);ce(n,"htmx:afterRequest",I);ce(n,"htmx:afterOnLoad",I);if(!se(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(se(r)){t=r}}if(t){ce(t,"htmx:afterRequest",I);ce(t,"htmx:afterOnLoad",I)}}ie(o);w()}catch(e){fe(n,"htmx:onLoadError",le({error:e},I));throw e}};b.onerror=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendError",I);ie(s);w()};b.onabort=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:sendAbort",I);ie(s);w()};b.ontimeout=function(){lr(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:timeout",I);ie(s);w()};if(!ce(n,"htmx:beforeRequest",I)){ie(o);w();return l}var k=or(n);var P=sr(n);oe(["loadstart","loadend","progress","abort"],function(t){oe([b,b.upload],function(e){e.addEventListener(t,function(e){ce(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ce(n,"htmx:beforeSend",I);var Y=q?null:Er(b,n,T);b.send(Y);return l}function Pr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=ne(e,"hx-push-url");var l=ne(e,"hx-replace-url");var u=ae(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function Mr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;var h=u.select;if(!ce(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){_e(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){er();var r=f.getResponseHeader("HX-Location");var v;if(r.indexOf("{")===0){v=E(r);r=v["path"];delete v["path"]}Nr("GET",r,v).then(function(){tr(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){if(f.getResponseHeader("HX-Retarget")==="this"){u.target=l}else{u.target=ue(l,f.getResponseHeader("HX-Retarget"))}}var d=Pr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var g=f.response;var a=f.status>=400;var p=Q.config.ignoreTitle;var o=le({shouldSwap:i,serverResponse:g,isError:a,ignoreTitle:p},u);if(!ce(c,"htmx:beforeSwap",o))return;c=o.target;g=o.serverResponse;a=o.isError;p=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){at(l)}R(l,function(e){g=e.transformResponse(g,f,l)});if(d.type){er()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var v=wr(l,s);if(v.hasOwnProperty("ignoreTitle")){p=v.ignoreTitle}c.classList.add(Q.config.swappingClass);var m=null;var x=null;var y=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(h){r=h}if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}if(d.type){ce(re().body,"htmx:beforeHistoryUpdate",le({history:d},u));if(d.type==="push"){tr(d.path);ce(re().body,"htmx:pushedIntoHistory",{path:d.path})}else{rr(d.path);ce(re().body,"htmx:replacedInHistory",{path:d.path})}}var n=T(c);je(v.swapStyle,c,l,g,n,r);if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){var i=document.getElementById(ee(t.elt,"id"));var a={preventScroll:v.focusScroll!==undefined?!v.focusScroll:!Q.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Q.config.swappingClass);oe(n.elts,function(e){if(e.classList){e.classList.add(Q.config.settlingClass)}ce(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!se(l)){o=re().body}_e(f,"HX-Trigger-After-Swap",o)}var s=function(){oe(n.tasks,function(e){e.call()});oe(n.elts,function(e){if(e.classList){e.classList.remove(Q.config.settlingClass)}ce(e,"htmx:afterSettle",u)});if(u.pathInfo.anchor){var e=re().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!p){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Cr(n.elts,v);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!se(l)){r=re().body}_e(f,"HX-Trigger-After-Settle",r)}ie(m)};if(v.settleDelay>0){setTimeout(s,v.settleDelay)}else{s()}}catch(e){fe(l,"htmx:swapError",u);ie(x);throw e}};var b=Q.config.globalViewTransitions;if(v.hasOwnProperty("transition")){b=v.transition}if(b&&ce(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var w=new Promise(function(e,t){m=e;x=t});var S=y;y=function(){document.startViewTransition(function(){S();return w})}}if(v.swapDelay>0){setTimeout(y,v.swapDelay)}else{y()}}if(a){fe(l,"htmx:responseError",le({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Xr={};function Dr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ur(e,t){if(t.init){t.init(r)}Xr[e]=le(Dr(),t)}function Br(e){delete Xr[e]}function Fr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=te(e,"hx-ext");if(t){oe(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Xr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Fr(u(e),r,n)}var Vr=false;re().addEventListener("DOMContentLoaded",function(){Vr=true});function jr(e){if(Vr||re().readyState==="complete"){e()}else{re().addEventListener("DOMContentLoaded",e)}}function _r(){if(Q.config.includeIndicatorStyles!==false){re().head.insertAdjacentHTML("beforeend","")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/web/static/scripts/files.js b/web/static/scripts/files.js new file mode 100644 index 00000000..236bbdc8 --- /dev/null +++ b/web/static/scripts/files.js @@ -0,0 +1,51 @@ +const pasteContainer = document.querySelector(".pasteContainer"); +const addButton = document.querySelector(".addPaste"); +let count = 0; + +addButton.addEventListener("click", (e) => { + let files = pasteContainer.getElementsByClassName("pasteArea"); + + if (files.length >= 5) { + return; + } + + count += 1; + + const pasteHTML = `
+
+ + Delete File +
+ +
`; + + pasteContainer.insertAdjacentHTML("beforeend", pasteHTML); + + files = pasteContainer.getElementsByClassName("pasteArea"); + for (let file of files) { + file.querySelector(".pasteHeader .deleteFile").classList.remove("disabled"); + } + + if (files.length >= 5) { + addButton.style.display = "none"; + } +}); + +function deleteFile(identifier) { + let files = pasteContainer.getElementsByClassName("pasteArea"); + + if (files.length == 1) { + return; + } else { + addButton.style.display = "flex"; + } + + document.getElementById(identifier).remove(); + + files = pasteContainer.getElementsByClassName("pasteArea"); + if (files.length == 1) { + files[0] + .querySelector(".pasteHeader .deleteFile") + .classList.add("disabled"); + } +} diff --git a/web/static/scripts/hidecopy.js b/web/static/scripts/hidecopy.js new file mode 100644 index 00000000..2715543d --- /dev/null +++ b/web/static/scripts/hidecopy.js @@ -0,0 +1,43 @@ +function hideFile(button, index) { + const pastec = document.getElementById(`__paste_c_${index}`); + const pastea = document.getElementById(`__paste_a_${index}`); + + if (!pastec || !pastea) { + return; + } + + if (button.textContent == "Hide") { + button.textContent = "Show"; + pastec.style.display = "none"; + pastea.style.flexGrow = "0"; + } else { + button.textContent = "Hide"; + pastec.style.display = "block"; + pastea.style.flexGrow = "1"; + } +} + +async function copyFile(index) { + let button = document.getElementById(`__paste_copy_${index}`); + + if (button.textContent != "Copy") { + button.textContent = "✓"; + return; + } + + if (pasteStores.length == 0) { + return + } + + let content = pasteStores[index]; + if (!content) { + return; + } + + await navigator.clipboard.writeText(content); + button.textContent = "✓"; + + setTimeout(() => { + button.textContent = "Copy"; + }, 3500); +} diff --git a/web/static/scripts/highlights.js b/web/static/scripts/highlights.js new file mode 100644 index 00000000..93292680 --- /dev/null +++ b/web/static/scripts/highlights.js @@ -0,0 +1,18 @@ +let pasteStores = []; + + +document.addEventListener("htmx:afterRequest", function (evt) { + if (evt.detail.xhr.status != 200) { + return + } + + if (evt.detail.target.id == "pastecontainer" || evt.detail.target.id == "content") { + const codes = document.querySelectorAll("pre > code"); + for (let code of codes) { + pasteStores.push(code.textContent); + } + + hljs.highlightAll(); + hljs.initLineNumbersOnLoad(); + } +}); \ No newline at end of file diff --git a/web/static/scripts/themes.js b/web/static/scripts/themes.js new file mode 100644 index 00000000..799e5f4f --- /dev/null +++ b/web/static/scripts/themes.js @@ -0,0 +1,44 @@ +function calculateSettingAsThemeString({ + localStorageTheme, + systemSettingDark, +}) { + if (localStorageTheme !== null) { + return localStorageTheme; + } + + if (systemSettingDark.matches) { + return "dark"; + } + + return "light"; +} + +function updateButton({ checkboxEl, isDark }) { + checkboxEl.checked = isDark ? true : false; +} + +function updateThemeOnHtmlEl({ theme }) { + document.querySelector("html").setAttribute("data-theme", theme); +} + +const checkbox = document.querySelector("#themeSwitch"); +const localStorageTheme = localStorage.getItem("theme"); +const systemSettingDark = window.matchMedia("(prefers-color-scheme: dark)"); + +let currentThemeSetting = calculateSettingAsThemeString({ + localStorageTheme, + systemSettingDark, +}); + +updateButton({ checkboxEl: checkbox, isDark: currentThemeSetting === "dark" }); +updateThemeOnHtmlEl({ theme: currentThemeSetting }); + +checkbox.addEventListener("click", (event) => { + const newTheme = currentThemeSetting === "dark" ? "light" : "dark"; + + localStorage.setItem("theme", newTheme); + updateButton({ checkboxEl: checkbox, isDark: newTheme === "dark" }); + updateThemeOnHtmlEl({ theme: newTheme }); + + currentThemeSetting = newTheme; +}); diff --git a/web/static/styles/global.css b/web/static/styles/global.css new file mode 100644 index 00000000..9eece374 --- /dev/null +++ b/web/static/styles/global.css @@ -0,0 +1,482 @@ +[data-theme="light"] { + --color-switch: rgba(31, 31, 65, 0.9); + --color-accent: #9069a7; + --color-error: #dd374d; + --color-security: #004ac0; + --color-background: #eff2f7; + --color-background--header: #fff; + --color-background--pastes: #fff; + --color-background--resizer: rgb(255, 255, 255, 0.9); + --color-background--button: #eff2f7; + --color-foreground: #2e2e33; + --color-foreground--dim: rgb(46, 46, 51, 0.6); + --color-foreground--border: rgb(46, 46, 51, 0.2); + --color-annotation: rgb(185, 52, 69); + --button--brightness: brightness(0.95); + --button--brightness-hover: brightness(0.85); + --button--brightness-active: brightness(0.95); +} + +[data-theme="dark"] { + --color-switch: rgb(246, 249, 255, 0.6); + --color-accent: #c89ee0; + --color-error: #dd374d; + --color-security: #c8e09e; + --color-background: #15151c; + --color-background--header: #1d1d26; + --color-background--pastes: rgb(29, 29, 38, 0.9); + --color-background--resizer: rgb(29, 29, 38, 0.9); + --color-background--button: #1d1d26; + --color-foreground: #c9c9d1; + --color-foreground--dim: rgb(201, 201, 209, 0.6); + --color-foreground--border: rgb(201, 201, 209, 0.2); + --color-annotation: rgb(192, 99, 112, 0.8); + --button--brightness: brightness(1.2); + --button--brightness-hover: brightness(1.1); + --button--brightness-active: brightness(1.1); +} + +* { + box-sizing: border-box; +} + +html, +body { + padding: 0; + margin: 0; + scrollbar-color: var(--color-background) var(--color-background--header); + scrollbar-width: auto; +} + +body { + background-color: var(--color-background); + color: var(--color-foreground); + font-family: "Lato", sans-serif; + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +a { + color: var(--color-accent); +} + +.logo { + height: 2.25rem; + width: 2.25rem; +} + +.header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 1rem 2rem; + background-color: var(--color-background--header); +} + +.headerSection { + display: flex; + flex-direction: row; + align-items: center; + gap: 0.5rem; + font-size: 1.3em; + color: var(--color-accent); +} + +.headerSection { + text-decoration: none; +} + +.footer { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 1rem 2rem; +} + +.footerSection { + align-items: center; + display: flex; + flex-direction: row; + gap: 1rem; + font-size: 0.8em; +} + +.footerSection>a { + display: flex; + flex-direction: row; + align-items: center; + gap: 0.25rem; + font-size: 0.9em; +} + +.footerText { + font-size: 0.9em; + color: var(--color-foreground--dim); +} + +.content { + display: flex; + flex-direction: column; + flex-grow: 1; + padding: 2rem; + gap: 2rem; +} + +.pasteArea { + display: flex; + flex-grow: 1; + flex-shrink: 1; + flex-direction: column; + width: 100%; + max-width: 100%; + height: 100%; + background-color: var(--color-background--pastes); + border-radius: 0.25rem; +} + +.pasteContainer { + display: flex; + flex-direction: column; + gap: 2rem; + flex-grow: 1; + width: 100%; + max-width: 100%; + border-radius: 0.25rem; +} + +.pasteHeader { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 1rem; + width: 100%; +} + +input[type="password"] { + background-color: var(--color-background--pastes) !important; + color: var(--color-foreground) !important; +} + +.filenameArea { + resize: none; + background-color: var(--color-background--pastes); + color: var(--color-foreground); + border-radius: 0.25rem; + font-family: "JetBrains Mono", monospace; + font-optical-sizing: auto; + font-style: normal; + outline: none; + border: var(--color-foreground--border) 1px solid; + padding: 0.25rem; + white-space: pre; + overflow-wrap: normal; + overflow-x: hidden; +} + +.filenameArea:focus { + outline: var(--color-foreground--dim) 1px solid; +} + +.pasteArea>textarea { + display: flex; + flex-grow: 1; + resize: vertical; + background-color: var(--color-background--pastes); + color: var(--color-foreground); + outline: none; + border-radius: 0.25rem; + font-family: "JetBrains Mono", monospace; + font-optical-sizing: auto; + font-style: normal; + white-space: pre; + overflow-wrap: normal; + overflow-x: scroll; + border: none; + padding: 1rem; + width: 100%; + height: 100%; + min-height: 32rem; +} + +textarea::-webkit-resizer { + background-color: var(--color-background--resizer); +} + +textarea { + scrollbar-color: var(--color-background) var(--color-background--header); + scrollbar-width: auto; +} + +.addPaste { + display: flex; + padding: 1rem; + border-radius: 0 0 0.25rem 0.25rem; + background-color: var(--color-background--header); + color: var(--color-accent); + justify-content: center; + align-content: center; + user-select: none; +} + +.addPaste:hover { + cursor: pointer; + filter: var(--button--brightness-hover); +} + +.addPaste:active { + filter: var(--button--brightness-active); +} + +.deleteFile { + display: flex; + padding: 1rem; + border-radius: 0.25rem; + background-color: var(--color-background--header); + filter: brightness(0.8); + color: var(--color-accent); + justify-content: center; + align-content: center; + user-select: none; +} + +.deleteFile:hover { + cursor: pointer; + filter: brightness(0.9); +} + +.deleteFile:active { + filter: brightness(0.8); +} + +.disabled { + cursor: unset; + opacity: 0.6; + filter: brightness(0.9); +} + +.disabled:hover { + cursor: unset; + filter: brightness(0.9); +} + +.disabled:active { + filter: brightness(0.9); +} + +.pasteOptions { + display: flex; + flex-direction: column; + background-color: var(--color-background--header); + border-radius: 0 0 0.25rem 0.25rem; +} + +.pasteOptions>.hrLight { + width: 98%; +} + +.hrLight { + border-top: none; + border-left: none; + border-right: none; + outline: none; + border-bottom: 1px solid var(--color-foreground--border); + margin: 1rem 0; + align-self: center; +} + +.pasteOptionsSection { + display: flex; + flex-direction: row; + align-items: center; + gap: 2rem; + padding: 1rem; + width: 100%; +} + +.savePaste { + display: flex; + padding: 1rem 4rem; + border-radius: 0.25rem; + background-color: var(--color-background--button); + filter: var(--button--brightness); + color: var(--color-accent); + justify-content: center; + align-content: center; + user-select: none; +} + +.savePaste:hover { + cursor: pointer; + filter: var(--button--brightness-hover); +} + +.savePaste:active { + filter: var(--button--brightness-active); +} + +.fileContent { + padding: 0.5rem; + overflow-x: auto; +} + +.identifierHeader { + display: flex; + flex-direction: row; + gap: 2rem; +} + +.identifierHeaderLeft { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.identifierHeaderLeft>a { + font-weight: 600; +} + +.identifierHeaderLeft>span { + color: var(--color-foreground--dim); + font-weight: 400; + font-size: 0.7em; +} + +.identifierHeaderSection { + font-size: 0.9em; + display: flex; + flex-direction: row; + gap: 0.5rem; +} + +.linenos { + font-family: "JetBrains Mono", monospace; +} + +.code { + font-family: "JetBrains Mono", monospace; +} + +.pre { + font-family: "JetBrains Mono", monospace; +} + +.vsc { + width: 14px; + height: 14px; +} + +.pasteButton { + font-size: 0.8em; + color: var(--color-accent); + user-select: none; +} + +.pasteButton:hover { + cursor: pointer; + filter: brightness(1.1); +} + +#errorResponse { + color: var(--color-error); + padding: 1rem; +} + +.protected { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.protectedPassword { + resize: none; + background-color: var(--color-background--pastes); + color: var(--color-foreground); + border-radius: 0.25rem; + font-family: "JetBrains Mono", monospace; + font-optical-sizing: auto; + font-style: normal; + outline: none; + border: var(--color-foreground--border) 1px solid; + padding: 0.5rem; + white-space: pre; + overflow-wrap: normal; + overflow-x: hidden; + height: 3rem; +} + +.protectedPassword:focus { + outline: var(--color-foreground--dim) 1px solid; +} + +.annotations { + font-size: 0.9em; + color: var(--color-annotation); + padding-left: 1rem; +} + +.security { + color: var(--color-security); +} + +/* Theme Switch */ +.themeSwitch { + --size: 1.5rem; + + appearance: none; + outline: none; + cursor: pointer; + + width: var(--size); + height: var(--size); + box-shadow: inset calc(var(--size) * 0.33) calc(var(--size) * -0.25) 0; + border-radius: 999px; + color: var(--color-switch); + + transition: all 500ms; + + &:checked { + --ray-size: calc(var(--size) * -0.4); + --offset-orthogonal: calc(var(--size) * 0.65); + --offset-diagonal: calc(var(--size) * 0.45); + + transform: scale(0.75); + color: var(--color-switch); + box-shadow: inset 0 0 0 var(--size), + calc(var(--offset-orthogonal) * -1) 0 0 var(--ray-size), + var(--offset-orthogonal) 0 0 var(--ray-size), + 0 calc(var(--offset-orthogonal) * -1) 0 var(--ray-size), + 0 var(--offset-orthogonal) 0 var(--ray-size), + calc(var(--offset-diagonal) * -1) calc(var(--offset-diagonal) * -1) 0 var(--ray-size), + var(--offset-diagonal) var(--offset-diagonal) 0 var(--ray-size), + calc(var(--offset-diagonal) * -1) var(--offset-diagonal) 0 var(--ray-size), + var(--offset-diagonal) calc(var(--offset-diagonal) * -1) 0 var(--ray-size); + } +} + +@media screen and (max-width: 600px) { + .savePaste { + padding: 1rem; + font-size: 0.8em; + } + + .footer { + flex-direction: column; + } + + .identifierHeaderSection { + font-size: 0.8em; + } + + .identifierHeader { + gap: 1rem; + } + + .filenameArea { + font-size: 0.7em; + } +} \ No newline at end of file diff --git a/web/static/styles/highlights.css b/web/static/styles/highlights.css new file mode 100644 index 00000000..7aa68cef --- /dev/null +++ b/web/static/styles/highlights.css @@ -0,0 +1,188 @@ +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em; + font-family: "JetBrains Mono", monospace; + font-size: 0.8em; +} + +code.hljs { + padding: 3px 5px; + font-family: "JetBrains Mono", monospace; +} + +.hljs-ln td { + padding-right: 16px; +} + +.hljs-ln-n { + opacity: 0.7; +} + +[data-theme="light"] { + .hljs { + color: #383a42; + } + + .hljs-comment, + .hljs-quote { + color: #a0a1a7; + font-style: italic; + } + + .hljs-doctag, + .hljs-keyword, + .hljs-formula { + color: #a626a4; + } + + .hljs-section, + .hljs-name, + .hljs-selector-tag, + .hljs-deletion, + .hljs-subst { + color: #e45649; + } + + .hljs-literal { + color: #0184bb; + } + + .hljs-string, + .hljs-regexp, + .hljs-addition, + .hljs-attribute, + .hljs-meta .hljs-string { + color: #50a14f; + } + + .hljs-attr, + .hljs-variable, + .hljs-template-variable, + .hljs-type, + .hljs-selector-class, + .hljs-selector-attr, + .hljs-selector-pseudo, + .hljs-number { + color: #986801; + } + + .hljs-symbol, + .hljs-bullet, + .hljs-link, + .hljs-meta, + .hljs-selector-id, + .hljs-title { + color: #4078f2; + } + + .hljs-built_in, + .hljs-title.class_, + .hljs-class .hljs-title { + color: #c18401; + } + + .hljs-emphasis { + font-style: italic; + } + + .hljs-strong { + font-weight: bold; + } + + .hljs-link { + text-decoration: underline; + } +} + +[data-theme="dark"] { + .hljs { + color: #b5bdca; + } + + .hljs-comment, + .hljs-quote { + color: #5c6370; + font-style: italic; + } + + .hljs-doctag, + .hljs-keyword, + .hljs-formula { + color: #c678dd; + } + + .hljs-section, + .hljs-name, + .hljs-selector-tag, + .hljs-deletion, + .hljs-subst { + color: #f5c2e4; + } + + .hljs-literal { + color: #f3a472; + } + + .hljs-string, + .hljs-regexp, + .hljs-addition, + .hljs-attribute, + .hljs-meta .hljs-string { + color: #8ca878; + } + + .hljs-attr, + .hljs-variable, + .hljs-template-variable, + .hljs-type, + .hljs-selector-class, + .hljs-selector-attr, + .hljs-selector-pseudo, + .hljs-number { + color: #d19a66; + } + + .hljs-meta { + color: #ebb371; + } + + .hljs-symbol, + .hljs-bullet, + .hljs-link, + .hljs-selector-id, + .hljs-title { + color: #61aeee; + } + + .hljs-built_in { + color: #6cb4ed; + } + + .hljs-title.class_, + .hljs-class .hljs-title { + color: #6cb4ed; + } + + .hljs-emphasis { + font-style: italic; + } + + .hljs-strong { + font-weight: bold; + } + + .hljs-link { + text-decoration: underline; + } + + .hljs-params { + color: #b5bdca; + } +} + +@media screen and (max-width: 600px) { + pre code.hljs { + font-size: 0.6em; + } +} \ No newline at end of file