commit cd59ccd99bf2c5793de0f46cff6eb797bb922e28 Author: fs Date: Wed Feb 4 23:31:53 2026 +0100 baseline diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..b6ad05d Binary files /dev/null and b/.DS_Store differ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..cd8cbc0 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +APP_NAME=IMVS +APP_SLOGAN=Immobilien Makler Verkaufs Software +APP_URL=http://localhost:8080 +APP_ENV=dev +APP_DEBUG=true +APP_TIMEZONE=Europe/Berlin +APP_STORAGE_PATH=./storage +APP_LOCALE=de +APP_LOCALES=de,en +SESSION_NAME=IMVS + +DB_HOST=db +DB_PORT=3306 +DB_NAME=imvs +DB_USER=imvs +DB_PASS=imvs +DB_ROOT_PASSWORD=imvs_root + +CACHE_SERVERS=memcached:11211 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64f7eb7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.vscode/ +composer.phar +config/config.php +.env +/vendor/ +/web/debugger/ +/data/ +/storage/* +!/storage/.gitkeep diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..0c6ad6a --- /dev/null +++ b/.htaccess @@ -0,0 +1,5 @@ + + RewriteEngine on + RewriteRule ^$ web/ [L] + RewriteRule (.*) web/$1 [L] + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a8ec1db --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Maurits van der Schee + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..19bd1bc --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +

MintyPHP MintyPHP

+ +MintyPHP aims to be a full-stack PHP 7 (or 8) framework that is: + + - Easy to learn + - Secure by design + - Light-weight + +By design, it does: + + - … have one variable scope for all layers. + - … require you to write SQL queries (no ORM). + - … use PHP as a templating language. + +Mainly to make it easy to learn for PHP developers. + +[Download](https://mintyphp.github.io/installation/) / +[Documentation](https://mintyphp.github.io/docs/) + +## External links + +- [MintyPHP v3 is released](https://tqdev.com/2022-mintyphp-v3-is-released) +- [MintyPHP now on packagist!](https://tqdev.com/2018-mindaphp-now-on-packagist) + +## Quickstart (Docker) + +1. Copy `.env.example` to `.env` (defaults are fine for local dev). +2. Build and start the stack: + + ```bash + docker compose up --build + ``` + +3. Open the app at `http://localhost:8080`. +4. Register a user, then visit `/admin` (protected route). +5. phpMyAdmin is available at `http://localhost:8081`. + +### Notes + +- MintyPHP uses Memcached for its firewall cache; the compose stack includes a `memcached` service and the PHP container has the extension enabled. +- `config/config.php` is generated and gitignored by default; adjust `.gitignore` if you want to commit it. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8989d23 --- /dev/null +++ b/composer.json @@ -0,0 +1,33 @@ +{ + "name": "mintyphp/mintyphp", + "description": "A refreshingly different PHP web framework that is easy to learn", + "license": "MIT", + "authors": [ + { + "name": "Maurits van der Schee", + "email": "maurits@vdschee.nl", + "homepage": "https://www.tqdev.com" + } + ], + "require": { + "php": "^8.5", + "mintyphp/core": "*", + "phpmailer/phpmailer": "^7.0" + }, + "require-dev": { + "mintyphp/tools": "*", + "mintyphp/debugger": "*", + "phpunit/phpunit": "*", + "phpstan/phpstan": "^1.9" + }, + "autoload-dev": { + "psr-4": { + "MintyPHP\\Tests\\": "tests/" + } + }, + "autoload": { + "psr-4": { + "MintyPHP\\": "lib/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..669090b --- /dev/null +++ b/composer.lock @@ -0,0 +1,1847 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "b811586ea9f2ec608a5ce87c611f8f76", + "packages": [ + { + "name": "mintyphp/core", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/mintyphp/core.git", + "reference": "458ea5f1b3857c7d31748eb1d131c4947b9e2100" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mintyphp/core/zipball/458ea5f1b3857c7d31748eb1d131c4947b9e2100", + "reference": "458ea5f1b3857c7d31748eb1d131c4947b9e2100", + "shasum": "" + }, + "require": { + "ext-memcached": "*", + "ext-mysqli": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "MintyPHP\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maurits van der Schee", + "email": "maurits@vdschee.nl", + "homepage": "https://www.tqdev.com" + } + ], + "support": { + "issues": "https://github.com/mintyphp/core/issues", + "source": "https://github.com/mintyphp/core/tree/v3.2.0" + }, + "time": "2025-02-23T15:53:20+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v7.0.2", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088", + "reference": "ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^10.0.0@dev", + "squizlabs/php_codesniffer": "^3.13.5", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", + "directorytree/imapengine": "For uploading sent messages via IMAP, see gmail example", + "ext-imap": "Needed to support advanced email address parsing according to RFC822", + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v7.0.2" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2026-01-09T18:02:33+00:00" + } + ], + "packages-dev": [ + { + "name": "mintyphp/debugger", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/mintyphp/debugger.git", + "reference": "6143b246c71d3c30c2acbcf7deea9331c6ecbe2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mintyphp/debugger/zipball/6143b246c71d3c30c2acbcf7deea9331c6ecbe2d", + "reference": "6143b246c71d3c30c2acbcf7deea9331c6ecbe2d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maurits van der Schee", + "email": "maurits@vdschee.nl", + "homepage": "https://www.tqdev.com" + } + ], + "support": { + "issues": "https://github.com/mintyphp/debugger/issues", + "source": "https://github.com/mintyphp/debugger/tree/v3.0.0" + }, + "time": "2022-09-23T06:36:53+00:00" + }, + { + "name": "mintyphp/tools", + "version": "v3.1.0", + "source": { + "type": "git", + "url": "https://github.com/mintyphp/tools.git", + "reference": "fb3805ff30aaf5b218ec5b154d4cbfa30d65de29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mintyphp/tools/zipball/fb3805ff30aaf5b218ec5b154d4cbfa30d65de29", + "reference": "fb3805ff30aaf5b218ec5b154d4cbfa30d65de29", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maurits van der Schee", + "email": "maurits@vdschee.nl", + "homepage": "https://www.tqdev.com" + } + ], + "support": { + "issues": "https://github.com/mintyphp/tools/issues", + "source": "https://github.com/mintyphp/tools/tree/v3.1.0" + }, + "time": "2025-02-23T20:21:07+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-02-12T12:17:51+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + }, + "time": "2024-12-30T11:07:19+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.12.19", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "c42ba9bab7a940ed00092ecb1c77bad98896d789" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c42ba9bab7a940ed00092ecb1c77bad98896d789", + "reference": "c42ba9bab7a940ed00092ecb1c77bad98896d789", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2025-02-19T15:42:21+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "12.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2e3038bff41d56114e5396151060f5ac9d2d6751" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e3038bff41d56114e5396151060f5ac9d2d6751", + "reference": "2e3038bff41d56114e5396151060f5ac9d2d6751", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.4.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-18T14:04:13+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:37+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:58+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:16+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:38+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "12.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "e469daf4e173c4b7f2d6154d364f468f3713f632" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e469daf4e173c4b7f2d6154d364f468f3713f632", + "reference": "e469daf4e173c4b7f2d6154d364f468f3713f632", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.0.3", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.0.0", + "sebastian/comparator": "^7.0.0", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.0", + "sebastian/exporter": "^7.0.0", + "sebastian/global-state": "^8.0.0", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.0", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.0-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.0.4" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-02-21T06:10:40+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:53:50+00:00" + }, + { + "name": "sebastian/comparator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "18eb5a4f854dbd1d6512c459b605de2edb5a0b47" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/18eb5a4f854dbd1d6512c459b605de2edb5a0b47", + "reference": "18eb5a4f854dbd1d6512c459b605de2edb5a0b47", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:54:52+00:00" + }, + { + "name": "sebastian/complexity", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8afe311eca49171bf95405cc0078be9a3821f9f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8afe311eca49171bf95405cc0078be9a3821f9f2", + "reference": "8afe311eca49171bf95405cc0078be9a3821f9f2", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:56:08+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:56:42+00:00" + }, + { + "name": "sebastian/global-state", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:56:59+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:28+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:48+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:17+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:01+00:00" + }, + { + "name": "sebastian/type", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "533fe082889a616f330bcba6f50965135f4f2fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/533fe082889a616f330bcba6f50965135f4f2fab", + "reference": "533fe082889a616f330bcba6f50965135f4f2fab", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:19+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^8.5" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} diff --git a/config/config.php.example b/config/config.php.example new file mode 100644 index 0000000..34828b0 --- /dev/null +++ b/config/config.php.example @@ -0,0 +1,69 @@ + 'IVMS', + 'app_locale' => 'de', + 'app_theme' => 'dark', + 'app_theme_user' => '1', + 'app_primary_color' => '#9b3dc7', +); diff --git a/db/init/init.sql b/db/init/init.sql new file mode 100644 index 0000000..872fdb9 --- /dev/null +++ b/db/init/init.sql @@ -0,0 +1,425 @@ +-- Consolidated schema (fresh install) +-- Includes all current tables, columns, indexes, and seed data. + +CREATE TABLE IF NOT EXISTS `users` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` CHAR(36) NOT NULL, + `first_name` VARCHAR(100) NOT NULL, + `last_name` VARCHAR(100) NOT NULL, + `email` VARCHAR(255) NOT NULL, + `profile_description` TEXT NULL, + `job_title` VARCHAR(160) NULL, + `phone` VARCHAR(50) NULL, + `mobile` VARCHAR(50) NULL, + `short_dial` VARCHAR(20) NULL, + `address` VARCHAR(255) NULL, + `postal_code` VARCHAR(20) NULL, + `city` VARCHAR(100) NULL, + `country` VARCHAR(100) NULL, + `region` VARCHAR(100) NULL, + `hire_date` DATE NULL, + `email_verified_at` DATETIME NULL DEFAULT NULL, + `password` VARCHAR(255) NOT NULL, + `locale` VARCHAR(10) DEFAULT NULL, + `totp_secret` VARCHAR(255) DEFAULT NULL, + `theme` VARCHAR(10) NOT NULL DEFAULT 'light', + `primary_tenant_id` INT NULL, + `current_tenant_id` INT UNSIGNED NULL, + `created_by` INT UNSIGNED DEFAULT NULL, + `modified_by` INT UNSIGNED DEFAULT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `active` TINYINT(1) NOT NULL DEFAULT 1, + `active_changed_at` DATETIME NULL DEFAULT NULL, + `active_changed_by` INT UNSIGNED DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_users_uuid` (`uuid`), + UNIQUE KEY `uniq_users_email` (`email`), + KEY `idx_users_primary_tenant_id` (`primary_tenant_id`), + KEY `idx_users_current_tenant_id` (`current_tenant_id`), + KEY `idx_users_created_by` (`created_by`), + KEY `idx_users_modified_by` (`modified_by`), + KEY `idx_users_active_changed_by` (`active_changed_by`), + CONSTRAINT `fk_users_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_users_modified_by` FOREIGN KEY (`modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_users_active_changed_by` FOREIGN KEY (`active_changed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_users_current_tenant` FOREIGN KEY (`current_tenant_id`) REFERENCES `tenants` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `tenants` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` CHAR(36) NOT NULL, + `description` VARCHAR(255) NOT NULL, + `address` VARCHAR(255) NULL, + `postal_code` VARCHAR(20) NULL, + `city` VARCHAR(100) NULL, + `country` VARCHAR(100) NULL, + `region` VARCHAR(100) NULL, + `vat_id` VARCHAR(50) NULL, + `tax_number` VARCHAR(50) NULL, + `phone` VARCHAR(50) NULL, + `fax` VARCHAR(100) NULL, + `email` VARCHAR(190) NULL, + `support_email` VARCHAR(190) NULL, + `support_phone` VARCHAR(50) NULL, + `billing_email` VARCHAR(190) NULL, + `website` VARCHAR(255) NULL, + `privacy_url` VARCHAR(255) NULL, + `imprint_url` VARCHAR(255) NULL, + `primary_color` VARCHAR(7) NULL, + `status` VARCHAR(20) NOT NULL DEFAULT 'active', + `status_changed_at` DATETIME NULL DEFAULT NULL, + `status_changed_by` INT UNSIGNED DEFAULT NULL, + `created_by` INT UNSIGNED DEFAULT NULL, + `modified_by` INT UNSIGNED DEFAULT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_tenants_uuid` (`uuid`), + KEY `idx_tenants_created_by` (`created_by`), + KEY `idx_tenants_modified_by` (`modified_by`), + KEY `idx_tenants_status_changed_by` (`status_changed_by`), + CONSTRAINT `fk_tenants_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_tenants_modified_by` FOREIGN KEY (`modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_tenants_status_changed_by` FOREIGN KEY (`status_changed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `roles` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` CHAR(36) NOT NULL, + `description` VARCHAR(255) NOT NULL, + `created_by` INT UNSIGNED DEFAULT NULL, + `modified_by` INT UNSIGNED DEFAULT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_roles_uuid` (`uuid`), + KEY `idx_roles_created_by` (`created_by`), + KEY `idx_roles_modified_by` (`modified_by`), + CONSTRAINT `fk_roles_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_roles_modified_by` FOREIGN KEY (`modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `permissions` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `key` VARCHAR(191) NOT NULL, + `description` VARCHAR(255) DEFAULT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_permissions_key` (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `departments` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` CHAR(36) NOT NULL, + `description` VARCHAR(255) NOT NULL, + `created_by` INT UNSIGNED DEFAULT NULL, + `modified_by` INT UNSIGNED DEFAULT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_departments_uuid` (`uuid`), + KEY `idx_departments_created_by` (`created_by`), + KEY `idx_departments_modified_by` (`modified_by`), + CONSTRAINT `fk_departments_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_departments_modified_by` FOREIGN KEY (`modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `pages` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `uuid` CHAR(36) NOT NULL, + `slug` VARCHAR(190) NOT NULL, + `created_by` INT UNSIGNED NULL, + `modified_by` INT UNSIGNED NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_pages_uuid` (`uuid`), + UNIQUE KEY `uniq_pages_slug` (`slug`), + KEY `idx_pages_created_by` (`created_by`), + KEY `idx_pages_modified_by` (`modified_by`), + CONSTRAINT `fk_pages_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_pages_modified_by` FOREIGN KEY (`modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `page_contents` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `page_id` INT UNSIGNED NOT NULL, + `locale` VARCHAR(10) NOT NULL, + `content` LONGTEXT NULL, + `created_by` INT UNSIGNED NULL, + `modified_by` INT UNSIGNED NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_page_locale` (`page_id`, `locale`), + KEY `idx_page_id` (`page_id`), + KEY `idx_page_contents_created_by` (`created_by`), + KEY `idx_page_contents_modified_by` (`modified_by`), + CONSTRAINT `fk_page_contents_page` FOREIGN KEY (`page_id`) REFERENCES `pages` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_page_contents_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_page_contents_modified_by` FOREIGN KEY (`modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `settings` ( + `key` VARCHAR(64) NOT NULL, + `value` TEXT NULL, + `description` VARCHAR(255) NULL, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `user_tenants` ( + `user_id` INT UNSIGNED NOT NULL, + `tenant_id` INT UNSIGNED NOT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`user_id`, `tenant_id`), + KEY `idx_user_tenants_user` (`user_id`), + KEY `idx_user_tenants_tenant` (`tenant_id`), + CONSTRAINT `fk_user_tenants_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_tenants_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `user_roles` ( + `user_id` INT UNSIGNED NOT NULL, + `role_id` INT UNSIGNED NOT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`user_id`, `role_id`), + KEY `idx_user_roles_user` (`user_id`), + KEY `idx_user_roles_role` (`role_id`), + CONSTRAINT `fk_user_roles_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_roles_role` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `role_permissions` ( + `role_id` INT UNSIGNED NOT NULL, + `permission_id` INT UNSIGNED NOT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`role_id`, `permission_id`), + KEY `idx_role_permissions_permission` (`permission_id`), + CONSTRAINT `fk_role_permissions_role` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_role_permissions_permission` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `user_departments` ( + `user_id` INT UNSIGNED NOT NULL, + `department_id` INT UNSIGNED NOT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`user_id`, `department_id`), + KEY `idx_user_departments_user` (`user_id`), + KEY `idx_user_departments_department` (`department_id`), + CONSTRAINT `fk_user_departments_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_departments_department` FOREIGN KEY (`department_id`) REFERENCES `departments` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `tenant_departments` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `tenant_id` INT UNSIGNED NOT NULL, + `department_id` INT UNSIGNED NOT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_tenant_department` (`tenant_id`, `department_id`), + KEY `idx_tenant_departments_tenant` (`tenant_id`), + KEY `idx_tenant_departments_department` (`department_id`), + CONSTRAINT `fk_tenant_departments_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_tenant_departments_department` FOREIGN KEY (`department_id`) REFERENCES `departments` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `mail_log` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `to_email` VARCHAR(255) NOT NULL, + `subject` VARCHAR(255) NOT NULL, + `template` VARCHAR(100) NULL, + `status` VARCHAR(20) NOT NULL DEFAULT 'queued', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `sent_at` DATETIME NULL DEFAULT NULL, + `error_message` TEXT NULL, + `provider_message_id` VARCHAR(255) NULL, + PRIMARY KEY (`id`), + KEY `idx_mail_log_status` (`status`), + KEY `idx_mail_log_to_email` (`to_email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `password_resets` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` INT UNSIGNED NOT NULL, + `code_hash` VARCHAR(255) NOT NULL, + `expires_at` DATETIME NOT NULL, + `attempts` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Failed verification attempts', + `used_at` DATETIME NULL DEFAULT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_password_resets_user` (`user_id`), + KEY `idx_password_resets_expires` (`expires_at`), + CONSTRAINT `fk_password_resets_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `email_verifications` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` INT UNSIGNED NOT NULL, + `code_hash` VARCHAR(255) NOT NULL, + `expires_at` DATETIME NOT NULL, + `attempts` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Failed verification attempts', + `used_at` DATETIME NULL DEFAULT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_email_verifications_user` (`user_id`), + KEY `idx_email_verifications_expires` (`expires_at`), + CONSTRAINT `fk_email_verifications_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `user_remember_tokens` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` INT UNSIGNED NOT NULL, + `selector` CHAR(24) NOT NULL, + `token_hash` CHAR(64) NOT NULL, + `expires_at` DATETIME NOT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `last_used` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_user_remember_selector` (`selector`), + KEY `idx_user_remember_user` (`user_id`), + KEY `idx_user_remember_expires` (`expires_at`), + CONSTRAINT `fk_user_remember_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +INSERT INTO `tenants` (`uuid`, `description`, `created`) +SELECT UUID(), 'Cronus', NOW() +WHERE NOT EXISTS (SELECT 1 FROM tenants WHERE description = 'Cronus'); + +INSERT INTO `departments` (`uuid`, `description`, `created`) +SELECT UUID(), 'IT', NOW() +WHERE NOT EXISTS (SELECT 1 FROM departments WHERE description = 'IT'); + +INSERT INTO `roles` (`uuid`, `description`, `created`) +SELECT UUID(), 'Admin', NOW() +WHERE NOT EXISTS (SELECT 1 FROM roles WHERE description = 'Admin'); + +INSERT INTO `users` ( + `uuid`, + `first_name`, + `last_name`, + `email`, + `password`, + `locale`, + `totp_secret`, + `theme`, + `primary_tenant_id`, + `created_by`, + `created`, + `active`, + `active_changed_at`, + `active_changed_by` +) +SELECT + UUID(), + 'Detlef', + 'Demo', + 'demo@user.com', + '$2y$12$KVCQvuy4Pl1aySBuzSpc7ehpZhAzYZkndDI9OaMi05E2P/Mhob5HO', + 'de', + NULL, + 'light', + (SELECT id FROM tenants WHERE description = 'Cronus' LIMIT 1), + NULL, + NOW(), + 1, + NOW(), + NULL +WHERE NOT EXISTS (SELECT 1 FROM users WHERE email = 'demo@user.com'); + +INSERT INTO `user_tenants` (`user_id`, `tenant_id`, `created`) +SELECT u.id, t.id, NOW() +FROM users u +JOIN tenants t ON t.description = 'Cronus' +WHERE u.email = 'demo@user.com' + AND NOT EXISTS ( + SELECT 1 FROM user_tenants ut WHERE ut.user_id = u.id AND ut.tenant_id = t.id + ); + +INSERT INTO `user_departments` (`user_id`, `department_id`, `created`) +SELECT u.id, d.id, NOW() +FROM users u +JOIN departments d ON d.description = 'IT' +WHERE u.email = 'demo@user.com' + AND NOT EXISTS ( + SELECT 1 FROM user_departments ud WHERE ud.user_id = u.id AND ud.department_id = d.id + ); + +INSERT INTO `tenant_departments` (`tenant_id`, `department_id`, `created`) +SELECT t.id, d.id, NOW() +FROM tenants t +JOIN departments d ON d.description = 'IT' +WHERE t.description = 'Cronus' + AND NOT EXISTS ( + SELECT 1 FROM tenant_departments td WHERE td.tenant_id = t.id AND td.department_id = d.id + ); + +INSERT INTO `user_roles` (`user_id`, `role_id`, `created`) +SELECT u.id, r.id, NOW() +FROM users u +JOIN roles r ON r.description = 'Admin' +WHERE u.email = 'demo@user.com' + AND NOT EXISTS ( + SELECT 1 FROM user_roles ur WHERE ur.user_id = u.id AND ur.role_id = r.id + ); + +INSERT INTO `pages` (`uuid`, `slug`, `created`) +SELECT UUID(), 'impressum', NOW() +WHERE NOT EXISTS (SELECT 1 FROM pages WHERE slug = 'impressum'); + +INSERT INTO `page_contents` (`page_id`, `locale`, `content`, `created`) +SELECT p.id, 'de', NULL, p.created +FROM pages p +WHERE NOT EXISTS ( + SELECT 1 FROM page_contents pc WHERE pc.page_id = p.id AND pc.locale = 'de' +); + +INSERT INTO `permissions` (`key`, `description`) +VALUES + ('users.view', 'Can view users'), + ('users.create', 'Can create users'), + ('users.update', 'Can update users'), + ('users.delete', 'Can delete users'), + ('users.self_update', 'Can update own user'), + ('users.update_assignments', 'Can update user assignments'), + ('address_book.view', 'Can view address book'), + ('tenants.view', 'Can view tenants'), + ('tenants.create', 'Can create tenants'), + ('tenants.update', 'Can update tenants'), + ('tenants.delete', 'Can delete tenants'), + ('departments.view', 'Can view departments'), + ('departments.create', 'Can create departments'), + ('departments.update', 'Can update departments'), + ('departments.delete', 'Can delete departments'), + ('roles.view', 'Can view roles'), + ('roles.create', 'Can create roles'), + ('roles.update', 'Can update roles'), + ('roles.delete', 'Can delete roles'), + ('permissions.view', 'Can view permissions'), + ('permissions.create', 'Can create permissions'), + ('permissions.update', 'Can update permissions'), + ('permissions.delete', 'Can delete permissions'), + ('settings.view', 'Can view settings'), + ('settings.update', 'Can update settings'), + ('mail_log.view', 'Can view mail logs'), + ('stats.view', 'Can view statistics') +ON DUPLICATE KEY UPDATE `description` = VALUES(`description`); + +INSERT INTO `role_permissions` (`role_id`, `permission_id`, `created`) +SELECT r.id, p.id, NOW() +FROM roles r +JOIN permissions p ON p.`key` IN ( + 'users.view', 'users.create', 'users.update', 'users.delete', + 'users.self_update', 'users.update_assignments', + 'address_book.view', + 'tenants.view', 'tenants.create', 'tenants.update', 'tenants.delete', + 'departments.view', 'departments.create', 'departments.update', 'departments.delete', + 'roles.view', 'roles.create', 'roles.update', 'roles.delete', + 'permissions.view', 'permissions.create', 'permissions.update', 'permissions.delete', + 'settings.view', 'settings.update', + 'mail_log.view', + 'stats.view' +) +WHERE r.description IN ('Admin', 'Administrator') OR r.id = 1 +ON DUPLICATE KEY UPDATE role_id = role_id; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7fe0344 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,55 @@ +services: + nginx: + image: nginx:1.25-alpine + ports: + - "8080:80" + volumes: + - ./:/var/www + - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro + depends_on: + - php + + php: + build: + context: . + dockerfile: docker/php/Dockerfile + env_file: + - .env + volumes: + - ./:/var/www + depends_on: + - db + - memcached + + db: + image: mariadb:11.4 + env_file: + - .env + environment: + MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + MARIADB_DATABASE: ${DB_NAME} + MARIADB_USER: ${DB_USER} + MARIADB_PASSWORD: ${DB_PASS} + volumes: + - db_data:/var/lib/mysql + - ./db/init:/docker-entrypoint-initdb.d:ro + + phpmyadmin: + image: phpmyadmin:5 + ports: + - "8081:80" + env_file: + - .env + environment: + PMA_HOST: db + PMA_PORT: ${DB_PORT} + PMA_USER: ${DB_USER} + PMA_PASSWORD: ${DB_PASS} + depends_on: + - db + + memcached: + image: memcached:1.6-alpine + +volumes: + db_data: diff --git a/docker/.DS_Store b/docker/.DS_Store new file mode 100644 index 0000000..0473253 Binary files /dev/null and b/docker/.DS_Store differ diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf new file mode 100644 index 0000000..40d83a1 --- /dev/null +++ b/docker/nginx/default.conf @@ -0,0 +1,29 @@ +server { + listen 80; + server_name _; + + root /var/www/web; + index index.php; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_pass php:9000; + } + + location ~* \.mjs$ { + default_type application/javascript; + try_files $uri =404; + expires 30d; + access_log off; + } + + location ~* \.(css|js|png|jpg|jpeg|gif|svg|ico)$ { + expires 30d; + access_log off; + } +} diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile new file mode 100644 index 0000000..8a879c9 --- /dev/null +++ b/docker/php/Dockerfile @@ -0,0 +1,22 @@ +FROM php:8.5-fpm + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + git \ + unzip \ + libmemcached-dev \ + zlib1g-dev \ + libssl-dev \ + libjpeg-dev \ + libpng-dev \ + libwebp-dev \ + && pecl install memcached \ + && docker-php-ext-enable memcached \ + && docker-php-ext-configure gd --with-jpeg --with-webp \ + && docker-php-ext-install pdo_mysql mysqli gd \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer +COPY docker/php/php.ini /usr/local/etc/php/conf.d/zzz-minty-dev.ini + +WORKDIR /var/www diff --git a/docker/php/php.ini b/docker/php/php.ini new file mode 100644 index 0000000..7c7ed3f --- /dev/null +++ b/docker/php/php.ini @@ -0,0 +1,4 @@ +display_errors=1 +display_startup_errors=1 +error_reporting=E_ALL +memory_limit=256M diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..5726081 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,717 @@ +# IMVS - Architektur und Grundprinzipien + +## Inhaltsverzeichnis + +1. [Projektübersicht](#1-projektübersicht) +2. [Framework: MintyPHP](#2-framework-mintyphp) +3. [Verzeichnisstruktur](#3-verzeichnisstruktur) +4. [Datenbankarchitektur](#4-datenbankarchitektur) +5. [PHP-Architektur](#5-php-architektur) +6. [Frontend-Architektur](#6-frontend-architektur) +7. [Template-System](#7-template-system) +8. [Authentifizierung und Autorisierung](#8-authentifizierung-und-autorisierung) +9. [Internationalisierung](#9-internationalisierung) +10. [Best Practices](#10-best-practices) + +--- + +## 1. Projektübersicht + +**Projektname:** IMVS +**Framework:** MintyPHP v3+ +**PHP-Version:** 8.5+ +**Datenbank:** MySQL 5.7+ +**Lizenz:** MIT + +### Kernfeatures + +- Multi-Tenant-Architektur +- Rollen- und Berechtigungssystem +- Mehrsprachigkeit (DE/EN) +- Dark/Light Theme +- E-Mail-Integration mit Templates + +--- + +## 2. Framework: MintyPHP + +### Philosophie + +MintyPHP ist ein leichtgewichtiges PHP-Framework mit folgenden Prinzipien: + +- **Easy to learn** - Einfach für PHP-Entwickler zu erlernen +- **Secure by design** - Sicherheit als Grundprinzip +- **Lightweight** - Minimaler Overhead +- **Single variable scope** - Eine Variable-Scope für alle Layer +- **SQL required** - Direktes SQL statt ORM +- **PHP as templating** - PHP selbst als Template-Sprache + +### Kern-Komponenten + +| Komponente | Beschreibung | +|------------|--------------| +| `Router` | URL-Routing zu Pages | +| `DB` | Datenbankzugriff mit Prepared Statements | +| `Auth` | Basis-Authentifizierung | +| `Session` | Session-Management | +| `Buffer` | Output-Buffering für Templates | +| `I18n` | Internationalisierung | +| `Cache` | Memcached-Integration | +| `Firewall` | DDoS-Schutz | + +### Router-Konvention + +``` +URL: /admin/users/edit/abc-123 +Datei: pages/admin/users/edit($id).php +Variable: $id = 'abc-123' +``` + +--- + +## 3. Verzeichnisstruktur + +``` +ImmobilienMaklerVerkaufssoftware/ +├── config/ # Konfigurationsdateien +│ ├── config.php # Hauptkonfiguration +│ ├── router.php # Route-Definitionen +│ └── settings.php # App-Einstellungen (generiert) +├── db/ # Datenbank +│ └── init/init.sql # Schema und Seeds +├── docker/ # Docker-Konfiguration +├── docs/ # Dokumentation +├── i18n/ # Sprachdateien +│ ├── default_de.json +│ └── default_en.json +├── lib/ # Geschäftslogik +│ ├── Http/ # HTTP-Utilities +│ ├── Repository/ # Datenbankschicht +│ ├── Service/ # Business-Logic +│ └── Support/ # Helper und Guards +├── pages/ # Seiten (Controller) +│ ├── admin/ # Geschützte Admin-Seiten +│ ├── auth/ # Authentifizierung +│ ├── error/ # Fehlerseiten +│ └── page/ # CMS-Seiten +├── storage/ # Dateiablage +│ ├── branding/ # Logos, Favicons +│ ├── tenants/ # Tenant-Dateien +│ └── users/ # User-Avatare +├── templates/ # View-Templates +│ ├── partials/ # Wiederverwendbare Teile +│ └── emails/ # E-Mail-Templates +├── tests/ # PHPUnit Tests +├── vendor/ # Composer Dependencies +└── web/ # Public Root + ├── css/ # Stylesheets + ├── js/ # JavaScript-Module + ├── vendor/ # Frontend-Libraries + └── index.php # Entry Point +``` + +--- + +## 4. Datenbankarchitektur + +### Entity-Relationship-Diagramm + +``` +┌─────────────┐ ┌─────────────────┐ ┌─────────────┐ +│ USERS │────<│ USER_TENANTS │>────│ TENANTS │ +└─────────────┘ └─────────────────┘ └─────────────┘ + │ │ + │ ┌─────────────────┐ │ + └────────────<│ USER_ROLES │ │ + │ └─────────────────┘ │ + │ │ │ + │ ▼ │ + │ ┌─────────────┐ │ + │ │ ROLES │ │ + │ └─────────────┘ │ + │ │ │ + │ ┌─────────────────┐ │ + │ │ROLE_PERMISSIONS │ │ + │ └─────────────────┘ │ + │ │ │ + │ ▼ │ + │ ┌─────────────┐ │ + │ │ PERMISSIONS │ │ + │ └─────────────┘ │ + │ │ + │ ┌─────────────────┐ ┌──────┴──────┐ + └────────────<│USER_DEPARTMENTS │>────│ DEPARTMENTS │ + └─────────────────┘ └─────────────┘ + │ + ┌──────┴──────┐ + │TENANT_DEPTS │ + └─────────────┘ +``` + +### Haupttabellen + +#### users +```sql +id INT PRIMARY KEY AUTO_INCREMENT +uuid VARCHAR(36) UNIQUE +first_name VARCHAR(100) +last_name VARCHAR(100) +email VARCHAR(255) UNIQUE +password VARCHAR(255) -- bcrypt hash +locale VARCHAR(10) -- 'de', 'en' +theme VARCHAR(20) -- 'light', 'dark' +active TINYINT DEFAULT 1 +primary_tenant_id INT FK -> tenants.id +current_tenant_id INT FK -> tenants.id +created_by INT FK -> users.id +modified_by INT FK -> users.id +created DATETIME +modified DATETIME +``` + +#### tenants +```sql +id INT PRIMARY KEY AUTO_INCREMENT +uuid VARCHAR(36) UNIQUE +description VARCHAR(255) -- Name/Firma +address VARCHAR(255) +postal_code VARCHAR(20) +city VARCHAR(100) +country VARCHAR(100) +vat_id VARCHAR(50) -- MwSt-ID +tax_number VARCHAR(50) +phone VARCHAR(50) +email VARCHAR(255) +website VARCHAR(255) +created_by INT FK -> users.id +modified_by INT FK -> users.id +created DATETIME +modified DATETIME +``` + +#### roles +```sql +id INT PRIMARY KEY AUTO_INCREMENT +uuid VARCHAR(36) UNIQUE +description VARCHAR(255) -- Rollenname +created_by INT FK -> users.id +modified_by INT FK -> users.id +created DATETIME +modified DATETIME +``` + +#### permissions +```sql +id INT PRIMARY KEY AUTO_INCREMENT +key VARCHAR(100) UNIQUE -- z.B. 'users.view' +description VARCHAR(255) +created DATETIME +``` + +#### departments +```sql +id INT PRIMARY KEY AUTO_INCREMENT +uuid VARCHAR(36) UNIQUE +description VARCHAR(255) -- Abteilungsname +created_by INT FK -> users.id +modified_by INT FK -> users.id +created DATETIME +modified DATETIME +``` + +### Verknüpfungstabellen + +| Tabelle | Beschreibung | +|---------|--------------| +| `user_tenants` | User ↔ Tenant (M:N) | +| `user_roles` | User ↔ Role (M:N) | +| `user_departments` | User ↔ Department (M:N) | +| `role_permissions` | Role ↔ Permission (M:N) | +| `tenant_departments` | Tenant ↔ Department (M:N) | + +### Vordefinierte Permissions + +``` +users.view, users.create, users.update, users.delete +users.self_update, users.update_assignments +tenants.view, tenants.create, tenants.update, tenants.delete +departments.view, departments.create, departments.update, departments.delete +roles.view, roles.create, roles.update, roles.delete +permissions.view, permissions.create, permissions.update, permissions.delete +settings.view, settings.update +mail_log.view +stats.view +``` + +--- + +## 5. PHP-Architektur + +### Schichten-Architektur + +``` +┌─────────────────────────────────────────────┐ +│ Pages │ +│ (Controller/Actions) │ +├─────────────────────────────────────────────┤ +│ Services │ +│ (Business Logic, Validation) │ +├─────────────────────────────────────────────┤ +│ Repositories │ +│ (Data Access, SQL) │ +├─────────────────────────────────────────────┤ +│ DB │ +│ (MintyPHP Database) │ +└─────────────────────────────────────────────┘ +``` + +### Service-Layer (`lib/Service/`) + +Services implementieren die Geschäftslogik: + +```php +namespace MintyPHP\Service; + +class UserService +{ + // Listen + public static function list(): array + public static function listPaged(array $options): array + + // Finder + public static function findByUuid(string $uuid): ?array + public static function findById(int $id): ?array + public static function findByEmail(string $email): ?array + + // CRUD + public static function createFromAdmin(array $input, int $currentUserId): array + public static function updateFromAdmin(int $userId, array $input, int $currentUserId): array + public static function deleteByUuid(string $uuid, int $currentUserId): array + + // Bulk-Operationen + public static function deleteByUuids(array $uuids, int $currentUserId): array + public static function setActiveByUuids(array $uuids, bool $active, int $currentUserId): array +} +``` + +**Wichtige Services:** + +| Service | Verantwortung | +|---------|---------------| +| `AuthService` | Login, Logout, Session-Management | +| `UserService` | Benutzerverwaltung | +| `TenantService` | Mandantenverwaltung | +| `RoleService` | Rollenverwaltung | +| `PermissionService` | Berechtigungsprüfung und -verwaltung | +| `DepartmentService` | Abteilungsverwaltung | +| `MailService` | E-Mail-Versand | +| `SettingService` | App-Einstellungen | + +### Repository-Layer (`lib/Repository/`) + +Repositories kapseln Datenbankzugriffe: + +```php +namespace MintyPHP\Repository; + +class UserRepository +{ + public static function list(): array + { + return DB::select('SELECT * FROM users ORDER BY last_name, first_name'); + } + + public static function find(int $id): ?array + { + return DB::selectOne('SELECT * FROM users WHERE id = ?', [$id]); + } + + public static function create(array $data): int + { + return DB::insert('users', $data); + } + + public static function update(int $id, array $data): bool + { + return DB::update('users', $data, ['id' => $id]) > 0; + } +} +``` + +### Support-Layer (`lib/Support/`) + +#### Guard - Zugriffskontrolle + +```php +namespace MintyPHP\Support; + +class Guard +{ + // Nur für angemeldete Benutzer + public static function requireLogin(string $redirect = 'login'): void + + // Spezifische Berechtigung erforderlich + public static function requirePermission(string $permissionKey, string $redirect = 'admin'): void + + // Berechtigung oder 403 (für AJAX) + public static function requirePermissionOrForbidden(string $permissionKey): void +} +``` + +#### Flash - Session-Nachrichten + +```php +namespace MintyPHP\Support; + +class Flash +{ + public static function success(string $message, ?string $scope = null, ?string $key = null): string + public static function error(string $message, ?string $scope = null, ?string $key = null): string + public static function info(string $message, ?string $scope = null, ?string $key = null): string + public static function peek(?string $scope = null): array + public static function has(): bool +} +``` + +### Helper-Funktionen (`lib/Support/helpers/`) + +```php +// HTML-Escape +e($string); + +// Asset-URL +asset('css/style.css'); + +// Lokalisierte URL +lurl('admin/users'); + +// Übersetzung +t('Hello'); +t('Welcome %s', $name); + +// App-Einstellung +appSetting('app_title'); + +// Berechtigung prüfen +can('users.view'); +``` + +--- + +## 6. Frontend-Architektur + +### CSS-Struktur (`web/css/`) + +``` +web/css/ +├── base/ +│ └── variables.css # CSS-Variablen +├── layout/ +│ ├── app-shell.css # Haupt-Layout +│ ├── app-topbar.css # Header +│ ├── app-sidebar.css # Navigation +│ └── app-aside-icon-bar.css +├── components/ +│ ├── app-buttons.css +│ ├── app-forms.css +│ ├── app-list-table.css +│ ├── app-badges.css +│ ├── app-tabs.css +│ ├── app-flash.css +│ └── vendor-*.css # Third-Party +└── pages/ + └── app-login.css +``` + +### JavaScript-Module (`web/js/`) + +ES-Module-Architektur: + +``` +web/js/ +├── app-init.js # Haupt-Initialisierung +├── core/ +│ └── app-grid-factory.js # Grid/Tabellen-Generator +├── components/ +│ ├── app-tenant-switcher.js +│ ├── app-theme-toggle.js +│ ├── app-bulk-selection.js +│ ├── app-flash-auto-dismiss.js +│ └── ... +└── pages/ + ├── app-users-list.js + └── app-list-utils.js +``` + +### Modul-Pattern + +```javascript +// web/js/components/app-tenant-switcher.js +const switchTenant = async (link) => { + const tenantId = link.dataset.switchTenant; + // ... +}; + +const initTenantSwitcher = () => { + const tenantLinks = document.querySelectorAll('[data-switch-tenant]'); + tenantLinks.forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault(); + switchTenant(link); + }); + }); +}; + +// Auto-Init bei DOM Ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initTenantSwitcher); +} else { + initTenantSwitcher(); +} +``` + +### Import in app-init.js + +```javascript +// web/js/app-init.js +import './components/app-tenant-switcher.js'; +import './components/app-theme-toggle.js'; +import './components/app-flash-auto-dismiss.js'; +// ... +``` + +--- + +## 7. Template-System + +### Struktur + +``` +templates/ +├── default.phtml # Haupt-Layout +├── login.phtml # Login-Layout +├── error.phtml # Fehler-Layout +├── partials/ +│ ├── app-topbar.phtml +│ ├── app-sidebar.phtml +│ ├── app-aside.phtml +│ ├── app-flash.phtml +│ └── app-footer.phtml +└── emails/ + ├── de/ + │ ├── access_info.html + │ └── reset_code.html + └── en/ + └── ... +``` + +### Template-Rendering + +```php + + + + + <?php e(Buffer::get('title') ?? appTitle()); ?> + + + + + +
+ + +
+ + + + +``` + +### Page mit View + +```php +// pages/admin/users/index().php +use MintyPHP\Buffer; +use MintyPHP\Support\Guard; + +Guard::requirePermissionOrForbidden('users.view'); + +$users = UserService::list(); + +Buffer::set('title', t('Users')); + +// View wird automatisch geladen: pages/admin/users/index(default).phtml +``` + +--- + +## 8. Authentifizierung und Autorisierung + +### Login-Ablauf + +``` +1. POST /login + │ + ├─> AuthService::login(email, password) + │ ├─> Auth::login() - Passwort-Verifikation + │ ├─> $_SESSION['user'] setzen + │ ├─> PermissionService::getUserPermissions() + │ │ └─> $_SESSION['permissions'] setzen + │ └─> AuthService::loadTenantDataIntoSession() + │ ├─> $_SESSION['available_tenants'] + │ └─> $_SESSION['current_tenant'] + │ + └─> Router::redirect('admin') +``` + +### Berechtigungsprüfung + +```php +// In Page +Guard::requirePermissionOrForbidden(PermissionService::USERS_VIEW); + +// Im Template + + Neuer Benutzer + +``` + +### Session-Struktur + +```php +$_SESSION = [ + 'user' => [ + 'id' => 1, + 'uuid' => 'abc-123', + 'email' => 'user@example.com', + 'first_name' => 'Max', + 'last_name' => 'Mustermann', + 'locale' => 'de', + 'theme' => 'dark', + ], + 'permissions' => [ + 'user_id' => 1, + 'keys' => ['users.view', 'users.create', ...], + ], + 'available_tenants' => [ + ['id' => 1, 'uuid' => '...', 'description' => 'Tenant A'], + ['id' => 2, 'uuid' => '...', 'description' => 'Tenant B'], + ], + 'current_tenant' => [ + 'id' => 1, + 'uuid' => '...', + 'description' => 'Tenant A', + ], +]; +``` + +--- + +## 9. Internationalisierung + +### Sprachdateien + +```json +// i18n/default_de.json +{ + "Login": "Anmelden", + "Users": "Benutzer", + "At least %d characters": "Mindestens %d Zeichen" +} + +// i18n/default_en.json +{ + "Login": "Login", + "Users": "Users", + "At least %d characters": "At least %d characters" +} +``` + +### Verwendung + +```php +// Einfache Übersetzung +t('Login') + +// Mit Parametern +t('At least %d characters', 12) + +// Im Template + +``` + +### Sprachauflösung + +Priorität: +1. URL-Präfix (`/de/admin`, `/en/admin`) +2. Session (`$_SESSION['user']['locale']`) +3. Cookie (`locale=de`) +4. Default (`APP_LOCALE`) + +--- + +## 10. Best Practices + +### Service-Rückgabewerte + +```php +// Erfolg +return [ + 'ok' => true, + 'uuid' => $uuid, + 'id' => $id, +]; + +// Fehler +return [ + 'ok' => false, + 'error' => 'validation_error', + 'errors' => ['email' => 'Email ist erforderlich'], + 'form' => $sanitizedInput, +]; +``` + +### Datenbankzugriff + +```php +// RICHTIG - Prepared Statements +$user = DB::selectOne('SELECT * FROM users WHERE email = ?', [$email]); + +// FALSCH - SQL-Injection +$user = DB::selectOne("SELECT * FROM users WHERE email = '$email'"); +``` + +### Template-Ausgabe + +```php +// RICHTIG - Escape + + +// FALSCH - XSS-Risiko + +``` + +### Passwort-Anforderungen + +- Mindestens 12 Zeichen +- Mindestens 1 Großbuchstabe +- Mindestens 1 Kleinbuchstabe +- Mindestens 1 Ziffer +- Mindestens 1 Sonderzeichen + +### Code-Organisation + +1. **Services** - Geschäftslogik und Validierung +2. **Repositories** - Nur Datenbankzugriff, keine Logik +3. **Guards** - Zugriffskontrolle +4. **Helpers** - Wiederverwendbare Funktionen +5. **Pages** - Koordination, keine Logik + +--- + +## Weiterführende Dokumentation + +- [MintyPHP Framework](https://github.com/mevdschee/php-crud-api) +- [Docker Setup](../docker-compose.yml) +- [Database Schema](../db/init/init.sql) diff --git a/docs/README-search.md b/docs/README-search.md new file mode 100644 index 0000000..3db032e --- /dev/null +++ b/docs/README-search.md @@ -0,0 +1,25 @@ +# Search + Upload TODO (Internal) + +Goal: Unified search across DB data and file contents (PDF, DOCX), starting with MariaDB FULLTEXT and private file storage. + +## MVP Steps +1) Create `documents` table (uuid, original_name, mime, size, storage_path, content_text, created). +2) Add FULLTEXT index on `content_text` (and later title/metadata fields if needed). +3) Build `FileUploadService`: + - Validate size/type (PDF/DOCX). + - Store outside `/web` (e.g. `storage/uploads/{uuid}/file.ext`). + - Extract text: + - PDF: `pdftotext` (poppler-utils). + - DOCX: unzip + parse `word/document.xml` (fallback: LibreOffice headless). + - Save `content_text` + metadata. +4) Add upload action + form (admin area). +5) Add search endpoint: + - Search DB entities. + - Search `documents.content_text`. + - Merge results into one list. + +## Future Enhancements +- Async extraction (queue/worker) for large files. +- Dedicated search engine (Meilisearch/Typesense) when scale/relevance needs grow. +- Result ranking with field weighting (title > body). +- Optional language detection / stemming. diff --git a/docs/conventions.md b/docs/conventions.md new file mode 100644 index 0000000..b164be6 --- /dev/null +++ b/docs/conventions.md @@ -0,0 +1,17 @@ +# Conventions (Short) + +## CSS +- All project files use `app-` prefix. +- Vendor overrides use `vendor-` prefix. +- Structure: `base/`, `layout/`, `components/`, `pages/`. + +## JS +- All project files use `app-` prefix. +- Vendor scripts stay in `web/vendor`. +- Structure: `core/`, `components/`, `pages/`. + +## Partials +- All partials use `app-` prefix (e.g. `app-footer.phtml`). + +## PHP (Lib) +- Services in `lib/Service`, repositories in `lib/Repository`. diff --git a/docs/todo-editorjs-image.md b/docs/todo-editorjs-image.md new file mode 100644 index 0000000..65daf0b --- /dev/null +++ b/docs/todo-editorjs-image.md @@ -0,0 +1,63 @@ +# TODO: Editor.js Image Tool Integration + +## Goal +Integrate the Editor.js **Image Tool** with proper upload endpoints, storage, and access control. + +## Why +- Editor.js expects uploads to respond with `{ success: 1, file: { url: "..." } }`. +- We want private storage with a controlled proxy (like avatars) and optional resizing. + +## Open Decisions +- Public access? (e.g. impressum pages should be public) +- Allowed file types (recommended: PNG/JPG/WebP; avoid SVG for security) +- Resize policy (max width + WebP variants?) + +## Proposed Structure + +### Storage +- `storage/pages/{page_uuid}/assets/{asset_uuid}/original.` +- Optional variants: `w1280.webp`, `w640.webp`, etc. + +### DB +Create `page_assets` table: +- `id`, `uuid`, `page_id`, `locale` +- `file_name`, `mime`, `size`, `width`, `height` +- `created_by`, `created` + +### Endpoints +1) **Upload by file** +`pages/page/upload-image(none).phtml` + - Accepts multipart (field name `image`). + - Validates MIME/size. + - Stores file + creates DB record. + - Returns `{ success: 1, file: { url } }`. + +2) **Fetch by URL** (optional) +`pages/page/fetch-image(none).phtml` + - Accepts JSON `{ url: "..." }`. + - Downloads, validates, stores like upload. + - Returns same response. + +3) **Media proxy** +`pages/page/media(none).phtml?uuid=...` + - Checks permissions (public vs. logged‑in). + - Reads storage file, sets headers, returns file. + +### Editor.js config +Add `image` tool to `web/js/components/page-editor.js`: +```js +image: { + class: ImageTool, + config: { + endpoints: { + byFile: 'page/upload-image', + byUrl: 'page/fetch-image' + } + } +} +``` + +## Next Steps +- Decide public vs. private access rules. +- Implement service + migration + endpoints. +- Add tool config and i18n messages. diff --git a/i18n/default_de.json b/i18n/default_de.json new file mode 100644 index 0000000..5763f45 --- /dev/null +++ b/i18n/default_de.json @@ -0,0 +1,481 @@ +{ + "Login": "Login", + "Register": "Registrieren", + "Password": "Passwort", + "Forgot password": "Passwort vergessen", + "Remember me": "Angemeldet bleiben", + "Clear login tokens": "Login-Tokens löschen", + "Clear all login tokens for this user?": "Alle Login-Tokens für diesen Benutzer löschen?", + "Login tokens cleared": "Login-Tokens gelöscht", + "Send access": "Zugang versenden", + "Send access email to this user?": "Zugangsdaten an diesen Benutzer senden?", + "Access email sent": "Zugangsdaten gesendet", + "Access email failed": "Zugangsdaten konnten nicht gesendet werden", + "Your access details": "Deine Zugangsdaten", + "Reset password": "Passwort zurücksetzen", + "Verification code": "Bestätigungscode", + "Verify code": "Code prüfen", + "Verify": "Prüfen", + "Send code": "Code senden", + "Send code again": "Code erneut senden", + "Enter your email address and we will send you a verification code.": "Gib deine E-Mail-Adresse ein, wir senden dir einen Bestätigungscode.", + "Enter the verification code we sent to your email.": "Gib den Bestätigungscode ein, den wir dir per E-Mail gesendet haben.", + "Please enter a valid email address": "Bitte gib eine gültige E-Mail-Adresse ein", + "Please enter the 6-digit code": "Bitte gib den 6-stelligen Code ein", + "Invalid or expired code": "Code ist ungültig oder abgelaufen", + "Code verified": "Code bestätigt", + "Password reset code": "Code zum Zurücksetzen des Passworts", + "If the email exists, a verification code has been sent.": "Falls die E-Mail existiert, wurde ein Bestätigungscode gesendet.", + "Choose a new password for your account.": "Wähle ein neues Passwort für dein Konto.", + "Password updated": "Passwort aktualisiert", + "Password can not be updated": "Passwort kann nicht aktualisiert werden", + "Reset request not found": "Zurücksetz-Anfrage nicht gefunden", + "Reset request already used": "Zurücksetz-Anfrage wurde bereits verwendet", + "Reset request expired": "Zurücksetz-Anfrage ist abgelaufen", + "Username": "Benutzername", + "Admin": "Admin", + "Logout": "Logout", + "Account": "Konto", + "Please enter your credentials": "Bitte geben Sie Ihre Zugangsdaten ein", + "Create your account": "Erstelle dein Konto", + "First name": "Vorname", + "Last name": "Nachname", + "Email": "E-Mail", + "Password (again)": "Passwort bestätigen", + "Password requirements": "Passwort-Anforderungen", + "At least %d characters": "Mindestens %d Zeichen", + "At least one uppercase letter": "Mindestens ein Großbuchstabe", + "At least one lowercase letter": "Mindestens ein Kleinbuchstabe", + "At least one number": "Mindestens eine Zahl", + "At least one symbol": "Mindestens ein Sonderzeichen", + "Must not contain your email": "Darf deine E-Mail nicht enthalten", + "Login successful": "Login erfolgreich", + "Email/password not valid": "E-Mail oder Passwort ist ungültig", + "First name cannot be empty": "Vorname darf nicht leer sein", + "Last name cannot be empty": "Nachname darf nicht leer sein", + "Email cannot be empty": "E-Mail darf nicht leer sein", + "Email is not valid": "E-Mail ist ungültig", + "Password cannot be empty": "Passwort darf nicht leer sein", + "Passwords must match": "Passwörter müssen übereinstimmen", + "Password must be at least %d characters": "Passwort muss mindestens %d Zeichen haben", + "Password must include an uppercase letter": "Passwort muss einen Großbuchstaben enthalten", + "Password must include a lowercase letter": "Passwort muss einen Kleinbuchstaben enthalten", + "Password must include a number": "Passwort muss eine Zahl enthalten", + "Password must include a symbol": "Passwort muss ein Sonderzeichen enthalten", + "Password must not contain your email": "Passwort darf deine E-Mail nicht enthalten", + "Email is already taken": "E-Mail ist bereits vergeben", + "User can not be registered": "Benutzer kann nicht registriert werden", + "Language not supported": "Sprache wird nicht unterstützt", + "Language": "Sprache", + "Sales": "Vertrieb", + "Sales rooms": "Verkaufsräume", + "Sales processes": "Verkaufsprozesse", + "Master data": "Stammdaten", + "Properties": "Objekte", + "Units": "Einheiten", + "Organization": "Organisation", + "Users & permissions": "Benutzer & Rechte", + "Roles & permissions": "Rollen & Rechte", + "Permissions": "Berechtigungen", + "Permission key": "Berechtigungs-Schlüssel", + "Create permission": "Berechtigung erstellen", + "Edit permission": "Berechtigung bearbeiten", + "Delete this permission?": "Diese Berechtigung löschen?", + "Permission created": "Berechtigung erstellt", + "Permission updated": "Berechtigung aktualisiert", + "Permission deleted": "Berechtigung gelöscht", + "Permission not found": "Berechtigung nicht gefunden", + "Permission can not be created": "Berechtigung kann nicht erstellt werden", + "Permission can not be updated": "Berechtigung kann nicht aktualisiert werden", + "Permission key cannot be empty": "Berechtigungs-Schlüssel darf nicht leer sein", + "Permission key is invalid": "Berechtigungs-Schlüssel ist ungültig", + "Permission key already exists": "Berechtigungs-Schlüssel existiert bereits", + "Assigned permissions": "Zugewiesene Berechtigungen", + "Select permissions": "Berechtigungen auswählen", + "Permission denied": "Keine Berechtigung", + "No active tenant assigned": "Kein aktiver Mandant zugewiesen", + "Please select at least one tenant": "Bitte mindestens einen Mandanten auswählen", + "Effective permissions": "Effektive Berechtigungen", + "perm.users.view": "Benutzer anzeigen", + "perm.users.create": "Benutzer erstellen", + "perm.users.update": "Benutzer bearbeiten", + "perm.users.delete": "Benutzer löschen", + "perm.users.self_update": "Eigenen Benutzer bearbeiten", + "perm.users.update_assignments": "Benutzerzuweisungen bearbeiten", + "Permission origin": "Herkunft der Berechtigung", + "perm.tenants.view": "Mandanten anzeigen", + "perm.tenants.create": "Mandanten erstellen", + "perm.tenants.update": "Mandanten bearbeiten", + "perm.tenants.delete": "Mandanten löschen", + "perm.departments.view": "Abteilungen anzeigen", + "perm.departments.create": "Abteilungen erstellen", + "perm.departments.update": "Abteilungen bearbeiten", + "perm.departments.delete": "Abteilungen löschen", + "perm.roles.view": "Rollen anzeigen", + "perm.roles.create": "Rollen erstellen", + "perm.roles.update": "Rollen bearbeiten", + "perm.roles.delete": "Rollen löschen", + "perm.permissions.view": "Berechtigungen anzeigen", + "perm.permissions.create": "Berechtigungen erstellen", + "perm.permissions.update": "Berechtigungen bearbeiten", + "perm.permissions.delete": "Berechtigungen löschen", + "perm.settings.view": "Einstellungen anzeigen", + "perm.settings.update": "Einstellungen bearbeiten", + "perm.mail_log.view": "E-Mail-Protokolle anzeigen", + "Select row": "Zeile auswählen", + "Select all": "Alle auswählen", + "Activate": "Aktivieren", + "Deactivate": "Deaktivieren", + "Delete": "Löschen", + "Activate users?": "Benutzer aktivieren?", + "Deactivate users?": "Benutzer deaktivieren?", + "Delete users?": "Benutzer löschen?", + "Send access emails to selected users?": "Zugangsdaten an ausgewählte Benutzer senden?", + "%d users activated": "%d Benutzer aktiviert", + "%d users deactivated": "%d Benutzer deaktiviert", + "%d users deleted": "%d Benutzer gelöscht", + "Access emails sent to %d users": "Zugangsdaten an %d Benutzer gesendet", + "Access emails sent to %d users, %f failed": "Zugangsdaten an %d Benutzer gesendet, %f fehlgeschlagen", + "Failed to activate users": "Benutzer konnten nicht aktiviert werden", + "Failed to deactivate users": "Benutzer konnten nicht deaktiviert werden", + "Failed to delete users": "Benutzer konnten nicht gelöscht werden", + "Failed to send access emails": "Zugangsdaten konnten nicht gesendet werden", + "Read": "Gelesen", + "Home": "Home", + "Users": "Benutzer", + "All users": "Alle Benutzer", + "Avatar": "Avatar", + "Theme": "Theme", + "Light": "Hell", + "Dark": "Dunkel", + "Toggle theme": "Theme umschalten", + "Toggle Sidebar": "Sidebar umschalten", + "Toggle Detail Sidebar": "Detail-Seitenleiste umschalten", + "2FA Settings": "2FA Einstellungen", + "%d selected": "%d ausgewählt", + "Create user": "Benutzer anlegen", + "Add a new user account": "Neues Benutzerkonto anlegen", + "Edit user": "Benutzer bearbeiten", + "Update account details": "Kontodaten aktualisieren", + "Create": "Erstellen", + "Create & close": "Erstellen & schließen", + "Save": "Speichern", + "Save & close": "Speichern & schließen", + "Cancel": "Abbrechen", + "Active": "Aktiv", + "Actions": "Aktionen", + "ID": "ID", + "Created": "Erstellt", + "Created by": "Erstellt von", + "Modified by": "Bearbeitet von", + "Modified": "Aktualisiert", + "No users found": "Keine Benutzer gefunden", + "Yes": "Ja", + "No": "Nein", + "User created": "Benutzer erstellt", + "User updated": "Benutzer aktualisiert", + "User deleted": "Benutzer gelöscht", + "User activated": "Benutzer aktiviert", + "User deactivated": "Benutzer deaktiviert", + "User not found": "Benutzer nicht gefunden", + "User can not be updated": "Benutzer kann nicht aktualisiert werden", + "Avatar updated": "Profilbild aktualisiert", + "Avatar removed": "Profilbild entfernt", + "Upload failed": "Upload fehlgeschlagen", + "File is too large": "Datei ist zu groß", + "Invalid image file": "Ungültige Bilddatei", + "No file uploaded": "Keine Datei hochgeladen", + "You cannot delete your own account": "Du kannst deinen eigenen Account nicht löschen", + "Login required": "Login erforderlich", + "You cannot deactivate your own account": "Du kannst dein eigenes Konto nicht deaktivieren", + "Welcome back": "Willkommen zurück", + "Total users": "Benutzer gesamt", + "Open user management": "Benutzerverwaltung öffnen", + "New password (optional)": "Neues Passwort (optional)", + "TOTP secret": "TOTP-Secret", + "Account is inactive": "Konto ist deaktiviert", + "No account yet": "Noch kein Konto", + "Inactive": "Inaktiv", + "Filter": "Filter", + "UUID": "UUID", + "Profile image": "Profilbild", + "Profile": "Profil", + "Profile description": "Profilbeschreibung", + "About": "Über", + "Info": "Info", + "Search results": "Suchergebnisse", + "Result": "Ergebnis", + "Type": "Typ", + "Page": "Seite", + "Upload image": "Bild hochladen", + "Remove image": "Bild entfernen", + "App logo": "App-Logo", + "Upload logo": "Logo hochladen", + "Remove logo": "Logo entfernen", + "Logo updated": "Logo aktualisiert", + "Logo removed": "Logo entfernt", + "Allowed file types: SVG, PNG, JPG, WEBP": "Erlaubte Dateitypen: SVG, PNG, JPG, WEBP", + "Favicon": "Favicon", + "Upload favicon": "Favicon hochladen", + "Remove favicon": "Favicon entfernen", + "Favicon updated": "Favicon aktualisiert", + "Favicon removed": "Favicon entfernt", + "Allowed file types: PNG": "Erlaubte Dateitypen: PNG", + "Square images are recommended (icons are center-cropped).": "Quadratische Bilder empfohlen (Icons werden zentriert zugeschnitten).", + "Favicon preview": "Favicon Vorschau", + "Primary color": "Primärfarbe", + "Primary color is invalid": "Primärfarbe ist ungültig", + "Show filters": "Filter anzeigen", + "Hide filters": "Filter ausblenden", + "Export CSV": "CSV exportieren", + "Reset filters": "Filter zurücksetzen", + "State": "Status", + "Edit": "Bearbeiten", + "Status": "Status", + "All": "Alle", + "All people": "Alle Personen", + "Explorer": "Explorer", + "Search": "Suche", + "Search...": "Suchen...", + "Created from": "Erstellt von", + "Created to": "Erstellt bis", + "Files": "Dateien", + "Settings": "Einstellungen", + "System": "System", + "Statistics": "Statistiken", + "Organization breakdown": "Organisations-Aufschlüsselung", + "Department assignments": "Abteilungs-Zuweisungen", + "Assigned users (Active/Inactive)": "Benutzer zugewiesen (Aktiv/Inaktiv)", + "User status": "Benutzerstatus", + "Users & roles": "Benutzer & Rollen", + "Active users": "Aktive Benutzer", + "Inactive users": "Inaktive Benutzer", + "Total emails": "E-Mails gesamt", + "Sent emails": "Gesendete E-Mails", + "Failed emails": "Fehlgeschlagene E-Mails", + "Queued emails": "E-Mails in Warteschlange", + "Last 24 hours": "Letzte 24 Stunden", + "Total (24h)": "Gesamt (24h)", + "Sent (24h)": "Gesendet (24h)", + "Failed (24h)": "Fehlgeschlagen (24h)", + "Queued (24h)": "Warteschlange (24h)", + "Sidebar sections": "Sidebar-Bereiche", + "All files": "Alle Dateien", + "My files": "Meine Dateien", + "Shared with me": "Mit mir geteilt", + "Add": "Hinzufügen", + "Search options": "Suchoptionen", + "New folder": "Neuer Ordner", + "Toggle navigation": "Navigation umschalten", + "Primary navigation": "Hauptnavigation", + "Sort column ascending": "Spalte aufsteigend sortieren", + "Sort column descending": "Spalte absteigend sortieren", + "Previous": "Zurück", + "Next": "Weiter", + "Page %d of %d": "Seite %d von %d", + "Page %d": "Seite %d", + "Showing": "Zeige", + "of": "von", + "to": "bis", + "results": "Ergebnisse", + "Loading...": "Lade...", + "No records found": "Keine Einträge gefunden", + "An error happened while fetching the data": "Fehler beim Laden der Daten", + "Delete this user?": "Diesen Benutzer wirklich löschen?", + "Delete this tenant?": "Diesen Mandanten wirklich löschen?", + "Tenant": "Mandant", + "Default tenant": "Standard-Mandant", + "Default role": "Standard-Rolle", + "Default department": "Standard-Abteilung", + "Default": "Standard", + "Identity": "Stammdaten", + "Access": "Zugang", + "Access & password": "Zugang & Passwort", + "Security": "Sicherheit", + "Preferences": "Einstellungen", + "Assignments": "Zuweisungen", + "My account": "Meine Benutzereinstellungen", + "Defaults": "Standards", + "None": "Keine", + "Settings updated": "Einstellungen gespeichert", + "setting.default_tenant": "Standard-Mandant, wenn bei der Benutzeranlage keiner zugewiesen wird", + "setting.default_role": "Standard-Rolle, wenn bei der Benutzeranlage keine Rolle zugewiesen wird", + "setting.default_department": "Standard-Abteilung, wenn bei der Benutzeranlage keine Abteilung zugewiesen wird", + "setting.app_title": "App-Titel für den Browser-Tab", + "setting.app_locale": "Standard-Sprache, wenn keine Sprache gesetzt ist", + "Default language": "Standard-Sprache", + "setting.app_theme": "Standard-Theme, wenn der Benutzer keine Präferenz hat", + "setting.app_theme_user": "Benutzer dürfen ihr eigenes Theme wählen", + "setting.app_primary_color": "Überschreibt die Standard-Primärfarbe (Hex wie #2FA4A4)", + "Default theme": "Standard-Theme", + "Allow user theme": "Benutzer-Theme erlauben", + "General": "Allgemein", + "Appearance": "Darstellung", + "Use default color": "Standardfarbe verwenden", + "Tenants can override this color in their appearance settings.": "Mandanten können diese Farbe in ihren Darstellungseinstellungen überschreiben.", + "Using the default color applies the global system appearance.": "Standardfarbe verwenden heißt: Es werden die globalen Systemeinstellungen für die Darstellung genutzt.", + "Status & meta": "Status & Meta", + "Audit": "Audit", + "Overview": "Übersicht", + "Quick actions": "Schnellaktionen", + "Send email": "E-Mail senden", + "Call phone": "Telefon anrufen", + "Call mobile": "Mobil anrufen", + "Choose color": "Farbe wählen", + "Be careful - this resets the tenants color": "Achtung – das setzt die Mandantenfarbe zurück", + "Internationalization": "Internationalisierung", + "User creation rules": "Benutzeranlage-Regeln", + "Branding": "Branding", + "App title": "App-Titel", + "Tenants": "Mandanten", + "Create tenant": "Mandant anlegen", + "Edit tenant": "Mandant bearbeiten", + "Tenant created": "Mandant erstellt", + "Tenant updated": "Mandant aktualisiert", + "Tenant deleted": "Mandant gelöscht", + "Tenant not found": "Mandant nicht gefunden", + "Tenant can not be created": "Mandant kann nicht erstellt werden", + "Tenant can not be updated": "Mandant kann nicht aktualisiert werden", + "Tenant image": "Mandantenbild", + "Assigned tenants": "Zugewiesene Mandanten", + "Primary tenant": "Hauptmandant", + "Select primary tenant": "Hauptmandant auswählen", + "Please select a primary tenant": "Bitte einen Hauptmandanten auswählen", + "Primary tenant must be one of the assigned tenants": "Der Hauptmandant muss einem zugewiesenen Mandanten entsprechen", + "Structure": "Struktur", + "Departments & roles": "Abteilungen und Rollen", + "Department assignments cleaned: %d": "Abteilungszuweisungen bereinigt: %d", + "Select tenants": "Mandanten auswählen", + "Assigned roles": "Zugewiesene Rollen", + "Select roles": "Rollen auswählen", + "Department": "Abteilung", + "Departments": "Abteilungen", + "Create department": "Abteilung anlegen", + "Edit department": "Abteilung bearbeiten", + "Department created": "Abteilung erstellt", + "Department updated": "Abteilung aktualisiert", + "Department deleted": "Abteilung gelöscht", + "Department not found": "Abteilung nicht gefunden", + "Department can not be created": "Abteilung kann nicht erstellt werden", + "Department can not be updated": "Abteilung kann nicht aktualisiert werden", + "Delete this department?": "Diese Abteilung wirklich löschen?", + "Assigned departments": "Zugewiesene Abteilungen", + "Select departments": "Abteilungen auswählen", + "Role": "Rolle", + "Roles": "Rollen", + "Create role": "Rolle anlegen", + "Edit role": "Rolle bearbeiten", + "Role created": "Rolle erstellt", + "Role updated": "Rolle aktualisiert", + "Role deleted": "Rolle gelöscht", + "Role not found": "Rolle nicht gefunden", + "Role can not be created": "Rolle kann nicht erstellt werden", + "Role can not be updated": "Rolle kann nicht aktualisiert werden", + "Delete this role?": "Diese Rolle wirklich löschen?", + "Description": "Beschreibung", + "Description cannot be empty": "Beschreibung darf nicht leer sein", + "Status is invalid": "Status ist ungültig", + "Address": "Adresse", + "Postal code": "Postleitzahl", + "City": "Ort", + "Country": "Land", + "Location": "Standort", + "Mail logs": "E-Mail-Protokolle", + "Mail log": "E-Mail-Protokoll", + "View mail log": "E-Mail-Protokoll ansehen", + "Mail log not found": "E-Mail-Protokoll nicht gefunden", + "Email details": "E-Mail-Details", + "Recipient": "Empfänger", + "Subject": "Betreff", + "Template": "Vorlage", + "Sent": "Gesendet", + "Failed": "Fehlgeschlagen", + "Queued": "Warteschlange", + "Error message": "Fehlermeldung", + "Error information": "Fehlerinformationen", + "Provider message ID": "Provider-Nachrichten-ID", + "Sent at": "Gesendet am", + "Created at": "Erstellt am", + "Delivery information": "Versandinformationen", + "All statuses": "Alle Status", + "Contact": "Kontakt", + "Support": "Support", + "Support email": "Support-E-Mail", + "Support phone": "Support-Telefon", + "VAT ID": "USt-ID", + "Tax number": "Steuernummer", + "Billing email": "Rechnungs-E-Mail", + "Phone": "Telefon", + "Website": "Webseite", + "Accounting info": "Buchhaltungsinfo", + "Communication": "Kommunikation", + "Legal": "Rechtliches", + "Privacy URL": "Datenschutz-URL", + "Imprint URL": "Impressum-URL", + "Region": "Region", + "Fax": "Fax", + "Back to Login": "Zurück zum Login", + "Back": "Zurück", + "Access denied": "Zugriff verweigert", + "You do not have permission to view this content.": "Du hast keine Berechtigung, diesen Inhalt zu sehen.", + "If you believe this is an error, please contact your system administrator.": "Wenn du glaubst, dass dies ein Fehler ist, wende dich bitte an den Systemadministrator.", + "Requested URL": "Angeforderte URL", + "Audit": "Audit", + "Forward": "Vorwärts", + "Status changed": "Status geändert", + "Status changed by": "Status geändert von", + "Page": "Seite", + "Requested path": "Angeforderter Pfad", + "Page not found": "Seite nicht gefunden", + "The page you requested does not exist or has moved.": "Die angeforderte Seite existiert nicht oder wurde verschoben.", + "Page updated": "Seite aktualisiert", + "Page can not be updated": "Seite kann nicht aktualisiert werden", + "Content is invalid": "Inhalt ist ungültig", + "You are not allowed to edit this page": "Du darfst diese Seite nicht bearbeiten", + "Form expired, please try again": "Formular abgelaufen, bitte erneut versuchen", + "Start writing...": "Mit dem Schreiben beginnen...", + "View": "Ansicht", + "Heading": "Überschrift", + "Copy content to": "Inhalt kopieren nach", + "Existing content in the target language will be overwritten": "Bestehender Inhalt in der Zielsprache wird überschrieben", + "Content copied": "Inhalt kopiert", + "Target language required": "Zielsprache erforderlich", + "Source content not found": "Quellinhalt nicht gefunden", + "Employment": "Arbeitsverhältnis", + "Job title": "Position", + "Hire date": "Eintrittsdatum", + "German": "Deutsch", + "English": "Englisch", + "Switch tenant": "Mandant wechseln", + "Copy": "Kopieren", + "Registration successful! Please check your email for the verification code.": "Registrierung erfolgreich! Bitte prüfe deine E-Mails für den Bestätigungscode.", + "A new verification code has been sent.": "Ein neuer Bestätigungscode wurde gesendet.", + "Email already verified. Please login.": "E-Mail bereits bestätigt. Bitte einloggen.", + "If the email exists, a new verification code has been sent.": "Falls die E-Mail existiert, wurde ein neuer Bestätigungscode gesendet.", + "Email verified successfully! Please login.": "E-Mail erfolgreich bestätigt! Bitte einloggen.", + "Too many failed attempts. Please request a new code.": "Zu viele Fehlversuche. Bitte fordere einen neuen Code an.", + "Verify email": "E-Mail bestätigen", + "Enter the verification code we sent to your email to complete your registration.": "Gib den Bestätigungscode ein, den wir dir per E-Mail gesendet haben, um deine Registrierung abzuschließen.", + "Email verified": "E-Mail bestätigt", + "Not verified": "Nicht bestätigt", + "Verified": "Bestätigt", + "Email settings": "E-Mail Einstellungen", + "SMTP host": "SMTP Host", + "SMTP port": "SMTP Port", + "SMTP user": "SMTP Benutzer", + "SMTP password": "SMTP Passwort", + "SMTP security": "SMTP Sicherheit", + "SMTP from address": "Absender-Adresse", + "SMTP from name": "Absendername", + "Address book": "Adressbuch", + "All people": "Alle Personen", + "Mobile": "Mobil", + "Short dial": "Kurzwahl", + "setting.smtp_host": "SMTP-Serveradresse (z.B. smtp.example.com)", + "setting.smtp_port": "SMTP-Port (z.B. 587)", + "setting.smtp_user": "SMTP-Benutzername", + "setting.smtp_password": "Leer lassen, um das bestehende Passwort zu behalten", + "setting.smtp_secure": "Verschlüsselung für SMTP", + "setting.smtp_from": "Absender-E-Mail für ausgehende Nachrichten", + "setting.smtp_from_name": "Absendername für ausgehende Nachrichten" +} diff --git a/i18n/default_en.json b/i18n/default_en.json new file mode 100644 index 0000000..5b8ecdd --- /dev/null +++ b/i18n/default_en.json @@ -0,0 +1,481 @@ +{ + "Login": "Login", + "Register": "Register", + "Password": "Password", + "Forgot password": "Forgot password", + "Remember me": "Remember me", + "Clear login tokens": "Clear login tokens", + "Clear all login tokens for this user?": "Clear all login tokens for this user?", + "Login tokens cleared": "Login tokens cleared", + "Send access": "Send access", + "Send access email to this user?": "Send access email to this user?", + "Access email sent": "Access email sent", + "Access email failed": "Access email failed", + "Your access details": "Your access details", + "Reset password": "Reset password", + "Verification code": "Verification code", + "Verify code": "Verify code", + "Verify": "Verify", + "Send code": "Send code", + "Send code again": "Send code again", + "Enter your email address and we will send you a verification code.": "Enter your email address and we will send you a verification code.", + "Enter the verification code we sent to your email.": "Enter the verification code we sent to your email.", + "Please enter a valid email address": "Please enter a valid email address", + "Please enter the 6-digit code": "Please enter the 6-digit code", + "Invalid or expired code": "Invalid or expired code", + "Code verified": "Code verified", + "Password reset code": "Password reset code", + "If the email exists, a verification code has been sent.": "If the email exists, a verification code has been sent.", + "Choose a new password for your account.": "Choose a new password for your account.", + "Password updated": "Password updated", + "Password can not be updated": "Password can not be updated", + "Reset request not found": "Reset request not found", + "Reset request already used": "Reset request already used", + "Reset request expired": "Reset request expired", + "Username": "Username", + "Admin": "Admin", + "Logout": "Logout", + "Account": "Account", + "Please enter your credentials": "Please enter your credentials", + "Create your account": "Create your account", + "First name": "First name", + "Last name": "Last name", + "Email": "Email", + "Password (again)": "Password confirm", + "Password requirements": "Password requirements", + "At least %d characters": "At least %d characters", + "At least one uppercase letter": "At least one uppercase letter", + "At least one lowercase letter": "At least one lowercase letter", + "At least one number": "At least one number", + "At least one symbol": "At least one symbol", + "Must not contain your email": "Must not contain your email", + "Login successful": "Login successful", + "Email/password not valid": "Email or password is invalid", + "First name cannot be empty": "First name cannot be empty", + "Last name cannot be empty": "Last name cannot be empty", + "Email cannot be empty": "Email cannot be empty", + "Email is not valid": "Email is not valid", + "Password cannot be empty": "Password cannot be empty", + "Passwords must match": "Passwords must match", + "Password must be at least %d characters": "Password must be at least %d characters", + "Password must include an uppercase letter": "Password must include an uppercase letter", + "Password must include a lowercase letter": "Password must include a lowercase letter", + "Password must include a number": "Password must include a number", + "Password must include a symbol": "Password must include a symbol", + "Password must not contain your email": "Password must not contain your email", + "Email is already taken": "Email is already taken", + "User can not be registered": "User can not be registered", + "Language not supported": "Language not supported", + "Language": "Language", + "Sales": "Sales", + "Sales rooms": "Sales rooms", + "Sales processes": "Sales processes", + "Master data": "Master data", + "Properties": "Properties", + "Units": "Units", + "Organization": "Organization", + "Users & permissions": "Users & permissions", + "Roles & permissions": "Roles & permissions", + "Permissions": "Permissions", + "Permission key": "Permission key", + "Create permission": "Create permission", + "Edit permission": "Edit permission", + "Delete this permission?": "Delete this permission?", + "Permission created": "Permission created", + "Permission updated": "Permission updated", + "Permission deleted": "Permission deleted", + "Permission not found": "Permission not found", + "Permission can not be created": "Permission can not be created", + "Permission can not be updated": "Permission can not be updated", + "Permission key cannot be empty": "Permission key cannot be empty", + "Permission key is invalid": "Permission key is invalid", + "Permission key already exists": "Permission key already exists", + "Assigned permissions": "Assigned permissions", + "Select permissions": "Select permissions", + "Permission denied": "Permission denied", + "No active tenant assigned": "No active tenant assigned", + "Please select at least one tenant": "Please select at least one tenant", + "Effective permissions": "Effective permissions", + "perm.users.view": "View users", + "perm.users.create": "Create users", + "perm.users.update": "Update users", + "perm.users.delete": "Delete users", + "perm.users.self_update": "Update own user", + "perm.users.update_assignments": "Update user assignments", + "Permission origin": "Permission origin", + "perm.tenants.view": "View tenants", + "perm.tenants.create": "Create tenants", + "perm.tenants.update": "Update tenants", + "perm.tenants.delete": "Delete tenants", + "perm.departments.view": "View departments", + "perm.departments.create": "Create departments", + "perm.departments.update": "Update departments", + "perm.departments.delete": "Delete departments", + "perm.roles.view": "View roles", + "perm.roles.create": "Create roles", + "perm.roles.update": "Update roles", + "perm.roles.delete": "Delete roles", + "perm.permissions.view": "View permissions", + "perm.permissions.create": "Create permissions", + "perm.permissions.update": "Update permissions", + "perm.permissions.delete": "Delete permissions", + "perm.settings.view": "View settings", + "perm.settings.update": "Update settings", + "perm.mail_log.view": "View mail logs", + "Select row": "Select row", + "Select all": "Select all", + "Activate": "Activate", + "Deactivate": "Deactivate", + "Delete": "Delete", + "Activate users?": "Activate users?", + "Deactivate users?": "Deactivate users?", + "Delete users?": "Delete users?", + "Send access emails to selected users?": "Send access emails to selected users?", + "%d users activated": "%d users activated", + "%d users deactivated": "%d users deactivated", + "%d users deleted": "%d users deleted", + "Access emails sent to %d users": "Access emails sent to %d users", + "Access emails sent to %d users, %f failed": "Access emails sent to %d users, %f failed", + "Failed to activate users": "Failed to activate users", + "Failed to deactivate users": "Failed to deactivate users", + "Failed to delete users": "Failed to delete users", + "Failed to send access emails": "Failed to send access emails", + "Read": "Read", + "Home": "Home", + "Users": "Users", + "All users": "All users", + "Avatar": "Avatar", + "Theme": "Theme", + "Light": "Light", + "Dark": "Dark", + "Toggle theme": "Toggle theme", + "Toggle Sidebar": "Toggle Sidebar", + "Toggle Detail Sidebar": "Toggle Detail Sidebar", + "2FA Settings": "2FA Settings", + "%d selected": "%d selected", + "Create user": "Create user", + "Add a new user account": "Add a new user account", + "Edit user": "Edit user", + "Update account details": "Update account details", + "Create": "Create", + "Create & close": "Create & close", + "Save": "Save", + "Save & close": "Save & close", + "Cancel": "Cancel", + "Active": "Active", + "Actions": "Actions", + "ID": "ID", + "Created": "Created", + "Created by": "Created by", + "Modified by": "Modified by", + "Modified": "Modified", + "No users found": "No users found", + "Yes": "Yes", + "No": "No", + "User created": "User created", + "User updated": "User updated", + "User deleted": "User deleted", + "User activated": "User activated", + "User deactivated": "User deactivated", + "User not found": "User not found", + "User can not be updated": "User can not be updated", + "Avatar updated": "Profile image updated", + "Avatar removed": "Profile image removed", + "Upload failed": "Upload failed", + "File is too large": "File is too large", + "Invalid image file": "Invalid image file", + "No file uploaded": "No file uploaded", + "You cannot delete your own account": "You cannot delete your own account", + "Login required": "Login required", + "You cannot deactivate your own account": "You cannot deactivate your own account", + "Welcome back": "Welcome back", + "Total users": "Total users", + "Open user management": "Open user management", + "New password (optional)": "New password (optional)", + "TOTP secret": "TOTP secret", + "Account is inactive": "Account is inactive", + "No account yet": "No account yet", + "Inactive": "Inactive", + "Filter": "Filter", + "UUID": "UUID", + "Profile image": "Profile image", + "Profile": "Profile", + "Profile description": "Profile description", + "About": "About", + "Info": "Info", + "Search results": "Search results", + "Result": "Result", + "Type": "Type", + "Page": "Page", + "Upload image": "Upload image", + "Remove image": "Remove image", + "App logo": "App logo", + "Upload logo": "Upload logo", + "Remove logo": "Remove logo", + "Logo updated": "Logo updated", + "Logo removed": "Logo removed", + "Allowed file types: SVG, PNG, JPG, WEBP": "Allowed file types: SVG, PNG, JPG, WEBP", + "Favicon": "Favicon", + "Upload favicon": "Upload favicon", + "Remove favicon": "Remove favicon", + "Favicon updated": "Favicon updated", + "Favicon removed": "Favicon removed", + "Allowed file types: PNG": "Allowed file types: PNG", + "Square images are recommended (icons are center-cropped).": "Square images are recommended (icons are center-cropped).", + "Favicon preview": "Favicon preview", + "Primary color": "Primary color", + "Primary color is invalid": "Primary color is invalid", + "Show filters": "Show filters", + "Hide filters": "Hide filters", + "Export CSV": "Export CSV", + "Reset filters": "Reset filters", + "State": "State", + "Edit": "Edit", + "Status": "Status", + "All": "All", + "All people": "All people", + "Explorer": "Explorer", + "Search": "Search", + "Search...": "Search...", + "Created from": "Created from", + "Created to": "Created to", + "Files": "Files", + "Settings": "Settings", + "System": "System", + "Statistics": "Statistics", + "Organization breakdown": "Organization breakdown", + "Department assignments": "Department assignments", + "Assigned users (Active/Inactive)": "Assigned users (Active/Inactive)", + "User status": "User status", + "Users & roles": "Users & roles", + "Active users": "Active users", + "Inactive users": "Inactive users", + "Total emails": "Total emails", + "Sent emails": "Sent emails", + "Failed emails": "Failed emails", + "Queued emails": "Queued emails", + "Last 24 hours": "Last 24 hours", + "Total (24h)": "Total (24h)", + "Sent (24h)": "Sent (24h)", + "Failed (24h)": "Failed (24h)", + "Queued (24h)": "Queued (24h)", + "Sidebar sections": "Sidebar sections", + "All files": "All files", + "My files": "My files", + "Shared with me": "Shared with me", + "Add": "Add", + "Search options": "Search options", + "New folder": "New folder", + "Toggle navigation": "Toggle navigation", + "Primary navigation": "Primary navigation", + "Sort column ascending": "Sort column ascending", + "Sort column descending": "Sort column descending", + "Previous": "Previous", + "Next": "Next", + "Page %d of %d": "Page %d of %d", + "Page %d": "Page %d", + "Showing": "Showing", + "of": "of", + "to": "to", + "results": "results", + "Loading...": "Loading...", + "No records found": "No records found", + "An error happened while fetching the data": "An error happened while fetching the data", + "Delete this user?": "Delete this user?", + "Delete this tenant?": "Delete this tenant?", + "Tenant": "Tenant", + "Default tenant": "Default tenant", + "Default role": "Default role", + "Default department": "Default department", + "Default": "Default", + "Identity": "Identity", + "Access": "Access", + "Access & password": "Access & password", + "Security": "Security", + "Preferences": "Preferences", + "Assignments": "Assignments", + "My account": "My account", + "Defaults": "Defaults", + "None": "None", + "Settings updated": "Settings updated", + "setting.default_tenant": "Default tenant used when no tenant is assigned on user creation", + "setting.default_role": "Default role used when no role is assigned on user creation", + "setting.default_department": "Default department used when no department is assigned on user creation", + "setting.app_title": "App title shown in the browser title bar", + "setting.app_locale": "Default language used when no user or URL locale is set", + "Default language": "Default language", + "setting.app_theme": "Default theme used when a user has no theme preference", + "setting.app_theme_user": "Allow users to choose their own theme", + "setting.app_primary_color": "Overrides the default primary color (hex like #2FA4A4)", + "Default theme": "Default theme", + "Allow user theme": "Allow user theme", + "General": "General", + "Appearance": "Appearance", + "Use default color": "Use default color", + "Tenants can override this color in their appearance settings.": "Tenants can override this color in their appearance settings.", + "Using the default color applies the global system appearance.": "Using the default color applies the global system appearance.", + "Status & meta": "Status & meta", + "Audit": "Audit", + "Overview": "Overview", + "Quick actions": "Quick actions", + "Send email": "Send email", + "Call phone": "Call phone", + "Call mobile": "Call mobile", + "Choose color": "Choose color", + "Be careful - this resets the tenants color": "Be careful - this resets the tenant color", + "Internationalization": "Internationalization", + "User creation rules": "User creation rules", + "Branding": "Branding", + "App title": "App title", + "Tenants": "Tenants", + "Create tenant": "Create tenant", + "Edit tenant": "Edit tenant", + "Tenant created": "Tenant created", + "Tenant updated": "Tenant updated", + "Tenant deleted": "Tenant deleted", + "Tenant not found": "Tenant not found", + "Tenant can not be created": "Tenant can not be created", + "Tenant can not be updated": "Tenant can not be updated", + "Tenant image": "Tenant image", + "Assigned tenants": "Assigned tenants", + "Primary tenant": "Primary tenant", + "Select primary tenant": "Select primary tenant", + "Please select a primary tenant": "Please select a primary tenant", + "Primary tenant must be one of the assigned tenants": "Primary tenant must be one of the assigned tenants", + "Structure": "Structure", + "Departments & roles": "Departments & roles", + "Department assignments cleaned: %d": "Department assignments cleaned: %d", + "Select tenants": "Select tenants", + "Assigned roles": "Assigned roles", + "Select roles": "Select roles", + "Department": "Department", + "Departments": "Departments", + "Create department": "Create department", + "Edit department": "Edit department", + "Department created": "Department created", + "Department updated": "Department updated", + "Department deleted": "Department deleted", + "Department not found": "Department not found", + "Department can not be created": "Department can not be created", + "Department can not be updated": "Department can not be updated", + "Delete this department?": "Delete this department?", + "Assigned departments": "Assigned departments", + "Select departments": "Select departments", + "Role": "Role", + "Roles": "Roles", + "Create role": "Create role", + "Edit role": "Edit role", + "Role created": "Role created", + "Role updated": "Role updated", + "Role deleted": "Role deleted", + "Role not found": "Role not found", + "Role can not be created": "Role can not be created", + "Role can not be updated": "Role can not be updated", + "Delete this role?": "Delete this role?", + "Description": "Description", + "Description cannot be empty": "Description cannot be empty", + "Status is invalid": "Status is invalid", + "Address": "Address", + "Postal code": "Postal code", + "City": "City", + "Country": "Country", + "Location": "Location", + "Mail logs": "Mail logs", + "Mail log": "Mail log", + "View mail log": "View mail log", + "Mail log not found": "Mail log not found", + "Email details": "Email details", + "Recipient": "Recipient", + "Subject": "Subject", + "Template": "Template", + "Sent": "Sent", + "Failed": "Failed", + "Queued": "Queued", + "Error message": "Error message", + "Error information": "Error information", + "Provider message ID": "Provider message ID", + "Sent at": "Sent at", + "Created at": "Created at", + "Delivery information": "Delivery information", + "All statuses": "All statuses", + "Contact": "Contact", + "Support": "Support", + "Support email": "Support email", + "Support phone": "Support phone", + "VAT ID": "VAT ID", + "Tax number": "Tax number", + "Billing email": "Billing email", + "Phone": "Phone", + "Website": "Website", + "Accounting info": "Accounting info", + "Communication": "Communication", + "Legal": "Legal", + "Privacy URL": "Privacy URL", + "Imprint URL": "Imprint URL", + "Region": "Region", + "Fax": "Fax", + "Back to Login": "Back to Login", + "Back": "Back", + "Access denied": "Access denied", + "You do not have permission to view this content.": "You do not have permission to view this content.", + "If you believe this is an error, please contact your system administrator.": "If you believe this is an error, please contact your system administrator.", + "Requested URL": "Requested URL", + "Audit": "Audit", + "Forward": "Forward", + "Status changed": "Status changed", + "Status changed by": "Status changed by", + "Page": "Page", + "Requested path": "Requested path", + "Page not found": "Page not found", + "The page you requested does not exist or has moved.": "The page you requested does not exist or has moved.", + "Page updated": "Page updated", + "Page can not be updated": "Page can not be updated", + "Content is invalid": "Content is invalid", + "You are not allowed to edit this page": "You are not allowed to edit this page", + "Form expired, please try again": "Form expired, please try again", + "Start writing...": "Start writing...", + "View": "View", + "Heading": "Heading", + "Copy content to": "Copy content to", + "Existing content in the target language will be overwritten": "Existing content in the target language will be overwritten", + "Content copied": "Content copied", + "Target language required": "Target language required", + "Source content not found": "Source content not found", + "Employment": "Employment", + "Job title": "Job title", + "Hire date": "Hire date", + "German": "German", + "English": "English", + "Switch tenant": "Switch tenant", + "Copy": "Copy", + "Registration successful! Please check your email for the verification code.": "Registration successful! Please check your email for the verification code.", + "A new verification code has been sent.": "A new verification code has been sent.", + "Email already verified. Please login.": "Email already verified. Please login.", + "If the email exists, a new verification code has been sent.": "If the email exists, a new verification code has been sent.", + "Email verified successfully! Please login.": "Email verified successfully! Please login.", + "Too many failed attempts. Please request a new code.": "Too many failed attempts. Please request a new code.", + "Verify email": "Verify email", + "Enter the verification code we sent to your email to complete your registration.": "Enter the verification code we sent to your email to complete your registration.", + "Email verified": "Email verified", + "Not verified": "Not verified", + "Verified": "Verified", + "Email settings": "Email settings", + "SMTP host": "SMTP host", + "SMTP port": "SMTP port", + "SMTP user": "SMTP user", + "SMTP password": "SMTP password", + "SMTP security": "SMTP security", + "SMTP from address": "SMTP from address", + "SMTP from name": "SMTP from name", + "Address book": "Address book", + "All people": "All people", + "Mobile": "Mobile", + "Short dial": "Short dial", + "setting.smtp_host": "SMTP server host (e.g. smtp.example.com)", + "setting.smtp_port": "SMTP port (e.g. 587)", + "setting.smtp_user": "SMTP username", + "setting.smtp_password": "Leave empty to keep the existing password", + "setting.smtp_secure": "SMTP encryption method", + "setting.smtp_from": "From address for outgoing emails", + "setting.smtp_from_name": "From name for outgoing emails" +} diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 0000000..1453188 Binary files /dev/null and b/lib/.DS_Store differ diff --git a/lib/Http/AccessControl.php b/lib/Http/AccessControl.php new file mode 100644 index 0000000..26ff5cc --- /dev/null +++ b/lib/Http/AccessControl.php @@ -0,0 +1,96 @@ +configuredPublicPaths = $publicPaths ?? (defined('APP_PUBLIC_PATHS') ? APP_PUBLIC_PATHS : []); + } + + /** + * Check if a path is publicly accessible (no authentication required). + */ + public function isPublicPath(string $path): bool + { + $normalizedPath = $this->normalizePath($path); + + // Root path check + if ($normalizedPath === '/') { + return in_array('/', $this->configuredPublicPaths, true); + } + + // Check configured public paths + if (in_array($normalizedPath, $this->configuredPublicPaths, true)) { + return true; + } + + // Check page/ prefix with slug lookup + if (str_starts_with($normalizedPath, 'page/')) { + $pageSlug = substr($normalizedPath, strlen('page/')); + if (in_array($pageSlug, $this->configuredPublicPaths, true)) { + return true; + } + } + + // Check always-public paths + if (in_array($normalizedPath, self::ALWAYS_PUBLIC_PATHS, true)) { + return true; + } + + // Check always-public prefixes + foreach (self::ALWAYS_PUBLIC_PREFIXES as $prefix) { + if (str_starts_with($normalizedPath, $prefix)) { + return true; + } + } + + return false; + } + + /** + * Redirect to login if the path requires authentication and user is not logged in. + * + * @return bool True if access is allowed, false if redirected + */ + public function requireAuthOrRedirect(string $path, bool $isLoggedIn): bool + { + if ($this->isPublicPath($path) || $isLoggedIn) { + return true; + } + + $locale = I18n::$locale ?? I18n::$defaultLocale; + Router::redirect(Request::withLocale('login', $locale)); + + return false; + } + + private function normalizePath(string $path): string + { + $normalized = trim($path, '/'); + return $normalized === '' ? '/' : $normalized; + } +} diff --git a/lib/Http/AssetDetector.php b/lib/Http/AssetDetector.php new file mode 100644 index 0000000..f6db33b --- /dev/null +++ b/lib/Http/AssetDetector.php @@ -0,0 +1,56 @@ +defaultLocale = $defaultLocale ?? I18n::$defaultLocale; + $this->availableLocales = $availableLocales ?? (defined('APP_LOCALES') ? APP_LOCALES : [$this->defaultLocale]); + $this->basePath = trim(parse_url(Router::getBaseUrl(), PHP_URL_PATH) ?: '/', '/'); + } + + /** + * Parse a request URI and extract locale information. + * + * @return array{ + * locale: string, + * pathWithoutLocale: string, + * query: string, + * hadLocaleInUrl: bool, + * hadInvalidLocale: bool + * } + */ + public function parseUri(string $uri): array + { + $path = parse_url($uri, PHP_URL_PATH) ?: ''; + $query = parse_url($uri, PHP_URL_QUERY) ?: ''; + + $relativePath = $this->stripBasePath($path); + $segments = $relativePath === '' ? [] : explode('/', $relativePath); + + $localeCandidate = $segments[0] ?? ''; + $hadLocaleInUrl = false; + $hadInvalidLocale = false; + $locale = ''; + + if ($localeCandidate !== '' && in_array($localeCandidate, $this->availableLocales, true)) { + $locale = array_shift($segments); + $hadLocaleInUrl = true; + } elseif ($localeCandidate !== '' && $this->looksLikeLocale($localeCandidate)) { + array_shift($segments); + $hadInvalidLocale = true; + } + + return [ + 'locale' => $locale, + 'pathWithoutLocale' => implode('/', $segments), + 'query' => $query, + 'hadLocaleInUrl' => $hadLocaleInUrl, + 'hadInvalidLocale' => $hadInvalidLocale, + ]; + } + + /** + * Resolve the effective locale from multiple sources (priority order): + * 1. URL segment (explicit) + * 2. User session preference + * 3. Cookie preference + * 4. Default locale + */ + public function resolveLocale(string $urlLocale, ?string $userLocale = null, ?string $cookieLocale = null): string + { + if ($urlLocale !== '' && $this->isValidLocale($urlLocale)) { + return $urlLocale; + } + + if ($userLocale !== null && $userLocale !== '' && $this->isValidLocale($userLocale)) { + return $userLocale; + } + + if ($cookieLocale !== null && $cookieLocale !== '' && $this->isValidLocale($cookieLocale)) { + return $cookieLocale; + } + + return $this->defaultLocale; + } + + /** + * Build a new REQUEST_URI without the locale segment. + */ + public function buildUriWithoutLocale(string $pathWithoutLocale, string $query): string + { + $newPath = $this->basePath !== '' ? '/' . $this->basePath : ''; + + if ($pathWithoutLocale !== '') { + $newPath .= '/' . $pathWithoutLocale; + } elseif ($newPath === '') { + $newPath = '/'; + } + + return $newPath . ($query !== '' ? '?' . $query : ''); + } + + /** + * Check if a locale string is valid (exists in available locales). + */ + public function isValidLocale(string $locale): bool + { + return in_array($locale, $this->availableLocales, true); + } + + public function getAvailableLocales(): array + { + return $this->availableLocales; + } + + public function getDefaultLocale(): string + { + return $this->defaultLocale; + } + + private function stripBasePath(string $path): string + { + $relativePath = ltrim($path, '/'); + + if ($this->basePath !== '' && strpos($relativePath, $this->basePath . '/') === 0) { + return substr($relativePath, strlen($this->basePath) + 1); + } + + if ($this->basePath !== '' && $relativePath === $this->basePath) { + return ''; + } + + return $relativePath; + } + + private function looksLikeLocale(string $candidate): bool + { + return preg_match('/^[a-z]{2}(?:-[a-z]{2})?$/i', $candidate) === 1; + } +} diff --git a/lib/Http/Request.php b/lib/Http/Request.php new file mode 100644 index 0000000..818a8f7 --- /dev/null +++ b/lib/Http/Request.php @@ -0,0 +1,102 @@ + $id > 0); + if (!$tenantIds) { + return []; + } + $placeholders = implode(',', array_fill(0, count($tenantIds), '?')); + $rows = DB::select( + 'select departments.id, departments.uuid, departments.description, departments.created_by, departments.modified_by, departments.created, departments.modified ' . + 'from departments departments join tenant_departments td on td.department_id = departments.id ' . + 'where td.tenant_id in (' . $placeholders . ') ' . + 'group by departments.id, departments.uuid, departments.description, departments.created_by, departments.modified_by, departments.created, departments.modified ' . + 'order by departments.description asc', + ...array_map('strval', $tenantIds) + ); + return self::unwrapList($rows); + } + + public static function listByIds(array $departmentIds): array + { + $departmentIds = array_values(array_unique(array_map('intval', $departmentIds))); + $departmentIds = array_filter($departmentIds, static fn ($id) => $id > 0); + if (!$departmentIds) { + return []; + } + $placeholders = implode(',', array_fill(0, count($departmentIds), '?')); + $rows = DB::select( + 'select id, uuid, description, created_by, modified_by, created, modified from departments where id in (' . $placeholders . ') order by description asc', + ...array_map('strval', $departmentIds) + ); + return self::unwrapList($rows); + } + + public static function listPaged(array $options): array + { + $limit = (int) ($options['limit'] ?? 10); + if ($limit < 1) { + $limit = 10; + } elseif ($limit > 100) { + $limit = 100; + } + + $offset = (int) ($options['offset'] ?? 0); + if ($offset < 0) { + $offset = 0; + } + + $search = trim((string) ($options['search'] ?? '')); + $tenant = trim((string) ($options['tenant'] ?? '')); + $order = (string) ($options['order'] ?? 'id'); + $dir = strtolower((string) ($options['dir'] ?? 'desc')); + $allowedOrder = ['id', 'uuid', 'description', 'created', 'modified']; + if (!in_array($order, $allowedOrder, true)) { + $order = 'id'; + } + if (!in_array($dir, ['asc', 'desc'], true)) { + $dir = 'desc'; + } + + $where = []; + $params = []; + if ($search !== '') { + $like = '%' . $search . '%'; + $where[] = '(description like ? or uuid like ?)'; + $params[] = $like; + $params[] = $like; + } + if ($tenant !== '') { + $where[] = "exists (select 1 from tenant_departments td join tenants t on t.id = td.tenant_id and t.status = 'active' where td.department_id = departments.id and t.uuid = ?)"; + $params[] = $tenant; + } + if (!empty($options['tenantUserId'])) { + $tenantUserId = (int) $options['tenantUserId']; + if ($tenantUserId > 0) { + if (defined('TENANT_SCOPE_STRICT') && TENANT_SCOPE_STRICT) { + $where[] = 'exists (select 1 from tenant_departments td ' . + "join tenants t on t.id = td.tenant_id and t.status = 'active' " . + 'join user_tenants ut on ut.tenant_id = td.tenant_id ' . + 'where ut.user_id = ? and td.department_id = departments.id)'; + $params[] = (string) $tenantUserId; + } else { + $where[] = '(not exists (select 1 from tenant_departments td where td.department_id = departments.id) ' . + 'or exists (select 1 from tenant_departments td ' . + "join tenants t on t.id = td.tenant_id and t.status = 'active' " . + 'join user_tenants ut on ut.tenant_id = td.tenant_id ' . + 'where ut.user_id = ? and td.department_id = departments.id))'; + $params[] = (string) $tenantUserId; + } + } + } elseif (array_key_exists('tenantIds', $options)) { + $tenantIds = $options['tenantIds']; + $tenantIds = is_array($tenantIds) ? array_values(array_unique(array_map('intval', $tenantIds))) : []; + if ($tenantIds) { + if (defined('TENANT_SCOPE_STRICT') && TENANT_SCOPE_STRICT) { + $where[] = 'exists (select 1 from tenant_departments td where td.department_id = departments.id and td.tenant_id in (???))'; + $params[] = $tenantIds; + } else { + $where[] = '(not exists (select 1 from tenant_departments td where td.department_id = departments.id) ' . + 'or exists (select 1 from tenant_departments td where td.department_id = departments.id and td.tenant_id in (???)))'; + $params[] = $tenantIds; + } + } else { + if (defined('TENANT_SCOPE_STRICT') && TENANT_SCOPE_STRICT) { + $where[] = '1=0'; + } else { + $where[] = 'not exists (select 1 from tenant_departments td where td.department_id = departments.id)'; + } + } + } + + $whereSql = $where ? (' where ' . implode(' and ', $where)) : ''; + $count = DB::selectValue('select count(*) from departments' . $whereSql, ...$params); + $total = $count ? (int) $count : 0; + + $query = 'select id, uuid, description, created_by, modified_by, created, modified from departments' . + $whereSql . + sprintf(' order by `%s` %s limit ? offset ?', $order, $dir); + + $queryParams = array_merge($params, [(string) $limit, (string) $offset]); + $rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams)); + + $list = self::unwrapList($rows); + $ids = []; + foreach ($list as $department) { + if (isset($department['id'])) { + $ids[] = (int) $department['id']; + } + } + $tenantLabelsByDepartment = []; + if ($ids) { + $placeholders = implode(',', array_fill(0, count($ids), '?')); + $labelRows = DB::select( + "select td.department_id as department_id, t.description as description from tenant_departments td join tenants t on t.id = td.tenant_id and t.status = 'active' " . + 'where td.department_id in (' . $placeholders . ') order by t.description asc', + ...array_map('strval', $ids) + ); + $collectLabels = static function (array $rows): array { + $labelsByDepartment = []; + foreach ($rows as $row) { + $departmentId = 0; + $label = ''; + if (isset($row['td']) && is_array($row['td'])) { + $departmentId = (int) ($row['td']['department_id'] ?? 0); + } + if (isset($row['t']) && is_array($row['t'])) { + $label = (string) ($row['t']['description'] ?? ''); + } + if ($departmentId === 0 || $label === '') { + foreach ($row as $value) { + if (!is_array($value)) { + continue; + } + if ($departmentId === 0 && isset($value['department_id'])) { + $departmentId = (int) $value['department_id']; + } + if ($label === '' && isset($value['description'])) { + $label = (string) $value['description']; + } + } + } + if ($departmentId === 0 && isset($row['department_id'])) { + $departmentId = (int) $row['department_id']; + } + if ($label === '' && isset($row['description'])) { + $label = (string) $row['description']; + } + if ($departmentId > 0 && $label !== '') { + $labelsByDepartment[$departmentId] ??= []; + $labelsByDepartment[$departmentId][] = $label; + } + } + return $labelsByDepartment; + }; + $tenantLabelsByDepartment = $collectLabels($labelRows); + } + foreach ($list as &$department) { + $departmentId = (int) ($department['id'] ?? 0); + $department['tenant_labels'] = $tenantLabelsByDepartment[$departmentId] ?? []; + } + unset($department); + + return [ + 'total' => $total, + 'rows' => $list, + ]; + } + + public static function find(int $id): ?array + { + $row = DB::selectOne( + 'select id, uuid, description, created_by, modified_by, created, modified from departments where id = ? limit 1', + (string) $id + ); + return self::unwrap($row); + } + + public static function findByUuid(string $uuid): ?array + { + $row = DB::selectOne( + 'select id, uuid, description, created_by, modified_by, created, modified from departments where uuid = ? limit 1', + $uuid + ); + return self::unwrap($row); + } + + private static function uuidV4(): string + { + $data = random_bytes(16); + $data[6] = chr((ord($data[6]) & 0x0f) | 0x40); + $data[8] = chr((ord($data[8]) & 0x3f) | 0x80); + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); + } + + public static function create(array $data) + { + return DB::insert( + 'insert into departments (uuid, description, created_by, created) values (?,?,?,NOW())', + $data['uuid'] ?? self::uuidV4(), + $data['description'], + $data['created_by'] ?? null + ); + } + + public static function update(int $id, array $data): bool + { + $fields = [ + 'description' => $data['description'], + ]; + if (array_key_exists('modified_by', $data)) { + $fields['modified_by'] = $data['modified_by']; + } + + $setParts = []; + $params = []; + foreach ($fields as $field => $value) { + $setParts[] = sprintf('`%s` = ?', $field); + $params[] = $value; + } + $params[] = (string) $id; + + $query = 'update departments set ' . implode(', ', $setParts) . ' where id = ?'; + $result = DB::update($query, ...$params); + return $result !== false; + } + + public static function delete(int $id): bool + { + $result = DB::delete('delete from departments where id = ?', (string) $id); + return $result !== false; + } +} diff --git a/lib/Repository/EmailVerificationRepository.php b/lib/Repository/EmailVerificationRepository.php new file mode 100644 index 0000000..2edb68d --- /dev/null +++ b/lib/Repository/EmailVerificationRepository.php @@ -0,0 +1,71 @@ + UTC_TIMESTAMP() order by id desc limit 1', + (string) $userId + ); + if (!$row || !isset($row['email_verifications'])) { + return null; + } + return $row['email_verifications']; + } + + public static function findById(int $id): ?array + { + $row = DB::selectOne( + 'select id, user_id, code_hash, expires_at, attempts, used_at from email_verifications where id = ? limit 1', + (string) $id + ); + if (!$row || !isset($row['email_verifications'])) { + return null; + } + return $row['email_verifications']; + } + + public static function incrementAttempts(int $id): bool + { + $result = DB::update( + 'update email_verifications set attempts = attempts + 1 where id = ?', + (string) $id + ); + return $result !== false; + } + + public static function markUsed(int $id): bool + { + $result = DB::update( + 'update email_verifications set used_at = NOW() where id = ?', + (string) $id + ); + return $result !== false; + } +} diff --git a/lib/Repository/MailLogRepository.php b/lib/Repository/MailLogRepository.php new file mode 100644 index 0000000..409a235 --- /dev/null +++ b/lib/Repository/MailLogRepository.php @@ -0,0 +1,142 @@ + 100) { + $limit = 100; + } + + $offset = (int) ($options['offset'] ?? 0); + if ($offset < 0) { + $offset = 0; + } + + $search = trim((string) ($options['search'] ?? '')); + $order = (string) ($options['order'] ?? 'created_at'); + $dir = strtolower((string) ($options['dir'] ?? 'desc')); + $status = trim((string) ($options['status'] ?? '')); + $createdFrom = trim((string) ($options['created_from'] ?? '')); + $createdTo = trim((string) ($options['created_to'] ?? '')); + + $allowedOrder = ['id', 'created_at', 'sent_at', 'to_email', 'subject', 'status']; + if (!in_array($order, $allowedOrder, true)) { + $order = 'created_at'; + } + if (!in_array($dir, ['asc', 'desc'], true)) { + $dir = 'desc'; + } + + $where = []; + $params = []; + if ($search !== '') { + $like = '%' . $search . '%'; + $where[] = '(to_email like ? or subject like ? or template like ?)'; + $params[] = $like; + $params[] = $like; + $params[] = $like; + } + if ($status !== '') { + $where[] = 'status = ?'; + $params[] = $status; + } + if ($createdFrom !== '') { + $where[] = 'created_at >= ?'; + $params[] = $createdFrom . ' 00:00:00'; + } + if ($createdTo !== '') { + $where[] = 'created_at <= ?'; + $params[] = $createdTo . ' 23:59:59'; + } + + $whereSql = $where ? (' where ' . implode(' and ', $where)) : ''; + $count = DB::selectValue('select count(*) from mail_log' . $whereSql, ...$params); + $total = $count ? (int) $count : 0; + + $query = 'select id, to_email, subject, template, status, created_at, sent_at, error_message, provider_message_id from mail_log' . + $whereSql . + sprintf(' order by `%s` %s limit ? offset ?', $order, $dir); + + $queryParams = array_merge($params, [(string) $limit, (string) $offset]); + $rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams)); + + return [ + 'total' => $total, + 'rows' => self::unwrapList($rows), + ]; + } + + public static function find(int $id): ?array + { + $row = DB::selectOne( + 'select id, to_email, subject, template, status, created_at, sent_at, error_message, provider_message_id from mail_log where id = ? limit 1', + (string) $id + ); + return self::unwrap($row); + } +} diff --git a/lib/Repository/PageContentRepository.php b/lib/Repository/PageContentRepository.php new file mode 100644 index 0000000..a40fc41 --- /dev/null +++ b/lib/Repository/PageContentRepository.php @@ -0,0 +1,59 @@ + $data['content'] ?? null, + ]; + if (array_key_exists('modified_by', $data)) { + $fields['modified_by'] = $data['modified_by']; + } + + $setParts = []; + $params = []; + foreach ($fields as $field => $value) { + $setParts[] = sprintf('`%s` = ?', $field); + $params[] = $value; + } + $params[] = (string) $id; + + $query = 'update page_contents set ' . implode(', ', $setParts) . ', modified = NOW() where id = ?'; + $result = DB::update($query, ...$params); + return $result !== false; + } +} diff --git a/lib/Repository/PageRepository.php b/lib/Repository/PageRepository.php new file mode 100644 index 0000000..90061a5 --- /dev/null +++ b/lib/Repository/PageRepository.php @@ -0,0 +1,25 @@ + UTC_TIMESTAMP() order by id desc limit 1', + (string) $userId + ); + if (!$row || !isset($row['password_resets'])) { + return null; + } + return $row['password_resets']; + } + + public static function findById(int $id): ?array + { + $row = DB::selectOne( + 'select id, user_id, code_hash, expires_at, attempts, used_at from password_resets where id = ? limit 1', + (string) $id + ); + if (!$row || !isset($row['password_resets'])) { + return null; + } + return $row['password_resets']; + } + + public static function incrementAttempts(int $id): bool + { + $result = DB::update( + 'update password_resets set attempts = attempts + 1 where id = ?', + (string) $id + ); + return $result !== false; + } + + public static function markUsed(int $id): bool + { + $result = DB::update( + 'update password_resets set used_at = NOW() where id = ?', + (string) $id + ); + return $result !== false; + } +} diff --git a/lib/Repository/PermissionRepository.php b/lib/Repository/PermissionRepository.php new file mode 100644 index 0000000..403079d --- /dev/null +++ b/lib/Repository/PermissionRepository.php @@ -0,0 +1,104 @@ + '`key`', 'description' => 'description', 'created' => 'created']; + $orderBy = $allowedOrder[$order] ?? $allowedOrder['key']; + + $where = []; + $params = []; + if ($search !== '') { + $where[] = '(permissions.`key` like ? or permissions.description like ?)'; + $params[] = '%' . $search . '%'; + $params[] = '%' . $search . '%'; + } + $whereSql = $where ? ' where ' . implode(' and ', $where) : ''; + + $total = DB::selectValue('select count(*) from permissions' . $whereSql, ...$params); + $query = 'select id, `key`, description, created from permissions' . $whereSql . + ' order by ' . $orderBy . ' ' . $dir . ' limit ? offset ?'; + $queryParams = array_merge($params, [(string) $limit, (string) $offset]); + $rows = DB::select($query, ...$queryParams); + $list = []; + foreach ($rows as $row) { + $data = $row['permissions'] ?? $row; + if (is_array($data)) { + $list[] = $data; + } + } + return ['data' => $list, 'total' => (int) $total]; + } + + public static function find(int $id): ?array + { + $row = DB::selectOne('select id, `key`, description, created from permissions where id = ? limit 1', (string) $id); + $data = $row['permissions'] ?? $row; + return is_array($data) ? $data : null; + } + + public static function findByKey(string $key): ?array + { + $row = DB::selectOne('select id, `key`, description, created from permissions where `key` = ? limit 1', $key); + $data = $row['permissions'] ?? $row; + return is_array($data) ? $data : null; + } + + public static function create(array $data): ?int + { + $key = trim((string) ($data['key'] ?? '')); + $desc = trim((string) ($data['description'] ?? '')); + $result = DB::insert( + 'insert into permissions (`key`, description, created) values (?,?,NOW())', + $key, + $desc !== '' ? $desc : null + ); + return $result ? (int) $result : null; + } + + public static function update(int $id, array $data): bool + { + $key = trim((string) ($data['key'] ?? '')); + $desc = trim((string) ($data['description'] ?? '')); + $result = DB::update( + 'update permissions set `key` = ?, description = ? where id = ?', + $key, + $desc !== '' ? $desc : null, + (string) $id + ); + return $result !== false; + } + + public static function delete(int $id): bool + { + $result = DB::delete('delete from permissions where id = ?', (string) $id); + return (bool) $result; + } +} diff --git a/lib/Repository/RememberTokenRepository.php b/lib/Repository/RememberTokenRepository.php new file mode 100644 index 0000000..39a3240 --- /dev/null +++ b/lib/Repository/RememberTokenRepository.php @@ -0,0 +1,55 @@ + $key, + 'description' => $desc, + 'roles' => [], + ]; + } + $roleLabel = (string) (($rowRole['description'] ?? $rowRole['role'] ?? $row['role'] ?? '') ?? ''); + if ($roleLabel !== '') { + $permMap[$permId]['roles'][] = $roleLabel; + } + } + foreach ($permMap as &$item) { + $item['roles'] = array_values(array_unique($item['roles'] ?? [])); + } + unset($item); + return array_values($permMap); + } +} diff --git a/lib/Repository/RoleRepository.php b/lib/Repository/RoleRepository.php new file mode 100644 index 0000000..59153f3 --- /dev/null +++ b/lib/Repository/RoleRepository.php @@ -0,0 +1,170 @@ + 100) { + $limit = 100; + } + + $offset = (int) ($options['offset'] ?? 0); + if ($offset < 0) { + $offset = 0; + } + + $search = trim((string) ($options['search'] ?? '')); + $order = (string) ($options['order'] ?? 'id'); + $dir = strtolower((string) ($options['dir'] ?? 'desc')); + $allowedOrder = ['id', 'uuid', 'description', 'created', 'modified']; + if (!in_array($order, $allowedOrder, true)) { + $order = 'id'; + } + if (!in_array($dir, ['asc', 'desc'], true)) { + $dir = 'desc'; + } + + $where = []; + $params = []; + if ($search !== '') { + $like = '%' . $search . '%'; + $where[] = '(description like ? or uuid like ?)'; + $params[] = $like; + $params[] = $like; + } + + $whereSql = $where ? (' where ' . implode(' and ', $where)) : ''; + $count = DB::selectValue('select count(*) from roles' . $whereSql, ...$params); + $total = $count ? (int) $count : 0; + + $query = 'select id, uuid, description, created_by, modified_by, created, modified from roles' . + $whereSql . + sprintf(' order by `%s` %s limit ? offset ?', $order, $dir); + + $queryParams = array_merge($params, [(string) $limit, (string) $offset]); + $rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams)); + + return [ + 'total' => $total, + 'rows' => self::unwrapList($rows), + ]; + } + + public static function find(int $id): ?array + { + $row = DB::selectOne( + 'select id, uuid, description, created_by, modified_by, created, modified from roles where id = ? limit 1', + (string) $id + ); + return self::unwrap($row); + } + + public static function findByUuid(string $uuid): ?array + { + $row = DB::selectOne( + 'select id, uuid, description, created_by, modified_by, created, modified from roles where uuid = ? limit 1', + $uuid + ); + return self::unwrap($row); + } + + private static function uuidV4(): string + { + $data = random_bytes(16); + $data[6] = chr((ord($data[6]) & 0x0f) | 0x40); + $data[8] = chr((ord($data[8]) & 0x3f) | 0x80); + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); + } + + public static function create(array $data) + { + return DB::insert( + 'insert into roles (uuid, description, created_by, created) values (?,?,?,NOW())', + $data['uuid'] ?? self::uuidV4(), + $data['description'], + $data['created_by'] ?? null + ); + } + + public static function update(int $id, array $data): bool + { + $fields = [ + 'description' => $data['description'], + ]; + if (array_key_exists('modified_by', $data)) { + $fields['modified_by'] = $data['modified_by']; + } + + $setParts = []; + $params = []; + foreach ($fields as $field => $value) { + $setParts[] = sprintf('`%s` = ?', $field); + $params[] = $value; + } + $params[] = (string) $id; + + $query = 'update roles set ' . implode(', ', $setParts) . ' where id = ?'; + $result = DB::update($query, ...$params); + return $result !== false; + } + + public static function delete(int $id): bool + { + $result = DB::delete('delete from roles where id = ?', (string) $id); + return $result !== false; + } +} diff --git a/lib/Repository/SettingRepository.php b/lib/Repository/SettingRepository.php new file mode 100644 index 0000000..613b0a7 --- /dev/null +++ b/lib/Repository/SettingRepository.php @@ -0,0 +1,35 @@ + $id > 0); + if (!$tenantIds) { + return []; + } + $placeholders = implode(',', array_fill(0, count($tenantIds), '?')); + $rows = DB::select( + 'select department_id from tenant_departments where tenant_id in (' . $placeholders . ')', + ...array_map('strval', $tenantIds) + ); + if (!is_array($rows)) { + return []; + } + $ids = []; + foreach ($rows as $row) { + $data = $row['tenant_departments'] ?? $row; + if (is_array($data) && isset($data['department_id'])) { + $ids[] = (int) $data['department_id']; + } + } + return array_values(array_unique($ids)); + } + + public static function replaceForDepartment(int $departmentId, array $tenantIds): bool + { + DB::delete('delete from tenant_departments where department_id = ?', (string) $departmentId); + $tenantIds = array_values(array_unique(array_map('intval', $tenantIds))); + $tenantIds = array_filter($tenantIds, static fn ($id) => $id > 0); + if (!$tenantIds) { + return true; + } + + foreach ($tenantIds as $tenantId) { + DB::insert( + 'insert into tenant_departments (tenant_id, department_id, created) values (?,?,NOW())', + (string) $tenantId, + (string) $departmentId + ); + } + + return true; + } +} diff --git a/lib/Repository/TenantRepository.php b/lib/Repository/TenantRepository.php new file mode 100644 index 0000000..d0b49d7 --- /dev/null +++ b/lib/Repository/TenantRepository.php @@ -0,0 +1,249 @@ + 100) { + $limit = 100; + } + + $offset = (int) ($options['offset'] ?? 0); + if ($offset < 0) { + $offset = 0; + } + + $search = trim((string) ($options['search'] ?? '')); + $order = (string) ($options['order'] ?? 'id'); + $dir = strtolower((string) ($options['dir'] ?? 'desc')); + $allowedOrder = ['id', 'uuid', 'description', 'created', 'modified']; + if (!in_array($order, $allowedOrder, true)) { + $order = 'id'; + } + if (!in_array($dir, ['asc', 'desc'], true)) { + $dir = 'desc'; + } + + $where = []; + $params = []; + if ($search !== '') { + $like = '%' . $search . '%'; + $where[] = '(description like ? or uuid like ?)'; + $params[] = $like; + $params[] = $like; + } + if (!empty($options['tenantUserId'])) { + $tenantUserId = (int) $options['tenantUserId']; + if ($tenantUserId > 0) { + $where[] = 'exists (select 1 from user_tenants ut where ut.user_id = ? and ut.tenant_id = tenants.id)'; + $params[] = (string) $tenantUserId; + } + } + + $whereSql = $where ? (' where ' . implode(' and ', $where)) : ''; + $count = DB::selectValue('select count(*) from tenants' . $whereSql, ...$params); + $total = $count ? (int) $count : 0; + + $query = 'select id, uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, modified_by, created, modified from tenants' . + $whereSql . + sprintf(' order by `%s` %s limit ? offset ?', $order, $dir); + + $queryParams = array_merge($params, [(string) $limit, (string) $offset]); + $rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams)); + + return [ + 'total' => $total, + 'rows' => self::unwrapList($rows), + ]; + } + + public static function find(int $id): ?array + { + $row = DB::selectOne( + 'select id, uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, modified_by, created, modified from tenants where id = ? limit 1', + (string) $id + ); + return self::unwrap($row); + } + + public static function findByUuid(string $uuid): ?array + { + $row = DB::selectOne( + 'select id, uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, modified_by, created, modified from tenants where uuid = ? limit 1', + $uuid + ); + return self::unwrap($row); + } + + private static function uuidV4(): string + { + $data = random_bytes(16); + $data[6] = chr((ord($data[6]) & 0x0f) | 0x40); + $data[8] = chr((ord($data[8]) & 0x3f) | 0x80); + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); + } + + public static function create(array $data) + { + return DB::insert( + 'insert into tenants (uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, created) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())', + $data['uuid'] ?? self::uuidV4(), + $data['description'], + $data['address'] ?? null, + $data['postal_code'] ?? null, + $data['city'] ?? null, + $data['country'] ?? null, + $data['region'] ?? null, + $data['vat_id'] ?? null, + $data['tax_number'] ?? null, + $data['phone'] ?? null, + $data['fax'] ?? null, + $data['email'] ?? null, + $data['support_email'] ?? null, + $data['support_phone'] ?? null, + $data['billing_email'] ?? null, + $data['website'] ?? null, + $data['privacy_url'] ?? null, + $data['imprint_url'] ?? null, + $data['primary_color'] ?? null, + $data['status'] ?? 'active', + $data['status_changed_at'] ?? null, + $data['status_changed_by'] ?? null, + $data['created_by'] ?? null + ); + } + + public static function update(int $id, array $data): bool + { + $fields = [ + 'description' => $data['description'], + 'address' => $data['address'] ?? null, + 'postal_code' => $data['postal_code'] ?? null, + 'city' => $data['city'] ?? null, + 'country' => $data['country'] ?? null, + 'region' => $data['region'] ?? null, + 'vat_id' => $data['vat_id'] ?? null, + 'tax_number' => $data['tax_number'] ?? null, + 'phone' => $data['phone'] ?? null, + 'fax' => $data['fax'] ?? null, + 'email' => $data['email'] ?? null, + 'support_email' => $data['support_email'] ?? null, + 'support_phone' => $data['support_phone'] ?? null, + 'billing_email' => $data['billing_email'] ?? null, + 'website' => $data['website'] ?? null, + 'privacy_url' => $data['privacy_url'] ?? null, + 'imprint_url' => $data['imprint_url'] ?? null, + 'primary_color' => $data['primary_color'] ?? null, + 'status' => $data['status'] ?? 'active', + ]; + if (array_key_exists('modified_by', $data)) { + $fields['modified_by'] = $data['modified_by']; + } + if (array_key_exists('status_changed_at', $data)) { + $fields['status_changed_at'] = $data['status_changed_at']; + } + if (array_key_exists('status_changed_by', $data)) { + $fields['status_changed_by'] = $data['status_changed_by']; + } + + $setParts = []; + $params = []; + foreach ($fields as $field => $value) { + $setParts[] = sprintf('`%s` = ?', $field); + $params[] = $value; + } + $params[] = (string) $id; + + $query = 'update tenants set ' . implode(', ', $setParts) . ' where id = ?'; + $result = DB::update($query, ...$params); + return $result !== false; + } + + public static function delete(int $id): bool + { + $result = DB::delete('delete from tenants where id = ?', (string) $id); + return $result !== false; + } +} diff --git a/lib/Repository/UserDepartmentRepository.php b/lib/Repository/UserDepartmentRepository.php new file mode 100644 index 0000000..0087a5b --- /dev/null +++ b/lib/Repository/UserDepartmentRepository.php @@ -0,0 +1,54 @@ + $value) { + if ($key === 'users' || is_array($value)) { + continue; + } + $user[$key] = $value; + } + if (isset($row['pt']) && is_array($row['pt'])) { + $label = (string) ($row['pt']['description'] ?? ''); + if ($label !== '') { + $user['primary_tenant_label'] = $label; + } + } + $list[] = $user; + } + } + return $list; + } + + public static function list(): array + { + $rows = DB::select( + 'select id, uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, theme, primary_tenant_id, created_by, modified_by, created, modified, active from users order by id desc' + ); + return self::unwrapList($rows); + } + + public static function listPaged(array $options): array + { + $limit = (int) ($options['limit'] ?? 10); + if ($limit < 1) { + $limit = 10; + } elseif ($limit > 100) { + $limit = 100; + } + + $offset = (int) ($options['offset'] ?? 0); + if ($offset < 0) { + $offset = 0; + } + + $search = trim((string) ($options['search'] ?? '')); + $active = $options['active'] ?? null; + $createdFrom = trim((string) ($options['created_from'] ?? '')); + $createdTo = trim((string) ($options['created_to'] ?? '')); + $tenant = trim((string) ($options['tenant'] ?? '')); + $tenantUuids = array_filter(array_map('trim', explode(',', (string) ($options['tenants'] ?? '')))); + $roleIds = self::normalizeIdList($options['roles'] ?? []); + $departmentIds = self::normalizeIdList($options['departments'] ?? []); + $emailVerified = $options['email_verified'] ?? null; + $order = (string) ($options['order'] ?? 'id'); + $dir = strtolower((string) ($options['dir'] ?? 'desc')); + $allowedOrder = ['id', 'uuid', 'first_name', 'last_name', 'email', 'created', 'modified', 'active']; + if (!in_array($order, $allowedOrder, true)) { + $order = 'id'; + } + if (!in_array($dir, ['asc', 'desc'], true)) { + $dir = 'desc'; + } + + $where = []; + $params = []; + if ($search !== '') { + $like = '%' . $search . '%'; + $where[] = '(users.first_name like ? or users.last_name like ? or users.email like ?)'; + $params[] = $like; + $params[] = $like; + $params[] = $like; + } + + $activeFilter = null; + if ($active !== null && $active !== '' && $active !== 'all') { + $activeValue = strtolower((string) $active); + if (in_array($activeValue, ['1', 'true', 'active'], true)) { + $activeFilter = 1; + } elseif (in_array($activeValue, ['0', 'false', 'inactive'], true)) { + $activeFilter = 0; + } + } + if ($activeFilter !== null) { + $where[] = 'users.active = ?'; + $params[] = (string) $activeFilter; + } + + if ($createdFrom !== '') { + $where[] = 'users.created >= ?'; + $params[] = $createdFrom . ' 00:00:00'; + } + if ($createdTo !== '') { + $where[] = 'users.created <= ?'; + $params[] = $createdTo . ' 23:59:59'; + } + if ($tenantUuids) { + $where[] = 'exists (select 1 from user_tenants ut2 join tenants t2 on t2.id = ut2.tenant_id where ut2.user_id = users.id and t2.uuid in (???))'; + $params[] = array_values($tenantUuids); + } elseif ($tenant !== '') { + $where[] = 'exists (select 1 from user_tenants ut2 join tenants t2 on t2.id = ut2.tenant_id where ut2.user_id = users.id and t2.uuid = ?)'; + $params[] = $tenant; + } + if ($roleIds) { + $where[] = 'exists (select 1 from user_roles ur2 where ur2.user_id = users.id and ur2.role_id in (???))'; + $params[] = array_map('strval', $roleIds); + } + if ($departmentIds) { + $where[] = 'exists (select 1 from user_departments ud2 where ud2.user_id = users.id and ud2.department_id in (???))'; + $params[] = array_map('strval', $departmentIds); + } + if ($emailVerified !== null && $emailVerified !== '' && $emailVerified !== 'all') { + $emailVerifiedValue = strtolower((string) $emailVerified); + if (in_array($emailVerifiedValue, ['1', 'true', 'verified', 'yes'], true)) { + $where[] = 'users.email_verified_at is not null'; + } elseif (in_array($emailVerifiedValue, ['0', 'false', 'unverified', 'no'], true)) { + $where[] = 'users.email_verified_at is null'; + } + } + if (!empty($options['tenantUserId'])) { + $tenantUserId = (int) $options['tenantUserId']; + if ($tenantUserId > 0) { + if (defined('TENANT_SCOPE_STRICT') && TENANT_SCOPE_STRICT) { + $where[] = 'exists (select 1 from user_tenants ut join user_tenants ut2 on ut2.tenant_id = ut.tenant_id ' . + "join tenants t on t.id = ut.tenant_id and t.status = 'active' " . + 'where ut2.user_id = ? and ut.user_id = users.id)'; + $params[] = (string) $tenantUserId; + } else { + $where[] = '(not exists (select 1 from user_tenants ut where ut.user_id = users.id) ' . + 'or exists (select 1 from user_tenants ut join user_tenants ut2 on ut2.tenant_id = ut.tenant_id ' . + "join tenants t on t.id = ut.tenant_id and t.status = 'active' " . + 'where ut2.user_id = ? and ut.user_id = users.id))'; + $params[] = (string) $tenantUserId; + } + } + } + + $whereSql = $where ? (' where ' . implode(' and ', $where)) : ''; + $count = DB::selectValue('select count(*) from users' . $whereSql, ...$params); + $total = $count ? (int) $count : 0; + + $query = 'select users.id, users.uuid, users.first_name, users.last_name, users.email, users.profile_description, users.job_title, users.phone, users.mobile, users.short_dial, users.address, users.postal_code, users.city, users.country, users.region, users.hire_date, users.theme, users.primary_tenant_id, users.created_by, users.modified_by, users.created, users.modified, users.active, ' . + 'pt.description as primary_tenant_label ' . + "from users left join tenants pt on pt.id = users.primary_tenant_id and pt.status = 'active'" . + $whereSql . + sprintf(' order by `users`.`%s` %s limit ? offset ?', $order, $dir); + + $queryParams = array_merge($params, [(string) $limit, (string) $offset]); + $rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams)); + + $list = self::unwrapList($rows); + $labelRows = []; + $tenantMapByUser = []; + $roleLabelRows = []; + $departmentLabelRows = []; + + $ids = []; + foreach ($list as $user) { + if (isset($user['id'])) { + $ids[] = (int) $user['id']; + } + } + if ($ids) { + $scopeToActiveTenants = !empty($options['tenantUserId']); + $tenantLabelJoin = $scopeToActiveTenants + ? "join tenants t on t.id = ut.tenant_id and t.status = 'active' " + : 'join tenants t on t.id = ut.tenant_id '; + $placeholders = implode(',', array_fill(0, count($ids), '?')); + $labelRows = DB::select( + 'select ut.user_id as user_id, t.id as tenant_id, t.description as description from user_tenants ut ' . $tenantLabelJoin . + 'where ut.user_id in (' . $placeholders . ') order by t.description asc', + ...array_map('strval', $ids) + ); + $roleLabelRows = DB::select( + 'select ur.user_id as user_id, r.description as description from user_roles ur join roles r on r.id = ur.role_id ' . + 'where ur.user_id in (' . $placeholders . ') order by r.description asc', + ...array_map('strval', $ids) + ); + $departmentLabelRows = DB::select( + 'select ud.user_id as user_id, d.description as description from user_departments ud join departments d on d.id = ud.department_id ' . + 'where ud.user_id in (' . $placeholders . ') order by d.description asc', + ...array_map('strval', $ids) + ); + + $collectLabels = static function (array $rows, string $userKey, string $labelKey): array { + $labelsByUser = []; + foreach ($rows as $row) { + $userId = 0; + $label = ''; + if (isset($row[$userKey]) && is_array($row[$userKey])) { + $userId = (int) ($row[$userKey]['user_id'] ?? 0); + } + if (isset($row[$labelKey]) && is_array($row[$labelKey])) { + $label = (string) ($row[$labelKey]['description'] ?? ''); + } + if ($userId === 0 || $label === '') { + foreach ($row as $value) { + if (!is_array($value)) { + continue; + } + if ($userId === 0 && isset($value['user_id'])) { + $userId = (int) $value['user_id']; + } + if ($label === '' && isset($value['description'])) { + $label = (string) $value['description']; + } + } + } + if ($userId === 0 && isset($row['user_id'])) { + $userId = (int) $row['user_id']; + } + if ($label === '' && isset($row['description'])) { + $label = (string) $row['description']; + } + if ($userId > 0 && $label !== '') { + $labelsByUser[$userId] ??= []; + $labelsByUser[$userId][] = $label; + } + } + return $labelsByUser; + }; + + foreach ($labelRows as $row) { + $userId = 0; + $tenantId = 0; + $label = ''; + if (isset($row['ut']) && is_array($row['ut'])) { + $userId = (int) ($row['ut']['user_id'] ?? 0); + } + if (isset($row['t']) && is_array($row['t'])) { + $tenantId = (int) ($row['t']['tenant_id'] ?? 0); + $label = (string) ($row['t']['description'] ?? ''); + } + if ($userId === 0 || $tenantId === 0 || $label === '') { + foreach ($row as $value) { + if (!is_array($value)) { + continue; + } + if ($userId === 0 && isset($value['user_id'])) { + $userId = (int) $value['user_id']; + } + if ($tenantId === 0 && isset($value['tenant_id'])) { + $tenantId = (int) $value['tenant_id']; + } + if ($label === '' && isset($value['description'])) { + $label = (string) $value['description']; + } + } + } + if ($userId === 0 && isset($row['user_id'])) { + $userId = (int) $row['user_id']; + } + if ($tenantId === 0 && isset($row['tenant_id'])) { + $tenantId = (int) $row['tenant_id']; + } + if ($label === '' && isset($row['description'])) { + $label = (string) $row['description']; + } + if ($userId > 0 && $tenantId > 0 && $label !== '') { + $tenantMapByUser[$userId] ??= []; + $tenantMapByUser[$userId][$tenantId] = $label; + } + } + $tenantLabelsByUser = []; + foreach ($tenantMapByUser as $userId => $map) { + $tenantLabelsByUser[$userId] = array_values($map); + } + $roleLabelsByUser = $collectLabels($roleLabelRows, 'ur', 'r'); + $departmentLabelsByUser = $collectLabels($departmentLabelRows, 'ud', 'd'); + + foreach ($list as &$user) { + $userId = (int) ($user['id'] ?? 0); + $primaryId = (int) ($user['primary_tenant_id'] ?? 0); + $user['tenant_labels'] = $tenantLabelsByUser[$userId] ?? []; + if ($primaryId > 0 && isset($tenantMapByUser[$userId][$primaryId])) { + $user['primary_tenant_label'] = $tenantMapByUser[$userId][$primaryId]; + } + $user['role_labels'] = $roleLabelsByUser[$userId] ?? []; + $user['department_labels'] = $departmentLabelsByUser[$userId] ?? []; + } + unset($user); + } + + $result = [ + 'total' => $total, + 'rows' => $list, + ]; + return $result; + } + + public static function find(int $id): ?array + { + $row = DB::selectOne( + 'select id, uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, password, locale, totp_secret, theme, primary_tenant_id, created_by, modified_by, created, modified, active from users where id = ? limit 1', + (string) $id + ); + return self::unwrap($row); + } + + public static function findByUuid(string $uuid): ?array + { + $row = DB::selectOne( + 'select id, uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, password, locale, totp_secret, theme, primary_tenant_id, created_by, modified_by, created, modified, active, active_changed_at, active_changed_by from users where uuid = ? limit 1', + $uuid + ); + return self::unwrap($row); + } + + public static function findByEmail(string $email): ?array + { + $row = DB::selectOne( + 'select id, uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, email_verified_at, password, locale, totp_secret, theme, primary_tenant_id, created_by, modified_by, created, modified, active, active_changed_at, active_changed_by from users where email = ? limit 1', + $email + ); + return self::unwrap($row); + } + + private static function uuidV4(): string + { + $data = random_bytes(16); + $data[6] = chr((ord($data[6]) & 0x0f) | 0x40); + $data[8] = chr((ord($data[8]) & 0x3f) | 0x80); + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); + } + + public static function create(array $data) + { + $hash = password_hash($data['password'], PASSWORD_DEFAULT); + return DB::insert( + 'insert into users (uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, password, locale, totp_secret, theme, primary_tenant_id, current_tenant_id, created_by, created, active, active_changed_at, active_changed_by) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(),?,?,?)', + $data['uuid'] ?? self::uuidV4(), + $data['first_name'], + $data['last_name'], + $data['email'], + $data['profile_description'] ?? null, + $data['job_title'] ?? null, + $data['phone'] ?? null, + $data['mobile'] ?? null, + $data['short_dial'] ?? null, + $data['address'] ?? null, + $data['postal_code'] ?? null, + $data['city'] ?? null, + $data['country'] ?? null, + $data['region'] ?? null, + $data['hire_date'] ?? null, + $hash, + $data['locale'] ?? null, + $data['totp_secret'], + $data['theme'] ?? 'light', + $data['primary_tenant_id'] ?? null, + $data['current_tenant_id'] ?? $data['primary_tenant_id'] ?? null, + $data['created_by'] ?? null, + $data['active'], + $data['active_changed_at'] ?? null, + $data['active_changed_by'] ?? null + ); + } + + public static function update(int $id, array $data): bool + { + $fields = [ + 'first_name' => $data['first_name'], + 'last_name' => $data['last_name'], + 'email' => $data['email'], + 'profile_description' => $data['profile_description'] ?? null, + 'job_title' => $data['job_title'] ?? null, + 'phone' => $data['phone'] ?? null, + 'mobile' => $data['mobile'] ?? null, + 'short_dial' => $data['short_dial'] ?? null, + 'address' => $data['address'] ?? null, + 'postal_code' => $data['postal_code'] ?? null, + 'city' => $data['city'] ?? null, + 'country' => $data['country'] ?? null, + 'region' => $data['region'] ?? null, + 'hire_date' => $data['hire_date'] ?? null, + 'totp_secret' => $data['totp_secret'], + 'active' => $data['active'], + ]; + if (array_key_exists('locale', $data)) { + $fields['locale'] = $data['locale']; + } + if (array_key_exists('theme', $data)) { + $fields['theme'] = $data['theme']; + } + if (array_key_exists('primary_tenant_id', $data)) { + $fields['primary_tenant_id'] = $data['primary_tenant_id']; + } + if (array_key_exists('current_tenant_id', $data)) { + $fields['current_tenant_id'] = $data['current_tenant_id']; + } + if (array_key_exists('modified_by', $data)) { + $fields['modified_by'] = $data['modified_by']; + } + if (array_key_exists('active_changed_at', $data)) { + $fields['active_changed_at'] = $data['active_changed_at']; + } + if (array_key_exists('active_changed_by', $data)) { + $fields['active_changed_by'] = $data['active_changed_by']; + } + + if (!empty($data['password'])) { + $fields['password'] = password_hash($data['password'], PASSWORD_DEFAULT); + } + + $setParts = []; + $params = []; + foreach ($fields as $field => $value) { + $setParts[] = sprintf('`%s` = ?', $field); + $params[] = $value; + } + $params[] = (string) $id; + + $query = 'update users set ' . implode(', ', $setParts) . ' where id = ?'; + $result = DB::update($query, ...$params); + return $result !== false; + } + + public static function setActive(int $id, bool $active, ?int $changedBy = null): bool + { + $result = DB::update( + 'update users set active = ?, active_changed_at = NOW(), active_changed_by = ? where id = ?', + $active ? 1 : 0, + $changedBy, + (string) $id + ); + return $result !== false; + } + + public static function setCurrentTenant(int $id, int $tenantId): bool + { + $result = DB::update( + 'update users set current_tenant_id = ? where id = ?', + (string) $tenantId, + (string) $id + ); + return $result !== false; + } + + public static function setActiveByUuids(array $uuids, bool $active, ?int $modifiedBy = null): bool + { + $uuids = array_values(array_filter(array_map('trim', $uuids))); + if (!$uuids) { + return false; + } + $placeholders = implode(',', array_fill(0, count($uuids), '?')); + $query = "update users set active = ?, modified_by = ?, active_changed_at = NOW(), active_changed_by = ? where uuid in ($placeholders)"; + $params = array_merge([$active ? 1 : 0, $modifiedBy, $modifiedBy], $uuids); + $result = DB::update($query, ...$params); + return $result !== false; + } + + public static function setLocale(int $id, string $locale): bool + { + $result = DB::update('update users set locale = ? where id = ?', $locale, (string) $id); + return $result !== false; + } + + public static function setTheme(int $id, string $theme): bool + { + $result = DB::update('update users set theme = ? where id = ?', $theme, (string) $id); + return $result !== false; + } + + public static function setPassword(int $id, string $password): bool + { + $hash = password_hash($password, PASSWORD_DEFAULT); + $result = DB::update('update users set password = ?, modified_by = NULL where id = ?', $hash, (string) $id); + return $result !== false; + } + + public static function setEmailVerified(int $id): bool + { + $result = DB::update('update users set email_verified_at = NOW() where id = ?', (string) $id); + return $result !== false; + } + + public static function delete(int $id): bool + { + $result = DB::delete('delete from users where id = ?', (string) $id); + return $result !== false; + } + + public static function deleteByUuids(array $uuids): bool + { + $uuids = array_values(array_filter(array_map('trim', $uuids))); + if (!$uuids) { + return false; + } + $placeholders = implode(',', array_fill(0, count($uuids), '?')); + $query = "delete from users where uuid in ($placeholders)"; + $result = DB::delete($query, ...$uuids); + return $result !== false; + } + + private static function normalizeIdList($value): array + { + if (is_string($value)) { + $value = array_filter(array_map('trim', explode(',', $value))); + } elseif (!is_array($value)) { + return []; + } + $ids = []; + foreach ($value as $item) { + if ($item === '' || $item === null) { + continue; + } + $ids[] = (int) $item; + } + $ids = array_values(array_filter(array_unique($ids), static function ($id) { + return $id > 0; + })); + return $ids; + } +} diff --git a/lib/Repository/UserRoleRepository.php b/lib/Repository/UserRoleRepository.php new file mode 100644 index 0000000..fd8c9eb --- /dev/null +++ b/lib/Repository/UserRoleRepository.php @@ -0,0 +1,42 @@ + false, + 'message' => 'Please verify your email first', + 'flash_key' => 'login_not_verified', + 'needs_verification' => true, + 'email' => $email, + ]; + } + + $user = Auth::login($email, $password); + + if (!$user) { + return [ + 'ok' => false, + 'message' => 'Email/password not valid', + 'flash_key' => 'login_invalid', + ]; + } + + if (!($_SESSION['user']['active'] ?? 1)) { + Auth::logout(); + return [ + 'ok' => false, + 'message' => 'Account is inactive', + 'flash_key' => 'login_inactive', + ]; + } + + $userId = (int) ($_SESSION['user']['id'] ?? 0); + if ($userId > 0) { + PermissionService::getUserPermissions($userId, true); + self::loadTenantDataIntoSession($userId); + if (!empty($_SESSION['no_active_tenant'])) { + Auth::logout(); + return [ + 'ok' => false, + 'message' => 'No active tenant assigned', + 'flash_key' => 'login_no_active_tenant', + ]; + } + } + + return ['ok' => true]; + } + + public static function loginAndRedirect( + string $email, + string $password, + string $successTarget = 'admin', + string $failTarget = 'login', + bool $remember = false + ): void { + $result = self::login($email, $password); + + if (!($result['ok'] ?? false)) { + // Check if user needs to verify email + if (!empty($result['needs_verification'])) { + $_SESSION['email_verification_email'] = $result['email'] ?? $email; + Flash::error(t('Please verify your email first'), 'verify-email', 'login_not_verified'); + Router::redirect('verify-email'); + } + + $message = $result['message'] ?? 'Email/password not valid'; + $key = $result['flash_key'] ?? 'login_invalid'; + Flash::error($message, $failTarget, $key); + Router::redirect($failTarget); + } + + if ($remember) { + $userId = (int) ($_SESSION['user']['id'] ?? 0); + if ($userId > 0) { + RememberMeService::rememberUser($userId); + } + } + Router::redirect($successTarget); + } + + public static function register(array $data): array + { + $email = trim((string) ($data['email'] ?? '')); + $result = UserService::register($data); + if (!($result['ok'] ?? false)) { + return $result; + } + + // Get the created user to send verification email + $createdUser = UserRepository::findByEmail($email); + if ($createdUser && isset($createdUser['id'])) { + $userId = (int) $createdUser['id']; + EmailVerificationService::sendVerification($userId); + } + + // Don't login - user needs to verify email first + return ['ok' => true, 'email' => $email, 'needs_verification' => true]; + } + + public static function logout(): void + { + RememberMeService::forgetCurrentUser(); + Auth::logout(); + } + + public static function logoutAndRedirect(string $target = 'login'): void + { + RememberMeService::forgetCurrentUser(); + Auth::logout(); + Router::redirect($target); + } + + public static function loadTenantDataIntoSession(int $userId): void + { + if ($userId <= 0) { + return; + } + + // Load available tenants first + $availableTenants = UserService::getAvailableTenants($userId); + $_SESSION['available_tenants'] = $availableTenants; + $_SESSION['available_departments_by_tenant'] = UserService::getAvailableDepartmentsByTenant($userId); + + $hasTenantAdminAccess = PermissionService::userHas($userId, PermissionService::TENANTS_UPDATE) + || PermissionService::userHas($userId, PermissionService::SETTINGS_UPDATE); + + if (!$availableTenants && !$hasTenantAdminAccess) { + $_SESSION['no_active_tenant'] = true; + unset($_SESSION['current_tenant']); + return; + } + $_SESSION['no_active_tenant'] = false; + + // Load current tenant (with fallback to first available) + $currentTenant = UserService::getCurrentTenant($userId); + if (!$currentTenant && !empty($availableTenants)) { + // Fallback: use first available tenant + $currentTenant = $availableTenants[0]; + } + if ($currentTenant) { + $_SESSION['current_tenant'] = $currentTenant; + } + } +} diff --git a/lib/Service/BrandingFaviconService.php b/lib/Service/BrandingFaviconService.php new file mode 100644 index 0000000..abc5430 --- /dev/null +++ b/lib/Service/BrandingFaviconService.php @@ -0,0 +1,238 @@ + 'favicon-16x16.png', + 32 => 'favicon-32x32.png', + 96 => 'favicon-96x96.png', + 180 => 'apple-touch-icon.png', + 192 => 'web-app-manifest-192x192.png', + 512 => 'web-app-manifest-512x512.png', + ]; + + public static function storageBase(): string + { + if (defined('APP_STORAGE_PATH') && APP_STORAGE_PATH) { + return rtrim(APP_STORAGE_PATH, '/'); + } + return rtrim(dirname(__DIR__, 2) . '/storage', '/'); + } + + public static function storageDir(): string + { + return self::storageBase() . '/branding/favicon'; + } + + public static function publicDir(): string + { + return rtrim(dirname(__DIR__, 2) . '/web/favicon', '/'); + } + + public static function hasFavicon(): bool + { + $path = self::publicDir() . '/favicon-32x32.png'; + return is_file($path); + } + + public static function delete(): void + { + $dir = self::storageDir(); + if (is_dir($dir)) { + $matches = glob($dir . '/*') ?: []; + foreach ($matches as $file) { + if (is_file($file)) { + @unlink($file); + } + } + } + $public = self::publicDir(); + foreach (self::SIZES as $file) { + $target = $public . '/' . $file; + if (is_file($target)) { + @unlink($target); + } + } + } + + public static function saveUpload(array $file): array + { + if (empty($file) || !isset($file['tmp_name'])) { + return ['ok' => false, 'error' => t('No file uploaded')]; + } + if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + if (($file['size'] ?? 0) > self::MAX_SIZE) { + return ['ok' => false, 'error' => t('File is too large')]; + } + + $tmpPath = $file['tmp_name']; + $mime = self::detectMime($tmpPath); + if ($mime !== 'image/png') { + return ['ok' => false, 'error' => t('Invalid image file')]; + } + if (!self::canResize()) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + $dir = self::storageDir(); + if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + self::delete(); + $originalPath = $dir . '/original.png'; + if (!move_uploaded_file($tmpPath, $originalPath)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + @chmod($originalPath, 0644); + + $publicDir = self::publicDir(); + if (!is_dir($publicDir) && !mkdir($publicDir, 0755, true) && !is_dir($publicDir)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + foreach (self::SIZES as $size => $fileName) { + $target = $publicDir . '/' . $fileName; + if (!self::resizeSquare($originalPath, $target, $size)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + @chmod($target, 0644); + } + + self::updateManifest(); + return ['ok' => true, 'path' => $originalPath, 'mime' => $mime]; + } + + public static function detectMime(string $path): string + { + if (function_exists('finfo_open')) { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + if ($finfo) { + $mime = finfo_file($finfo, $path); + if (is_string($mime) && $mime !== '') { + return $mime; + } + } + } + return 'application/octet-stream'; + } + + private static function canResize(): bool + { + return function_exists('imagecreatetruecolor') && function_exists('imagecopyresampled'); + } + + private static function loadImage(string $path) + { + if (function_exists('imagecreatefrompng')) { + return imagecreatefrompng($path); + } + return false; + } + + private static function resizeSquare(string $sourcePath, string $targetPath, int $size): bool + { + $src = self::loadImage($sourcePath); + if (!$src) { + return false; + } + $srcWidth = imagesx($src); + $srcHeight = imagesy($src); + if ($srcWidth === 0 || $srcHeight === 0) { + imagedestroy($src); + return false; + } + + $cropSize = min($srcWidth, $srcHeight); + $srcX = (int) round(($srcWidth - $cropSize) / 2); + $srcY = (int) round(($srcHeight - $cropSize) / 2); + + $dst = imagecreatetruecolor($size, $size); + imagealphablending($dst, false); + imagesavealpha($dst, true); + $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127); + imagefilledrectangle($dst, 0, 0, $size, $size, $transparent); + + imagecopyresampled( + $dst, + $src, + 0, + 0, + $srcX, + $srcY, + $size, + $size, + $cropSize, + $cropSize + ); + + $saved = false; + if (function_exists('imagepng')) { + $saved = imagepng($dst, $targetPath, 6); + } + + imagedestroy($src); + imagedestroy($dst); + + return $saved; + } + + private static function updateManifest(): void + { + $manifestPath = self::publicDir() . '/site.webmanifest'; + $data = []; + if (is_file($manifestPath)) { + $json = file_get_contents($manifestPath); + $decoded = json_decode((string) $json, true); + if (is_array($decoded)) { + $data = $decoded; + } + } + + $title = null; + if (class_exists('MintyPHP\\Service\\SettingService')) { + $title = \MintyPHP\Service\SettingService::getAppTitle(); + } + if ($title === null || $title === '') { + $title = defined('APP_NAME') ? APP_NAME : 'IMVS'; + } + + $data['name'] = $title; + $data['short_name'] = $title; + if (!isset($data['theme_color'])) { + $data['theme_color'] = '#ffffff'; + } + if (!isset($data['background_color'])) { + $data['background_color'] = '#ffffff'; + } + if (!isset($data['display'])) { + $data['display'] = 'standalone'; + } + if (!isset($data['icons']) || !is_array($data['icons'])) { + $data['icons'] = [ + [ + 'src' => '/favicon/web-app-manifest-192x192.png', + 'sizes' => '192x192', + 'type' => 'image/png', + 'purpose' => 'maskable', + ], + [ + 'src' => '/favicon/web-app-manifest-512x512.png', + 'sizes' => '512x512', + 'type' => 'image/png', + 'purpose' => 'maskable', + ], + ]; + } + + $encoded = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + if (is_string($encoded)) { + file_put_contents($manifestPath, $encoded . "\n"); + } + } +} diff --git a/lib/Service/BrandingLogoService.php b/lib/Service/BrandingLogoService.php new file mode 100644 index 0000000..a4b00fb --- /dev/null +++ b/lib/Service/BrandingLogoService.php @@ -0,0 +1,293 @@ + false, 'error' => t('No file uploaded')]; + } + if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + if (($file['size'] ?? 0) > self::MAX_SIZE) { + return ['ok' => false, 'error' => t('File is too large')]; + } + + $tmpPath = $file['tmp_name']; + $mime = self::detectMime($tmpPath); + $isSvg = self::isSvgUpload($mime, $tmpPath); + $ext = self::extensionForMime($mime, $isSvg); + if (!$ext) { + return ['ok' => false, 'error' => t('Invalid image file')]; + } + if ($isSvg && !self::isSafeSvg($tmpPath)) { + return ['ok' => false, 'error' => t('Invalid image file')]; + } + + $dir = self::brandingDir(); + if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + self::delete(); + $originalPath = $dir . '/original.' . $ext; + if (!move_uploaded_file($tmpPath, $originalPath)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + @chmod($originalPath, 0644); + + $variantExt = function_exists('imagewebp') ? 'webp' : 'jpg'; + if (!$isSvg && self::canResize()) { + foreach (self::SIZES as $size) { + $target = $dir . '/logo-' . $size . '.' . $variantExt; + self::resizeAndFit($originalPath, $target, $size, $size, $variantExt); + } + } + + return ['ok' => true, 'path' => $originalPath, 'mime' => $mime]; + } + + public static function detectMime(string $path): string + { + if (function_exists('finfo_open')) { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + if ($finfo) { + $mime = finfo_file($finfo, $path); + if (is_string($mime) && $mime !== '') { + if (self::isSvgUpload($mime, $path)) { + return 'image/svg+xml'; + } + return $mime; + } + } + } + if (self::isSvgUpload('application/octet-stream', $path)) { + return 'image/svg+xml'; + } + return 'application/octet-stream'; + } + + private static function extensionForMime(string $mime, bool $isSvg = false): ?string + { + if ($isSvg) { + return 'svg'; + } + $map = [ + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/webp' => 'webp', + ]; + return $map[$mime] ?? null; + } + + private static function isSvgUpload(string $mime, string $path): bool + { + if ($mime === 'image/svg+xml') { + return true; + } + $head = @file_get_contents($path, false, null, 0, 1024); + if (!is_string($head) || $head === '') { + return false; + } + return stripos($head, ' filemtime($a); + }); + return $matches[0] ?? null; + } + + private static function findOriginalPath(string $dir): ?string + { + $matches = glob($dir . '/original.*'); + if (!$matches) { + return null; + } + usort($matches, static function ($a, $b) { + return filemtime($b) <=> filemtime($a); + }); + return $matches[0] ?? null; + } + + private static function canResize(): bool + { + return function_exists('imagecreatetruecolor') && function_exists('imagecopyresampled'); + } + + private static function createImageResource(string $path, string $mime) + { + if ($mime === 'image/jpeg' && function_exists('imagecreatefromjpeg')) { + return imagecreatefromjpeg($path); + } + if ($mime === 'image/png' && function_exists('imagecreatefrompng')) { + return imagecreatefrompng($path); + } + if ($mime === 'image/webp' && function_exists('imagecreatefromwebp')) { + return imagecreatefromwebp($path); + } + return false; + } + + private static function resizeAndFit(string $sourcePath, string $targetPath, int $width, int $height, string $ext): bool + { + $mime = self::detectMime($sourcePath); + $src = self::createImageResource($sourcePath, $mime); + if (!$src) { + return false; + } + + $srcWidth = imagesx($src); + $srcHeight = imagesy($src); + if ($srcWidth === 0 || $srcHeight === 0) { + imagedestroy($src); + return false; + } + + $scale = min($width / $srcWidth, $height / $srcHeight); + $dstWidth = (int) round($srcWidth * $scale); + $dstHeight = (int) round($srcHeight * $scale); + if ($dstWidth < 1) $dstWidth = 1; + if ($dstHeight < 1) $dstHeight = 1; + + $dst = imagecreatetruecolor($dstWidth, $dstHeight); + if ($ext === 'png' || $ext === 'webp') { + imagealphablending($dst, false); + imagesavealpha($dst, true); + $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127); + imagefilledrectangle($dst, 0, 0, $dstWidth, $dstHeight, $transparent); + } + + imagecopyresampled( + $dst, + $src, + 0, + 0, + 0, + 0, + $dstWidth, + $dstHeight, + $srcWidth, + $srcHeight + ); + + $saved = false; + if ($ext === 'webp' && function_exists('imagewebp')) { + $saved = imagewebp($dst, $targetPath, 82); + } elseif ($ext === 'png' && function_exists('imagepng')) { + $saved = imagepng($dst, $targetPath, 6); + } else { + $saved = imagejpeg($dst, $targetPath, 85); + } + + imagedestroy($src); + imagedestroy($dst); + + if ($saved) { + @chmod($targetPath, 0644); + } + return $saved; + } +} diff --git a/lib/Service/DepartmentService.php b/lib/Service/DepartmentService.php new file mode 100644 index 0000000..c26b997 --- /dev/null +++ b/lib/Service/DepartmentService.php @@ -0,0 +1,161 @@ + 0 && TenantScopeService::hasGlobalAccess($tenantUserId)) { + unset($options['tenantUserId'], $options['tenantIds']); + } + } + return DepartmentRepository::listPaged($options); + } + + public static function listByTenantIds(array $tenantIds): array + { + return DepartmentRepository::listByTenantIds($tenantIds); + } + + public static function listByIds(array $departmentIds): array + { + return DepartmentRepository::listByIds($departmentIds); + } + + public static function listForUserAssignments(array $tenantIds, array $selectedDepartmentIds): array + { + $departments = $tenantIds ? self::listByTenantIds($tenantIds) : self::list(); + $extraDepartments = $selectedDepartmentIds ? self::listByIds($selectedDepartmentIds) : []; + if (!$extraDepartments) { + return $departments; + } + $departmentMap = []; + foreach ($departments as $department) { + if (isset($department['id'])) { + $departmentMap[(int) $department['id']] = $department; + } + } + foreach ($extraDepartments as $department) { + if (isset($department['id'])) { + $departmentMap[(int) $department['id']] = $department; + } + } + return array_values($departmentMap); + } + + public static function findByUuid(string $uuid): ?array + { + return DepartmentRepository::findByUuid($uuid); + } + + public static function findById(int $id): ?array + { + return DepartmentRepository::find($id); + } + + public static function createFromAdmin(array $input, int $currentUserId = 0): array + { + $form = self::sanitizeBase($input); + $errors = self::validateBase($form); + + if ($errors) { + return ['ok' => false, 'errors' => $errors, 'form' => $form]; + } + + $createdId = DepartmentRepository::create([ + 'description' => $form['description'], + 'created_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + + if (!$createdId) { + return ['ok' => false, 'errors' => [t('Department can not be created')], 'form' => $form]; + } + + $createdDepartment = DepartmentRepository::find((int) $createdId); + $uuid = $createdDepartment['uuid'] ?? null; + if (!empty($input['is_default']) && $createdId) { + SettingService::setDefaultDepartmentId((int) $createdId); + } + return ['ok' => true, 'form' => $form, 'uuid' => $uuid, 'id' => (int) $createdId]; + } + + public static function updateFromAdmin(int $departmentId, array $input, int $currentUserId = 0): array + { + $form = self::sanitizeBase($input); + $errors = self::validateBase($form); + + if ($errors) { + return ['ok' => false, 'errors' => $errors, 'form' => $form]; + } + + $updated = DepartmentRepository::update($departmentId, [ + 'description' => $form['description'], + 'modified_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + + if (!$updated) { + return ['ok' => false, 'errors' => [t('Department can not be updated')], 'form' => $form]; + } + + return ['ok' => true, 'form' => $form]; + } + + public static function syncTenants(int $departmentId, array $tenantIds): int + { + $updated = TenantDepartmentRepository::replaceForDepartment($departmentId, $tenantIds); + if (!$updated) { + return -1; + } + return UserDepartmentRepository::removeInvalidForDepartment($departmentId); + } + + public static function deleteByUuid(string $uuid): array + { + $uuid = trim($uuid); + if ($uuid === '') { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $department = DepartmentRepository::findByUuid($uuid); + if (!$department || !isset($department['id'])) { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $deleted = DepartmentRepository::delete((int) $department['id']); + if (!$deleted) { + return ['ok' => false, 'status' => 500, 'error' => 'delete_failed']; + } + + return ['ok' => true, 'department' => $department]; + } + + private static function sanitizeBase(array $input): array + { + return [ + 'description' => trim((string) ($input['description'] ?? '')), + ]; + } + + private static function validateBase(array $form): array + { + $errors = []; + if ($form['description'] === '') { + $errors[] = t('Description cannot be empty'); + } + return $errors; + } +} diff --git a/lib/Service/EmailVerificationService.php b/lib/Service/EmailVerificationService.php new file mode 100644 index 0000000..1411e17 --- /dev/null +++ b/lib/Service/EmailVerificationService.php @@ -0,0 +1,146 @@ + false, 'error' => 'user_not_found']; + } + + $email = (string) ($user['email'] ?? ''); + if ($email === '') { + return ['ok' => false, 'error' => 'email_required']; + } + + EmailVerificationRepository::invalidateForUser($userId); + + $code = self::generateCode(); + $codeHash = password_hash($code, PASSWORD_DEFAULT); + $expiresAt = gmdate('Y-m-d H:i:s', time() + (self::EXPIRY_MINUTES * 60)); + + $verificationId = EmailVerificationRepository::create($userId, $codeHash, $expiresAt); + if (!$verificationId) { + return ['ok' => false, 'error' => 'create_failed']; + } + + $locale = $locale ?: ($user['locale'] ?? null) ?: (I18n::$locale ?? I18n::$defaultLocale); + $name = trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')); + $isGerman = strpos((string) $locale, 'de') === 0; + $greeting = $isGerman ? 'Hallo' : 'Hello'; + if ($name !== '') { + $greeting .= ' ' . $name; + } + $greeting .= ','; + $verifyPath = Request::withLocale('verify-email', $locale); + $verifyUrl = appUrl($verifyPath); + $previousLocale = I18n::$locale ?? null; + I18n::$locale = $locale; + $subject = t('Email verification code'); + I18n::$locale = $previousLocale; + + $vars = [ + 'app_name' => appTitle(), + 'app_logo_url' => appLogoUrlAbsolute(128), + 'imprint_url' => appUrl(Request::withLocale('imprint', $locale)), + 'privacy_url' => appUrl(Request::withLocale('privacy', $locale)), + 'code' => $code, + 'expires_minutes' => self::EXPIRY_MINUTES, + 'verify_url' => $verifyUrl, + 'greeting' => $greeting, + ]; + MailService::sendTemplate('email_verification', $vars, $email, $subject, $locale); + + return ['ok' => true]; + } + + public static function verifyCode(string $email, string $code): array + { + $email = trim($email); + $code = trim($code); + if ($email === '' || $code === '') { + return ['ok' => false, 'error' => 'invalid']; + } + + $user = UserRepository::findByEmail($email); + if (!$user || !isset($user['id'])) { + return ['ok' => false, 'error' => 'invalid']; + } + + $userId = (int) $user['id']; + + // Check if already verified + if (!empty($user['email_verified_at'])) { + return ['ok' => false, 'error' => 'already_verified']; + } + + $verification = EmailVerificationRepository::findActiveByUserId($userId); + if (!$verification || !isset($verification['id'])) { + return ['ok' => false, 'error' => 'invalid']; + } + + $attempts = (int) ($verification['attempts'] ?? 0); + if ($attempts >= self::MAX_ATTEMPTS) { + return ['ok' => false, 'error' => 'too_many_attempts']; + } + + $hash = (string) ($verification['code_hash'] ?? ''); + if ($hash === '' || !password_verify($code, $hash)) { + EmailVerificationRepository::incrementAttempts((int) $verification['id']); + return ['ok' => false, 'error' => 'invalid']; + } + + // Mark verification as used + EmailVerificationRepository::markUsed((int) $verification['id']); + + // Mark user email as verified + UserRepository::setEmailVerified($userId); + + return ['ok' => true, 'user_id' => $userId]; + } + + public static function resendVerification(string $email, ?string $locale = null): array + { + $email = trim($email); + if ($email === '') { + return ['ok' => false, 'error' => 'email_required']; + } + + $user = UserRepository::findByEmail($email); + if (!$user || !isset($user['id'])) { + // Don't reveal if user exists + return ['ok' => true]; + } + + // Check if already verified + if (!empty($user['email_verified_at'])) { + return ['ok' => false, 'error' => 'already_verified']; + } + + return self::sendVerification((int) $user['id'], $locale); + } + + public static function getExpiryMinutes(): int + { + return self::EXPIRY_MINUTES; + } + + private static function generateCode(): string + { + $max = (10 ** self::CODE_LENGTH) - 1; + $code = (string) random_int(0, $max); + return str_pad($code, self::CODE_LENGTH, '0', STR_PAD_LEFT); + } +} diff --git a/lib/Service/MailLogService.php b/lib/Service/MailLogService.php new file mode 100644 index 0000000..b94a759 --- /dev/null +++ b/lib/Service/MailLogService.php @@ -0,0 +1,18 @@ + $template, + ]); + } + + public static function send( + string $to, + string $subject, + string $html, + string $text, + array $meta = [] + ): array { + $logId = MailLogRepository::create([ + 'to_email' => $to, + 'subject' => $subject, + 'template' => $meta['template'] ?? null, + 'status' => 'queued', + ]); + + if (!class_exists(PHPMailer::class)) { + if ($logId) { + MailLogRepository::markFailed($logId, 'PHPMailer is not installed'); + } + return ['ok' => false, 'error' => 'mailer_missing']; + } + + try { + $mailer = self::createMailer(); + $mailer->addAddress($to); + $mailer->Subject = $subject; + $mailer->Body = $html; + $mailer->AltBody = $text; + $mailer->isHTML(true); + $mailer->send(); + + if ($logId) { + MailLogRepository::markSent($logId, $mailer->getLastMessageID() ?: null); + } + return ['ok' => true]; + } catch (MailerException $e) { + if ($logId) { + MailLogRepository::markFailed($logId, $e->getMessage()); + } + return ['ok' => false, 'error' => 'send_failed']; + } + } + + private static function renderTemplate(string $template, array $vars, string $locale): array + { + $base = dirname(__DIR__, 2) . '/templates/emails'; + $htmlPath = $base . '/' . $locale . '/' . $template . '.html'; + $textPath = $base . '/' . $locale . '/' . $template . '.txt'; + $htmlHeaderPath = $base . '/' . $locale . '/partials/header.html'; + $htmlFooterPath = $base . '/' . $locale . '/partials/footer.html'; + $textHeaderPath = $base . '/' . $locale . '/partials/header.txt'; + $textFooterPath = $base . '/' . $locale . '/partials/footer.txt'; + + if (!is_file($htmlPath)) { + $htmlPath = $base . '/' . $template . '.html'; + } + if (!is_file($textPath)) { + $textPath = $base . '/' . $template . '.txt'; + } + if (!is_file($htmlHeaderPath)) { + $htmlHeaderPath = $base . '/partials/header.html'; + } + if (!is_file($htmlFooterPath)) { + $htmlFooterPath = $base . '/partials/footer.html'; + } + if (!is_file($textHeaderPath)) { + $textHeaderPath = $base . '/partials/header.txt'; + } + if (!is_file($textFooterPath)) { + $textFooterPath = $base . '/partials/footer.txt'; + } + + $html = is_file($htmlPath) ? file_get_contents($htmlPath) : ''; + $text = is_file($textPath) ? file_get_contents($textPath) : ''; + if (!isset($vars['email_header'])) { + $vars['email_header'] = is_file($htmlHeaderPath) ? file_get_contents($htmlHeaderPath) : ''; + } + if (!isset($vars['email_footer'])) { + $vars['email_footer'] = is_file($htmlFooterPath) ? file_get_contents($htmlFooterPath) : ''; + } + if (!isset($vars['email_header_text'])) { + $vars['email_header_text'] = is_file($textHeaderPath) ? file_get_contents($textHeaderPath) : ''; + } + if (!isset($vars['email_footer_text'])) { + $vars['email_footer_text'] = is_file($textFooterPath) ? file_get_contents($textFooterPath) : ''; + } + + foreach ($vars as $key => $value) { + $placeholder = '{{' . $key . '}}'; + $html = str_replace($placeholder, (string) $value, $html); + $text = str_replace($placeholder, (string) $value, $text); + } + // Second pass to resolve placeholders inside injected header/footer content. + foreach ($vars as $key => $value) { + $placeholder = '{{' . $key . '}}'; + $html = str_replace($placeholder, (string) $value, $html); + $text = str_replace($placeholder, (string) $value, $text); + } + + return [$html, $text]; + } + + private static function createMailer(): PHPMailer + { + $mailer = new PHPMailer(true); + $mailer->isSMTP(); + $host = SettingService::getSmtpHost() ?? (getenv('SMTP_HOST') ?: ''); + $port = SettingService::getSmtpPort() ?? (int) (getenv('SMTP_PORT') ?: 587); + $user = SettingService::getSmtpUser() ?? (getenv('SMTP_USER') ?: ''); + $pass = SettingService::getSmtpPassword() ?? (getenv('SMTP_PASS') ?: ''); + $secure = SettingService::getSmtpSecure() ?? strtolower((string) (getenv('SMTP_SECURE') ?: 'tls')); + $from = SettingService::getSmtpFrom() ?? (getenv('SMTP_FROM') ?: ($user ?: 'no-reply@localhost')); + $fromName = SettingService::getSmtpFromName() ?? (getenv('SMTP_FROM_NAME') ?: appTitle()); + + $mailer->Host = $host; + $mailer->Port = $port; + $mailer->SMTPAuth = $user !== '' || $pass !== ''; + $mailer->Username = $user; + $mailer->Password = $pass; + if (in_array($secure, ['tls', 'ssl'], true)) { + $mailer->SMTPSecure = $secure; + } + $mailer->setFrom($from, $fromName); + $mailer->CharSet = 'UTF-8'; + return $mailer; + } +} diff --git a/lib/Service/PageService.php b/lib/Service/PageService.php new file mode 100644 index 0000000..1261531 --- /dev/null +++ b/lib/Service/PageService.php @@ -0,0 +1,161 @@ + 0) { + $fallbackLocale = APP_LOCALES[0]; + } + + if (!$content && $fallbackLocale !== $locale) { + $content = PageContentRepository::findByPageIdAndLocale((int) $page['id'], $fallbackLocale); + if ($content) { + $usedLocale = $fallbackLocale; + } + } + + if (!$content) { + $content = [ + 'locale' => $usedLocale, + 'content' => null, + ]; + } + + return [ + 'page' => $page, + 'content' => $content, + 'locale' => $usedLocale, + ]; + } + + public static function updateContentBySlug( + string $slug, + string $content, + int $currentUserId = 0, + ?string $locale = null + ): array + { + $slug = trim($slug); + if ($slug === '') { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $page = PageRepository::findBySlug($slug); + if (!$page || !isset($page['id'])) { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $locale = $locale ?: (I18n::$locale ?? I18n::$defaultLocale); + + $content = trim($content); + $normalized = null; + if ($content !== '') { + $decoded = json_decode($content, true); + if (!is_array($decoded)) { + return ['ok' => false, 'errors' => [t('Content is invalid')]]; + } + if (!isset($decoded['blocks']) || !is_array($decoded['blocks'])) { + $decoded['blocks'] = []; + } + $normalized = json_encode($decoded, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + + $existing = PageContentRepository::findByPageIdAndLocale((int) $page['id'], $locale); + $updated = false; + if ($existing && isset($existing['id'])) { + $updated = PageContentRepository::update((int) $existing['id'], [ + 'content' => $normalized, + 'modified_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + } else { + $createdId = PageContentRepository::create([ + 'page_id' => (int) $page['id'], + 'locale' => $locale, + 'content' => $normalized, + 'created_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + $updated = (bool) $createdId; + } + + if (!$updated) { + return ['ok' => false, 'errors' => [t('Page can not be updated')]]; + } + + return ['ok' => true, 'page' => $page]; + } + + public static function copyContentToLocale( + string $slug, + string $fromLocale, + string $toLocale, + int $currentUserId = 0 + ): array { + $slug = trim($slug); + $fromLocale = trim($fromLocale); + $toLocale = trim($toLocale); + if ($slug === '' || $fromLocale === '' || $toLocale === '') { + return ['ok' => false, 'errors' => [t('Target language required')]]; + } + + $page = PageRepository::findBySlug($slug); + if (!$page || !isset($page['id'])) { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $source = PageContentRepository::findByPageIdAndLocale((int) $page['id'], $fromLocale); + if (!$source) { + return ['ok' => false, 'errors' => [t('Source content not found')]]; + } + + $target = PageContentRepository::findByPageIdAndLocale((int) $page['id'], $toLocale); + + $content = $source['content'] ?? null; + $updated = false; + if ($target && isset($target['id'])) { + $updated = PageContentRepository::update((int) $target['id'], [ + 'content' => $content, + 'modified_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + } else { + $createdId = PageContentRepository::create([ + 'page_id' => (int) $page['id'], + 'locale' => $toLocale, + 'content' => $content, + 'created_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + $updated = (bool) $createdId; + } + + if (!$updated) { + return ['ok' => false, 'errors' => [t('Page can not be updated')]]; + } + + return ['ok' => true]; + } +} diff --git a/lib/Service/PasswordResetService.php b/lib/Service/PasswordResetService.php new file mode 100644 index 0000000..d6d61ff --- /dev/null +++ b/lib/Service/PasswordResetService.php @@ -0,0 +1,147 @@ + false, 'error' => 'email_required']; + } + + $user = UserRepository::findByEmail($email); + if (!$user || !isset($user['id'])) { + return ['ok' => true]; + } + + $userId = (int) $user['id']; + PasswordResetRepository::invalidateForUser($userId); + + $code = self::generateCode(); + $codeHash = password_hash($code, PASSWORD_DEFAULT); + $expiresAt = gmdate('Y-m-d H:i:s', time() + (self::EXPIRY_MINUTES * 60)); + + $resetId = PasswordResetRepository::create($userId, $codeHash, $expiresAt); + if (!$resetId) { + return ['ok' => false, 'error' => 'create_failed']; + } + + $locale = $locale ?: ($user['locale'] ?? null) ?: (I18n::$locale ?? I18n::$defaultLocale); + $name = trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')); + $isGerman = strpos((string) $locale, 'de') === 0; + $greeting = $isGerman ? 'Hallo' : 'Hello'; + if ($name !== '') { + $greeting .= ' ' . $name; + } + $greeting .= ','; + $verifyPath = Request::withLocale('password/verify', $locale); + $verifyUrl = appUrl($verifyPath); + $previousLocale = I18n::$locale ?? null; + I18n::$locale = $locale; + $subject = t('Password reset code'); + I18n::$locale = $previousLocale; + + $vars = [ + 'app_name' => appTitle(), + 'app_logo_url' => appLogoUrlAbsolute(128), + 'imprint_url' => appUrl(Request::withLocale('imprint', $locale)), + 'privacy_url' => appUrl(Request::withLocale('privacy', $locale)), + 'code' => $code, + 'expires_minutes' => self::EXPIRY_MINUTES, + 'verify_url' => $verifyUrl, + 'greeting' => $greeting, + ]; + MailService::sendTemplate('reset_code', $vars, $email, $subject, $locale); + + return ['ok' => true]; + } + + public static function verifyCode(string $email, string $code): array + { + $email = trim($email); + $code = trim($code); + if ($email === '' || $code === '') { + return ['ok' => false, 'error' => 'invalid']; + } + + $user = UserRepository::findByEmail($email); + if (!$user || !isset($user['id'])) { + return ['ok' => false, 'error' => 'invalid']; + } + + $reset = PasswordResetRepository::findActiveByUserId((int) $user['id']); + if (!$reset || !isset($reset['id'])) { + return ['ok' => false, 'error' => 'invalid']; + } + + $attempts = (int) ($reset['attempts'] ?? 0); + if ($attempts >= self::MAX_ATTEMPTS) { + return ['ok' => false, 'error' => 'too_many_attempts']; + } + + $hash = (string) ($reset['code_hash'] ?? ''); + if ($hash === '' || !password_verify($code, $hash)) { + PasswordResetRepository::incrementAttempts((int) $reset['id']); + return ['ok' => false, 'error' => 'invalid']; + } + + return ['ok' => true, 'reset_id' => (int) $reset['id'], 'user_id' => (int) $user['id']]; + } + + public static function resetPassword(int $resetId, string $password, string $password2): array + { + $reset = PasswordResetRepository::findById($resetId); + if (!$reset || !isset($reset['id'])) { + return ['ok' => false, 'errors' => [t('Reset request not found')]]; + } + + if (!empty($reset['used_at'])) { + return ['ok' => false, 'errors' => [t('Reset request already used')]]; + } + + $expiresAt = (string) ($reset['expires_at'] ?? ''); + if ($expiresAt !== '') { + try { + $expiry = new \DateTimeImmutable($expiresAt, new \DateTimeZone('UTC')); + if ($expiry->getTimestamp() <= time()) { + return ['ok' => false, 'errors' => [t('Reset request expired')]]; + } + } catch (\Exception $e) { + return ['ok' => false, 'errors' => [t('Reset request expired')]]; + } + } + + $userId = (int) ($reset['user_id'] ?? 0); + if ($userId <= 0) { + return ['ok' => false, 'errors' => [t('Reset request not found')]]; + } + + $result = UserService::resetPassword($userId, $password, $password2); + if (!($result['ok'] ?? false)) { + return $result; + } + + PasswordResetRepository::markUsed($resetId); + RememberMeService::forgetAllForUser($userId); + return ['ok' => true]; + } + + private static function generateCode(): string + { + $max = (10 ** self::CODE_LENGTH) - 1; + $code = (string) random_int(0, $max); + return str_pad($code, self::CODE_LENGTH, '0', STR_PAD_LEFT); + } +} diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php new file mode 100644 index 0000000..a355a89 --- /dev/null +++ b/lib/Service/PermissionService.php @@ -0,0 +1,180 @@ + $userId, + 'keys' => $keys, + ]; + return $keys; + } + + public static function getCachedPermissions(int $userId): array + { + if ($userId <= 0) { + return []; + } + $cache = $_SESSION['permissions'] ?? null; + if (is_array($cache) && (int) ($cache['user_id'] ?? 0) === $userId) { + $keys = $cache['keys'] ?? []; + return is_array($keys) ? $keys : []; + } + return []; + } + + public static function clearUserCache(int $userId): void + { + $cache = $_SESSION['permissions'] ?? null; + if (is_array($cache) && (int) ($cache['user_id'] ?? 0) === $userId) { + unset($_SESSION['permissions']); + } + } + + public static function list(): array + { + return PermissionRepository::list(); + } + + public static function listPaged(array $options): array + { + return PermissionRepository::listPaged($options); + } + + public static function find(int $id): ?array + { + return PermissionRepository::find($id); + } + + public static function findByKey(string $key): ?array + { + return PermissionRepository::findByKey($key); + } + + public static function createFromAdmin(array $input): array + { + $form = self::sanitizeBase($input); + $errors = self::validateBase($form); + if ($errors) { + return ['ok' => false, 'errors' => $errors, 'form' => $form]; + } + if (PermissionRepository::findByKey($form['key'])) { + return ['ok' => false, 'errors' => [t('Permission key already exists')], 'form' => $form]; + } + $createdId = PermissionRepository::create($form); + if (!$createdId) { + return ['ok' => false, 'errors' => [t('Permission can not be created')], 'form' => $form]; + } + return ['ok' => true, 'form' => $form, 'id' => (int) $createdId]; + } + + public static function updateFromAdmin(int $id, array $input): array + { + $form = self::sanitizeBase($input); + $errors = self::validateBase($form); + if ($errors) { + return ['ok' => false, 'errors' => $errors, 'form' => $form]; + } + $existing = PermissionRepository::findByKey($form['key']); + if ($existing && (int) ($existing['id'] ?? 0) !== $id) { + return ['ok' => false, 'errors' => [t('Permission key already exists')], 'form' => $form]; + } + $updated = PermissionRepository::update($id, $form); + if (!$updated) { + return ['ok' => false, 'errors' => [t('Permission can not be updated')], 'form' => $form]; + } + return ['ok' => true, 'form' => $form]; + } + + public static function deleteById(int $id): array + { + $permission = PermissionRepository::find($id); + if (!$permission) { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + $deleted = PermissionRepository::delete($id); + if (!$deleted) { + return ['ok' => false, 'status' => 500, 'error' => 'delete_failed']; + } + return ['ok' => true, 'permission' => $permission]; + } + + private static function sanitizeBase(array $input): array + { + return [ + 'key' => trim((string) ($input['key'] ?? '')), + 'description' => trim((string) ($input['description'] ?? '')), + ]; + } + + private static function validateBase(array $form): array + { + $errors = []; + if ($form['key'] === '') { + $errors[] = t('Permission key cannot be empty'); + } elseif (!preg_match('/^[a-z0-9._-]+$/i', $form['key'])) { + $errors[] = t('Permission key is invalid'); + } + return $errors; + } +} diff --git a/lib/Service/RememberMeService.php b/lib/Service/RememberMeService.php new file mode 100644 index 0000000..d012d67 --- /dev/null +++ b/lib/Service/RememberMeService.php @@ -0,0 +1,157 @@ + 0) { + PermissionService::getUserPermissions($userId, true); + AuthService::loadTenantDataIntoSession($userId); + } + + $newToken = bin2hex(random_bytes(32)); + $newHash = hash('sha256', $newToken); + $newExpires = gmdate('Y-m-d H:i:s', time() + self::lifetimeSeconds()); + RememberTokenRepository::updateToken((int) $record['id'], $newHash, $newExpires); + self::setCookie($selector, $newToken); + + return true; + } + + public static function forgetCurrentUser(): void + { + $value = $_COOKIE[self::cookieName()] ?? ''; + if ($value !== '' && strpos($value, ':') !== false) { + [$selector] = explode(':', $value, 2); + $selector = trim($selector); + if ($selector !== '') { + $record = RememberTokenRepository::findBySelector($selector); + if ($record && isset($record['id'])) { + RememberTokenRepository::deleteById((int) $record['id']); + } + } + } + self::clearCookie(); + } + + public static function forgetAllForUser(int $userId): void + { + RememberTokenRepository::deleteByUserId($userId); + } + + private static function setCookie(string $selector, string $token): void + { + $value = $selector . ':' . $token; + $expires = time() + self::lifetimeSeconds(); + $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') + || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'); + + setcookie(self::cookieName(), $value, [ + 'expires' => $expires, + 'path' => '/', + 'secure' => $secure, + 'httponly' => true, + 'samesite' => 'Lax', + ]); + } + + private static function clearCookie(): void + { + $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') + || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'); + setcookie(self::cookieName(), '', [ + 'expires' => time() - 3600, + 'path' => '/', + 'secure' => $secure, + 'httponly' => true, + 'samesite' => 'Lax', + ]); + } + + private static function lifetimeSeconds(): int + { + return self::LIFETIME_DAYS * 24 * 60 * 60; + } + + private static function cookieName(): string + { + $name = getenv('REMEMBER_COOKIE_NAME') ?: self::COOKIE_NAME; + return trim($name) !== '' ? $name : self::COOKIE_NAME; + } +} diff --git a/lib/Service/RoleService.php b/lib/Service/RoleService.php new file mode 100644 index 0000000..910d8d5 --- /dev/null +++ b/lib/Service/RoleService.php @@ -0,0 +1,116 @@ + false, 'errors' => $errors, 'form' => $form]; + } + + $createdId = RoleRepository::create([ + 'description' => $form['description'], + 'created_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + + if (!$createdId) { + return ['ok' => false, 'errors' => [t('Role can not be created')], 'form' => $form]; + } + + $createdRole = RoleRepository::find((int) $createdId); + $uuid = $createdRole['uuid'] ?? null; + if (!empty($input['is_default']) && $createdId) { + SettingService::setDefaultRoleId((int) $createdId); + } + return ['ok' => true, 'form' => $form, 'uuid' => $uuid, 'id' => (int) $createdId]; + } + + public static function updateFromAdmin(int $roleId, array $input, int $currentUserId = 0): array + { + $form = self::sanitizeBase($input); + $errors = self::validateBase($form); + + if ($errors) { + return ['ok' => false, 'errors' => $errors, 'form' => $form]; + } + + $updated = RoleRepository::update($roleId, [ + 'description' => $form['description'], + 'modified_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + + if (!$updated) { + return ['ok' => false, 'errors' => [t('Role can not be updated')], 'form' => $form]; + } + + return ['ok' => true, 'form' => $form]; + } + + public static function deleteByUuid(string $uuid): array + { + $uuid = trim($uuid); + if ($uuid === '') { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $role = RoleRepository::findByUuid($uuid); + if (!$role || !isset($role['id'])) { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + if (($role['description'] ?? '') === 'Admin') { + return ['ok' => false, 'status' => 403, 'error' => 'admin_role_protected']; + } + + $deleted = RoleRepository::delete((int) $role['id']); + if (!$deleted) { + return ['ok' => false, 'status' => 500, 'error' => 'delete_failed']; + } + + return ['ok' => true, 'role' => $role]; + } + + private static function sanitizeBase(array $input): array + { + return [ + 'description' => trim((string) ($input['description'] ?? '')), + ]; + } + + private static function validateBase(array $form): array + { + $errors = []; + if ($form['description'] === '') { + $errors[] = t('Description cannot be empty'); + } + return $errors; + } +} diff --git a/lib/Service/SettingService.php b/lib/Service/SettingService.php new file mode 100644 index 0000000..c0dea4a --- /dev/null +++ b/lib/Service/SettingService.php @@ -0,0 +1,340 @@ + 0 ? $id : null; + } + + public static function setDefaultTenantId(?int $tenantId, ?string $description = null): bool + { + $value = $tenantId && $tenantId > 0 ? (string) $tenantId : null; + if ($value !== null) { + $exists = TenantRepository::find($tenantId ?? 0); + if (!$exists) { + return false; + } + } + $desc = $description ?? 'setting.default_tenant'; + return SettingRepository::set(self::DEFAULT_TENANT_KEY, $value, $desc); + } + + public static function getDefaultRoleId(): ?int + { + $id = self::getInt(self::DEFAULT_ROLE_KEY); + return $id && $id > 0 ? $id : null; + } + + public static function setDefaultRoleId(?int $roleId, ?string $description = null): bool + { + $value = $roleId && $roleId > 0 ? (string) $roleId : null; + if ($value !== null) { + $exists = RoleRepository::find($roleId ?? 0); + if (!$exists) { + return false; + } + } + $desc = $description ?? 'setting.default_role'; + return SettingRepository::set(self::DEFAULT_ROLE_KEY, $value, $desc); + } + + public static function getDefaultDepartmentId(): ?int + { + $id = self::getInt(self::DEFAULT_DEPARTMENT_KEY); + return $id && $id > 0 ? $id : null; + } + + public static function setDefaultDepartmentId(?int $departmentId, ?string $description = null): bool + { + $value = $departmentId && $departmentId > 0 ? (string) $departmentId : null; + if ($value !== null) { + $exists = DepartmentRepository::find($departmentId ?? 0); + if (!$exists) { + return false; + } + } + $desc = $description ?? 'setting.default_department'; + return SettingRepository::set(self::DEFAULT_DEPARTMENT_KEY, $value, $desc); + } + + public static function getAppTitle(): ?string + { + $value = SettingRepository::getValue(self::APP_TITLE_KEY); + $value = $value !== null ? trim($value) : null; + return $value !== '' ? $value : null; + } + + public static function setAppTitle(?string $title, ?string $description = null): bool + { + $value = $title !== null ? trim($title) : null; + if ($value === '') { + $value = null; + } + $desc = $description ?? 'setting.app_title'; + return SettingRepository::set(self::APP_TITLE_KEY, $value, $desc); + } + + public static function getAppLocale(): ?string + { + $value = SettingRepository::getValue(self::APP_LOCALE_KEY); + $value = $value !== null ? trim((string) $value) : ''; + return $value !== '' ? $value : null; + } + + public static function setAppLocale(?string $locale, ?string $description = null): bool + { + $value = $locale !== null ? trim((string) $locale) : ''; + if ($value === '') { + $value = null; + } else { + $allowed = defined('APP_LOCALES') ? APP_LOCALES : []; + if ($allowed && !in_array($value, $allowed, true)) { + return false; + } + } + $desc = $description ?? 'setting.app_locale'; + return SettingRepository::set(self::APP_LOCALE_KEY, $value, $desc); + } + + public static function getAppTheme(): ?string + { + $value = SettingRepository::getValue(self::APP_THEME_KEY); + $value = $value !== null ? strtolower(trim((string) $value)) : ''; + if (!in_array($value, ['light', 'dark'], true)) { + return null; + } + return $value; + } + + public static function setAppTheme(?string $theme, ?string $description = null): bool + { + $value = $theme !== null ? strtolower(trim((string) $theme)) : ''; + if ($value === '' || !in_array($value, ['light', 'dark'], true)) { + $value = null; + } + $desc = $description ?? 'setting.app_theme'; + return SettingRepository::set(self::APP_THEME_KEY, $value, $desc); + } + + public static function isUserThemeAllowed(): bool + { + $value = SettingRepository::getValue(self::APP_THEME_USER_KEY); + if ($value === null || $value === '') { + return true; + } + return in_array(strtolower($value), ['1', 'true', 'yes', 'on'], true); + } + + public static function setUserThemeAllowed(bool $allowed, ?string $description = null): bool + { + $value = $allowed ? '1' : '0'; + $desc = $description ?? 'setting.app_theme_user'; + return SettingRepository::set(self::APP_THEME_USER_KEY, $value, $desc); + } + + public static function getAppPrimaryColor(): ?string + { + $value = SettingRepository::getValue(self::APP_PRIMARY_COLOR_KEY); + $value = $value !== null ? strtolower(trim((string) $value)) : ''; + if ($value === '') { + return null; + } + if (!preg_match('/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i', $value)) { + return null; + } + return $value; + } + + public static function setAppPrimaryColor(?string $color, ?string $description = null): bool + { + $value = $color !== null ? strtolower(trim((string) $color)) : ''; + if ($value !== '' && $value[0] !== '#') { + if (preg_match('/^[0-9a-f]{3}$/i', $value) + || preg_match('/^[0-9a-f]{4}$/i', $value) + || preg_match('/^[0-9a-f]{6}$/i', $value) + || preg_match('/^[0-9a-f]{8}$/i', $value)) { + $value = '#' . $value; + } + } + if ($value === '') { + $value = null; + } elseif (!preg_match('/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i', $value)) { + return false; + } + $desc = $description ?? 'setting.app_primary_color'; + return SettingRepository::set(self::APP_PRIMARY_COLOR_KEY, $value, $desc); + } + + public static function getSmtpHost(): ?string + { + $value = SettingRepository::getValue(self::SMTP_HOST_KEY); + $value = $value !== null ? trim((string) $value) : ''; + return $value !== '' ? $value : null; + } + + public static function setSmtpHost(?string $host, ?string $description = null): bool + { + $value = $host !== null ? trim((string) $host) : ''; + if ($value === '') { + $value = null; + } + $desc = $description ?? 'setting.smtp_host'; + return SettingRepository::set(self::SMTP_HOST_KEY, $value, $desc); + } + + public static function getSmtpPort(): ?int + { + $value = SettingRepository::getValue(self::SMTP_PORT_KEY); + if ($value === null || $value === '') { + return null; + } + $port = (int) $value; + return $port > 0 ? $port : null; + } + + public static function setSmtpPort(?int $port, ?string $description = null): bool + { + $value = $port && $port > 0 ? (string) $port : null; + $desc = $description ?? 'setting.smtp_port'; + return SettingRepository::set(self::SMTP_PORT_KEY, $value, $desc); + } + + public static function getSmtpUser(): ?string + { + $value = SettingRepository::getValue(self::SMTP_USER_KEY); + $value = $value !== null ? trim((string) $value) : ''; + return $value !== '' ? $value : null; + } + + public static function setSmtpUser(?string $user, ?string $description = null): bool + { + $value = $user !== null ? trim((string) $user) : ''; + if ($value === '') { + $value = null; + } + $desc = $description ?? 'setting.smtp_user'; + return SettingRepository::set(self::SMTP_USER_KEY, $value, $desc); + } + + public static function getSmtpPassword(): ?string + { + $value = SettingRepository::getValue(self::SMTP_PASSWORD_KEY); + $value = $value !== null ? (string) $value : ''; + return $value !== '' ? $value : null; + } + + public static function setSmtpPassword(?string $password, ?string $description = null): bool + { + $value = $password !== null ? (string) $password : ''; + if ($value === '') { + $value = null; + } + $desc = $description ?? 'setting.smtp_password'; + return SettingRepository::set(self::SMTP_PASSWORD_KEY, $value, $desc); + } + + public static function getSmtpSecure(): ?string + { + $value = SettingRepository::getValue(self::SMTP_SECURE_KEY); + $value = $value !== null ? strtolower(trim((string) $value)) : ''; + if (!in_array($value, ['tls', 'ssl', 'none'], true)) { + return null; + } + return $value === 'none' ? null : $value; + } + + public static function setSmtpSecure(?string $secure, ?string $description = null): bool + { + $value = $secure !== null ? strtolower(trim((string) $secure)) : ''; + if ($value === '' || $value === 'none') { + $value = null; + } elseif (!in_array($value, ['tls', 'ssl'], true)) { + return false; + } + $desc = $description ?? 'setting.smtp_secure'; + return SettingRepository::set(self::SMTP_SECURE_KEY, $value, $desc); + } + + public static function getSmtpFrom(): ?string + { + $value = SettingRepository::getValue(self::SMTP_FROM_KEY); + $value = $value !== null ? trim((string) $value) : ''; + return $value !== '' ? $value : null; + } + + public static function setSmtpFrom(?string $from, ?string $description = null): bool + { + $value = $from !== null ? trim((string) $from) : ''; + if ($value === '') { + $value = null; + } + $desc = $description ?? 'setting.smtp_from'; + return SettingRepository::set(self::SMTP_FROM_KEY, $value, $desc); + } + + public static function getSmtpFromName(): ?string + { + $value = SettingRepository::getValue(self::SMTP_FROM_NAME_KEY); + $value = $value !== null ? trim((string) $value) : ''; + return $value !== '' ? $value : null; + } + + public static function setSmtpFromName(?string $fromName, ?string $description = null): bool + { + $value = $fromName !== null ? trim((string) $fromName) : ''; + if ($value === '') { + $value = null; + } + $desc = $description ?? 'setting.smtp_from_name'; + return SettingRepository::set(self::SMTP_FROM_NAME_KEY, $value, $desc); + } +} diff --git a/lib/Service/TenantAvatarService.php b/lib/Service/TenantAvatarService.php new file mode 100644 index 0000000..4769b1c --- /dev/null +++ b/lib/Service/TenantAvatarService.php @@ -0,0 +1,328 @@ + false, 'error' => t('Tenant not found')]; + } + if (empty($file) || !isset($file['tmp_name'])) { + return ['ok' => false, 'error' => t('No file uploaded')]; + } + if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + if (($file['size'] ?? 0) > self::MAX_SIZE) { + return ['ok' => false, 'error' => t('File is too large')]; + } + + $tmpPath = $file['tmp_name']; + $mime = self::detectMime($tmpPath); + $isSvg = self::isSvgUpload($mime, $tmpPath); + $ext = self::extensionForMime($mime, $isSvg); + if (!$ext) { + return ['ok' => false, 'error' => t('Invalid image file')]; + } + if ($isSvg && !self::isSafeSvg($tmpPath)) { + return ['ok' => false, 'error' => t('Invalid image file')]; + } + + $dir = self::tenantDir($uuid); + if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + self::delete($uuid); + $originalPath = $dir . '/original.' . $ext; + if (!move_uploaded_file($tmpPath, $originalPath)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + @chmod($originalPath, 0644); + + $variantExt = function_exists('imagewebp') ? 'webp' : 'jpg'; + if (!$isSvg && self::canResize()) { + foreach (self::SIZES as $size) { + $target = $dir . '/avatar-' . $size . '.' . $variantExt; + self::resizeAndFit($originalPath, $target, $size, $size, $variantExt); + } + } + + return ['ok' => true, 'path' => $originalPath, 'mime' => $mime]; + } + + public static function detectMime(string $path): string + { + if (function_exists('finfo_open')) { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + if ($finfo) { + $mime = finfo_file($finfo, $path); + if (is_string($mime) && $mime !== '') { + if (self::isSvgUpload($mime, $path)) { + return 'image/svg+xml'; + } + return $mime; + } + } + } + if (self::isSvgUpload('application/octet-stream', $path)) { + return 'image/svg+xml'; + } + return 'application/octet-stream'; + } + + private static function extensionForMime(string $mime, bool $isSvg = false): ?string + { + if ($isSvg) { + return 'svg'; + } + $map = [ + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/webp' => 'webp', + ]; + return $map[$mime] ?? null; + } + + private static function isSvgUpload(string $mime, string $path): bool + { + if ($mime === 'image/svg+xml') { + return true; + } + $head = @file_get_contents($path, false, null, 0, 1024); + if (!is_string($head) || $head === '') { + return false; + } + return stripos($head, ' filemtime($a); + }); + return $matches[0] ?? null; + } + + private static function findOriginalPath(string $dir): ?string + { + $matches = glob($dir . '/original.*'); + if (!$matches) { + return null; + } + usort($matches, static function ($a, $b) { + return filemtime($b) <=> filemtime($a); + }); + return $matches[0] ?? null; + } + + private static function canResize(): bool + { + return function_exists('imagecreatetruecolor') && function_exists('imagecopyresampled'); + } + + private static function createImageResource(string $path, string $mime) + { + if ($mime === 'image/jpeg' && function_exists('imagecreatefromjpeg')) { + return imagecreatefromjpeg($path); + } + if ($mime === 'image/png' && function_exists('imagecreatefrompng')) { + return imagecreatefrompng($path); + } + if ($mime === 'image/webp' && function_exists('imagecreatefromwebp')) { + return imagecreatefromwebp($path); + } + return false; + } + + private static function resizeAndFit(string $sourcePath, string $targetPath, int $width, int $height, string $ext): bool + { + $mime = self::detectMime($sourcePath); + $src = self::createImageResource($sourcePath, $mime); + if (!$src) { + return false; + } + + $srcWidth = imagesx($src); + $srcHeight = imagesy($src); + if ($srcWidth === 0 || $srcHeight === 0) { + imagedestroy($src); + return false; + } + + $scale = min($width / $srcWidth, $height / $srcHeight); + $dstWidth = (int) round($srcWidth * $scale); + $dstHeight = (int) round($srcHeight * $scale); + if ($dstWidth < 1) $dstWidth = 1; + if ($dstHeight < 1) $dstHeight = 1; + + $dst = imagecreatetruecolor($dstWidth, $dstHeight); + if ($ext === 'png' || $ext === 'webp') { + imagealphablending($dst, false); + imagesavealpha($dst, true); + $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127); + imagefilledrectangle($dst, 0, 0, $dstWidth, $dstHeight, $transparent); + } + + imagecopyresampled( + $dst, + $src, + 0, + 0, + 0, + 0, + $dstWidth, + $dstHeight, + $srcWidth, + $srcHeight + ); + + $saved = false; + if ($ext === 'webp' && function_exists('imagewebp')) { + $saved = imagewebp($dst, $targetPath, 82); + } elseif ($ext === 'png' && function_exists('imagepng')) { + $saved = imagepng($dst, $targetPath, 6); + } else { + $saved = imagejpeg($dst, $targetPath, 85); + } + + imagedestroy($src); + imagedestroy($dst); + + if ($saved) { + @chmod($targetPath, 0644); + } + return $saved; + } + + private static function avatarDirs(string $uuid): array + { + $dirs = [self::tenantDir($uuid)]; + $legacy = self::legacyTenantDir($uuid); + if ($legacy !== $dirs[0]) { + $dirs[] = $legacy; + } + return $dirs; + } + + private static function legacyTenantDir(string $uuid): string + { + return self::storageBase() . '/tenants/' . $uuid; + } +} diff --git a/lib/Service/TenantFaviconService.php b/lib/Service/TenantFaviconService.php new file mode 100644 index 0000000..5625324 --- /dev/null +++ b/lib/Service/TenantFaviconService.php @@ -0,0 +1,214 @@ + 'favicon-16x16.png', + 32 => 'favicon-32x32.png', + 96 => 'favicon-96x96.png', + 180 => 'apple-touch-icon.png', + 192 => 'web-app-manifest-192x192.png', + 512 => 'web-app-manifest-512x512.png', + ]; + + public static function isValidUuid(string $uuid): bool + { + return (bool) preg_match('/^[a-f0-9-]{36}$/i', $uuid); + } + + public static function storageBase(): string + { + if (defined('APP_STORAGE_PATH') && APP_STORAGE_PATH) { + return rtrim(APP_STORAGE_PATH, '/'); + } + return rtrim(dirname(__DIR__, 2) . '/storage', '/'); + } + + public static function storageDir(string $uuid): string + { + return self::storageBase() . '/tenants/' . $uuid . '/favicon'; + } + + public static function publicDir(string $uuid): string + { + return rtrim(dirname(__DIR__, 2) . '/web/favicon/tenants/' . $uuid, '/'); + } + + public static function hasFavicon(string $uuid): bool + { + if (!self::isValidUuid($uuid)) { + return false; + } + $path = self::publicDir($uuid) . '/favicon-32x32.png'; + return is_file($path); + } + + public static function delete(string $uuid): void + { + if (!self::isValidUuid($uuid)) { + return; + } + foreach (self::storageDirs($uuid) as $dir) { + if (!is_dir($dir)) { + continue; + } + $matches = glob($dir . '/*') ?: []; + foreach ($matches as $file) { + if (is_file($file)) { + @unlink($file); + } + } + } + $public = self::publicDir($uuid); + foreach (self::SIZES as $file) { + $target = $public . '/' . $file; + if (is_file($target)) { + @unlink($target); + } + } + } + + public static function saveUpload(string $uuid, array $file): array + { + if (!self::isValidUuid($uuid)) { + return ['ok' => false, 'error' => t('Tenant not found')]; + } + if (empty($file) || !isset($file['tmp_name'])) { + return ['ok' => false, 'error' => t('No file uploaded')]; + } + if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + if (($file['size'] ?? 0) > self::MAX_SIZE) { + return ['ok' => false, 'error' => t('File is too large')]; + } + + $tmpPath = $file['tmp_name']; + $mime = self::detectMime($tmpPath); + if ($mime !== 'image/png') { + return ['ok' => false, 'error' => t('Invalid image file')]; + } + if (!self::canResize()) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + $dir = self::storageDir($uuid); + if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + self::delete($uuid); + $originalPath = $dir . '/original.png'; + if (!move_uploaded_file($tmpPath, $originalPath)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + @chmod($originalPath, 0644); + + $publicDir = self::publicDir($uuid); + if (!is_dir($publicDir) && !mkdir($publicDir, 0755, true) && !is_dir($publicDir)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + foreach (self::SIZES as $size => $fileName) { + $target = $publicDir . '/' . $fileName; + if (!self::resizeSquare($originalPath, $target, $size)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + @chmod($target, 0644); + } + + return ['ok' => true, 'path' => $originalPath, 'mime' => $mime]; + } + + public static function detectMime(string $path): string + { + if (function_exists('finfo_open')) { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + if ($finfo) { + $mime = finfo_file($finfo, $path); + if (is_string($mime) && $mime !== '') { + return $mime; + } + } + } + return 'application/octet-stream'; + } + + private static function canResize(): bool + { + return function_exists('imagecreatetruecolor') && function_exists('imagecopyresampled'); + } + + private static function loadImage(string $path) + { + if (function_exists('imagecreatefrompng')) { + return imagecreatefrompng($path); + } + return false; + } + + private static function resizeSquare(string $sourcePath, string $targetPath, int $size): bool + { + $src = self::loadImage($sourcePath); + if (!$src) { + return false; + } + $srcWidth = imagesx($src); + $srcHeight = imagesy($src); + if ($srcWidth === 0 || $srcHeight === 0) { + imagedestroy($src); + return false; + } + + $cropSize = min($srcWidth, $srcHeight); + $srcX = (int) round(($srcWidth - $cropSize) / 2); + $srcY = (int) round(($srcHeight - $cropSize) / 2); + + $dst = imagecreatetruecolor($size, $size); + imagealphablending($dst, false); + imagesavealpha($dst, true); + $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127); + imagefilledrectangle($dst, 0, 0, $size, $size, $transparent); + + imagecopyresampled( + $dst, + $src, + 0, + 0, + $srcX, + $srcY, + $size, + $size, + $cropSize, + $cropSize + ); + + $saved = false; + if (function_exists('imagepng')) { + $saved = imagepng($dst, $targetPath, 6); + } + + imagedestroy($src); + imagedestroy($dst); + + return $saved; + } + + private static function storageDirs(string $uuid): array + { + $dirs = [self::storageDir($uuid)]; + $legacy = self::legacyStorageDir($uuid); + if ($legacy !== $dirs[0]) { + $dirs[] = $legacy; + } + return $dirs; + } + + private static function legacyStorageDir(string $uuid): string + { + return self::storageBase() . '/branding/tenants/' . $uuid . '/favicon'; + } +} diff --git a/lib/Service/TenantScopeService.php b/lib/Service/TenantScopeService.php new file mode 100644 index 0000000..b5f4921 --- /dev/null +++ b/lib/Service/TenantScopeService.php @@ -0,0 +1,118 @@ + 0 ? [$resourceId] : []; + } + if ($resource === 'users') { + return array_values(array_unique(array_map('intval', UserTenantRepository::listTenantIdsByUserId($resourceId)))); + } + if ($resource === 'departments') { + return array_values(array_unique(array_map('intval', TenantDepartmentRepository::listTenantIdsByDepartmentId($resourceId)))); + } + return []; + } + + private static function filterActiveTenantIds(array $tenantIds): array + { + $tenantIds = array_values(array_unique(array_map('intval', $tenantIds))); + if (!$tenantIds) { + return []; + } + return TenantRepository::listActiveIdsByIds($tenantIds); + } + + private static function canBypass(int $userId): bool + { + if ($userId <= 0) { + return false; + } + return PermissionService::userHas($userId, PermissionService::TENANTS_UPDATE) + || PermissionService::userHas($userId, PermissionService::SETTINGS_UPDATE); + } +} diff --git a/lib/Service/TenantService.php b/lib/Service/TenantService.php new file mode 100644 index 0000000..a501db0 --- /dev/null +++ b/lib/Service/TenantService.php @@ -0,0 +1,207 @@ + false, 'errors' => $errors, 'form' => $form]; + } + + $statusChangedAt = gmdate('Y-m-d H:i:s'); + $createdId = TenantRepository::create([ + 'description' => $form['description'], + 'address' => $form['address'], + 'postal_code' => $form['postal_code'], + 'city' => $form['city'], + 'country' => $form['country'], + 'region' => $form['region'], + 'vat_id' => $form['vat_id'], + 'tax_number' => $form['tax_number'], + 'phone' => $form['phone'], + 'fax' => $form['fax'], + 'email' => $form['email'], + 'support_email' => $form['support_email'], + 'support_phone' => $form['support_phone'], + 'billing_email' => $form['billing_email'], + 'website' => $form['website'], + 'privacy_url' => $form['privacy_url'], + 'imprint_url' => $form['imprint_url'], + 'primary_color' => $form['primary_color_use_default'] + ? null + : ($form['primary_color'] !== '' ? $form['primary_color'] : null), + 'status' => $form['status'], + 'status_changed_at' => $statusChangedAt, + 'status_changed_by' => $currentUserId > 0 ? $currentUserId : null, + 'created_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + + if (!$createdId) { + return ['ok' => false, 'errors' => [t('Tenant can not be created')], 'form' => $form]; + } + + $createdTenant = TenantRepository::find((int) $createdId); + $uuid = $createdTenant['uuid'] ?? null; + if (!empty($input['is_default']) && $createdId) { + SettingService::setDefaultTenantId((int) $createdId); + } + return ['ok' => true, 'form' => $form, 'uuid' => $uuid, 'id' => (int) $createdId]; + } + + public static function updateFromAdmin(int $tenantId, array $input, int $currentUserId = 0): array + { + $form = self::sanitizeBase($input); + $form['status'] = self::normalizeStatus($form['status'] ?? ''); + $errors = self::validateBase($form); + + if ($errors) { + return ['ok' => false, 'errors' => $errors, 'form' => $form]; + } + + $existing = TenantRepository::find($tenantId); + $statusChanged = false; + if ($existing && isset($existing['status'])) { + $statusChanged = (string) $existing['status'] !== $form['status']; + } + + $updateData = [ + 'description' => $form['description'], + 'address' => $form['address'], + 'postal_code' => $form['postal_code'], + 'city' => $form['city'], + 'country' => $form['country'], + 'region' => $form['region'], + 'vat_id' => $form['vat_id'], + 'tax_number' => $form['tax_number'], + 'phone' => $form['phone'], + 'fax' => $form['fax'], + 'email' => $form['email'], + 'support_email' => $form['support_email'], + 'support_phone' => $form['support_phone'], + 'billing_email' => $form['billing_email'], + 'website' => $form['website'], + 'privacy_url' => $form['privacy_url'], + 'imprint_url' => $form['imprint_url'], + 'primary_color' => $form['primary_color_use_default'] + ? null + : ($form['primary_color'] !== '' ? $form['primary_color'] : null), + 'status' => $form['status'], + 'modified_by' => $currentUserId > 0 ? $currentUserId : null, + ]; + if ($statusChanged) { + $updateData['status_changed_at'] = gmdate('Y-m-d H:i:s'); + $updateData['status_changed_by'] = $currentUserId > 0 ? $currentUserId : null; + } + + $updated = TenantRepository::update($tenantId, $updateData); + + if (!$updated) { + return ['ok' => false, 'errors' => [t('Tenant can not be updated')], 'form' => $form]; + } + + return ['ok' => true, 'form' => $form]; + } + + public static function deleteByUuid(string $uuid): array + { + $uuid = trim($uuid); + if ($uuid === '') { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $tenant = TenantRepository::findByUuid($uuid); + if (!$tenant || !isset($tenant['id'])) { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $deleted = TenantRepository::delete((int) $tenant['id']); + if (!$deleted) { + return ['ok' => false, 'status' => 500, 'error' => 'delete_failed']; + } + + return ['ok' => true, 'tenant' => $tenant]; + } + + private static function sanitizeBase(array $input): array + { + return [ + 'description' => trim((string) ($input['description'] ?? '')), + 'address' => trim((string) ($input['address'] ?? '')), + 'postal_code' => trim((string) ($input['postal_code'] ?? '')), + 'city' => trim((string) ($input['city'] ?? '')), + 'country' => trim((string) ($input['country'] ?? '')), + 'region' => trim((string) ($input['region'] ?? '')), + 'vat_id' => trim((string) ($input['vat_id'] ?? '')), + 'tax_number' => trim((string) ($input['tax_number'] ?? '')), + 'phone' => trim((string) ($input['phone'] ?? '')), + 'fax' => trim((string) ($input['fax'] ?? '')), + 'email' => trim((string) ($input['email'] ?? '')), + 'support_email' => trim((string) ($input['support_email'] ?? '')), + 'support_phone' => trim((string) ($input['support_phone'] ?? '')), + 'billing_email' => trim((string) ($input['billing_email'] ?? '')), + 'website' => trim((string) ($input['website'] ?? '')), + 'privacy_url' => trim((string) ($input['privacy_url'] ?? '')), + 'imprint_url' => trim((string) ($input['imprint_url'] ?? '')), + 'primary_color' => trim((string) ($input['primary_color'] ?? '')), + 'primary_color_use_default' => !empty($input['primary_color_use_default']), + 'status' => trim((string) ($input['status'] ?? '')), + ]; + } + + private static function validateBase(array $form): array + { + $errors = []; + if ($form['description'] === '') { + $errors[] = t('Description cannot be empty'); + } + if (!in_array($form['status'], ['active', 'inactive'], true)) { + $errors[] = t('Status is invalid'); + } + if ( + !$form['primary_color_use_default'] + && $form['primary_color'] !== '' + && !preg_match('/^#([0-9a-f]{3}|[0-9a-f]{6})$/i', $form['primary_color']) + ) { + $errors[] = t('Primary color is invalid'); + } + return $errors; + } + + private static function normalizeStatus(string $status): string + { + $status = strtolower(trim($status)); + if (!in_array($status, ['active', 'inactive'], true)) { + return 'active'; + } + return $status; + } +} diff --git a/lib/Service/UserAvatarService.php b/lib/Service/UserAvatarService.php new file mode 100644 index 0000000..0f86d61 --- /dev/null +++ b/lib/Service/UserAvatarService.php @@ -0,0 +1,307 @@ + false, 'error' => t('User not found')]; + } + if (empty($file) || !isset($file['tmp_name'])) { + return ['ok' => false, 'error' => t('No file uploaded')]; + } + if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + if (($file['size'] ?? 0) > self::MAX_SIZE) { + return ['ok' => false, 'error' => t('File is too large')]; + } + + $tmpPath = $file['tmp_name']; + $mime = self::detectMime($tmpPath); + $isSvg = self::isSvgUpload($mime, $tmpPath); + $ext = self::extensionForMime($mime, $isSvg); + if (!$ext) { + return ['ok' => false, 'error' => t('Invalid image file')]; + } + if ($isSvg && !self::isSafeSvg($tmpPath)) { + return ['ok' => false, 'error' => t('Invalid image file')]; + } + + $dir = self::userDir($uuid); + if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + + self::delete($uuid); + $originalPath = $dir . '/original.' . $ext; + if (!move_uploaded_file($tmpPath, $originalPath)) { + return ['ok' => false, 'error' => t('Upload failed')]; + } + @chmod($originalPath, 0644); + + $variantExt = function_exists('imagewebp') ? 'webp' : 'jpg'; + if (!$isSvg && self::canResize()) { + foreach (self::SIZES as $size) { + $target = $dir . '/avatar-' . $size . '.' . $variantExt; + self::resizeAndFit($originalPath, $target, $size, $size, $variantExt); + } + } + + return ['ok' => true, 'path' => $originalPath, 'mime' => $mime]; + } + + public static function detectMime(string $path): string + { + if (function_exists('finfo_open')) { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + if ($finfo) { + $mime = finfo_file($finfo, $path); + if (is_string($mime) && $mime !== '') { + if (self::isSvgUpload($mime, $path)) { + return 'image/svg+xml'; + } + return $mime; + } + } + } + if (self::isSvgUpload('application/octet-stream', $path)) { + return 'image/svg+xml'; + } + return 'application/octet-stream'; + } + + private static function extensionForMime(string $mime, bool $isSvg = false): ?string + { + if ($isSvg) { + return 'svg'; + } + $map = [ + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/webp' => 'webp', + ]; + return $map[$mime] ?? null; + } + + private static function isSvgUpload(string $mime, string $path): bool + { + if ($mime === 'image/svg+xml') { + return true; + } + $head = @file_get_contents($path, false, null, 0, 1024); + if (!is_string($head) || $head === '') { + return false; + } + return stripos($head, ' filemtime($a); + }); + return $matches[0] ?? null; + } + + private static function findOriginalPath(string $dir): ?string + { + $matches = glob($dir . '/original.*'); + if (!$matches) { + return null; + } + usort($matches, static function ($a, $b) { + return filemtime($b) <=> filemtime($a); + }); + return $matches[0] ?? null; + } + + private static function canResize(): bool + { + return function_exists('imagecreatetruecolor') && function_exists('imagecopyresampled'); + } + + private static function createImageResource(string $path, string $mime) + { + if ($mime === 'image/jpeg' && function_exists('imagecreatefromjpeg')) { + return imagecreatefromjpeg($path); + } + if ($mime === 'image/png' && function_exists('imagecreatefrompng')) { + return imagecreatefrompng($path); + } + if ($mime === 'image/webp' && function_exists('imagecreatefromwebp')) { + return imagecreatefromwebp($path); + } + return false; + } + + private static function resizeAndFit(string $sourcePath, string $targetPath, int $width, int $height, string $ext): bool + { + $mime = self::detectMime($sourcePath); + $src = self::createImageResource($sourcePath, $mime); + if (!$src) { + return false; + } + + $srcWidth = imagesx($src); + $srcHeight = imagesy($src); + if ($srcWidth === 0 || $srcHeight === 0) { + imagedestroy($src); + return false; + } + + $scale = min($width / $srcWidth, $height / $srcHeight); + $dstWidth = (int) round($srcWidth * $scale); + $dstHeight = (int) round($srcHeight * $scale); + if ($dstWidth < 1) $dstWidth = 1; + if ($dstHeight < 1) $dstHeight = 1; + + $dst = imagecreatetruecolor($dstWidth, $dstHeight); + if ($ext === 'png' || $ext === 'webp') { + imagealphablending($dst, false); + imagesavealpha($dst, true); + $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127); + imagefilledrectangle($dst, 0, 0, $dstWidth, $dstHeight, $transparent); + } + + imagecopyresampled( + $dst, + $src, + 0, + 0, + 0, + 0, + $dstWidth, + $dstHeight, + $srcWidth, + $srcHeight + ); + + $saved = false; + if ($ext === 'webp' && function_exists('imagewebp')) { + $saved = imagewebp($dst, $targetPath, 82); + } elseif ($ext === 'png' && function_exists('imagepng')) { + $saved = imagepng($dst, $targetPath, 6); + } else { + $saved = imagejpeg($dst, $targetPath, 85); + } + + imagedestroy($src); + imagedestroy($dst); + + if ($saved) { + @chmod($targetPath, 0644); + } + return $saved; + } +} diff --git a/lib/Service/UserService.php b/lib/Service/UserService.php new file mode 100644 index 0000000..0e7f9cf --- /dev/null +++ b/lib/Service/UserService.php @@ -0,0 +1,804 @@ + 0 && TenantScopeService::hasGlobalAccess($tenantUserId)) { + unset($options['tenantUserId']); + } + } + return UserRepository::listPaged($options); + } + + public static function findByUuid(string $uuid): ?array + { + return UserRepository::findByUuid($uuid); + } + + public static function findById(int $id): ?array + { + return UserRepository::find($id); + } + + public static function findByEmail(string $email): ?array + { + return UserRepository::findByEmail($email); + } + + public static function setLocale(int $userId, string $locale): bool + { + return UserRepository::setLocale($userId, $locale); + } + + public static function setTheme(int $userId, string $theme): bool + { + $theme = self::normalizeTheme($theme); + return UserRepository::setTheme($userId, $theme); + } + + public static function deleteByUuid(string $uuid, int $currentUserId = 0): array + { + $uuid = trim($uuid); + if ($uuid === '') { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $user = UserRepository::findByUuid($uuid); + if (!$user || !isset($user['id'])) { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $userId = (int) $user['id']; + if ($currentUserId && $currentUserId === $userId) { + return [ + 'ok' => false, + 'status' => 400, + 'error' => 'self_delete', + 'message' => t('You cannot delete your own account'), + ]; + } + + $deleted = UserRepository::delete($userId); + if (!$deleted) { + return ['ok' => false, 'status' => 500, 'error' => 'delete_failed']; + } + + return ['ok' => true, 'user' => $user]; + } + + public static function deleteByUuids(array $uuids, int $currentUserId = 0): array + { + $uuids = array_values(array_filter(array_map('trim', $uuids))); + if (!$uuids) { + return ['ok' => false, 'error' => 'no_selection']; + } + + if ($currentUserId > 0) { + $uuids = self::filterUuidsByTenantScope($uuids, $currentUserId); + if (!$uuids) { + return ['ok' => false, 'error' => 'permission_denied']; + } + } + + if ($currentUserId > 0) { + $currentUser = UserRepository::find($currentUserId); + $currentUuid = $currentUser['uuid'] ?? ''; + if ($currentUuid !== '') { + $uuids = array_values(array_filter($uuids, static fn ($uuid) => $uuid !== $currentUuid)); + } + if (!$uuids) { + return ['ok' => false, 'error' => 'self_delete']; + } + } + + $deleted = UserRepository::deleteByUuids($uuids); + if (!$deleted) { + return ['ok' => false, 'error' => 'delete_failed']; + } + + return ['ok' => true, 'count' => count($uuids)]; + } + + public static function createFromAdmin(array $input, int $currentUserId = 0): array + { + $form = self::sanitizeBase($input); + $form['totp_secret'] = trim((string) ($input['totp_secret'] ?? '')); + $form['theme'] = self::normalizeTheme($input['theme'] ?? null); + $form['locale'] = self::normalizeLocale($input['locale'] ?? null); + $primaryTenantId = (int) ($input['primary_tenant_id'] ?? 0); + if (array_key_exists('active', $input)) { + $form['active'] = isset($input['active']) ? 1 : 0; + } else { + $form['active'] = 1; + } + $password = (string) ($input['password'] ?? ''); + $password2 = (string) ($input['password2'] ?? ''); + + $errors = self::validateBase($form); + $errors = array_merge($errors, self::validatePassword($password, $password2, true, $form['email'])); + + $tenantIds = $input['tenant_ids'] ?? []; + if (!is_array($tenantIds)) { + $tenantIds = [$tenantIds]; + } + $tenantIds = self::normalizeTenantIds($tenantIds); + if (!$tenantIds) { + $defaultTenantId = SettingService::getDefaultTenantId(); + if ($defaultTenantId) { + $tenantIds = [$defaultTenantId]; + } + } + [$primaryTenantId, $primaryErrors] = self::normalizePrimaryTenant($primaryTenantId, $tenantIds); + $errors = array_merge($errors, $primaryErrors); + + if ($errors) { + $form['primary_tenant_id'] = $primaryTenantId; + return ['ok' => false, 'errors' => $errors, 'form' => $form]; + } + + $activeChangedAt = gmdate('Y-m-d H:i:s'); + $created = UserRepository::create([ + 'first_name' => $form['first_name'], + 'last_name' => $form['last_name'], + 'email' => $form['email'], + 'profile_description' => $form['profile_description'] !== '' ? $form['profile_description'] : null, + 'job_title' => $form['job_title'] !== '' ? $form['job_title'] : null, + 'phone' => $form['phone'] !== '' ? $form['phone'] : null, + 'mobile' => $form['mobile'] !== '' ? $form['mobile'] : null, + 'short_dial' => $form['short_dial'] !== '' ? $form['short_dial'] : null, + 'address' => $form['address'] !== '' ? $form['address'] : null, + 'postal_code' => $form['postal_code'] !== '' ? $form['postal_code'] : null, + 'city' => $form['city'] !== '' ? $form['city'] : null, + 'country' => $form['country'] !== '' ? $form['country'] : null, + 'region' => $form['region'] !== '' ? $form['region'] : null, + 'hire_date' => $form['hire_date'] !== '' ? $form['hire_date'] : null, + 'password' => $password, + 'locale' => $form['locale'], + 'totp_secret' => $form['totp_secret'], + 'theme' => $form['theme'], + 'primary_tenant_id' => $primaryTenantId > 0 ? $primaryTenantId : null, + 'active' => $form['active'], + 'created_by' => $currentUserId > 0 ? $currentUserId : null, + 'active_changed_at' => $activeChangedAt, + 'active_changed_by' => $currentUserId > 0 ? $currentUserId : null, + ]); + + if (!$created) { + return ['ok' => false, 'errors' => [t('User can not be registered')], 'form' => $form]; + } + + $createdUser = UserRepository::findByEmail($form['email']); + $uuid = $createdUser['uuid'] ?? null; + $userId = (int) ($createdUser['id'] ?? 0); + + if ($userId > 0 && $tenantIds) { + self::syncTenants($userId, $tenantIds); + } + + $roleIds = self::normalizeIdInput($input['role_ids'] ?? []); + if (!$roleIds) { + $defaultRoleId = SettingService::getDefaultRoleId(); + if ($defaultRoleId) { + $roleIds = [$defaultRoleId]; + } + } + if ($userId > 0 && $roleIds) { + self::syncRoles($userId, $roleIds); + } + + $departmentIds = self::normalizeIdInput($input['department_ids'] ?? []); + if (!$departmentIds) { + $defaultDepartmentId = SettingService::getDefaultDepartmentId(); + if ($defaultDepartmentId) { + $departmentIds = [$defaultDepartmentId]; + } + } + if ($userId > 0 && $departmentIds) { + self::syncDepartments($userId, $departmentIds); + } + + return ['ok' => true, 'form' => $form, 'uuid' => $uuid]; + } + + public static function updateFromAdmin(int $userId, array $input, int $currentUserId = 0): array + { + $form = self::sanitizeBase($input); + $form['totp_secret'] = trim((string) ($input['totp_secret'] ?? '')); + $form['theme'] = self::normalizeTheme($input['theme'] ?? null); + $form['locale'] = self::normalizeLocale($input['locale'] ?? null); + $primaryTenantId = (int) ($input['primary_tenant_id'] ?? 0); + $tenantIdsProvided = array_key_exists('tenant_ids', $input); + $primaryProvided = array_key_exists('primary_tenant_id', $input); + if ($tenantIdsProvided) { + $tenantIds = $input['tenant_ids'] ?? []; + if (!is_array($tenantIds)) { + $tenantIds = [$tenantIds]; + } + $tenantIds = self::normalizeTenantIds($tenantIds); + } else { + $tenantIds = UserTenantRepository::listTenantIdsByUserId($userId); + } + if (array_key_exists('active', $input)) { + $form['active'] = isset($input['active']) ? 1 : 0; + } else { + $existing = UserRepository::find($userId); + $form['active'] = (int) ($existing['active'] ?? 1); + } + $password = (string) ($input['password'] ?? ''); + $password2 = (string) ($input['password2'] ?? ''); + + $errors = self::validateBase($form, $userId); + if ($userId === $currentUserId && !$form['active']) { + $errors[] = t('You cannot deactivate your own account'); + } + $errors = array_merge($errors, self::validatePassword($password, $password2, false, $form['email'])); + if ($tenantIds && ($tenantIdsProvided || $primaryProvided)) { + [$primaryTenantId, $primaryErrors] = self::normalizePrimaryTenant($primaryTenantId, $tenantIds); + $errors = array_merge($errors, $primaryErrors); + } + + if ($errors) { + if ($tenantIdsProvided || $primaryProvided) { + $form['primary_tenant_id'] = $primaryTenantId; + } + return ['ok' => false, 'errors' => $errors, 'form' => $form]; + } + + if ($tenantIdsProvided || $primaryProvided) { + $form['primary_tenant_id'] = $primaryTenantId > 0 ? $primaryTenantId : null; + } + + $updateData = [ + 'first_name' => $form['first_name'], + 'last_name' => $form['last_name'], + 'email' => $form['email'], + 'profile_description' => $form['profile_description'] !== '' ? $form['profile_description'] : null, + 'job_title' => $form['job_title'] !== '' ? $form['job_title'] : null, + 'phone' => $form['phone'] !== '' ? $form['phone'] : null, + 'mobile' => $form['mobile'] !== '' ? $form['mobile'] : null, + 'short_dial' => $form['short_dial'] !== '' ? $form['short_dial'] : null, + 'address' => $form['address'] !== '' ? $form['address'] : null, + 'postal_code' => $form['postal_code'] !== '' ? $form['postal_code'] : null, + 'city' => $form['city'] !== '' ? $form['city'] : null, + 'country' => $form['country'] !== '' ? $form['country'] : null, + 'region' => $form['region'] !== '' ? $form['region'] : null, + 'hire_date' => $form['hire_date'] !== '' ? $form['hire_date'] : null, + 'totp_secret' => $form['totp_secret'], + 'theme' => $form['theme'], + 'locale' => $form['locale'], + 'active' => $form['active'], + 'password' => $password, + 'modified_by' => $currentUserId > 0 ? $currentUserId : null, + ]; + if ($tenantIdsProvided || $primaryProvided) { + $updateData['primary_tenant_id'] = $form['primary_tenant_id'] ?? null; + } + + if ((int) ($existing['active'] ?? 1) !== (int) $form['active']) { + $updateData['active_changed_at'] = gmdate('Y-m-d H:i:s'); + $updateData['active_changed_by'] = $currentUserId > 0 ? $currentUserId : null; + } + + $updated = UserRepository::update($userId, $updateData); + + if (!$updated) { + return ['ok' => false, 'errors' => [t('User can not be updated')], 'form' => $form]; + } + + return ['ok' => true, 'form' => $form]; + } + + public static function setActiveByUuid(string $uuid, bool $active, int $currentUserId = 0): array + { + $uuid = trim($uuid); + if ($uuid === '') { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $user = UserRepository::findByUuid($uuid); + if (!$user || !isset($user['id'])) { + return ['ok' => false, 'status' => 404, 'error' => 'not_found']; + } + + $userId = (int) $user['id']; + if (!$active && $currentUserId && $currentUserId === $userId) { + return [ + 'ok' => false, + 'status' => 400, + 'error' => 'self_deactivate', + 'message' => t('You cannot deactivate your own account'), + ]; + } + + $updated = UserRepository::setActive($userId, $active, $currentUserId > 0 ? $currentUserId : null); + if (!$updated) { + return ['ok' => false, 'status' => 500, 'error' => 'update_failed']; + } + + return ['ok' => true, 'user' => $user]; + } + + public static function setActiveByUuids(array $uuids, bool $active, int $currentUserId = 0): array + { + $uuids = array_values(array_filter(array_map('trim', $uuids))); + if (!$uuids) { + return ['ok' => false, 'error' => 'no_selection']; + } + + if ($currentUserId > 0) { + $uuids = self::filterUuidsByTenantScope($uuids, $currentUserId); + if (!$uuids) { + return ['ok' => false, 'error' => 'permission_denied']; + } + } + + $updated = UserRepository::setActiveByUuids($uuids, $active, $currentUserId > 0 ? $currentUserId : null); + if (!$updated) { + return ['ok' => false, 'error' => 'update_failed']; + } + + return ['ok' => true, 'count' => count($uuids)]; + } + + private static function filterUuidsByTenantScope(array $uuids, int $currentUserId): array + { + $allowed = []; + foreach ($uuids as $uuid) { + $user = UserRepository::findByUuid((string) $uuid); + if (!$user || !isset($user['id'])) { + continue; + } + $userId = (int) $user['id']; + if ($userId === $currentUserId) { + $allowed[] = (string) $uuid; + continue; + } + if (TenantScopeService::canAccess('users', $userId, $currentUserId)) { + $allowed[] = (string) $uuid; + } + } + return array_values(array_unique($allowed)); + } + + public static function register(array $input): array + { + $form = self::sanitizeBase($input); + $password = (string) ($input['password'] ?? ''); + $password2 = (string) ($input['password2'] ?? ''); + + $errors = self::validateBase($form); + $errors = array_merge($errors, self::validatePassword($password, $password2, true, $form['email'])); + + if ($errors) { + return ['ok' => false, 'error' => $errors[0]]; + } + + $defaultTheme = SettingService::getAppTheme(); + $defaultTenantId = SettingService::getDefaultTenantId(); + $activeChangedAt = gmdate('Y-m-d H:i:s'); + $created = UserRepository::create([ + 'first_name' => $form['first_name'], + 'last_name' => $form['last_name'], + 'email' => $form['email'], + 'password' => $password, + 'locale' => I18n::$locale, + 'totp_secret' => '', + 'theme' => self::normalizeTheme($defaultTheme ?? 'light'), + 'primary_tenant_id' => $defaultTenantId ?: null, + 'active' => 1, + 'created_by' => null, + 'active_changed_at' => $activeChangedAt, + 'active_changed_by' => null, + ]); + + if (!$created) { + return ['ok' => false, 'error' => t('User can not be registered')]; + } + + $createdUser = UserRepository::findByEmail($form['email']); + $userId = (int) ($createdUser['id'] ?? 0); + if ($userId > 0) { + if ($defaultTenantId) { + self::syncTenants($userId, [$defaultTenantId]); + } + $defaultRoleId = SettingService::getDefaultRoleId(); + if ($defaultRoleId) { + self::syncRoles($userId, [$defaultRoleId]); + } + $defaultDepartmentId = SettingService::getDefaultDepartmentId(); + if ($defaultDepartmentId) { + self::syncDepartments($userId, [$defaultDepartmentId]); + } + } + + return ['ok' => true]; + } + + public static function syncTenants(int $userId, array $tenantIds): bool + { + $ids = self::normalizeTenantIds($tenantIds); + return UserTenantRepository::replaceForUser($userId, $ids); + } + + public static function syncRoles(int $userId, array $roleIds): bool + { + $ids = array_values(array_unique(array_map('intval', $roleIds))); + $ids = array_filter($ids, static fn ($id) => $id > 0); + $validIds = RoleRepository::listIds(); + if ($validIds) { + $validMap = array_fill_keys($validIds, true); + $ids = array_values(array_filter($ids, static fn ($id) => isset($validMap[$id]))); + } + $result = UserRoleRepository::replaceForUser($userId, $ids); + PermissionService::clearUserCache($userId); + return $result; + } + + public static function syncDepartments(int $userId, array $departmentIds): bool + { + $ids = array_values(array_unique(array_map('intval', $departmentIds))); + $ids = array_filter($ids, static fn ($id) => $id > 0); + $userTenantIds = TenantScopeService::getUserTenantIds($userId); + if (!$userTenantIds) { + $ids = []; + } else { + $allowedIds = TenantDepartmentRepository::listDepartmentIdsByTenantIds($userTenantIds); + if ($allowedIds) { + $allowedMap = array_fill_keys($allowedIds, true); + $ids = array_values(array_filter($ids, static fn ($id) => isset($allowedMap[$id]))); + } else { + $ids = []; + } + } + return UserDepartmentRepository::replaceForUser($userId, $ids); + } + + private static function sanitizeBase(array $input): array + { + return [ + 'first_name' => trim((string) ($input['first_name'] ?? '')), + 'last_name' => trim((string) ($input['last_name'] ?? '')), + 'email' => trim((string) ($input['email'] ?? '')), + 'profile_description' => trim((string) ($input['profile_description'] ?? '')), + 'job_title' => trim((string) ($input['job_title'] ?? '')), + 'phone' => trim((string) ($input['phone'] ?? '')), + 'mobile' => trim((string) ($input['mobile'] ?? '')), + 'short_dial' => trim((string) ($input['short_dial'] ?? '')), + 'address' => trim((string) ($input['address'] ?? '')), + 'postal_code' => trim((string) ($input['postal_code'] ?? '')), + 'city' => trim((string) ($input['city'] ?? '')), + 'country' => trim((string) ($input['country'] ?? '')), + 'region' => trim((string) ($input['region'] ?? '')), + 'hire_date' => trim((string) ($input['hire_date'] ?? '')), + ]; + } + + public static function normalizeIdInput($value): array + { + if (!is_array($value)) { + $value = [$value]; + } + $ids = array_values(array_unique(array_map('intval', $value))); + return array_values(array_filter($ids, static fn ($id) => $id > 0)); + } + + private static function normalizeTenantIds(array $tenantIds): array + { + $ids = array_values(array_unique(array_map('intval', $tenantIds))); + $ids = array_filter($ids, static fn ($id) => $id > 0); + $validIds = TenantRepository::listIds(); + if ($validIds) { + $validMap = array_fill_keys($validIds, true); + $ids = array_values(array_filter($ids, static fn ($id) => isset($validMap[$id]))); + } + return $ids; + } + + private static function normalizeTheme($value): string + { + $theme = strtolower(trim((string) $value)); + if (!in_array($theme, ['dark', 'light'], true)) { + return 'light'; + } + return $theme; + } + + private static function normalizeLocale($value): string + { + $locale = strtolower(trim((string) $value)); + $available = defined('APP_LOCALES') ? APP_LOCALES : [I18n::$defaultLocale]; + if ($locale === '' || !in_array($locale, $available, true)) { + return I18n::$locale ?? I18n::$defaultLocale; + } + return $locale; + } + + private static function validateBase(array $form, ?int $excludeId = null): array + { + $errors = []; + if ($form['first_name'] === '') { + $errors[] = t('First name cannot be empty'); + } + if ($form['last_name'] === '') { + $errors[] = t('Last name cannot be empty'); + } + if ($form['email'] === '') { + $errors[] = t('Email cannot be empty'); + } elseif (!filter_var($form['email'], FILTER_VALIDATE_EMAIL)) { + $errors[] = t('Email is not valid'); + } else { + $existing = UserRepository::findByEmail($form['email']); + if ($existing && (!isset($existing['id']) || (int) $existing['id'] !== (int) $excludeId)) { + $errors[] = t('Email is already taken'); + } + } + + return $errors; + } + + private static function normalizePrimaryTenant(int $primaryTenantId, array $tenantIds): array + { + if (!$tenantIds) { + return [0, []]; + } + if ($primaryTenantId > 0 && !in_array($primaryTenantId, $tenantIds, true)) { + return [$primaryTenantId, [t('Primary tenant must be one of the assigned tenants')]]; + } + if ($primaryTenantId === 0 && count($tenantIds) > 1) { + return [0, [t('Please select a primary tenant')]]; + } + if ($primaryTenantId === 0 && count($tenantIds) === 1) { + return [(int) $tenantIds[0], []]; + } + return [$primaryTenantId, []]; + } + + private static function validatePassword( + string $password, + string $password2, + bool $required, + ?string $email + ): array { + return self::validatePasswordStrength($password, $password2, $required, $email); + } + + public static function passwordHints(): array + { + return [ + ['rule' => 'min', 'text' => t('At least %d characters', self::PASSWORD_MIN_LENGTH)], + ['rule' => 'upper', 'text' => t('At least one uppercase letter')], + ['rule' => 'lower', 'text' => t('At least one lowercase letter')], + ['rule' => 'number', 'text' => t('At least one number')], + ['rule' => 'symbol', 'text' => t('At least one symbol')], + ['rule' => 'email', 'text' => t('Must not contain your email')], + ]; + } + + public static function passwordMinLength(): int + { + return self::PASSWORD_MIN_LENGTH; + } + + public static function resetPassword(int $userId, string $password, string $password2): array + { + $errors = self::validatePasswordStrength($password, $password2, true, ''); + if ($errors) { + return ['ok' => false, 'errors' => $errors]; + } + + $updated = UserRepository::setPassword($userId, $password); + if (!$updated) { + return ['ok' => false, 'errors' => [t('Password can not be updated')]]; + } + + return ['ok' => true]; + } + + private static function validatePasswordStrength( + string $password, + string $password2, + bool $required, + ?string $email + ): array { + $errors = []; + if ($required && $password === '') { + $errors[] = t('Password cannot be empty'); + return $errors; + } + if ($password !== '' && $password !== $password2) { + $errors[] = t('Passwords must match'); + } + if ($password === '') { + return $errors; + } + if (strlen($password) < self::PASSWORD_MIN_LENGTH) { + $errors[] = t('Password must be at least %d characters', self::PASSWORD_MIN_LENGTH); + } + if (!preg_match('/[A-Z]/', $password)) { + $errors[] = t('Password must include an uppercase letter'); + } + if (!preg_match('/[a-z]/', $password)) { + $errors[] = t('Password must include a lowercase letter'); + } + if (!preg_match('/\\d/', $password)) { + $errors[] = t('Password must include a number'); + } + if (!preg_match('/[^a-zA-Z0-9]/', $password)) { + $errors[] = t('Password must include a symbol'); + } + if ($email) { + $email = trim((string) $email); + if ($email !== '' && stripos($password, $email) !== false) { + $errors[] = t('Password must not contain your email'); + } + } + return $errors; + } + + /** + * Get the current tenant ID for a user (with fallback to primary tenant) + */ + public static function getCurrentTenantId(int $userId): ?int + { + $user = UserRepository::find($userId); + if (!$user) { + return null; + } + + $activeTenantIds = TenantScopeService::getUserTenantIds($userId); + if (!$activeTenantIds) { + return null; + } + + $currentTenantId = (int) ($user['current_tenant_id'] ?? 0); + if ($currentTenantId > 0 && in_array($currentTenantId, $activeTenantIds, true)) { + return $currentTenantId; + } + + $primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0); + if ($primaryTenantId > 0 && in_array($primaryTenantId, $activeTenantIds, true)) { + return $primaryTenantId; + } + + return $activeTenantIds[0] ?? null; + } + + /** + * Get the current tenant data for a user + */ + public static function getCurrentTenant(int $userId): ?array + { + $currentTenantId = self::getCurrentTenantId($userId); + if (!$currentTenantId) { + return null; + } + + return TenantRepository::find($currentTenantId); + } + + /** + * Get all tenants the user has access to + */ + public static function getAvailableTenants(int $userId): array + { + $tenantIds = TenantScopeService::getUserTenantIds($userId); + if (!$tenantIds) { + return []; + } + + $tenants = []; + foreach ($tenantIds as $tenantId) { + $tenant = TenantRepository::find($tenantId); + if ($tenant && (($tenant['status'] ?? 'active') === 'active')) { + $tenants[] = $tenant; + } + } + + // Sort by description + usort($tenants, static function ($a, $b) { + return strcmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? '')); + }); + + return $tenants; + } + + /** + * Get available departments grouped by tenant for a user. + * Returns an array of groups: ['tenant' => [...], 'departments' => [...]] + */ + public static function getAvailableDepartmentsByTenant(int $userId): array + { + $tenants = self::getAvailableTenants($userId); + if (!$tenants) { + return []; + } + + $groups = []; + foreach ($tenants as $tenant) { + $tenantId = (int) ($tenant['id'] ?? 0); + if ($tenantId <= 0) { + continue; + } + $departmentIds = TenantDepartmentRepository::listDepartmentIdsByTenantIds([$tenantId]); + $departments = $departmentIds ? DepartmentRepository::listByIds($departmentIds) : []; + usort($departments, static function ($a, $b) { + return strcmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? '')); + }); + $departments = array_map(static function (array $department): array { + return [ + 'id' => (int) ($department['id'] ?? 0), + 'uuid' => $department['uuid'] ?? '', + 'description' => $department['description'] ?? '', + ]; + }, $departments); + + $groups[] = [ + 'tenant' => [ + 'id' => (int) ($tenant['id'] ?? 0), + 'uuid' => $tenant['uuid'] ?? '', + 'description' => $tenant['description'] ?? '', + ], + 'departments' => $departments, + ]; + } + + return $groups; + } + + /** + * Set the current tenant for a user (with validation) + */ + public static function setCurrentTenant(int $userId, int $tenantId): array + { + if ($tenantId <= 0) { + return ['ok' => false, 'error' => 'invalid_tenant']; + } + + // Verify user has access to this tenant + $userTenantIds = UserTenantRepository::listTenantIdsByUserId($userId); + if (!in_array($tenantId, $userTenantIds, true)) { + return ['ok' => false, 'error' => 'tenant_not_assigned']; + } + + // Verify tenant exists + $tenant = TenantRepository::find($tenantId); + if (!$tenant) { + return ['ok' => false, 'error' => 'tenant_not_found']; + } + if (($tenant['status'] ?? 'active') !== 'active') { + return ['ok' => false, 'error' => 'tenant_inactive']; + } + + // Update current_tenant_id + $updated = UserRepository::setCurrentTenant($userId, $tenantId); + + if (!$updated) { + return ['ok' => false, 'error' => 'update_failed']; + } + + return ['ok' => true, 'tenant' => $tenant]; + } +} diff --git a/lib/Support/Flash.php b/lib/Support/Flash.php new file mode 100644 index 0000000..c79d3a3 --- /dev/null +++ b/lib/Support/Flash.php @@ -0,0 +1,103 @@ + $id, + 'type' => $type, + 'message' => $message, + 'scope' => $scope, + 'key' => $key, + ]; + return $id; + } + + public static function success(string $message, ?string $scope = null, ?string $key = null): string + { + return self::add('success', $message, $scope, $key); + } + + public static function error(string $message, ?string $scope = null, ?string $key = null): string + { + return self::add('error', $message, $scope, $key); + } + + public static function info(string $message, ?string $scope = null, ?string $key = null): string + { + return self::add('info', $message, $scope, $key); + } + + public static function peek(?string $scope = null): array + { + self::ensureSession(); + $messages = $_SESSION[self::SESSION_KEY] ?? []; + if ($scope === null) { + return $messages; + } + + return array_values(array_filter($messages, function ($message) use ($scope) { + $messageScope = $message['scope'] ?? null; + return $messageScope === null || $messageScope === $scope; + })); + } + + public static function has(): bool + { + self::ensureSession(); + return !empty($_SESSION[self::SESSION_KEY]); + } + + public static function dismiss(string $id): void + { + self::ensureSession(); + $messages = $_SESSION[self::SESSION_KEY] ?? []; + $messages = array_values(array_filter($messages, function ($message) use ($id) { + return ($message['id'] ?? '') !== $id; + })); + + if ($messages) { + $_SESSION[self::SESSION_KEY] = $messages; + } else { + unset($_SESSION[self::SESSION_KEY]); + } + } + + public static function keep(int $times = 1) + { + self::ensureSession(); + $times = max(1, $times); + $current = (int) ($_SESSION[self::KEEP_KEY] ?? 0); + $_SESSION[self::KEEP_KEY] = max($current, $times); + } +} diff --git a/lib/Support/Guard.php b/lib/Support/Guard.php new file mode 100644 index 0000000..31eb747 --- /dev/null +++ b/lib/Support/Guard.php @@ -0,0 +1,92 @@ + 0) { + AuthService::loadTenantDataIntoSession($userId); + } else { + unset($_SESSION['current_tenant']); + } + } + + private static function refreshTenantContext(): void + { + $userId = (int) ($_SESSION['user']['id'] ?? 0); + if ($userId <= 0) { + return; + } + $last = (int) ($_SESSION['tenant_context_refreshed_at'] ?? 0); + $now = time(); + if ($last > 0 && ($now - $last) < self::TENANT_REFRESH_INTERVAL) { + return; + } + AuthService::loadTenantDataIntoSession($userId); + $_SESSION['tenant_context_refreshed_at'] = $now; + } + + public static function requirePermission(string $permissionKey, string $redirect = 'admin'): void + { + self::requireLogin(); + self::validateCurrentTenant(); + $userId = (int) ($_SESSION['user']['id'] ?? 0); + if (!$userId || !PermissionService::userHas($userId, $permissionKey)) { + Flash::error('Permission denied', $redirect, 'permission_denied'); + Router::redirect($redirect); + } + } + + public static function requirePermissionOrForbidden(string $permissionKey): void + { + self::requireLogin(); + self::validateCurrentTenant(); + $userId = (int) ($_SESSION['user']['id'] ?? 0); + if (!$userId || !PermissionService::userHas($userId, $permissionKey)) { + if (Request::wantsJson()) { + http_response_code(403); + header('Content-Type: application/json; charset=utf-8'); + echo json_encode(['error' => 'forbidden']); + exit; + } + Router::redirect('error/forbidden'); + } + } +} diff --git a/lib/Support/Hello.php b/lib/Support/Hello.php new file mode 100644 index 0000000..c8b2f79 --- /dev/null +++ b/lib/Support/Hello.php @@ -0,0 +1,13 @@ + 'and exists (select 1 from user_tenants ut where ut.user_id = users.id and ut.tenant_id in (???))', + 'address-book' => 'and exists (select 1 from user_tenants ut where ut.user_id = users.id and ut.tenant_id in (???))', + 'tenants' => 'and id in (???)', + 'departments' => 'and exists (select 1 from tenant_departments td where td.department_id = departments.id and td.tenant_id in (???))', + ]; + } + + public static function resources(string $query, string $locale): array + { + $like = '%' . $query . '%'; + + return [ + [ + 'key' => 'address-book', + 'label' => t('Address book'), + 'permission' => PermissionService::ADDRESS_BOOK_VIEW, + 'countSql' => 'select count(*) from users where active = 1 and (first_name like ? or last_name like ? or email like ?) {{tenantFilter}}', + 'countParams' => [$like, $like, $like], + 'previewSql' => 'select uuid, first_name, last_name, email from users where active = 1 and (first_name like ? or last_name like ? or email like ?) {{tenantFilter}} order by last_name, first_name limit ?', + 'previewParams' => [$like, $like, $like], + 'resultSql' => 'select uuid, first_name, last_name, email from users where active = 1 and (first_name like ? or last_name like ? or email like ?) {{tenantFilter}} order by last_name, first_name', + 'resultParams' => [$like, $like, $like], + ], + [ + 'key' => 'users', + 'label' => t('Users'), + 'permission' => PermissionService::USERS_VIEW, + 'countSql' => 'select count(*) from users where (first_name like ? or last_name like ? or email like ?) {{tenantFilter}}', + 'countParams' => [$like, $like, $like], + 'previewSql' => 'select uuid, first_name, last_name, email from users where (first_name like ? or last_name like ? or email like ?) {{tenantFilter}} order by last_name, first_name limit ?', + 'previewParams' => [$like, $like, $like], + 'resultSql' => 'select uuid, first_name, last_name, email from users where (first_name like ? or last_name like ? or email like ?) {{tenantFilter}} order by last_name, first_name', + 'resultParams' => [$like, $like, $like], + ], + [ + 'key' => 'tenants', + 'label' => t('Tenants'), + 'permission' => PermissionService::TENANTS_VIEW, + 'countSql' => 'select count(*) from tenants where description like ? {{tenantFilter}}', + 'countParams' => [$like], + 'previewSql' => 'select uuid, description from tenants where description like ? {{tenantFilter}} order by description limit ?', + 'previewParams' => [$like], + 'resultSql' => 'select uuid, description from tenants where description like ? {{tenantFilter}} order by description', + 'resultParams' => [$like], + ], + [ + 'key' => 'departments', + 'label' => t('Departments'), + 'permission' => PermissionService::DEPARTMENTS_VIEW, + 'countSql' => 'select count(*) from departments where description like ? {{tenantFilter}}', + 'countParams' => [$like], + 'previewSql' => 'select uuid, description from departments where description like ? {{tenantFilter}} order by description limit ?', + 'previewParams' => [$like], + 'resultSql' => 'select uuid, description from departments where description like ? {{tenantFilter}} order by description', + 'resultParams' => [$like], + ], + [ + 'key' => 'roles', + 'label' => t('Roles'), + 'permission' => PermissionService::ROLES_VIEW, + 'countSql' => 'select count(*) from roles where description like ?', + 'countParams' => [$like], + 'previewSql' => 'select uuid, description from roles where description like ? order by description limit ?', + 'previewParams' => [$like], + 'resultSql' => 'select uuid, description from roles where description like ? order by description', + 'resultParams' => [$like], + ], + [ + 'key' => 'permissions', + 'label' => t('Permissions'), + 'permission' => PermissionService::PERMISSIONS_VIEW, + 'countSql' => 'select count(*) from permissions where `key` like ? or description like ?', + 'countParams' => [$like, $like], + 'previewSql' => 'select id, description, `key` from permissions where `key` like ? or description like ? order by `key` limit ?', + 'previewParams' => [$like, $like], + 'resultSql' => 'select id, description, `key` from permissions where `key` like ? or description like ? order by `key`', + 'resultParams' => [$like, $like], + ], + [ + 'key' => 'pages', + 'label' => t('Pages'), + 'permission' => '', + 'countSql' => 'select count(*) from pages p join page_contents pc on pc.page_id = p.id and pc.locale = ? where p.slug like ? or pc.content like ?', + 'countParams' => [$locale, $like, $like], + 'previewSql' => 'select p.slug from pages p join page_contents pc on pc.page_id = p.id and pc.locale = ? where p.slug like ? or pc.content like ? order by p.slug limit ?', + 'previewParams' => [$locale, $like, $like], + 'resultSql' => 'select p.slug from pages p join page_contents pc on pc.page_id = p.id and pc.locale = ? where p.slug like ? or pc.content like ? order by p.slug', + 'resultParams' => [$locale, $like, $like], + ], + [ + 'key' => 'settings', + 'label' => t('Settings'), + 'permission' => PermissionService::SETTINGS_VIEW, + 'countSql' => 'select count(*) from settings where `key` like ? or value like ?', + 'countParams' => [$like, $like], + 'previewSql' => null, + 'previewParams' => [], + 'resultSql' => 'select `key`, value from settings where `key` like ? or value like ? order by `key`', + 'resultParams' => [$like, $like], + ], + ]; + } + + public static function iconForKey(string $key): string + { + return match ($key) { + 'address-book' => 'bi-people', + 'users' => 'bi-person-badge', + 'tenants' => 'bi-buildings', + 'departments' => 'bi-diagram-3', + 'roles' => 'bi-people', + 'permissions' => 'bi-shield-lock', + 'pages' => 'bi-file-text', + 'settings' => 'bi-gear', + default => 'bi-search', + }; + } + + public static function listUrl(string $key, string $query): string + { + $encoded = urlencode($query); + + return match ($key) { + 'address-book' => lurl('address-book') . '?search=' . $encoded, + 'users' => lurl('admin/users') . '?search=' . $encoded, + 'tenants' => lurl('admin/tenants') . '?search=' . $encoded, + 'departments' => lurl('admin/departments') . '?search=' . $encoded, + 'roles' => lurl('admin/roles') . '?search=' . $encoded, + 'permissions' => lurl('admin/permissions') . '?search=' . $encoded, + 'pages' => lurl(''), + 'settings' => lurl('admin/settings'), + default => lurl(''), + }; + } + + public static function mapPreviewItem(string $key, array $data, string $query): ?array + { + if ($key === 'users') { + $label = trim(($data['first_name'] ?? '') . ' ' . ($data['last_name'] ?? '')); + $email = trim((string) ($data['email'] ?? '')); + if ($email !== '') { + $label = $label === '' ? $email : ($label . ' — ' . $email); + } + $url = lurl('admin/users/edit/' . ($data['uuid'] ?? '')) . '?search=' . urlencode($query); + } elseif ($key === 'address-book') { + $label = trim(($data['first_name'] ?? '') . ' ' . ($data['last_name'] ?? '')); + $email = trim((string) ($data['email'] ?? '')); + if ($email !== '') { + $label = $label === '' ? $email : ($label . ' — ' . $email); + } + $url = lurl('address-book/view/' . ($data['uuid'] ?? '')) . '?search=' . urlencode($query); + } elseif ($key === 'permissions') { + $label = (string) ($data['description'] ?? ''); + $url = lurl('admin/permissions/edit/' . ($data['id'] ?? '')) . '?search=' . urlencode($query); + } elseif ($key === 'pages') { + $label = (string) ($data['slug'] ?? ''); + $url = lurl('page/' . $label) . '?search=' . urlencode($query); + } else { + $label = (string) ($data['description'] ?? ''); + $url = lurl('admin/' . $key . '/edit/' . ($data['uuid'] ?? '')) . '?search=' . urlencode($query); + } + + if ($label === '' || $url === '') { + return null; + } + return ['label' => $label, 'url' => $url]; + } + + public static function mapResultItem(string $key, string $label, array $data): ?array + { + $title = ''; + $description = ''; + $url = ''; + $image = ''; + if ($key === 'users') { + $title = trim(($data['first_name'] ?? '') . ' ' . ($data['last_name'] ?? '')); + $description = (string) ($data['email'] ?? ''); + $url = lurl('admin/users/edit/' . ($data['uuid'] ?? '')); + } elseif ($key === 'address-book') { + $title = trim(($data['first_name'] ?? '') . ' ' . ($data['last_name'] ?? '')); + $description = (string) ($data['email'] ?? ''); + $uuid = (string) ($data['uuid'] ?? ''); + $url = lurl('address-book/view/' . $uuid); + if ($uuid !== '' && UserAvatarService::hasAvatar($uuid)) { + $image = lurl('admin/users/avatar-file') . '?uuid=' . urlencode($uuid) . '&size=64'; + } + } elseif ($key === 'permissions') { + $title = (string) ($data['key'] ?? ''); + $description = (string) ($data['description'] ?? ''); + $url = lurl('admin/permissions/edit/' . ($data['id'] ?? '')); + } elseif ($key === 'pages') { + $title = (string) ($data['slug'] ?? ''); + $description = t('Page'); + $url = lurl('page/' . $title); + } elseif ($key === 'settings') { + $title = (string) ($data['key'] ?? ''); + $description = (string) ($data['value'] ?? ''); + $url = lurl('admin/settings'); + } else { + $title = (string) ($data['description'] ?? ''); + $description = $label; + $url = lurl('admin/' . $key . '/edit/' . ($data['uuid'] ?? '')); + } + + if ($title === '' || $url === '') { + return null; + } + + return [ + 'type' => $label, + 'title' => $title, + 'description' => $description, + 'url' => $url, + 'image' => $image, + 'icon' => self::iconForKey($key), + ]; + } +} diff --git a/lib/Support/Tile.php b/lib/Support/Tile.php new file mode 100644 index 0000000..d1e1a93 --- /dev/null +++ b/lib/Support/Tile.php @@ -0,0 +1,28 @@ + '#', + 'label' => '', + 'count' => null, + 'icon' => 'bi bi-grid-1x2', + 'iconBg' => null, + 'iconColor' => null, + 'class' => '', + ]; + + $data = array_merge($defaults, $options); + $template = dirname(__DIR__, 2) . '/templates/partials/app-tile.phtml'; + if (!is_file($template)) { + return; + } + + extract($data, EXTR_SKIP); + require $template; + } +} diff --git a/lib/Support/helpers.php b/lib/Support/helpers.php new file mode 100644 index 0000000..a889544 --- /dev/null +++ b/lib/Support/helpers.php @@ -0,0 +1,6 @@ + + strcasecmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? '')) + ); +} diff --git a/lib/Support/helpers/auth.php b/lib/Support/helpers/auth.php new file mode 100644 index 0000000..c7a1db9 --- /dev/null +++ b/lib/Support/helpers/auth.php @@ -0,0 +1,27 @@ +getTimestamp()))->setTimezone($displayTz); + } elseif (is_string($value) && $value !== '') { + try { + // Treat DB timestamps as UTC and convert for display + $date = new \DateTimeImmutable($value, new \DateTimeZone('UTC')); + $date = $date->setTimezone($displayTz); + } catch (\Exception $e) { + return $value; + } + } else { + return ''; + } + + if ($format === null) { + $locale = \MintyPHP\I18n::$locale ?? ''; + $format = (strpos((string) $locale, 'de') === 0) ? 'd.m.Y H:i' : 'Y-m-d H:i'; + } + + return $date->format($format); +} diff --git a/lib/Support/helpers/ui.php b/lib/Support/helpers/ui.php new file mode 100644 index 0000000..a19879f --- /dev/null +++ b/lib/Support/helpers/ui.php @@ -0,0 +1,178 @@ + [ + 'placeholder' => t('Search...'), + ], + 'sort' => [ + 'sortAsc' => t('Sort column ascending'), + 'sortDesc' => t('Sort column descending'), + ], + 'pagination' => [ + 'previous' => t('Previous'), + 'next' => t('Next'), + 'navigate' => t('Page %d of %d'), + 'page' => t('Page %d'), + 'showing' => t('Showing'), + 'of' => t('of'), + 'to' => t('to'), + 'results' => t('results'), + ], + 'loading' => t('Loading...'), + 'noRecordsFound' => t('No records found'), + 'error' => t('An error happened while fetching the data'), + ]; +} + +/** + * Render a multi-select filter field with hidden input sync. + * + * @param string $id Base ID for the hidden input (select gets "-ui" suffix) + * @param string $label Field label (will be translated) + * @param string $placeholder Placeholder text (will be translated) + * @param array $items Array of items with 'id' and 'description' keys + * @param array $selected Array of selected item IDs + */ +function multiSelectFilter( + string $id, + string $label, + string $placeholder, + array $items, + array $selected +): void { + ?> + + + + + $isActive ? 'active' : '', + 'aria' => $isActive ? 'aria-current="page"' : '', + 'isActive' => $isActive, + ]; +} + +function navActivePublicPages(array $extraSlugs = []): array +{ + $current = \MintyPHP\Http\Request::path(); + if ($current === '') { + return ['class' => '', 'aria' => '', 'isActive' => false]; + } + + $publicSlugs = array_merge(['imprint', 'privacy'], $extraSlugs); + $isActive = strpos($current, 'page/') === 0 || in_array($current, $publicSlugs, true); + + return [ + 'class' => $isActive ? 'active' : '', + 'aria' => $isActive ? 'aria-current="page"' : '', + 'isActive' => $isActive, + ]; +} diff --git a/pages/.DS_Store b/pages/.DS_Store new file mode 100644 index 0000000..d45d925 Binary files /dev/null and b/pages/.DS_Store differ diff --git a/pages/account/profile().php b/pages/account/profile().php new file mode 100644 index 0000000..5353d08 --- /dev/null +++ b/pages/account/profile().php @@ -0,0 +1,18 @@ + $limit, + 'offset' => $offset, + 'search' => $search, + 'order' => $order, + 'dir' => $dir, + 'tenant' => $tenant, + 'tenants' => $tenants, + 'departments' => $departments, + 'roles' => $roles, + 'tenantUserId' => $currentUserId, + 'active' => 'active', +]); + +$rows = []; +foreach ($result['rows'] as $row) { + $tenantLabels = $row['tenant_labels'] ?? []; + if (is_string($tenantLabels)) { + $tenantList = $tenantLabels !== '' + ? array_values(array_filter(array_map('trim', explode('||', $tenantLabels)))) + : []; + } elseif (is_array($tenantLabels)) { + $tenantList = array_values(array_filter(array_map('trim', $tenantLabels))); + } else { + $tenantList = []; + } + + $departmentLabels = $row['department_labels'] ?? []; + if (is_string($departmentLabels)) { + $departmentList = $departmentLabels !== '' + ? array_values(array_filter(array_map('trim', explode('||', $departmentLabels)))) + : []; + } elseif (is_array($departmentLabels)) { + $departmentList = array_values(array_filter(array_map('trim', $departmentLabels))); + } else { + $departmentList = []; + } + + $roleLabels = $row['role_labels'] ?? []; + if (is_string($roleLabels)) { + $roleList = $roleLabels !== '' + ? array_values(array_filter(array_map('trim', explode('||', $roleLabels)))) + : []; + } elseif (is_array($roleLabels)) { + $roleList = array_values(array_filter(array_map('trim', $roleLabels))); + } else { + $roleList = []; + } + + $uuid = (string) ($row['uuid'] ?? ''); + $rows[] = [ + 'uuid' => $uuid, + 'first_name' => $row['first_name'] ?? '', + 'last_name' => $row['last_name'] ?? '', + 'email' => $row['email'] ?? '', + 'phone' => $row['phone'] ?? '', + 'mobile' => $row['mobile'] ?? '', + 'short_dial' => $row['short_dial'] ?? '', + 'tenants' => $tenantList, + 'departments' => $departmentList, + 'roles' => $roleList, + 'has_avatar' => $uuid !== '' && UserAvatarService::hasAvatar($uuid), + ]; +} + +Router::json([ + 'data' => $rows, + 'total' => $result['total'] ?? 0, +]); diff --git a/pages/address-book/index().php b/pages/address-book/index().php new file mode 100644 index 0000000..22349e0 --- /dev/null +++ b/pages/address-book/index().php @@ -0,0 +1,52 @@ + 0 && in_array($id, $tenantIds, true); + })); +} elseif (TenantScopeService::isStrict()) { + $tenants = []; +} +$tenantItems = array_map( + static fn (array $tenant): array => [ + 'id' => (string) ($tenant['uuid'] ?? ''), + 'description' => $tenant['description'] ?? '', + ], + $tenants +); + +$departments = $tenantIds + ? DepartmentService::listByTenantIds($tenantIds) + : (TenantScopeService::isStrict() ? [] : DepartmentService::list()); + +$roles = RoleService::list(); + +$csrfKey = Session::$csrfSessionKey; +$csrfToken = $_SESSION[$csrfKey] ?? ''; + +Buffer::set('title', t('Address book')); +Buffer::set('grid_lang', json_encode(gridLang(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); +Buffer::set( + 'grid_csrf', + json_encode(['key' => $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) +); diff --git a/pages/address-book/index(default).phtml b/pages/address-book/index(default).phtml new file mode 100644 index 0000000..50189f5 --- /dev/null +++ b/pages/address-book/index(default).phtml @@ -0,0 +1,156 @@ + + +
+

+
+ +
+
+
+ + + + +
+
+
+
+ + + + diff --git a/pages/address-book/view($id).php b/pages/address-book/view($id).php new file mode 100644 index 0000000..770d220 --- /dev/null +++ b/pages/address-book/view($id).php @@ -0,0 +1,111 @@ + 0 && $label !== '') { + $departmentMap[$tenantId][] = $label; + } +} +foreach ($departmentMap as $tenantId => $labels) { + $departmentMap[$tenantId] = array_values(array_unique($labels)); +} + +$primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0); +$tenantGroups = []; +foreach (is_array($tenantRows) ? $tenantRows : [] as $row) { + $tenantId = (int) ($getRowValue($row, 'id') ?? 0); + $tenantLabel = (string) ($getRowValue($row, 'description') ?? ''); + if ($tenantId <= 0 || $tenantLabel === '') { + continue; + } + $tenantGroups[] = [ + 'label' => $tenantLabel, + 'is_primary' => $primaryTenantId > 0 && $tenantId === $primaryTenantId, + 'departments' => $departmentMap[$tenantId] ?? [], + ]; +} + +$roleLabels = $extractLabels(is_array($roleRows) ? $roleRows : []); + +$user['tenant_groups'] = $tenantGroups; +$user['role_labels'] = $roleLabels; + +$name = trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')); +$title = $name !== '' ? $name : ($user['email'] ?? t('Address book')); +Buffer::set('title', $title); diff --git a/pages/address-book/view(default).phtml b/pages/address-book/view(default).phtml new file mode 100644 index 0000000..fdd3e4d --- /dev/null +++ b/pages/address-book/view(default).phtml @@ -0,0 +1,277 @@ +format($format); + } catch (\Exception $e) { + $hireDateLabel = $hireDate; + } +} +$avatarUuid = (string) ($values['uuid'] ?? ''); +$hasAvatar = $avatarUuid !== '' && UserAvatarService::hasAvatar($avatarUuid); + +$initials = ''; +if ($displayName !== '') { + $parts = array_filter(array_map('trim', preg_split('/\s+/', $displayName) ?: [])); + $chars = ''; + foreach ($parts as $part) { + if (function_exists('mb_substr')) { + $chars .= mb_substr($part, 0, 1); + } else { + $chars .= substr($part, 0, 1); + } + } + $initials = strtoupper($chars !== '' ? $chars : '?'); +} +$tenantGroups = $values['tenant_groups'] ?? []; +$roleLabels = $values['role_labels'] ?? []; +$hasContact = ($email !== '' || $phone !== '' || $mobile !== '' || $shortDial !== ''); +$hasAddress = ($address !== '' || $postalCode !== '' || $city !== '' || $region !== '' || $country !== ''); +$hasOrganization = (!empty($tenantGroups) || !empty($roleLabels)); +$aboutSummary = ''; +if ($profileDescription !== '') { + foreach (preg_split('/\\r\\n|\\r|\\n/', $profileDescription) as $line) { + $line = trim((string) $line); + if ($line !== '') { + $aboutSummary = $line; + break; + } + } +} + +?> + +
+
+
+ +
+ + +
+
+
+
+
+ + + + + + + +
+
+
+

+ +

+ + +

+ + + +

+ +
+
+
+
+
+
+
+
+
+ + + + + + + + + + +
+ +
+
+ +
+ +

+
+ + +
+ +

+
+ +
+
+ +
+ +

+
+ + +
+ +

+
+ +
+
+ + +
+
+ +

+ + + +

+ + +

+ + +

+ +
+
+ + +
+ + + + + + + + + + + + + + + + + +
+ + + () + + + + + + - + +
+
+ +
+ +

+
+ +

-

+ + + + + + + + + + + + + +
+ +
+ +
+ +
+ +

+
+ + + + +

+ + + +

-

+ +
+
+
+
+
+ +
diff --git a/pages/admin/departments/_form.phtml b/pages/admin/departments/_form.phtml new file mode 100644 index 0000000..d56c158 --- /dev/null +++ b/pages/admin/departments/_form.phtml @@ -0,0 +1,55 @@ + +
+
+ Stammdaten +
+ +
+ + + + + +
diff --git a/pages/admin/departments/create().php b/pages/admin/departments/create().php new file mode 100644 index 0000000..e8f5d98 --- /dev/null +++ b/pages/admin/departments/create().php @@ -0,0 +1,75 @@ + '', +]; +$tenants = TenantService::list(); +if ($allowedTenantIds) { + $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool { + $tenantId = (int) ($tenant['id'] ?? 0); + return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true); + })); +} elseif (TenantScopeService::isStrict()) { + $tenants = []; +} +$selectedTenantIds = []; + +if (isset($_POST['description'])) { + $result = DepartmentService::createFromAdmin($_POST, $currentUserId); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + $tenantIds = $_POST['tenant_ids'] ?? []; + if (!is_array($tenantIds)) { + $tenantIds = [$tenantIds]; + } + $selectedTenantIds = array_values(array_unique(array_map('intval', $tenantIds))); + $selectedTenantIds = TenantScopeService::filterTenantIdsForUser($selectedTenantIds, $currentUserId); + + if (TenantScopeService::isStrict() && !$selectedTenantIds) { + $errors[] = t('Please select at least one tenant'); + } + + if (($result['ok'] ?? false) && !$errors) { + $departmentId = (int) ($result['id'] ?? 0); + if ($departmentId) { + DepartmentService::syncTenants($departmentId, $selectedTenantIds); + } + $action = (string) ($_POST['action'] ?? 'create'); + if ($action === 'create_close') { + Flash::success('Department created', 'admin/departments', 'department_created'); + Router::redirect('admin/departments'); + } else { + $uuid = (string) ($result['uuid'] ?? ''); + if ($uuid !== '') { + $target = "admin/departments/edit/{$uuid}"; + Flash::success('Department created', $target, 'department_created'); + Router::redirect($target); + } else { + Flash::success('Department created', 'admin/departments', 'department_created'); + Router::redirect('admin/departments'); + } + } + } +} + +Buffer::set('title', t('Create department')); diff --git a/pages/admin/departments/create(default).phtml b/pages/admin/departments/create(default).phtml new file mode 100644 index 0000000..047c48a --- /dev/null +++ b/pages/admin/departments/create(default).phtml @@ -0,0 +1,44 @@ + $errors + * @var array $form + */ + +?> +
+
+ +
+

+ + +

+
+ + +
+
+ +
+
    + +
  • + +
+
+ + +
+
diff --git a/pages/admin/departments/data().php b/pages/admin/departments/data().php new file mode 100644 index 0000000..105d165 --- /dev/null +++ b/pages/admin/departments/data().php @@ -0,0 +1,52 @@ + $limit, + 'offset' => $offset, + 'search' => $search, + 'order' => $order, + 'dir' => $dir, + 'tenant' => $tenant, + 'tenantIds' => $tenantIds, + 'tenantUserId' => $currentUserId, +]); + +$defaultDepartmentId = SettingService::getDefaultDepartmentId(); +$rows = []; +foreach ($result['rows'] as $row) { + $departmentId = (int) ($row['id'] ?? 0); + $rows[] = [ + 'id' => $row['id'] ?? null, + 'uuid' => $row['uuid'] ?? '', + 'is_default' => $departmentId > 0 && $defaultDepartmentId === $departmentId, + 'description' => $row['description'] ?? '', + 'tenants' => $row['tenant_labels'] ?? [], + 'created' => dt($row['created'] ?? ''), + 'modified' => dt($row['modified'] ?? ''), + ]; +} + +Router::json([ + 'data' => $rows, + 'total' => $result['total'] ?? 0, +]); diff --git a/pages/admin/departments/delete($id).php b/pages/admin/departments/delete($id).php new file mode 100644 index 0000000..6f32cad --- /dev/null +++ b/pages/admin/departments/delete($id).php @@ -0,0 +1,37 @@ + 0) { + AuthService::loadTenantDataIntoSession($currentUserId); +} + +Flash::success('Department deleted', 'admin/departments', 'department_deleted'); +Router::redirect('admin/departments'); diff --git a/pages/admin/departments/edit($id).php b/pages/admin/departments/edit($id).php new file mode 100644 index 0000000..24dada4 --- /dev/null +++ b/pages/admin/departments/edit($id).php @@ -0,0 +1,111 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} +$allowedTenantIds = TenantScopeService::getUserTenantIds($currentUserId); + +$uuid = trim((string) ($id ?? '')); +$department = $uuid !== '' ? DepartmentService::findByUuid($uuid) : null; +if (!$department) { + Flash::error('Department not found', 'admin/departments', 'department_not_found'); + Router::redirect('admin/departments'); +} + +$departmentId = (int) ($department['id'] ?? 0); +if (!TenantScopeService::canAccess('departments', $departmentId, $currentUserId)) { + Router::redirect('error/forbidden'); + return; +} +$creatorId = (int) ($department['created_by'] ?? 0); +if ($creatorId > 0) { + $creator = UserService::findById($creatorId); + if ($creator) { + $creatorName = trim(($creator['first_name'] ?? '') . ' ' . ($creator['last_name'] ?? '')); + $department['created_by_label'] = $creatorName !== '' ? $creatorName : ($creator['email'] ?? ''); + $department['created_by_uuid'] = $creator['uuid'] ?? null; + } +} +$modifierId = (int) ($department['modified_by'] ?? 0); +if ($modifierId > 0) { + $modifier = UserService::findById($modifierId); + if ($modifier) { + $modifierName = trim(($modifier['first_name'] ?? '') . ' ' . ($modifier['last_name'] ?? '')); + $department['modified_by_label'] = $modifierName !== '' ? $modifierName : ($modifier['email'] ?? ''); + $department['modified_by_uuid'] = $modifier['uuid'] ?? null; + } +} + +$errors = []; +$form = $department; +$tenants = TenantService::list(); +$selectedTenantIds = TenantDepartmentRepository::listTenantIdsByDepartmentId($departmentId); +if ($allowedTenantIds) { + $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool { + $tenantId = (int) ($tenant['id'] ?? 0); + return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true); + })); + $selectedTenantIds = array_values(array_intersect($selectedTenantIds, $allowedTenantIds)); +} elseif (TenantScopeService::isStrict()) { + $tenants = []; + $selectedTenantIds = []; +} + +if (isset($_POST['description'])) { + if (!PermissionService::userHas($currentUserId, PermissionService::DEPARTMENTS_UPDATE)) { + Router::redirect('error/forbidden'); + return; + } + $result = DepartmentService::updateFromAdmin($departmentId, $_POST, $currentUserId); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + $tenantIds = $_POST['tenant_ids'] ?? []; + if (!is_array($tenantIds)) { + $tenantIds = [$tenantIds]; + } + $selectedTenantIds = array_values(array_unique(array_map('intval', $tenantIds))); + $selectedTenantIds = TenantScopeService::filterTenantIdsForUser($selectedTenantIds, $currentUserId); + + if (TenantScopeService::isStrict() && !$selectedTenantIds) { + $errors[] = t('Please select at least one tenant'); + } + + if (($result['ok'] ?? false) && !$errors) { + $existingTenantIds = TenantDepartmentRepository::listTenantIdsByDepartmentId($departmentId); + $finalTenantIds = TenantScopeService::mergeTenantIdsPreservingOutOfScope( + $selectedTenantIds, + $existingTenantIds, + $allowedTenantIds + ); + $cleaned = DepartmentService::syncTenants($departmentId, $finalTenantIds); + $action = (string) ($_POST['action'] ?? 'save'); + if ($cleaned > 0) { + Flash::info(t('Department assignments cleaned: %d', $cleaned), "admin/departments/edit/{$uuid}", 'department_assignments_cleaned'); + } + if ($action === 'save_close') { + Flash::success('Department updated', 'admin/departments', 'department_updated'); + Router::redirect('admin/departments'); + } else { + Flash::success('Department updated', "admin/departments/edit/{$uuid}", 'department_updated'); + Router::redirect("admin/departments/edit/{$uuid}"); + } + } +} + +Buffer::set('title', t('Edit department')); diff --git a/pages/admin/departments/edit(default).phtml b/pages/admin/departments/edit(default).phtml new file mode 100644 index 0000000..9123041 --- /dev/null +++ b/pages/admin/departments/edit(default).phtml @@ -0,0 +1,167 @@ + $errors + * @var array $form + * @var array $department + */ + +use MintyPHP\Session; + +$values = $form ?? $department ?? []; +$isReadOnly = !can('departments.update'); + +?> + +
+
+ +
+

+ + +

+
+ + + + + + + + +
+
+ + +
+
+
    + +
  • + +
+
+
+ + + +
+ +
diff --git a/pages/admin/departments/index().php b/pages/admin/departments/index().php new file mode 100644 index 0000000..71313a9 --- /dev/null +++ b/pages/admin/departments/index().php @@ -0,0 +1,39 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +$tenants = TenantService::list(); +$allowedTenantIds = TenantScopeService::getUserTenantIds($currentUserId); +if ($allowedTenantIds) { + $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool { + $tenantId = (int) ($tenant['id'] ?? 0); + return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true); + })); +} elseif (TenantScopeService::isStrict()) { + $tenants = []; +} +usort($tenants, static function ($a, $b) { + return strcmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? '')); +}); + +Buffer::set('title', t('Departments')); +Buffer::set('grid_lang', json_encode(gridLang(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); +$csrfKey = Session::$csrfSessionKey; +$csrfToken = $_SESSION[$csrfKey] ?? ''; +Buffer::set( + 'grid_csrf', + json_encode(['key' => $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) +); diff --git a/pages/admin/departments/index(default).phtml b/pages/admin/departments/index(default).phtml new file mode 100644 index 0000000..4ae14df --- /dev/null +++ b/pages/admin/departments/index(default).phtml @@ -0,0 +1,175 @@ + 1; +?> + +
+

+
+ + + + + + +
+
+ +
+ + + + + + + + + + + +
+ +
+ + +
+
+
+
+ + + + diff --git a/pages/admin/index($slug).php b/pages/admin/index($slug).php new file mode 100644 index 0000000..50332a3 --- /dev/null +++ b/pages/admin/index($slug).php @@ -0,0 +1,16 @@ + +
+ Login +
+
+

+
+ +
+
+
+ +
diff --git a/pages/admin/mail-log/data().php b/pages/admin/mail-log/data().php new file mode 100644 index 0000000..fc06a88 --- /dev/null +++ b/pages/admin/mail-log/data().php @@ -0,0 +1,57 @@ + $limit, + 'offset' => $offset, + 'search' => $search, + 'order' => $order, + 'dir' => $dir, + 'status' => $status, + 'created_from' => $createdFrom, + 'created_to' => $createdTo, +]); + +$rows = []; +foreach ($result['rows'] as $row) { + $status = (string) ($row['status'] ?? ''); + $statusBadge = 'neutral'; + if ($status === 'sent') { + $statusBadge = 'success'; + } elseif ($status === 'failed') { + $statusBadge = 'danger'; + } + + $rows[] = [ + 'id' => $row['id'] ?? null, + 'to_email' => $row['to_email'] ?? '', + 'subject' => $row['subject'] ?? '', + 'template' => $row['template'] ?? '', + 'status' => $status, + 'status_badge' => $statusBadge, + 'status_label' => t(ucfirst($status)), + 'created_at' => dt($row['created_at'] ?? ''), + 'sent_at' => dt($row['sent_at'] ?? ''), + ]; +} + +Router::json([ + 'data' => $rows, + 'total' => $result['total'] ?? 0, +]); diff --git a/pages/admin/mail-log/index().php b/pages/admin/mail-log/index().php new file mode 100644 index 0000000..2ca4213 --- /dev/null +++ b/pages/admin/mail-log/index().php @@ -0,0 +1,23 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +Buffer::set('title', t('Mail logs')); +Buffer::set('grid_lang', json_encode(gridLang(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); +$csrfKey = Session::$csrfSessionKey; +$csrfToken = $_SESSION[$csrfKey] ?? ''; +Buffer::set( + 'grid_csrf', + json_encode(['key' => $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) +); diff --git a/pages/admin/mail-log/index(default).phtml b/pages/admin/mail-log/index(default).phtml new file mode 100644 index 0000000..fd761c2 --- /dev/null +++ b/pages/admin/mail-log/index(default).phtml @@ -0,0 +1,174 @@ + + +
+

+
+ +
+
+
+ + + + +
+
+
+
+ + + + diff --git a/pages/admin/mail-log/view($id).php b/pages/admin/mail-log/view($id).php new file mode 100644 index 0000000..e933416 --- /dev/null +++ b/pages/admin/mail-log/view($id).php @@ -0,0 +1,25 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +$mailLogId = (int) ($id ?? 0); +$mailLog = $mailLogId > 0 ? MailLogService::find($mailLogId) : null; +if (!$mailLog) { + Flash::error('Mail log not found', 'admin/mail-log', 'mail_log_not_found'); + Router::redirect('admin/mail-log'); +} + +Buffer::set('title', t('View mail log')); diff --git a/pages/admin/mail-log/view(default).phtml b/pages/admin/mail-log/view(default).phtml new file mode 100644 index 0000000..6baff72 --- /dev/null +++ b/pages/admin/mail-log/view(default).phtml @@ -0,0 +1,137 @@ + + +
+
+ +
+

+ + +

+
+ +
+
+ +
+
+
+
+ + +
+
+ + +
+ +
+
+ + +
+
+
+ + +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+
+ + + +
+
+ +
+
+
+ + +
+
+
+ +
+
+ +
\ No newline at end of file diff --git a/pages/admin/permissions/_form.phtml b/pages/admin/permissions/_form.phtml new file mode 100644 index 0000000..79e163f --- /dev/null +++ b/pages/admin/permissions/_form.phtml @@ -0,0 +1,38 @@ + +
+
> + Stammdaten +
+
+ + +
+
+
+ +
diff --git a/pages/admin/permissions/create().php b/pages/admin/permissions/create().php new file mode 100644 index 0000000..c286594 --- /dev/null +++ b/pages/admin/permissions/create().php @@ -0,0 +1,42 @@ + '', + 'description' => '', +]; + +if (isset($_POST['key'])) { + $result = PermissionService::createFromAdmin($_POST); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + + if ($result['ok'] ?? false) { + $action = (string) ($_POST['action'] ?? 'create'); + if ($action === 'create_close') { + Flash::success('Permission created', 'admin/permissions', 'permission_created'); + Router::redirect('admin/permissions'); + } else { + $id = (int) ($result['id'] ?? 0); + if ($id > 0) { + $target = "admin/permissions/edit/{$id}"; + Flash::success('Permission created', $target, 'permission_created'); + Router::redirect($target); + } else { + Flash::success('Permission created', 'admin/permissions', 'permission_created'); + Router::redirect('admin/permissions'); + } + } + } +} + +Buffer::set('title', t('Create permission')); diff --git a/pages/admin/permissions/create(default).phtml b/pages/admin/permissions/create(default).phtml new file mode 100644 index 0000000..2de9bae --- /dev/null +++ b/pages/admin/permissions/create(default).phtml @@ -0,0 +1,44 @@ + $errors + * @var array $form + */ + +?> +
+
+ +
+

+ + +

+
+ + +
+
+ +
+
    + +
  • + +
+
+ + +
+
diff --git a/pages/admin/permissions/data().php b/pages/admin/permissions/data().php new file mode 100644 index 0000000..ed71004 --- /dev/null +++ b/pages/admin/permissions/data().php @@ -0,0 +1,41 @@ + [], 'total' => 0]); +} + +$limit = (int) ($_GET['limit'] ?? 10); +$offset = (int) ($_GET['offset'] ?? 0); +$search = trim((string) ($_GET['search'] ?? '')); +$order = (string) ($_GET['order'] ?? 'key'); +$dir = (string) ($_GET['dir'] ?? 'asc'); + +$result = PermissionService::listPaged([ + 'limit' => $limit, + 'offset' => $offset, + 'search' => $search, + 'order' => $order, + 'dir' => $dir, +]); + +$rows = []; +foreach ($result['data'] ?? [] as $row) { + $rows[] = [ + 'id' => $row['id'] ?? null, + 'key' => $row['key'] ?? '', + 'description' => $row['description'] ?? '', + 'created' => dt($row['created'] ?? ''), + ]; +} + +Router::json([ + 'data' => $rows, + 'total' => $result['total'] ?? 0, +]); diff --git a/pages/admin/permissions/delete($id).php b/pages/admin/permissions/delete($id).php new file mode 100644 index 0000000..ec4a00a --- /dev/null +++ b/pages/admin/permissions/delete($id).php @@ -0,0 +1,23 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +$id = (int) ($id ?? 0); +$permission = $id > 0 ? PermissionService::find($id) : null; +if (!$permission) { + Flash::error('Permission not found', 'admin/permissions', 'permission_not_found'); + Router::redirect('admin/permissions'); +} + +$errors = []; +$form = $permission; +$roles = RoleRepository::list(); +$selectedRoleIds = RolePermissionRepository::listRoleIdsByPermissionId($id); + +if (isset($_POST['key'])) { + if (!PermissionService::userHas($currentUserId, PermissionService::PERMISSIONS_UPDATE)) { + Flash::error('Permission denied', "admin/permissions/edit/{$id}", 'permission_denied'); + Router::redirect("admin/permissions/edit/{$id}"); + return; + } + $result = PermissionService::updateFromAdmin($id, $_POST); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + $selectedRoleIds = $_POST['role_ids'] ?? $selectedRoleIds; + if (!is_array($selectedRoleIds)) { + $selectedRoleIds = [$selectedRoleIds]; + } + $selectedRoleIds = array_values(array_unique(array_map('intval', $selectedRoleIds))); + + if ($result['ok'] ?? false) { + $roleIds = $_POST['role_ids'] ?? []; + if (!is_array($roleIds)) { + $roleIds = [$roleIds]; + } + $roleIds = array_values(array_unique(array_map('intval', $roleIds))); + RolePermissionRepository::replaceForPermission($id, $roleIds); + $action = (string) ($_POST['action'] ?? 'save'); + if ($action === 'save_close') { + Flash::success('Permission updated', 'admin/permissions', 'permission_updated'); + Router::redirect('admin/permissions'); + } else { + Flash::success('Permission updated', "admin/permissions/edit/{$id}", 'permission_updated'); + Router::redirect("admin/permissions/edit/{$id}"); + } + } +} + +Buffer::set('title', t('Edit permission')); diff --git a/pages/admin/permissions/edit(default).phtml b/pages/admin/permissions/edit(default).phtml new file mode 100644 index 0000000..607efc8 --- /dev/null +++ b/pages/admin/permissions/edit(default).phtml @@ -0,0 +1,79 @@ + $errors + * @var array $form + * @var array $permission + */ + +?> +
+
+ +
+

+ + +

+
+ + + + + + + + +
+
+ +
+
    + +
  • + +
+
+ + +
+ +
\ No newline at end of file diff --git a/pages/admin/permissions/index().php b/pages/admin/permissions/index().php new file mode 100644 index 0000000..9dd650a --- /dev/null +++ b/pages/admin/permissions/index().php @@ -0,0 +1,23 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +Buffer::set('title', t('Permissions')); +Buffer::set('grid_lang', json_encode(gridLang(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); +$csrfKey = Session::$csrfSessionKey; +$csrfToken = $_SESSION[$csrfKey] ?? ''; +Buffer::set( + 'grid_csrf', + json_encode(['key' => $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) +); diff --git a/pages/admin/permissions/index(default).phtml b/pages/admin/permissions/index(default).phtml new file mode 100644 index 0000000..297c8ce --- /dev/null +++ b/pages/admin/permissions/index(default).phtml @@ -0,0 +1,121 @@ + + +
+

+
+ + + + + + +
+
+
+ +
+
+
+
+ + + + diff --git a/pages/admin/roles/_form.phtml b/pages/admin/roles/_form.phtml new file mode 100644 index 0000000..842591e --- /dev/null +++ b/pages/admin/roles/_form.phtml @@ -0,0 +1,39 @@ + +
+
+ Stammdaten +
+ +
+
+ +
> + +
+ +
+
+ + +
diff --git a/pages/admin/roles/create().php b/pages/admin/roles/create().php new file mode 100644 index 0000000..aaba5f9 --- /dev/null +++ b/pages/admin/roles/create().php @@ -0,0 +1,61 @@ + '', +]; +$permissions = PermissionRepository::list(); +$selectedPermissionIds = []; + +if (isset($_POST['description'])) { + $currentUserId = (int) ($_SESSION['user']['id'] ?? 0); + $result = RoleService::createFromAdmin($_POST, $currentUserId); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + $selectedPermissionIds = $_POST['permission_ids'] ?? $selectedPermissionIds; + if (!is_array($selectedPermissionIds)) { + $selectedPermissionIds = [$selectedPermissionIds]; + } + $selectedPermissionIds = array_values(array_unique(array_map('intval', $selectedPermissionIds))); + + if ($result['ok'] ?? false) { + $permissionIds = $_POST['permission_ids'] ?? []; + if (!is_array($permissionIds)) { + $permissionIds = [$permissionIds]; + } + $permissionIds = array_values(array_unique(array_map('intval', $permissionIds))); + $createdId = (int) ($result['id'] ?? 0); + if ($createdId > 0) { + RolePermissionRepository::replaceForRole($createdId, $permissionIds); + } + $action = (string) ($_POST['action'] ?? 'create'); + if ($action === 'create_close') { + Flash::success('Role created', 'admin/roles', 'role_created'); + Router::redirect('admin/roles'); + } else { + $uuid = (string) ($result['uuid'] ?? ''); + if ($uuid !== '') { + $target = "admin/roles/edit/{$uuid}"; + Flash::success('Role created', $target, 'role_created'); + Router::redirect($target); + } else { + Flash::success('Role created', 'admin/roles', 'role_created'); + Router::redirect('admin/roles'); + } + } + } +} + +Buffer::set('title', t('Create role')); diff --git a/pages/admin/roles/create(default).phtml b/pages/admin/roles/create(default).phtml new file mode 100644 index 0000000..4be59f1 --- /dev/null +++ b/pages/admin/roles/create(default).phtml @@ -0,0 +1,44 @@ + $errors + * @var array $form + */ + +?> +
+
+ +
+

+ + +

+
+ + +
+
+ +
+
    + +
  • + +
+
+ + +
+
diff --git a/pages/admin/roles/data().php b/pages/admin/roles/data().php new file mode 100644 index 0000000..a48edfa --- /dev/null +++ b/pages/admin/roles/data().php @@ -0,0 +1,43 @@ + $limit, + 'offset' => $offset, + 'search' => $search, + 'order' => $order, + 'dir' => $dir, +]); + +$defaultRoleId = SettingService::getDefaultRoleId(); +$rows = []; +foreach ($result['rows'] as $row) { + $roleId = (int) ($row['id'] ?? 0); + $rows[] = [ + 'id' => $row['id'] ?? null, + 'uuid' => $row['uuid'] ?? '', + 'is_default' => $roleId > 0 && $defaultRoleId === $roleId, + 'description' => $row['description'] ?? '', + 'created' => dt($row['created'] ?? ''), + 'modified' => dt($row['modified'] ?? ''), + ]; +} + +Router::json([ + 'data' => $rows, + 'total' => $result['total'] ?? 0, +]); diff --git a/pages/admin/roles/delete($id).php b/pages/admin/roles/delete($id).php new file mode 100644 index 0000000..92b4f1c --- /dev/null +++ b/pages/admin/roles/delete($id).php @@ -0,0 +1,29 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +$uuid = trim((string) ($id ?? '')); +$role = $uuid !== '' ? RoleService::findByUuid($uuid) : null; +if (!$role) { + Flash::error('Role not found', 'admin/roles', 'role_not_found'); + Router::redirect('admin/roles'); +} + +$roleId = (int) ($role['id'] ?? 0); +$creatorId = (int) ($role['created_by'] ?? 0); +if ($creatorId > 0) { + $creator = UserService::findById($creatorId); + if ($creator) { + $creatorName = trim(($creator['first_name'] ?? '') . ' ' . ($creator['last_name'] ?? '')); + $role['created_by_label'] = $creatorName !== '' ? $creatorName : ($creator['email'] ?? ''); + $role['created_by_uuid'] = $creator['uuid'] ?? null; + } +} +$modifierId = (int) ($role['modified_by'] ?? 0); +if ($modifierId > 0) { + $modifier = UserService::findById($modifierId); + if ($modifier) { + $modifierName = trim(($modifier['first_name'] ?? '') . ' ' . ($modifier['last_name'] ?? '')); + $role['modified_by_label'] = $modifierName !== '' ? $modifierName : ($modifier['email'] ?? ''); + $role['modified_by_uuid'] = $modifier['uuid'] ?? null; + } +} + +$errors = []; +$form = $role; +$permissions = PermissionRepository::list(); +$selectedPermissionIds = RolePermissionRepository::listPermissionIdsByRoleId($roleId); + +if (isset($_POST['description'])) { + if (!PermissionService::userHas($currentUserId, PermissionService::ROLES_UPDATE)) { + Flash::error('Permission denied', "admin/roles/edit/{$uuid}", 'permission_denied'); + Router::redirect("admin/roles/edit/{$uuid}"); + return; + } + $result = RoleService::updateFromAdmin($roleId, $_POST, $currentUserId); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + $selectedPermissionIds = $_POST['permission_ids'] ?? $selectedPermissionIds; + if (!is_array($selectedPermissionIds)) { + $selectedPermissionIds = [$selectedPermissionIds]; + } + $selectedPermissionIds = array_values(array_unique(array_map('intval', $selectedPermissionIds))); + + if ($result['ok'] ?? false) { + $permissionIds = $_POST['permission_ids'] ?? []; + if (!is_array($permissionIds)) { + $permissionIds = [$permissionIds]; + } + $permissionIds = array_values(array_unique(array_map('intval', $permissionIds))); + RolePermissionRepository::replaceForRole($roleId, $permissionIds); + $action = (string) ($_POST['action'] ?? 'save'); + if ($action === 'save_close') { + Flash::success('Role updated', 'admin/roles', 'role_updated'); + Router::redirect('admin/roles'); + } else { + Flash::success('Role updated', "admin/roles/edit/{$uuid}", 'role_updated'); + Router::redirect("admin/roles/edit/{$uuid}"); + } + } +} + +Buffer::set('title', t('Edit role')); diff --git a/pages/admin/roles/edit(default).phtml b/pages/admin/roles/edit(default).phtml new file mode 100644 index 0000000..46f3566 --- /dev/null +++ b/pages/admin/roles/edit(default).phtml @@ -0,0 +1,167 @@ + $errors + * @var array $form + * @var array $role + */ + +use MintyPHP\Session; + +$values = $form ?? $role ?? []; +$isReadOnly = !can('roles.update'); + +?> + +
+
+ +
+

+ + +

+
+ + + + + + + + +
+
+ + +
+
+
    + +
  • + +
+
+
+ + + +
+ +
diff --git a/pages/admin/roles/index().php b/pages/admin/roles/index().php new file mode 100644 index 0000000..33c7f07 --- /dev/null +++ b/pages/admin/roles/index().php @@ -0,0 +1,23 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +Buffer::set('title', t('Roles')); +Buffer::set('grid_lang', json_encode(gridLang(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); +$csrfKey = Session::$csrfSessionKey; +$csrfToken = $_SESSION[$csrfKey] ?? ''; +Buffer::set( + 'grid_csrf', + json_encode(['key' => $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) +); diff --git a/pages/admin/roles/index(default).phtml b/pages/admin/roles/index(default).phtml new file mode 100644 index 0000000..31b4731 --- /dev/null +++ b/pages/admin/roles/index(default).phtml @@ -0,0 +1,159 @@ + + +
+

+
+ + + + + + +
+
+
+ +
+
+
+
+ + + + diff --git a/pages/admin/search/data().php b/pages/admin/search/data().php new file mode 100644 index 0000000..96d8138 --- /dev/null +++ b/pages/admin/search/data().php @@ -0,0 +1,92 @@ + 0) { + PermissionService::getUserPermissions($userId); +} + +$query = trim((string) ($_GET['q'] ?? '')); +if ($query === '') { + Router::json([]); +} + +$previewLimit = 5; +$locale = I18n::$locale ?? (I18n::$defaultLocale ?? 'de'); +$tenantIds = $userId > 0 ? UserTenantRepository::listTenantIdsByUserId($userId) : []; +$tenantScoped = !empty($tenantIds); + +$tenantScopeFilters = SearchConfig::tenantScopeFilters(); +$resources = SearchConfig::resources($query, $locale); +$results = []; + +foreach ($resources as $resource) { + $key = (string) ($resource['key'] ?? ''); + if ($key === '') { + continue; + } + + $tenantFilter = $tenantScopeFilters[$key] ?? ''; + $isTenantScoped = $tenantFilter !== ''; + if ($isTenantScoped && !$tenantScoped) { + continue; + } + + $perm = (string) ($resource['permission'] ?? ''); + if ($perm !== '' && !PermissionService::userHas($userId, $perm)) { + continue; + } + + $countSql = str_replace('{{tenantFilter}}', $isTenantScoped ? $tenantFilter : '', $resource['countSql'] ?? ''); + $countParams = $resource['countParams'] ?? []; + if ($isTenantScoped && strpos($countSql, '???') !== false) { + $countParams[] = $tenantIds; + } + $count = $countSql !== '' ? (int) DB::selectValue($countSql, ...$countParams) : 0; + + $items = []; + $previewSql = $resource['previewSql'] ?? null; + if ($previewSql) { + $previewSql = str_replace('{{tenantFilter}}', $isTenantScoped ? $tenantFilter : '', $previewSql); + $previewParams = $resource['previewParams'] ?? []; + if ($isTenantScoped && strpos($previewSql, '???') !== false) { + $previewParams[] = $tenantIds; + } + $previewParams[] = $previewLimit; + $rows = DB::select($previewSql, ...$previewParams); + foreach ($rows as $row) { + $data = $row; + if (is_array($row) && count($row) === 1) { + $data = reset($row); + } + $data = is_array($data) ? $data : []; + $mapped = SearchConfig::mapPreviewItem($key, $data, $query); + if ($mapped !== null) { + $items[] = $mapped; + } + } + } + + $result = [ + 'key' => $key, + 'label' => $resource['label'] ?? '', + 'count' => $count, + 'url' => SearchConfig::listUrl($key, $query), + 'items' => $items, + ]; + if ($key === 'pages' && !empty($items[0]['url'])) { + $result['url'] = $items[0]['url']; + } + $results[] = $result; +} + +Router::json($results); diff --git a/pages/admin/settings/favicon().php b/pages/admin/settings/favicon().php new file mode 100644 index 0000000..18c79b6 --- /dev/null +++ b/pages/admin/settings/favicon().php @@ -0,0 +1,26 @@ + SettingService::getDefaultTenantId(), + 'default_role_id' => SettingService::getDefaultRoleId(), + 'default_department_id' => SettingService::getDefaultDepartmentId(), + 'app_title' => SettingService::getAppTitle(), + 'app_locale' => SettingService::getAppLocale(), + 'app_theme' => SettingService::getAppTheme(), + 'app_theme_user' => SettingService::isUserThemeAllowed(), + 'app_primary_color' => SettingService::getAppPrimaryColor(), + 'smtp_host' => SettingService::getSmtpHost(), + 'smtp_port' => SettingService::getSmtpPort(), + 'smtp_user' => SettingService::getSmtpUser(), + 'smtp_secure' => SettingService::getSmtpSecure(), + 'smtp_from' => SettingService::getSmtpFrom(), + 'smtp_from_name' => SettingService::getSmtpFromName(), +]; +$settings = [ + 'default_tenant' => SettingService::get(SettingService::DEFAULT_TENANT_KEY), + 'default_role' => SettingService::get(SettingService::DEFAULT_ROLE_KEY), + 'default_department' => SettingService::get(SettingService::DEFAULT_DEPARTMENT_KEY), + 'app_title' => SettingService::get(SettingService::APP_TITLE_KEY), + 'app_locale' => SettingService::get(SettingService::APP_LOCALE_KEY), + 'app_theme' => SettingService::get(SettingService::APP_THEME_KEY), + 'app_theme_user' => SettingService::get(SettingService::APP_THEME_USER_KEY), + 'app_primary_color' => SettingService::get(SettingService::APP_PRIMARY_COLOR_KEY), + 'smtp_host' => SettingService::get(SettingService::SMTP_HOST_KEY), + 'smtp_port' => SettingService::get(SettingService::SMTP_PORT_KEY), + 'smtp_user' => SettingService::get(SettingService::SMTP_USER_KEY), + 'smtp_password' => SettingService::get(SettingService::SMTP_PASSWORD_KEY), + 'smtp_secure' => SettingService::get(SettingService::SMTP_SECURE_KEY), + 'smtp_from' => SettingService::get(SettingService::SMTP_FROM_KEY), + 'smtp_from_name' => SettingService::get(SettingService::SMTP_FROM_NAME_KEY), +]; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!isset($_POST['settings_submit'])) { + Router::redirect('admin/settings'); + } + Guard::requirePermission(PermissionService::SETTINGS_UPDATE); + $defaultTenantId = (int) ($_POST['default_tenant_id'] ?? 0); + $defaultRoleId = (int) ($_POST['default_role_id'] ?? 0); + $defaultDepartmentId = (int) ($_POST['default_department_id'] ?? 0); + $appTitle = trim((string) ($_POST['app_title'] ?? '')); + $appLocale = trim((string) ($_POST['app_locale'] ?? '')); + $appTheme = trim((string) ($_POST['app_theme'] ?? '')); + $appThemeUser = isset($_POST['app_theme_user']); + $rawPrimaryColor = $_POST['app_primary_color'] ?? ''; + if (is_array($rawPrimaryColor)) { + $rawPrimaryColor = ''; + } + $appPrimaryColor = trim((string) $rawPrimaryColor); + if (in_array(strtolower($appPrimaryColor), ['null', 'undefined'], true)) { + $appPrimaryColor = ''; + } + $smtpHost = trim((string) ($_POST['smtp_host'] ?? '')); + $smtpPort = (int) ($_POST['smtp_port'] ?? 0); + $smtpUser = trim((string) ($_POST['smtp_user'] ?? '')); + $smtpPassword = (string) ($_POST['smtp_password'] ?? ''); + $smtpSecure = trim((string) ($_POST['smtp_secure'] ?? '')); + $smtpFrom = trim((string) ($_POST['smtp_from'] ?? '')); + $smtpFromName = trim((string) ($_POST['smtp_from_name'] ?? '')); + + SettingService::setDefaultTenantId($defaultTenantId > 0 ? $defaultTenantId : null); + SettingService::setDefaultRoleId($defaultRoleId > 0 ? $defaultRoleId : null); + SettingService::setDefaultDepartmentId($defaultDepartmentId > 0 ? $defaultDepartmentId : null); + SettingService::setAppTitle($appTitle); + SettingService::setAppLocale($appLocale); + SettingService::setAppTheme($appTheme); + SettingService::setUserThemeAllowed($appThemeUser); + $primaryOk = SettingService::setAppPrimaryColor($appPrimaryColor); + if (!$primaryOk) { + $appPrimaryColor = ''; + Flash::error('Primary color is invalid', 'admin/settings', 'app_primary_invalid'); + } + SettingService::setSmtpHost($smtpHost); + SettingService::setSmtpPort($smtpPort > 0 ? $smtpPort : null); + SettingService::setSmtpUser($smtpUser); + if (trim($smtpPassword) !== '') { + SettingService::setSmtpPassword($smtpPassword); + } + SettingService::setSmtpSecure($smtpSecure); + SettingService::setSmtpFrom($smtpFrom); + SettingService::setSmtpFromName($smtpFromName); + + $cacheFile = __DIR__ . '/../../../config/settings.php'; + $cacheData = []; + if (is_file($cacheFile)) { + $existing = include $cacheFile; + if (is_array($existing)) { + $cacheData = $existing; + } + } + $cacheData['app_title'] = $appTitle !== '' ? $appTitle : null; + $cacheData['app_locale'] = $appLocale !== '' ? $appLocale : null; + $cacheData['app_theme'] = $appTheme !== '' ? $appTheme : null; + $cacheData['app_theme_user'] = $appThemeUser ? '1' : '0'; + $cacheData['app_primary_color'] = $appPrimaryColor !== '' ? $appPrimaryColor : null; + file_put_contents($cacheFile, " + +
+
+ +
+

+ +

+
+ + + +
+
+ +
+ +
+ +
+ +
+
+
+ +
+
+
+ + + +
+
+ + + +
+
+
+ +
+
+ + + + + + +
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+ + + +
+
+ +
+
+ +
diff --git a/pages/admin/settings/logo().php b/pages/admin/settings/logo().php new file mode 100644 index 0000000..4a9c339 --- /dev/null +++ b/pages/admin/settings/logo().php @@ -0,0 +1,26 @@ + 0 && $tenantId > 0) { + if (!isset($userTenantAssignments[$userId])) { + $userTenantAssignments[$userId] = []; + } + $userTenantAssignments[$userId][] = $tenantId; + } + } +} + +// Load tenant-department assignments +$tenantDepartmentAssignments = []; +$tenantDepartmentRows = DB::select('select tenant_id, department_id from tenant_departments'); +if (is_array($tenantDepartmentRows)) { + foreach ($tenantDepartmentRows as $row) { + $data = $row['tenant_departments'] ?? $row; + $tenantId = (int) ($data['tenant_id'] ?? 0); + $departmentId = (int) ($data['department_id'] ?? 0); + if ($tenantId > 0 && $departmentId > 0) { + if (!isset($tenantDepartmentAssignments[$tenantId])) { + $tenantDepartmentAssignments[$tenantId] = []; + } + $tenantDepartmentAssignments[$tenantId][] = $departmentId; + } + } +} + +foreach ($tenants as $tenant) { + $tenantId = (int) ($tenant['id'] ?? 0); + if ($tenantId === 0) { + continue; + } + + // Count departments for this tenant + $deptCount = 0; + $tenantDepartmentIds = $tenantDepartmentAssignments[$tenantId] ?? []; + foreach ($departments as $dept) { + $deptId = (int) ($dept['id'] ?? 0); + if ($deptId > 0 && in_array($deptId, $tenantDepartmentIds, true)) { + $deptCount++; + } + } + + // Count users for this tenant + $totalUsers = 0; + $activeUsers = 0; + $inactiveUsers = 0; + foreach ($users as $user) { + $userId = (int) ($user['id'] ?? 0); + if ($userId === 0) { + continue; + } + + // Check if user is assigned to this tenant + $userTenantIds = $userTenantAssignments[$userId] ?? []; + if (in_array($tenantId, $userTenantIds, true)) { + $totalUsers++; + $isActive = (int) ($user['active'] ?? 1); + if ($isActive === 1) { + $activeUsers++; + } else { + $inactiveUsers++; + } + } + } + + $tenantBreakdown[] = [ + 'id' => $tenantId, + 'uuid' => $tenant['uuid'] ?? '', + 'description' => $tenant['description'] ?? '', + 'departments' => $deptCount, + 'total_users' => $totalUsers, + 'active_users' => $activeUsers, + 'inactive_users' => $inactiveUsers, + ]; +} + +// Sort by description +usort($tenantBreakdown, function ($a, $b) { + return strcmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? '')); +}); + +// Mail log stats +$mailLogTotalCount = DB::selectValue('select count(*) from mail_log'); +$mailLogTotalCount = $mailLogTotalCount ? (int) $mailLogTotalCount : 0; + +$mailLogSentCount = DB::selectValue("select count(*) from mail_log where status = 'sent'"); +$mailLogSentCount = $mailLogSentCount ? (int) $mailLogSentCount : 0; + +$mailLogFailedCount = DB::selectValue("select count(*) from mail_log where status = 'failed'"); +$mailLogFailedCount = $mailLogFailedCount ? (int) $mailLogFailedCount : 0; + +$mailLogQueuedCount = DB::selectValue("select count(*) from mail_log where status = 'queued'"); +$mailLogQueuedCount = $mailLogQueuedCount ? (int) $mailLogQueuedCount : 0; + +// Mail log stats (last 24h) +$mailLogTotal24hCount = DB::selectValue("select count(*) from mail_log where created_at >= NOW() - INTERVAL 24 HOUR"); +$mailLogTotal24hCount = $mailLogTotal24hCount ? (int) $mailLogTotal24hCount : 0; + +$mailLogSent24hCount = DB::selectValue("select count(*) from mail_log where status = 'sent' and created_at >= NOW() - INTERVAL 24 HOUR"); +$mailLogSent24hCount = $mailLogSent24hCount ? (int) $mailLogSent24hCount : 0; + +$mailLogFailed24hCount = DB::selectValue("select count(*) from mail_log where status = 'failed' and created_at >= NOW() - INTERVAL 24 HOUR"); +$mailLogFailed24hCount = $mailLogFailed24hCount ? (int) $mailLogFailed24hCount : 0; + +$mailLogQueued24hCount = DB::selectValue("select count(*) from mail_log where status = 'queued' and created_at >= NOW() - INTERVAL 24 HOUR"); +$mailLogQueued24hCount = $mailLogQueued24hCount ? (int) $mailLogQueued24hCount : 0; + +// Calculate date range for last 24 hours +$date24hAgo = gmdate('Y-m-d', strtotime('-24 hours')); +$dateToday = gmdate('Y-m-d'); + +Buffer::set('title', t('Statistics')); diff --git a/pages/admin/stats/index(default).phtml b/pages/admin/stats/index(default).phtml new file mode 100644 index 0000000..fc1b2a0 --- /dev/null +++ b/pages/admin/stats/index(default).phtml @@ -0,0 +1,283 @@ + +
+ +
+
+

+
+ +
+
+
+
+
+ +
+ + + + + +
+ + +
+
+ 'admin/tenants', + 'label' => t('Tenants'), + 'count' => (string) $tenantCount, + 'icon' => 'bi bi-building', + 'iconBg' => '#e9f0ff', + 'iconColor' => '#264db3', + ]); + ?> + 'admin/departments', + 'label' => t('Departments'), + 'count' => (string) $departmentCount, + 'icon' => 'bi bi-diagram-3-fill', + 'iconBg' => '#fff2d9', + 'iconColor' => '#9a5a00', + ]); + ?> + 'admin/users', + 'label' => t('Users'), + 'count' => (string) $userCount, + 'icon' => 'bi bi-people-fill', + 'iconBg' => '#d9f2e6', + 'iconColor' => '#1f6a3a', + ]); + ?> +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + ( + / + ) + +
+
+ +
+ + +
+
+
+
+ 'admin/users', + 'label' => t('Active users'), + 'count' => (string) $activeUserCount, + 'icon' => 'bi bi-person-check-fill', + 'iconBg' => '#d9f2e6', + 'iconColor' => '#1f6a3a', + ]); + ?> + 'admin/users', + 'label' => t('Inactive users'), + 'count' => (string) $inactiveUserCount, + 'icon' => 'bi bi-person-x-fill', + 'iconBg' => '#ffe9e9', + 'iconColor' => '#a32e2e', + ]); + ?> +
+
+
+
+ 'admin/roles', + 'label' => t('Roles'), + 'count' => (string) $roleCount, + 'icon' => 'bi bi-shield-lock-fill', + 'iconBg' => '#f3e9ff', + 'iconColor' => '#5b2c83', + ]); + ?> + 'admin/permissions', + 'label' => t('Permissions'), + 'count' => (string) $permissionCount, + 'icon' => 'bi bi-key-fill', + 'iconBg' => '#fff2d9', + 'iconColor' => '#9a5a00', + ]); + ?> +
+
+
+
+ + + +
+
+
+

+
+ 'admin/mail-log', + 'label' => t('Total emails'), + 'count' => (string) $mailLogTotalCount, + 'icon' => 'bi bi-envelope-fill', + 'iconBg' => '#e9f0ff', + 'iconColor' => '#264db3', + ]); + ?> + 'admin/mail-log?status=sent', + 'label' => t('Sent emails'), + 'count' => (string) $mailLogSentCount, + 'icon' => 'bi bi-envelope-check-fill', + 'iconBg' => '#d9f2e6', + 'iconColor' => '#1f6a3a', + ]); + ?> + 'admin/mail-log?status=failed', + 'label' => t('Failed emails'), + 'count' => (string) $mailLogFailedCount, + 'icon' => 'bi bi-envelope-x-fill', + 'iconBg' => '#ffe9e9', + 'iconColor' => '#a32e2e', + ]); + ?> + 'admin/mail-log', + 'label' => t('Queued emails'), + 'count' => (string) $mailLogQueuedCount, + 'icon' => 'bi bi-envelope-paper-fill', + 'iconBg' => '#fff2d9', + 'iconColor' => '#9a5a00', + ]); + ?> +
+
+
+

+ +

+
+ 'admin/mail-log?created_from=' . urlencode($date24hAgo ?? '') . '&created_to=' . urlencode($dateToday ?? ''), + 'label' => t('Total (24h)'), + 'count' => (string) $mailLogTotal24hCount, + 'icon' => 'bi bi-envelope-fill', + 'iconBg' => '#e9f0ff', + 'iconColor' => '#264db3', + ]); + ?> + 'admin/mail-log?status=sent&created_from=' . urlencode($date24hAgo ?? '') . '&created_to=' . urlencode($dateToday ?? ''), + 'label' => t('Sent (24h)'), + 'count' => (string) $mailLogSent24hCount, + 'icon' => 'bi bi-envelope-check-fill', + 'iconBg' => '#d9f2e6', + 'iconColor' => '#1f6a3a', + ]); + ?> + 'admin/mail-log?status=failed&created_from=' . urlencode($date24hAgo ?? '') . '&created_to=' . urlencode($dateToday ?? ''), + 'label' => t('Failed (24h)'), + 'count' => (string) $mailLogFailed24hCount, + 'icon' => 'bi bi-envelope-x-fill', + 'iconBg' => '#ffe9e9', + 'iconColor' => '#a32e2e', + ]); + ?> + 'admin/mail-log?status=queued&created_from=' . urlencode($date24hAgo ?? '') . '&created_to=' . urlencode($dateToday ?? ''), + 'label' => t('Queued (24h)'), + 'count' => (string) $mailLogQueued24hCount, + 'icon' => 'bi bi-envelope-paper-fill', + 'iconBg' => '#fff2d9', + 'iconColor' => '#9a5a00', + ]); + ?> +
+
+
+
+ +
+
\ No newline at end of file diff --git a/pages/admin/tenants/_form.phtml b/pages/admin/tenants/_form.phtml new file mode 100644 index 0000000..335a7fd --- /dev/null +++ b/pages/admin/tenants/_form.phtml @@ -0,0 +1,187 @@ + +
+
+
+ + + + + + +
+ +
+ + + +
+ + +
+
+ + +
+
+ +
+
+ + +
+ +
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+
+ + +
+
+ + + + + + + +
+
+
+
+ +
diff --git a/pages/admin/tenants/avatar($id).php b/pages/admin/tenants/avatar($id).php new file mode 100644 index 0000000..ec47278 --- /dev/null +++ b/pages/admin/tenants/avatar($id).php @@ -0,0 +1,33 @@ + '', + 'address' => '', + 'postal_code' => '', + 'city' => '', + 'country' => '', + 'region' => '', + 'vat_id' => '', + 'tax_number' => '', + 'phone' => '', + 'fax' => '', + 'email' => '', + 'support_email' => '', + 'support_phone' => '', + 'billing_email' => '', + 'website' => '', + 'privacy_url' => '', + 'imprint_url' => '', + 'primary_color' => '', + 'primary_color_use_default' => 0, + 'status' => 'active', +]; + +if (isset($_POST['description'])) { + $currentUserId = (int) ($_SESSION['user']['id'] ?? 0); + $result = TenantService::createFromAdmin($_POST, $currentUserId); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + + if ($result['ok'] ?? false) { + $action = (string) ($_POST['action'] ?? 'create'); + if ($action === 'create_close') { + Flash::success('Tenant created', 'admin/tenants', 'tenant_created'); + Router::redirect('admin/tenants'); + } else { + $uuid = (string) ($result['uuid'] ?? ''); + if ($uuid !== '') { + $target = "admin/tenants/edit/{$uuid}"; + Flash::success('Tenant created', $target, 'tenant_created'); + Router::redirect($target); + } else { + Flash::success('Tenant created', 'admin/tenants', 'tenant_created'); + Router::redirect('admin/tenants'); + } + } + } +} + +Buffer::set('title', t('Create tenant')); diff --git a/pages/admin/tenants/create(default).phtml b/pages/admin/tenants/create(default).phtml new file mode 100644 index 0000000..340bab4 --- /dev/null +++ b/pages/admin/tenants/create(default).phtml @@ -0,0 +1,44 @@ + $errors + * @var array $form + */ + +?> +
+
+ +
+

+ + +

+
+ + +
+
+ +
+
    + +
  • + +
+
+ + +
+
diff --git a/pages/admin/tenants/data().php b/pages/admin/tenants/data().php new file mode 100644 index 0000000..45f88e4 --- /dev/null +++ b/pages/admin/tenants/data().php @@ -0,0 +1,43 @@ + $limit, + 'offset' => $offset, + 'search' => $search, + 'order' => $order, + 'dir' => $dir, +]); + +$defaultTenantId = SettingService::getDefaultTenantId(); +$rows = []; +foreach ($result['rows'] as $row) { + $tenantId = (int) ($row['id'] ?? 0); + $rows[] = [ + 'id' => $row['id'] ?? null, + 'uuid' => $row['uuid'] ?? '', + 'is_default' => $tenantId > 0 && $defaultTenantId === $tenantId, + 'description' => $row['description'] ?? '', + 'created' => dt($row['created'] ?? ''), + 'modified' => dt($row['modified'] ?? ''), + ]; +} + +Router::json([ + 'data' => $rows, + 'total' => $result['total'] ?? 0, +]); diff --git a/pages/admin/tenants/delete($id).php b/pages/admin/tenants/delete($id).php new file mode 100644 index 0000000..b0daf1d --- /dev/null +++ b/pages/admin/tenants/delete($id).php @@ -0,0 +1,31 @@ + 0) { + AuthService::loadTenantDataIntoSession($currentUserId); +} + +Flash::success('Tenant deleted', 'admin/tenants', 'tenant_deleted'); +Router::redirect('admin/tenants'); diff --git a/pages/admin/tenants/edit($id).php b/pages/admin/tenants/edit($id).php new file mode 100644 index 0000000..97d08f5 --- /dev/null +++ b/pages/admin/tenants/edit($id).php @@ -0,0 +1,82 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +$uuid = trim((string) ($id ?? '')); +$tenant = $uuid !== '' ? TenantService::findByUuid($uuid) : null; +if (!$tenant) { + Flash::error('Tenant not found', 'admin/tenants', 'tenant_not_found'); + Router::redirect('admin/tenants'); +} + +$tenantId = (int) ($tenant['id'] ?? 0); +$creatorId = (int) ($tenant['created_by'] ?? 0); +if ($creatorId > 0) { + $creator = UserService::findById($creatorId); + if ($creator) { + $creatorName = trim(($creator['first_name'] ?? '') . ' ' . ($creator['last_name'] ?? '')); + $tenant['created_by_label'] = $creatorName !== '' ? $creatorName : ($creator['email'] ?? ''); + $tenant['created_by_uuid'] = $creator['uuid'] ?? null; + } +} +$modifierId = (int) ($tenant['modified_by'] ?? 0); +if ($modifierId > 0) { + $modifier = UserService::findById($modifierId); + if ($modifier) { + $modifierName = trim(($modifier['first_name'] ?? '') . ' ' . ($modifier['last_name'] ?? '')); + $tenant['modified_by_label'] = $modifierName !== '' ? $modifierName : ($modifier['email'] ?? ''); + $tenant['modified_by_uuid'] = $modifier['uuid'] ?? null; + } +} +$statusChangedById = (int) ($tenant['status_changed_by'] ?? 0); +if ($statusChangedById > 0) { + $statusChanger = UserService::findById($statusChangedById); + if ($statusChanger) { + $statusChangerName = trim(($statusChanger['first_name'] ?? '') . ' ' . ($statusChanger['last_name'] ?? '')); + $tenant['status_changed_by_label'] = $statusChangerName !== '' ? $statusChangerName : ($statusChanger['email'] ?? ''); + $tenant['status_changed_by_uuid'] = $statusChanger['uuid'] ?? null; + } +} + +$errors = []; +$form = $tenant; + +if (isset($_POST['description'])) { + if (!PermissionService::userHas($currentUserId, PermissionService::TENANTS_UPDATE)) { + Router::redirect('error/forbidden'); + return; + } + $result = TenantService::updateFromAdmin($tenantId, $_POST, $currentUserId); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + + if ($result['ok'] ?? false) { + if ($currentUserId > 0) { + \MintyPHP\Service\AuthService::loadTenantDataIntoSession($currentUserId); + } + $action = (string) ($_POST['action'] ?? 'save'); + if ($action === 'save_close') { + Flash::success('Tenant updated', 'admin/tenants', 'tenant_updated'); + Router::redirect('admin/tenants'); + } else { + Flash::success('Tenant updated', "admin/tenants/edit/{$uuid}", 'tenant_updated'); + Router::redirect("admin/tenants/edit/{$uuid}"); + } + } +} + +Buffer::set('title', t('Edit tenant')); diff --git a/pages/admin/tenants/edit(default).phtml b/pages/admin/tenants/edit(default).phtml new file mode 100644 index 0000000..f591904 --- /dev/null +++ b/pages/admin/tenants/edit(default).phtml @@ -0,0 +1,371 @@ + $errors + * @var array $form + * @var array $tenant + */ + +use MintyPHP\Session; +use MintyPHP\Service\TenantAvatarService; +use MintyPHP\Service\TenantFaviconService; + +$values = $form ?? $tenant ?? []; +$isReadOnly = !can('tenants.update'); +$avatarUuid = (string) ($values['uuid'] ?? ''); +$hasAvatar = $avatarUuid !== '' && TenantAvatarService::hasAvatar($avatarUuid); +$hasFavicon = $avatarUuid !== '' && TenantFaviconService::hasFavicon($avatarUuid); + +?> + +
+
+ +
+

+ + +

+
+ + + + + + + + +
+
+ + +
+
+
    + +
  • + +
+
+
+ + + +
+ +
diff --git a/pages/admin/tenants/favicon($id).php b/pages/admin/tenants/favicon($id).php new file mode 100644 index 0000000..721f64c --- /dev/null +++ b/pages/admin/tenants/favicon($id).php @@ -0,0 +1,33 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} + +Buffer::set('title', t('Tenants')); +Buffer::set('grid_lang', json_encode(gridLang(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); +$csrfKey = Session::$csrfSessionKey; +$csrfToken = $_SESSION[$csrfKey] ?? ''; +Buffer::set( + 'grid_csrf', + json_encode(['key' => $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) +); diff --git a/pages/admin/tenants/index(default).phtml b/pages/admin/tenants/index(default).phtml new file mode 100644 index 0000000..5d2befe --- /dev/null +++ b/pages/admin/tenants/index(default).phtml @@ -0,0 +1,159 @@ + + +
+

+
+ + + + + + +
+
+
+ +
+
+
+
+ + + + diff --git a/pages/admin/users/_form.phtml b/pages/admin/users/_form.phtml new file mode 100644 index 0000000..cf93bb2 --- /dev/null +++ b/pages/admin/users/_form.phtml @@ -0,0 +1,359 @@ + +
+
+
+ + + + + + + + + +
+ +
+
+ +
+
+ + +
+ +
+
+
+ +
+ + +
+
+
+ +
+
+ + + + +
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
    + +
  • + +
  • +
+
+
+ + + +
+
+ + +
+ +
+ +
+ + 0): ?> + + + + +
+
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ +
+
+ +
+ + +
+
+ +
+
+ + + +
+
+
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+ +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ +
+ +
diff --git a/pages/admin/users/activate($id).php b/pages/admin/users/activate($id).php new file mode 100644 index 0000000..107bb98 --- /dev/null +++ b/pages/admin/users/activate($id).php @@ -0,0 +1,41 @@ + 0 && $currentUserId !== $userId && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + if (Request::wantsJson()) { + http_response_code(403); + Router::json(['error' => 'permission_denied']); + return; + } + Router::redirect('error/forbidden'); + return; +} +$result = UserService::setActiveByUuid($uuid, true, $currentUserId); +if ($result['ok'] ?? false) { + if (Request::wantsJson()) { + http_response_code(204); + return; + } +} elseif (Request::wantsJson()) { + http_response_code((int) ($result['status'] ?? 404)); + Router::json(['error' => 'not_found']); + return; +} + +Router::redirect('admin/users'); diff --git a/pages/admin/users/avatar($id).php b/pages/admin/users/avatar($id).php new file mode 100644 index 0000000..c82145a --- /dev/null +++ b/pages/admin/users/avatar($id).php @@ -0,0 +1,41 @@ + 0 && $currentUserId !== $userId && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + Router::redirect('error/forbidden'); + return; +} + +$result = UserAvatarService::saveUpload($uuid, $_FILES['avatar'] ?? []); +if (!($result['ok'] ?? false)) { + $error = $result['error'] ?? t('Upload failed'); + Flash::error($error, "admin/users/edit/{$uuid}", 'avatar_upload_failed'); + Router::redirect("admin/users/edit/{$uuid}"); + return; +} + +Flash::success('Avatar updated', "admin/users/edit/{$uuid}", 'avatar_updated'); +Router::redirect("admin/users/edit/{$uuid}"); diff --git a/pages/admin/users/avatar-delete($id).php b/pages/admin/users/avatar-delete($id).php new file mode 100644 index 0000000..19c6db3 --- /dev/null +++ b/pages/admin/users/avatar-delete($id).php @@ -0,0 +1,34 @@ + 0 && $currentUserId !== $userId && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + Router::redirect('error/forbidden'); + return; +} + +UserAvatarService::delete($uuid); +Flash::success('Avatar removed', "admin/users/edit/{$uuid}", 'avatar_removed'); +Router::redirect("admin/users/edit/{$uuid}"); diff --git a/pages/admin/users/avatar-file().php b/pages/admin/users/avatar-file().php new file mode 100644 index 0000000..aaad6c5 --- /dev/null +++ b/pages/admin/users/avatar-file().php @@ -0,0 +1,37 @@ + 0 && $currentUserId !== $userId && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + http_response_code(403); + return; +} + +$path = UserAvatarService::findAvatarPath($uuid, $size); +if (!$path || !is_file($path)) { + http_response_code(404); + return; +} + +$mime = UserAvatarService::detectMime($path); +header('Content-Type: ' . $mime); +header('X-Content-Type-Options: nosniff'); +header('Content-Security-Policy: sandbox'); +header('Cache-Control: private, max-age=300'); +header('Content-Length: ' . filesize($path)); +readfile($path); diff --git a/pages/admin/users/avatar-file(none).phtml b/pages/admin/users/avatar-file(none).phtml new file mode 100644 index 0000000..4ba9f2b --- /dev/null +++ b/pages/admin/users/avatar-file(none).phtml @@ -0,0 +1,4 @@ + 'handleActivate', + 'deactivate' => 'handleDeactivate', + 'delete' => 'handleDelete', + 'send-access' => 'handleSendAccess', +]; +if (!isset($handlers[$action])) { + Router::json(['ok' => false, 'error' => 'invalid_action']); +} +if ($action === 'delete') { + Guard::requirePermissionOrForbidden(PermissionService::USERS_DELETE); +} +if ($action === 'send-access') { + Guard::requirePermissionOrForbidden(PermissionService::USERS_UPDATE); +} + +$raw = $_POST['uuids'] ?? ''; +$uuids = []; +if (is_array($raw)) { + $uuids = $raw; +} elseif (is_string($raw)) { + $uuids = array_filter(array_map('trim', explode(',', $raw))); +} +$uuids = array_values(array_unique(array_filter($uuids))); +if (!$uuids) { + Router::json(['ok' => false, 'error' => 'no_selection']); +} + +$currentUserId = (int) ($_SESSION['user']['id'] ?? 0); +call_user_func($handlers[$action], $uuids, $currentUserId); + +function handleActivate(array $uuids, int $currentUserId): void +{ + $result = UserService::setActiveByUuids($uuids, true, $currentUserId); + if (!($result['ok'] ?? false)) { + Router::json(['ok' => false, 'error' => 'update_failed']); + } + Router::json(['ok' => true, 'count' => (int) ($result['count'] ?? 0)]); +} + +function handleDeactivate(array $uuids, int $currentUserId): void +{ + if ($currentUserId > 0) { + $currentUser = UserService::findById($currentUserId); + $currentUuid = $currentUser['uuid'] ?? ''; + if ($currentUuid !== '') { + $uuids = array_values(array_filter($uuids, static fn ($uuid) => $uuid !== $currentUuid)); + } + if (!$uuids) { + Router::json(['ok' => false, 'error' => 'self_deactivate']); + } + } + + $result = UserService::setActiveByUuids($uuids, false, $currentUserId); + if (!($result['ok'] ?? false)) { + Router::json(['ok' => false, 'error' => 'update_failed']); + } + Router::json(['ok' => true, 'count' => (int) ($result['count'] ?? 0)]); +} + +function handleDelete(array $uuids, int $currentUserId): void +{ + $result = UserService::deleteByUuids($uuids, $currentUserId); + if (!($result['ok'] ?? false)) { + Router::json(['ok' => false, 'error' => $result['error'] ?? 'delete_failed']); + } + Router::json(['ok' => true, 'count' => (int) ($result['count'] ?? 0)]); +} + +function handleSendAccess(array $uuids, int $currentUserId): void +{ + $successCount = 0; + $failedCount = 0; + + foreach ($uuids as $uuid) { + $user = UserService::findByUuid($uuid); + if (!$user || empty($user['email'])) { + $failedCount++; + continue; + } + + $locale = $user['locale'] ?? (I18n::$locale ?? I18n::$defaultLocale); + $name = trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')); + $isGerman = strpos((string) $locale, 'de') === 0; + $greeting = $isGerman ? 'Hallo' : 'Hello'; + if ($name !== '') { + $greeting .= ' ' . $name; + } + $greeting .= ','; + + $loginUrl = appUrl(Request::withLocale('login', $locale)); + $resetUrl = appUrl(Request::withLocale('password/forgot', $locale)); + $previousLocale = I18n::$locale ?? null; + I18n::$locale = $locale; + $subject = t('Your access details'); + I18n::$locale = $previousLocale; + + $vars = [ + 'app_name' => appTitle(), + 'app_logo_url' => appLogoUrlAbsolute(128), + 'imprint_url' => appUrl(Request::withLocale('imprint', $locale)), + 'privacy_url' => appUrl(Request::withLocale('privacy', $locale)), + 'greeting' => $greeting, + 'username' => (string) ($user['email'] ?? ''), + 'login_url' => $loginUrl, + 'reset_url' => $resetUrl, + ]; + + $result = MailService::sendTemplate('access_info', $vars, (string) $user['email'], $subject, $locale); + if ($result['ok'] ?? false) { + $successCount++; + } else { + $failedCount++; + } + } + + Router::json([ + 'ok' => $successCount > 0, + 'count' => $successCount, + 'failed' => $failedCount, + ]); +} diff --git a/pages/admin/users/create().php b/pages/admin/users/create().php new file mode 100644 index 0000000..74bccd3 --- /dev/null +++ b/pages/admin/users/create().php @@ -0,0 +1,93 @@ + 0 && in_array($tenantId, $allowedTenantIds, true); + })); +} elseif (TenantScopeService::isStrict()) { + $tenants = []; +} +$roles = RoleService::list(); +$departments = $allowedTenantIds ? DepartmentService::listByTenantIds($allowedTenantIds) : []; +if (!$allowedTenantIds && !TenantScopeService::isStrict()) { + $departments = DepartmentService::list(); +} + +$errors = []; +$form = [ + 'first_name' => '', + 'last_name' => '', + 'email' => '', + 'profile_description' => '', + 'job_title' => '', + 'phone' => '', + 'mobile' => '', + 'short_dial' => '', + 'address' => '', + 'postal_code' => '', + 'city' => '', + 'country' => '', + 'region' => '', + 'hire_date' => '', + 'totp_secret' => '', + 'locale' => I18n::$locale ?? I18n::$defaultLocale, + 'active' => 1, +]; + +if (isset($_POST['email'])) { + $input = $_POST; + $tenantIds = UserService::normalizeIdInput($input['tenant_ids'] ?? []); + if ($allowedTenantIds) { + $tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds)); + } elseif (TenantScopeService::isStrict()) { + $tenantIds = []; + } + $defaultTenantId = SettingService::getDefaultTenantId(); + if (TenantScopeService::isStrict() && !$tenantIds && !$defaultTenantId) { + $errors[] = t('Please select at least one tenant'); + } + $input['tenant_ids'] = $tenantIds; + $result = UserService::createFromAdmin($input, $currentUserId); + $form = $result['form'] ?? $form; + $errors = array_merge($errors, $result['errors'] ?? []); + + if (($result['ok'] ?? false) && !$errors) { + $action = (string) ($_POST['action'] ?? 'create'); + if ($action === 'create_close') { + Flash::success('User created', 'admin/users', 'user_created'); + Router::redirect('admin/users'); + } else { + $uuid = (string) ($result['uuid'] ?? ''); + if ($uuid !== '') { + $target = "admin/users/edit/{$uuid}"; + Flash::success('User created', $target, 'user_created'); + Router::redirect($target); + } else { + Flash::success('User created', 'admin/users', 'user_created'); + Router::redirect('admin/users'); + } + } + } +} + +Buffer::set('title', t('Create user')); diff --git a/pages/admin/users/create(default).phtml b/pages/admin/users/create(default).phtml new file mode 100644 index 0000000..523e059 --- /dev/null +++ b/pages/admin/users/create(default).phtml @@ -0,0 +1,51 @@ + $errors + * @var array $form + */ + + + +?> +
+ +
+ +
+

+ + +

+
+ + +
+
+ +
+
    + +
  • + +
+
+ + +
+
\ No newline at end of file diff --git a/pages/admin/users/data().php b/pages/admin/users/data().php new file mode 100644 index 0000000..9170775 --- /dev/null +++ b/pages/admin/users/data().php @@ -0,0 +1,106 @@ + [], 'total' => 0]); +} + +$currentUserId = (int) ($_SESSION['user']['id'] ?? 0); + +$limit = (int) ($_GET['limit'] ?? 10); +$offset = (int) ($_GET['offset'] ?? 0); +$search = trim((string) ($_GET['search'] ?? '')); +$order = (string) ($_GET['order'] ?? 'last_name'); +$dir = (string) ($_GET['dir'] ?? 'asc'); +$active = $_GET['active'] ?? 'all'; +$createdFrom = trim((string) ($_GET['created_from'] ?? '')); +$createdTo = trim((string) ($_GET['created_to'] ?? '')); +$tenant = trim((string) ($_GET['tenant'] ?? '')); +$roles = $_GET['roles'] ?? ''; +$departments = $_GET['departments'] ?? ''; +$emailVerified = $_GET['email_verified'] ?? 'all'; + +$result = UserService::listPaged([ + 'limit' => $limit, + 'offset' => $offset, + 'search' => $search, + 'order' => $order, + 'dir' => $dir, + 'active' => $active, + 'created_from' => $createdFrom, + 'created_to' => $createdTo, + 'tenant' => $tenant, + 'roles' => $roles, + 'departments' => $departments, + 'email_verified' => $emailVerified, + 'tenantUserId' => $currentUserId, +]); + +$rows = []; +foreach ($result['rows'] as $row) { + $tenantLabels = $row['tenant_labels'] ?? []; + if (is_string($tenantLabels)) { + $tenantList = $tenantLabels !== '' + ? array_values(array_filter(array_map('trim', explode('||', $tenantLabels)))) + : []; + } elseif (is_array($tenantLabels)) { + $tenantList = array_values(array_filter(array_map('trim', $tenantLabels))); + } else { + $tenantList = []; + } + $roleLabels = $row['role_labels'] ?? []; + if (is_string($roleLabels)) { + $roleList = $roleLabels !== '' + ? array_values(array_filter(array_map('trim', explode('||', $roleLabels)))) + : []; + } elseif (is_array($roleLabels)) { + $roleList = array_values(array_filter(array_map('trim', $roleLabels))); + } else { + $roleList = []; + } + $departmentLabels = $row['department_labels'] ?? []; + if (is_string($departmentLabels)) { + $departmentList = $departmentLabels !== '' + ? array_values(array_filter(array_map('trim', explode('||', $departmentLabels)))) + : []; + } elseif (is_array($departmentLabels)) { + $departmentList = array_values(array_filter(array_map('trim', $departmentLabels))); + } else { + $departmentList = []; + } + $primaryTenantLabel = (string) ($row['primary_tenant_label'] ?? ''); + if ($primaryTenantLabel === '' && count($tenantList) === 1) { + $primaryTenantLabel = (string) $tenantList[0]; + } + $uuid = (string) ($row['uuid'] ?? ''); + $rows[] = [ + 'id' => $row['id'] ?? null, + 'uuid' => $uuid, + 'first_name' => $row['first_name'] ?? '', + 'last_name' => $row['last_name'] ?? '', + 'email' => $row['email'] ?? '', + 'phone' => $row['phone'] ?? '', + 'mobile' => $row['mobile'] ?? '', + 'short_dial' => $row['short_dial'] ?? '', + 'tenants' => [ + 'items' => $tenantList, + 'primary' => $primaryTenantLabel, + ], + 'departments' => $departmentList, + 'roles' => $roleList, + 'created' => dt($row['created'] ?? ''), + 'modified' => dt($row['modified'] ?? ''), + 'active' => (int) ($row['active'] ?? 0), + 'has_avatar' => $uuid !== '' && UserAvatarService::hasAvatar($uuid), + ]; +} + +Router::json([ + 'data' => $rows, + 'total' => $result['total'] ?? 0, +]); diff --git a/pages/admin/users/deactivate($id).php b/pages/admin/users/deactivate($id).php new file mode 100644 index 0000000..5af25e8 --- /dev/null +++ b/pages/admin/users/deactivate($id).php @@ -0,0 +1,53 @@ + 0 && $currentUserId !== $userId && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + if (Request::wantsJson()) { + http_response_code(403); + Router::json(['error' => 'permission_denied']); + return; + } + Router::redirect('error/forbidden'); + return; +} +$result = UserService::setActiveByUuid($uuid, false, $currentUserId); +if (!($result['ok'] ?? false)) { + if (($result['error'] ?? '') === 'self_deactivate') { + Flash::error('You cannot deactivate your own account', 'admin/users', 'user_self_deactivate'); + if (Request::wantsJson()) { + http_response_code(400); + Router::json(['error' => 'self_deactivate']); + return; + } + } elseif (Request::wantsJson()) { + http_response_code((int) ($result['status'] ?? 404)); + Router::json(['error' => 'not_found']); + return; + } + Router::redirect('admin/users'); + return; +} + +if (Request::wantsJson()) { + http_response_code(204); + return; +} + +Router::redirect('admin/users'); diff --git a/pages/admin/users/delete($id).php b/pages/admin/users/delete($id).php new file mode 100644 index 0000000..5d34d38 --- /dev/null +++ b/pages/admin/users/delete($id).php @@ -0,0 +1,61 @@ + 0 && $currentUserId !== $userId && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + if (Request::wantsJson()) { + http_response_code(403); + Router::json(['error' => 'permission_denied']); + return; + } + Router::redirect('error/forbidden'); + return; +} +$result = UserService::deleteByUuid($uuid, $currentUserId); + +if (!($result['ok'] ?? false)) { + if (($result['error'] ?? '') === 'self_delete') { + Flash::error('You cannot delete your own account', 'admin/users', 'user_self_delete'); + if (Request::wantsJson()) { + http_response_code(400); + Router::json(['error' => 'self_delete']); + return; + } + } else { + Flash::error('User not found', 'admin/users', 'user_not_found'); + if (Request::wantsJson()) { + http_response_code(404); + Router::json(['error' => 'not_found']); + return; + } + } + Router::redirect('admin/users'); + return; +} + +if (Request::wantsJson()) { + http_response_code(204); + Router::json([]); + return; +} + +Flash::success('User deleted', 'admin/users', 'user_deleted'); +Router::redirect('admin/users'); diff --git a/pages/admin/users/edit($id).php b/pages/admin/users/edit($id).php new file mode 100644 index 0000000..669c55a --- /dev/null +++ b/pages/admin/users/edit($id).php @@ -0,0 +1,160 @@ + 0) { + PermissionService::getUserPermissions($currentUserId, true); +} + +$uuid = trim((string) ($id ?? '')); +$user = $uuid !== '' ? UserService::findByUuid($uuid) : null; +if (!$user) { + Flash::error('User not found', 'admin/users', 'user_not_found'); + Router::redirect('admin/users'); +} + +$userId = (int) ($user['id'] ?? 0); +$canViewUsers = PermissionService::userHas($currentUserId, PermissionService::USERS_VIEW); +$canUpdateUser = PermissionService::userHas($currentUserId, PermissionService::USERS_UPDATE); +$canUpdateSelf = PermissionService::userHas($currentUserId, PermissionService::USERS_SELF_UPDATE); +$canUpdateAssignments = PermissionService::userHas($currentUserId, PermissionService::USERS_UPDATE_ASSIGNMENTS); +$isOwnAccount = $currentUserId === $userId; +$canEditUser = $isOwnAccount ? ($canUpdateUser || $canUpdateSelf) : $canUpdateUser; +$canEditAssignments = $canEditUser && $canUpdateAssignments; +if (!$isOwnAccount && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + Router::redirect('error/forbidden'); + return; +} +if (!$canViewUsers && !$canEditUser) { + Router::redirect('error/forbidden'); + return; +} +$creatorId = (int) ($user['created_by'] ?? 0); +if ($creatorId > 0) { + $creator = UserService::findById($creatorId); + if ($creator) { + $creatorName = trim(($creator['first_name'] ?? '') . ' ' . ($creator['last_name'] ?? '')); + $user['created_by_label'] = $creatorName !== '' ? $creatorName : ($creator['email'] ?? ''); + $user['created_by_uuid'] = $creator['uuid'] ?? null; + } +} +$modifierId = (int) ($user['modified_by'] ?? 0); +if ($modifierId > 0) { + $modifier = UserService::findById($modifierId); + if ($modifier) { + $modifierName = trim(($modifier['first_name'] ?? '') . ' ' . ($modifier['last_name'] ?? '')); + $user['modified_by_label'] = $modifierName !== '' ? $modifierName : ($modifier['email'] ?? ''); + $user['modified_by_uuid'] = $modifier['uuid'] ?? null; + } +} +$activeChangedById = (int) ($user['active_changed_by'] ?? 0); +if ($activeChangedById > 0) { + $activeChanger = UserService::findById($activeChangedById); + if ($activeChanger) { + $activeChangerName = trim(($activeChanger['first_name'] ?? '') . ' ' . ($activeChanger['last_name'] ?? '')); + $user['active_changed_by_label'] = $activeChangerName !== '' ? $activeChangerName : ($activeChanger['email'] ?? ''); + $user['active_changed_by_uuid'] = $activeChanger['uuid'] ?? null; + } +} +$errors = []; +$form = $user; +// Users with tenants.update permission can see ALL tenants (to assign new tenants to users) +$canManageTenants = PermissionService::userHas($currentUserId, PermissionService::TENANTS_UPDATE); +$allowedTenantIds = $canManageTenants ? null : TenantScopeService::getUserTenantIds($currentUserId); + +$tenants = TenantService::list(); +if ($allowedTenantIds) { + $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool { + $tenantId = (int) ($tenant['id'] ?? 0); + return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true); + })); +} elseif (!$canManageTenants && TenantScopeService::isStrict()) { + $tenants = []; +} +$selectedTenantIds = UserTenantRepository::listTenantIdsByUserId($userId); +if ($allowedTenantIds) { + $selectedTenantIds = array_values(array_intersect($selectedTenantIds, $allowedTenantIds)); +} +$primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0); +if (!$primaryTenantId && count($selectedTenantIds) === 1) { + $primaryTenantId = (int) $selectedTenantIds[0]; + $user['primary_tenant_id'] = $primaryTenantId; +} +$roles = RoleService::list(); +$selectedRoleIds = UserRoleRepository::listRoleIdsByUserId($userId); +$permissionRows = RolePermissionRepository::listPermissionsWithRolesByRoleIds($selectedRoleIds); +$departments = DepartmentService::listForUserAssignments($selectedTenantIds, []); +$selectedDepartmentIds = UserDepartmentRepository::listDepartmentIdsByUserId($userId); +if ($selectedDepartmentIds) { + $departments = DepartmentService::listForUserAssignments($selectedTenantIds, $selectedDepartmentIds); +} +// Keep initial $isOwnAccount/$canEditUser values for view permissions. + +if (isset($_POST['email'])) { + if (!$canEditUser) { + Router::redirect('error/forbidden'); + return; + } + $result = UserService::updateFromAdmin($userId, $_POST, $currentUserId); + $form = $result['form'] ?? $form; + $errors = $result['errors'] ?? []; + $tenantIds = UserService::normalizeIdInput($_POST['tenant_ids'] ?? []); + if ($allowedTenantIds) { + $tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds)); + } elseif (!$canManageTenants && TenantScopeService::isStrict()) { + $tenantIds = []; + } + $selectedTenantIds = $tenantIds; + $primaryTenantId = (int) ($_POST['primary_tenant_id'] ?? 0); + $selectedRoleIds = UserService::normalizeIdInput($_POST['role_ids'] ?? []); + $selectedDepartmentIds = UserService::normalizeIdInput($_POST['department_ids'] ?? []); + $departments = DepartmentService::listForUserAssignments($selectedTenantIds, $selectedDepartmentIds); + + if ($result['ok'] ?? false) { + if ($canEditAssignments) { + UserService::syncTenants($userId, $tenantIds); + UserService::syncRoles($userId, $selectedRoleIds); + UserService::syncDepartments($userId, $selectedDepartmentIds); + + // Update session if editing own account (tenant assignments changed) + if ($currentUserId === $userId) { + AuthService::loadTenantDataIntoSession($userId); + } + } + if ($currentUserId === $userId && isset($form['theme'])) { + $_SESSION['user']['theme'] = $form['theme'] ?: 'light'; + } + if ($currentUserId === $userId && isset($form['locale'])) { + $_SESSION['user']['locale'] = $form['locale']; + I18n::$locale = $form['locale']; + } + $action = (string) ($_POST['action'] ?? 'save'); + if ($action === 'save_close') { + Flash::success('User updated', 'admin/users', 'user_updated'); + Router::redirect('admin/users'); + } else { + Flash::success('User updated', "admin/users/edit/{$uuid}", 'user_updated'); + Router::redirect("admin/users/edit/{$uuid}"); + } + } +} + +Buffer::set('title', $isOwnAccount ? t('My account') : t('Edit user')); diff --git a/pages/admin/users/edit(default).phtml b/pages/admin/users/edit(default).phtml new file mode 100644 index 0000000..8f2e0e7 --- /dev/null +++ b/pages/admin/users/edit(default).phtml @@ -0,0 +1,312 @@ + $errors + * @var array $form + * @var array $user + */ + +use MintyPHP\Router; +use MintyPHP\Session; +use MintyPHP\Service\UserAvatarService; + +$values = $form ?? $user ?? []; +$avatarUuid = (string) ($values['uuid'] ?? ''); +$hasAvatar = $avatarUuid !== '' && UserAvatarService::hasAvatar($avatarUuid); +$currentUserId = (int) ($_SESSION['user']['id'] ?? 0); +$isOwnAccount = $currentUserId > 0 && $currentUserId === (int) ($values['id'] ?? 0); +$titleText = $isOwnAccount ? t('My account') : t('Edit user'); +$canViewUsers = can('users.view'); +$hideNavigation = $isOwnAccount && !$canViewUsers; + +?> + +
+
+ + + +
+

+ + + + +

+
+ + + + + + + + + + +
+
+ + +
+
+
    + +
  • + +
+
+
+ + + +
+ +
diff --git a/pages/admin/users/export().php b/pages/admin/users/export().php new file mode 100644 index 0000000..f0bbecf --- /dev/null +++ b/pages/admin/users/export().php @@ -0,0 +1,49 @@ + 5000) { + $limit = 5000; +} + +$result = UserService::listPaged([ + 'limit' => $limit, + 'offset' => 0, + 'search' => $search, + 'order' => $order, + 'dir' => $dir, + 'active' => $active, + 'created_from' => $createdFrom, + 'created_to' => $createdTo, + 'tenant' => $tenant, + 'roles' => $roles, + 'departments' => $departments, + 'tenantUserId' => $currentUserId, +]); + +$exportRows = $result['rows'] ?? []; +$exportFilename = 'users-' . date('Ymd-His') . '.csv'; diff --git a/pages/admin/users/export(none).phtml b/pages/admin/users/export(none).phtml new file mode 100644 index 0000000..d143521 --- /dev/null +++ b/pages/admin/users/export(none).phtml @@ -0,0 +1,75 @@ + 0 && $currentUserId === $userId; +$canEditUser = $isOwnAccount ? ($canUpdateUser || $canUpdateSelf) : $canUpdateUser; + +if (!$canEditUser) { + Router::redirect('error/forbidden'); + return; +} +if (!$isOwnAccount && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + Router::redirect('error/forbidden'); + return; +} + +RememberMeService::forgetAllForUser($userId); +Flash::success(t('Login tokens cleared'), "admin/users/edit/{$uuid}", 'tokens_cleared'); +Router::redirect("admin/users/edit/{$uuid}"); diff --git a/pages/admin/users/index().php b/pages/admin/users/index().php new file mode 100644 index 0000000..dcede09 --- /dev/null +++ b/pages/admin/users/index().php @@ -0,0 +1,48 @@ + 0) { + PermissionService::getUserPermissions($currentUserId); +} +$allowedTenantIds = TenantScopeService::getUserTenantIds($currentUserId); + +Buffer::set('title', t('Users')); +Buffer::set('grid_lang', json_encode(gridLang(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); +$csrfKey = Session::$csrfSessionKey; +$csrfToken = $_SESSION[$csrfKey] ?? ''; +Buffer::set( + 'grid_csrf', + json_encode(['key' => $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) +); + +$tenants = TenantService::list(); +if ($allowedTenantIds) { + $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool { + $tenantId = (int) ($tenant['id'] ?? 0); + return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true); + })); +} elseif (TenantScopeService::isStrict()) { + $tenants = []; +} +sortByDescription($tenants); + +$roles = RoleService::list(); +sortByDescription($roles); + +$departments = $allowedTenantIds ? DepartmentService::listByTenantIds($allowedTenantIds) : []; +if (!$allowedTenantIds && !TenantScopeService::isStrict()) { + $departments = DepartmentService::list(); +} +sortByDescription($departments); diff --git a/pages/admin/users/index(default).phtml b/pages/admin/users/index(default).phtml new file mode 100644 index 0000000..3b8258a --- /dev/null +++ b/pages/admin/users/index(default).phtml @@ -0,0 +1,389 @@ + 1; +?> + +
+

+
+ + + + + + + + + + + + + + + + + +
+
+ +
+ + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+
+
+ + + + + + diff --git a/pages/admin/users/send-access($id).php b/pages/admin/users/send-access($id).php new file mode 100644 index 0000000..956f60c --- /dev/null +++ b/pages/admin/users/send-access($id).php @@ -0,0 +1,73 @@ + 0 && $currentUserId === $userId; +$canEditUser = $isOwnAccount ? ($canUpdateUser || $canUpdateSelf) : $canUpdateUser; + +if (!$canEditUser) { + Router::redirect('error/forbidden'); + return; +} +if (!$isOwnAccount && !TenantScopeService::canAccess('users', $userId, $currentUserId)) { + Router::redirect('error/forbidden'); + return; +} + +$locale = $user['locale'] ?? (I18n::$locale ?? I18n::$defaultLocale); +$name = trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')); +$isGerman = strpos((string) $locale, 'de') === 0; +$greeting = $isGerman ? 'Hallo' : 'Hello'; +if ($name !== '') { + $greeting .= ' ' . $name; +} +$greeting .= ','; + +$loginUrl = appUrl(Request::withLocale('login', $locale)); +$resetUrl = appUrl(Request::withLocale('password/forgot', $locale)); +$previousLocale = I18n::$locale ?? null; +I18n::$locale = $locale; +$subject = t('Your access details'); +I18n::$locale = $previousLocale; + +$vars = [ + 'app_name' => appTitle(), + 'app_logo_url' => appLogoUrlAbsolute(128), + 'imprint_url' => appUrl(Request::withLocale('imprint', $locale)), + 'privacy_url' => appUrl(Request::withLocale('privacy', $locale)), + 'greeting' => $greeting, + 'username' => (string) ($user['email'] ?? ''), + 'login_url' => $loginUrl, + 'reset_url' => $resetUrl, +]; + +$result = MailService::sendTemplate('access_info', $vars, (string) $user['email'], $subject, $locale); +if (!($result['ok'] ?? false)) { + Flash::error(t('Access email failed'), "admin/users/edit/{$uuid}", 'access_email_failed'); + Router::redirect("admin/users/edit/{$uuid}"); +} + +Flash::success(t('Access email sent'), "admin/users/edit/{$uuid}", 'access_email_sent'); +Router::redirect("admin/users/edit/{$uuid}"); diff --git a/pages/admin/users/switch-tenant().php b/pages/admin/users/switch-tenant().php new file mode 100644 index 0000000..837643d --- /dev/null +++ b/pages/admin/users/switch-tenant().php @@ -0,0 +1,83 @@ + false, 'error' => 'csrf']); + return; + } + Router::redirect('admin'); + return; +} + +$userId = (int) ($_SESSION['user']['id'] ?? 0); +if ($userId <= 0) { + http_response_code(401); + if (Request::wantsJson()) { + Router::json(['ok' => false, 'error' => 'unauthorized']); + return; + } + Router::redirect('login'); + return; +} + +// Get tenant_id from POST +$tenantId = (int) ($_POST['tenant_id'] ?? 0); + +if ($tenantId <= 0) { + http_response_code(400); + if (Request::wantsJson()) { + Router::json(['ok' => false, 'error' => 'invalid_tenant_id']); + return; + } + Router::redirect('admin'); + return; +} + +$result = UserService::setCurrentTenant($userId, $tenantId); +if (!($result['ok'] ?? false)) { + $error = $result['error'] ?? 'update_failed'; + http_response_code(400); + if (Request::wantsJson()) { + Router::json(['ok' => false, 'error' => $error]); + return; + } + Router::redirect('admin'); + return; +} + +// Update session with new current tenant +if (isset($_SESSION['user'])) { + $_SESSION['user']['current_tenant_id'] = $tenantId; +} + +// Update session with full tenant data +$_SESSION['current_tenant'] = $result['tenant'] ?? null; + +// Reload available tenants and departments +$_SESSION['available_tenants'] = UserService::getAvailableTenants($userId); +$_SESSION['available_departments_by_tenant'] = UserService::getAvailableDepartmentsByTenant($userId); + +if (Request::wantsJson()) { + Router::json([ + 'ok' => true, + 'tenant_id' => $tenantId, + 'tenant' => $result['tenant'] ?? null, + ]); + return; +} + +Router::redirect('admin'); diff --git a/pages/admin/users/theme().php b/pages/admin/users/theme().php new file mode 100644 index 0000000..682fc32 --- /dev/null +++ b/pages/admin/users/theme().php @@ -0,0 +1,66 @@ + false, 'error' => 'csrf']); + return; + } + Router::redirect('admin'); + return; +} + +$userId = (int) ($_SESSION['user']['id'] ?? 0); +if ($userId <= 0) { + http_response_code(401); + if (Request::wantsJson()) { + Router::json(['ok' => false, 'error' => 'unauthorized']); + return; + } + Router::redirect('login'); + return; +} + +$theme = trim((string) ($_POST['theme'] ?? '')); +if (!in_array($theme, ['light', 'dark'], true)) { + http_response_code(400); + if (Request::wantsJson()) { + Router::json(['ok' => false, 'error' => 'invalid_theme']); + return; + } + Router::redirect('admin'); + return; +} + +$updated = UserService::setTheme($userId, $theme); +if (!$updated) { + http_response_code(500); + if (Request::wantsJson()) { + Router::json(['ok' => false, 'error' => 'update_failed']); + return; + } + Router::redirect('admin'); + return; +} + +$_SESSION['user']['theme'] = $theme; + +if (Request::wantsJson()) { + Router::json(['ok' => true, 'theme' => $theme]); + return; +} + +Router::redirect('admin'); diff --git a/pages/auth/forgot().php b/pages/auth/forgot().php new file mode 100644 index 0000000..5427125 --- /dev/null +++ b/pages/auth/forgot().php @@ -0,0 +1,31 @@ + $errors + * @var string $email + */ + +use MintyPHP\Buffer; +use MintyPHP\Session; + +Buffer::set('title', t('Forgot password')); +?> + +
+
+ +
+

+

+
+
+
+ + +
+
    + +
  • + +
+
+ + + +
+
+

+ +

+
diff --git a/pages/auth/login().php b/pages/auth/login().php new file mode 100644 index 0000000..2a25fd7 --- /dev/null +++ b/pages/auth/login().php @@ -0,0 +1,19 @@ + + +
+
+ +
+

+

+
+
+
+ + +

+ +

+

+ +

+

+ ? +

+ +
+
+

?

+
diff --git a/pages/auth/logout().php b/pages/auth/logout().php new file mode 100644 index 0000000..30eb6fd --- /dev/null +++ b/pages/auth/logout().php @@ -0,0 +1,5 @@ + $firstName, + 'last_name' => $lastName, + 'email' => $email, + 'password' => $password, + 'password2' => $password2, + ]); + + if (!($result['ok'] ?? false)) { + $error = $result['error'] ?? t('User can not be registered'); + } else { + // Store email in session for verify-email page + $_SESSION['email_verification_email'] = $result['email'] ?? $email; + Flash::success(t('Registration successful! Please check your email for the verification code.'), 'verify-email', 'registration_success'); + Router::redirect('verify-email'); + } +} diff --git a/pages/auth/register(login).phtml b/pages/auth/register(login).phtml new file mode 100644 index 0000000..7888419 --- /dev/null +++ b/pages/auth/register(login).phtml @@ -0,0 +1,74 @@ + + +
+
+ +
+

+

+
+
+
+ + + +
+ + + + + + + +
+ +
    + +
  • + +
  • +
+
+
+ + +
+ + +
+
diff --git a/pages/auth/reset().php b/pages/auth/reset().php new file mode 100644 index 0000000..78dce4b --- /dev/null +++ b/pages/auth/reset().php @@ -0,0 +1,40 @@ + $errors + * @var array $passwordHints + * @var int $passwordMinLength + */ + +use MintyPHP\Buffer; +use MintyPHP\Session; + +Buffer::set('title', t('Reset password')); +?> + +
+
+ +
+

+

+
+
+
+
+ + + + + + + + +
+ +
    + +
  • + +
  • +
+
+ +
+ +
+
    + +
  • + +
+
+ + + +
+
+

+ +

+
diff --git a/pages/auth/verify().php b/pages/auth/verify().php new file mode 100644 index 0000000..85bb291 --- /dev/null +++ b/pages/auth/verify().php @@ -0,0 +1,35 @@ + $errors + * @var string $email + * @var string $code + */ + +use MintyPHP\Buffer; +use MintyPHP\Session; + +Buffer::set('title', t('Verify code')); +?> + +
+
+ +
+

+

+
+
+
+ + + +
+
    + +
  • + +
+
+ + + +
+
+

+ +  ·  + +

+
diff --git a/pages/auth/verify-email().php b/pages/auth/verify-email().php new file mode 100644 index 0000000..b5cd5fb --- /dev/null +++ b/pages/auth/verify-email().php @@ -0,0 +1,60 @@ + $errors + * @var string $email + * @var string $code + */ + +use MintyPHP\Buffer; +use MintyPHP\Session; + +Buffer::set('title', t('Verify email')); +?> + +
+
+ +
+

+

+
+
+
+ + + +
+
    + +
  • + +
+
+ + + +
+
+
+ + + + +
+

+ +

+
diff --git a/pages/branding/logo(none).phtml b/pages/branding/logo(none).phtml new file mode 100644 index 0000000..10a684c --- /dev/null +++ b/pages/branding/logo(none).phtml @@ -0,0 +1,19 @@ + +
+

+

+
+

+

+
+ +

+ + \ No newline at end of file diff --git a/pages/error/method_not_allowed(error).phtml b/pages/error/method_not_allowed(error).phtml new file mode 100644 index 0000000..a1da22d --- /dev/null +++ b/pages/error/method_not_allowed(error).phtml @@ -0,0 +1,6 @@ + +Submitting data using method GET is not allowed \ No newline at end of file diff --git a/pages/error/not_found(error).phtml b/pages/error/not_found(error).phtml new file mode 100644 index 0000000..f67ccf4 --- /dev/null +++ b/pages/error/not_found(error).phtml @@ -0,0 +1,21 @@ + +
+

+

+
+ +

:

+ + + diff --git a/pages/flash/dismiss($id).php b/pages/flash/dismiss($id).php new file mode 100644 index 0000000..cfff6fe --- /dev/null +++ b/pages/flash/dismiss($id).php @@ -0,0 +1,14 @@ + 0; +$returnPath = Request::path(); +if ($returnPath === '') { + $returnPath = 'page/' . $slug; +} +$requestedReturn = Request::safeReturnTarget((string) ($_POST['return'] ?? '')); +if ($requestedReturn !== '') { + $returnPath = $requestedReturn; +} +$formAction = '/' . ltrim($returnPath, '/'); +$wantsJson = Request::wantsJson(); + +$sessionEdit = false; +if (!empty($_SESSION['page_edit_mode']) && is_array($_SESSION['page_edit_mode'])) { + $sessionEdit = !empty($_SESSION['page_edit_mode'][$slug]); + unset($_SESSION['page_edit_mode'][$slug]); +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!$canEdit || $notFound) { + http_response_code(403); + Flash::error(t('You are not allowed to edit this page'), $returnPath, 'page_forbidden'); + if ($wantsJson) { + Router::json(['ok' => false, 'error' => 'forbidden', 'redirect' => $returnPath]); + } + Router::redirect($returnPath); + } + + if (!Session::checkCsrfToken()) { + Flash::error(t('Form expired, please try again'), $returnPath, 'page_csrf'); + if ($wantsJson) { + Router::json(['ok' => false, 'error' => 'csrf', 'redirect' => $returnPath]); + } + Router::redirect($returnPath); + } + + $content = (string) ($_POST['content'] ?? ''); + $result = PageService::updateContentBySlug($slug, $content, $currentUserId, I18n::$locale ?? null); + + if (!($result['ok'] ?? false)) { + $errors = $result['errors'] ?? [t('Page can not be updated')]; + Flash::error((string) $errors[0], $returnPath, 'page_update_failed'); + if ($wantsJson) { + $_SESSION['page_edit_mode'][$slug] = 1; + Router::json(['ok' => false, 'error' => 'invalid', 'redirect' => $returnPath]); + } + $_SESSION['page_edit_mode'][$slug] = 1; + Router::redirect($returnPath); + } + + Flash::success(t('Page updated'), $returnPath, 'page_updated'); + if ($wantsJson) { + $_SESSION['page_edit_mode'][$slug] = 1; + Router::json(['ok' => true, 'redirect' => $returnPath]); + } + $_SESSION['page_edit_mode'][$slug] = 1; + Router::redirect($returnPath); +} + +$contentJson = ''; +if (is_array($pageContent) && isset($pageContent['content'])) { + $contentJson = (string) $pageContent['content']; +} +if ($contentJson === '') { + $contentJson = json_encode(['blocks' => []], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); +} + +$editMode = $canEdit && (($sessionEdit === true) || (($_GET['edit'] ?? '') === '1')); +$locales = defined('APP_LOCALES') ? APP_LOCALES : [I18n::$defaultLocale]; +$currentLocale = I18n::$locale ?? (I18n::$defaultLocale ?? 'de'); diff --git a/pages/page/index(default).phtml b/pages/page/index(default).phtml new file mode 100644 index 0000000..365f240 --- /dev/null +++ b/pages/page/index(default).phtml @@ -0,0 +1,113 @@ + +
+
+ +

+ +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + +
diff --git a/pages/search/data().php b/pages/search/data().php new file mode 100644 index 0000000..faf9aee --- /dev/null +++ b/pages/search/data().php @@ -0,0 +1,121 @@ + 0) { + PermissionService::getUserPermissions($userId); +} + +$query = trim((string) ($_GET['search'] ?? '')); +if ($query === '') { + Router::json(['data' => [], 'total' => 0]); +} + +$limit = (int) ($_GET['limit'] ?? 10); +if ($limit < 1) { + $limit = 10; +} elseif ($limit > 100) { + $limit = 100; +} +$offset = (int) ($_GET['offset'] ?? 0); +if ($offset < 0) { + $offset = 0; +} + +$locale = I18n::$locale ?? (I18n::$defaultLocale ?? 'de'); +$tenantIds = $userId > 0 ? UserTenantRepository::listTenantIdsByUserId($userId) : []; +$tenantScoped = !empty($tenantIds); + +$tenantScopeFilters = SearchConfig::tenantScopeFilters(); +$resources = SearchConfig::resources($query, $locale); +$items = []; + +foreach ($resources as $resource) { + $key = (string) ($resource['key'] ?? ''); + if ($key === '') { + continue; + } + + $perm = (string) ($resource['permission'] ?? ''); + if ($perm !== '' && !PermissionService::userHas($userId, $perm)) { + continue; + } + + $tenantFilter = $tenantScopeFilters[$key] ?? ''; + $isTenantScoped = $tenantFilter !== ''; + if ($isTenantScoped && !$tenantScoped) { + continue; + } + + $sql = $resource['resultSql'] ?? ''; + if ($sql === '') { + continue; + } + $sql = str_replace('{{tenantFilter}}', $isTenantScoped ? $tenantFilter : '', $sql); + $params = $resource['resultParams'] ?? []; + if ($isTenantScoped && strpos($sql, '???') !== false) { + $params[] = $tenantIds; + } + + $rows = DB::select($sql, ...$params); + foreach ($rows as $row) { + $data = $row; + if (is_array($row) && count($row) === 1) { + $data = reset($row); + } + $data = is_array($data) ? $data : []; + $mapped = SearchConfig::mapResultItem($key, (string) ($resource['label'] ?? ''), $data); + if ($mapped !== null) { + $items[] = $mapped; + } + } +} + +$queryLower = mb_strtolower($query); +$scoreItem = static function (array $item) use ($queryLower): int { + $title = mb_strtolower((string) ($item['title'] ?? '')); + $description = mb_strtolower((string) ($item['description'] ?? '')); + if ($title === $queryLower) { + return 400; + } + if ($title !== '' && strpos($title, $queryLower) === 0) { + return 300; + } + if ($title !== '' && strpos($title, $queryLower) !== false) { + return 200; + } + if ($description !== '' && strpos($description, $queryLower) !== false) { + return 100; + } + return 0; +}; + +usort($items, static function (array $a, array $b) use ($scoreItem): int { + $scoreA = $scoreItem($a); + $scoreB = $scoreItem($b); + if ($scoreA !== $scoreB) { + return $scoreB <=> $scoreA; + } + $title = strcmp((string) ($a['title'] ?? ''), (string) ($b['title'] ?? '')); + if ($title !== 0) { + return $title; + } + return strcmp((string) ($a['type'] ?? ''), (string) ($b['type'] ?? '')); +}); + +$total = count($items); +$items = array_slice($items, $offset, $limit); + +Router::json([ + 'data' => $items, + 'total' => $total, +]); diff --git a/pages/search/index().php b/pages/search/index().php new file mode 100644 index 0000000..b52b679 --- /dev/null +++ b/pages/search/index().php @@ -0,0 +1,17 @@ + $csrfKey, 'token' => $csrfToken], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) +); diff --git a/pages/search/index(default).phtml b/pages/search/index(default).phtml new file mode 100644 index 0000000..97747a4 --- /dev/null +++ b/pages/search/index(default).phtml @@ -0,0 +1,86 @@ + + +
+

+
+
+ +
+
+
+
+ + + + diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..ae4eb5f --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,13 @@ +parameters: + level: 5 + phpVersion: 80500 + scanFiles: + - web/index.php + paths: + - config + - lib + - pages + - templates + fileExtensions: + - php + - phtml diff --git a/storage/.gitkeep b/storage/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/storage/.gitkeep @@ -0,0 +1 @@ + diff --git a/templates/.DS_Store b/templates/.DS_Store new file mode 100644 index 0000000..c09e888 Binary files /dev/null and b/templates/.DS_Store differ diff --git a/templates/default.phtml b/templates/default.phtml new file mode 100644 index 0000000..1c87117 --- /dev/null +++ b/templates/default.phtml @@ -0,0 +1,104 @@ + + +style=""> + + + + + <?php e($defaultTitle); ?> <?php if (Buffer::get('title')) + ; ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + + +
+ + +
+
+ +
+
+ + + + + + + diff --git a/templates/emails/de/access_info.html b/templates/emails/de/access_info.html new file mode 100644 index 0000000..dd060ac --- /dev/null +++ b/templates/emails/de/access_info.html @@ -0,0 +1,18 @@ + + + + {{email_header}} +

{{greeting}}

+

dein Zugang wurde eingerichtet.

+

Benutzername: {{username}}

+

+ Zur Anmeldung +

+

+ Falls du dein Passwort nicht kennst, kannst du es hier zurücksetzen: + Passwort vergessen +

+

Viele Grüße
{{app_name}}

+ {{email_footer}} + + diff --git a/templates/emails/de/access_info.txt b/templates/emails/de/access_info.txt new file mode 100644 index 0000000..f900cd2 --- /dev/null +++ b/templates/emails/de/access_info.txt @@ -0,0 +1,17 @@ +{{email_header_text}} + +{{greeting}} + +dein Zugang wurde eingerichtet. + +Benutzername: {{username}} + +Zur Anmeldung: +{{login_url}} + +Falls du dein Passwort nicht kennst, kannst du es hier zurücksetzen: +{{reset_url}} + +Viele Grüße +{{app_name}} +{{email_footer_text}} diff --git a/templates/emails/de/email_verification.html b/templates/emails/de/email_verification.html new file mode 100644 index 0000000..4719398 --- /dev/null +++ b/templates/emails/de/email_verification.html @@ -0,0 +1,15 @@ + + + + {{email_header}} +

{{greeting}}

+

Vielen Dank für deine Registrierung! Bitte bestätige deine E-Mail-Adresse mit folgendem Code:

+

{{code}}

+

Dieser Code ist {{expires_minutes}} Minuten gültig.

+

Du kannst den Code hier eingeben:

+

{{verify_url}}

+

Wenn du dich nicht registriert hast, kannst du diese E-Mail ignorieren.

+

Viele Grüße
{{app_name}}

+ {{email_footer}} + + diff --git a/templates/emails/de/email_verification.txt b/templates/emails/de/email_verification.txt new file mode 100644 index 0000000..7ccdd1b --- /dev/null +++ b/templates/emails/de/email_verification.txt @@ -0,0 +1,18 @@ +{{email_header_text}} + +{{greeting}} + +Vielen Dank für deine Registrierung! Bitte bestätige deine E-Mail-Adresse mit folgendem Code: + +Dein Code: {{code}} + +Dieser Code ist {{expires_minutes}} Minuten gültig. + +Code hier eingeben: +{{verify_url}} + +Wenn du dich nicht registriert hast, kannst du diese E-Mail ignorieren. + +Viele Grüße +{{app_name}} +{{email_footer_text}} diff --git a/templates/emails/de/partials/footer.html b/templates/emails/de/partials/footer.html new file mode 100644 index 0000000..24390f4 --- /dev/null +++ b/templates/emails/de/partials/footer.html @@ -0,0 +1,16 @@ + + + + +
+ Impressum +  ·  + Datenschutz +
+ {{app_name}} · Diese E-Mail wurde automatisch erstellt. + + + + + + diff --git a/templates/emails/de/partials/footer.txt b/templates/emails/de/partials/footer.txt new file mode 100644 index 0000000..6864a0a --- /dev/null +++ b/templates/emails/de/partials/footer.txt @@ -0,0 +1,2 @@ +--- +{{app_name}} · Diese E-Mail wurde automatisch erstellt. diff --git a/templates/emails/de/partials/header.html b/templates/emails/de/partials/header.html new file mode 100644 index 0000000..e11502d --- /dev/null +++ b/templates/emails/de/partials/header.html @@ -0,0 +1,14 @@ + + + + +
+ + + + + + + + + + +
+
+ {{app_name}} + {{app_name}} +
+
diff --git a/templates/emails/de/partials/header.txt b/templates/emails/de/partials/header.txt new file mode 100644 index 0000000..8197968 --- /dev/null +++ b/templates/emails/de/partials/header.txt @@ -0,0 +1,2 @@ +{{app_name}} +================= diff --git a/templates/emails/de/reset_code.html b/templates/emails/de/reset_code.html new file mode 100644 index 0000000..9cd9409 --- /dev/null +++ b/templates/emails/de/reset_code.html @@ -0,0 +1,15 @@ + + + + {{email_header}} +

{{greeting}}

+

du hast einen Code zum Zurücksetzen deines Passworts angefordert.

+

{{code}}

+

Dieser Code ist {{expires_minutes}} Minuten gültig.

+

Du kannst den Code hier eingeben:

+

{{verify_url}}

+

Wenn du diese Anfrage nicht gestellt hast, kannst du diese E-Mail ignorieren.

+

Viele Grüße
{{app_name}}

+ {{email_footer}} + + diff --git a/templates/emails/de/reset_code.txt b/templates/emails/de/reset_code.txt new file mode 100644 index 0000000..dd1886e --- /dev/null +++ b/templates/emails/de/reset_code.txt @@ -0,0 +1,18 @@ +{{email_header_text}} + +{{greeting}} + +du hast einen Code zum Zurücksetzen deines Passworts angefordert. + +Dein Code: {{code}} + +Dieser Code ist {{expires_minutes}} Minuten gültig. + +Code hier eingeben: +{{verify_url}} + +Wenn du diese Anfrage nicht gestellt hast, kannst du diese E-Mail ignorieren. + +Viele Grüße +{{app_name}} +{{email_footer_text}} diff --git a/templates/emails/en/access_info.html b/templates/emails/en/access_info.html new file mode 100644 index 0000000..631ef40 --- /dev/null +++ b/templates/emails/en/access_info.html @@ -0,0 +1,18 @@ + + + + {{email_header}} +

{{greeting}}

+

Your access has been set up.

+

Username: {{username}}

+

+ Go to login +

+

+ If you do not know your password, you can reset it here: + Forgot password +

+

Best regards
{{app_name}}

+ {{email_footer}} + + diff --git a/templates/emails/en/access_info.txt b/templates/emails/en/access_info.txt new file mode 100644 index 0000000..c3a9b47 --- /dev/null +++ b/templates/emails/en/access_info.txt @@ -0,0 +1,17 @@ +{{email_header_text}} + +{{greeting}} + +Your access has been set up. + +Username: {{username}} + +Go to login: +{{login_url}} + +If you do not know your password, you can reset it here: +{{reset_url}} + +Best regards +{{app_name}} +{{email_footer_text}} diff --git a/templates/emails/en/email_verification.html b/templates/emails/en/email_verification.html new file mode 100644 index 0000000..4a02360 --- /dev/null +++ b/templates/emails/en/email_verification.html @@ -0,0 +1,15 @@ + + + + {{email_header}} +

{{greeting}}

+

Thank you for registering! Please confirm your email address with the following code:

+

{{code}}

+

This code is valid for {{expires_minutes}} minutes.

+

You can enter the code here:

+

{{verify_url}}

+

If you did not register, you can ignore this email.

+

Best regards
{{app_name}}

+ {{email_footer}} + + diff --git a/templates/emails/en/email_verification.txt b/templates/emails/en/email_verification.txt new file mode 100644 index 0000000..903dce2 --- /dev/null +++ b/templates/emails/en/email_verification.txt @@ -0,0 +1,18 @@ +{{email_header_text}} + +{{greeting}} + +Thank you for registering! Please confirm your email address with the following code: + +Your code: {{code}} + +This code is valid for {{expires_minutes}} minutes. + +Enter the code here: +{{verify_url}} + +If you did not register, you can ignore this email. + +Best regards +{{app_name}} +{{email_footer_text}} diff --git a/templates/emails/en/partials/footer.html b/templates/emails/en/partials/footer.html new file mode 100644 index 0000000..ea554b5 --- /dev/null +++ b/templates/emails/en/partials/footer.html @@ -0,0 +1,16 @@ +
+
+ Imprint +  ·  + Privacy +
+ {{app_name}} · This email was generated automatically. +
+
diff --git a/templates/emails/en/partials/footer.txt b/templates/emails/en/partials/footer.txt new file mode 100644 index 0000000..928429c --- /dev/null +++ b/templates/emails/en/partials/footer.txt @@ -0,0 +1,2 @@ +--- +{{app_name}} · This email was generated automatically. diff --git a/templates/emails/en/partials/header.html b/templates/emails/en/partials/header.html new file mode 100644 index 0000000..e11502d --- /dev/null +++ b/templates/emails/en/partials/header.html @@ -0,0 +1,14 @@ + + +
+ + + + + +
+
+ {{app_name}} + {{app_name}} +
+
diff --git a/templates/emails/en/partials/header.txt b/templates/emails/en/partials/header.txt new file mode 100644 index 0000000..8197968 --- /dev/null +++ b/templates/emails/en/partials/header.txt @@ -0,0 +1,2 @@ +{{app_name}} +================= diff --git a/templates/emails/en/reset_code.html b/templates/emails/en/reset_code.html new file mode 100644 index 0000000..1d68e71 --- /dev/null +++ b/templates/emails/en/reset_code.html @@ -0,0 +1,15 @@ + + + + {{email_header}} +

{{greeting}}

+

You requested a password reset code.

+

{{code}}

+

This code is valid for {{expires_minutes}} minutes.

+

You can enter the code here:

+

{{verify_url}}

+

If you did not request this, you can ignore this email.

+

Best regards
{{app_name}}

+ {{email_footer}} + + diff --git a/templates/emails/en/reset_code.txt b/templates/emails/en/reset_code.txt new file mode 100644 index 0000000..b5d7f88 --- /dev/null +++ b/templates/emails/en/reset_code.txt @@ -0,0 +1,18 @@ +{{email_header_text}} + +{{greeting}} + +You requested a password reset code. + +Your code: {{code}} + +This code is valid for {{expires_minutes}} minutes. + +Enter the code here: +{{verify_url}} + +If you did not request this, you can ignore this email. + +Best regards +{{app_name}} +{{email_footer_text}} diff --git a/templates/error.phtml b/templates/error.phtml new file mode 100644 index 0000000..5ebf3d7 --- /dev/null +++ b/templates/error.phtml @@ -0,0 +1,13 @@ + +

ERROR:

+ + +style="" > + + + + + <?php e($defaultTitle); ?> <?php if (Buffer::get('title')); ?> + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+ + + + + diff --git a/templates/partials/.DS_Store b/templates/partials/.DS_Store new file mode 100644 index 0000000..0676a32 Binary files /dev/null and b/templates/partials/.DS_Store differ diff --git a/templates/partials/app-aside-icon-bar.phtml b/templates/partials/app-aside-icon-bar.phtml new file mode 100644 index 0000000..9062c92 --- /dev/null +++ b/templates/partials/app-aside-icon-bar.phtml @@ -0,0 +1,69 @@ + + diff --git a/templates/partials/app-aside.phtml b/templates/partials/app-aside.phtml new file mode 100644 index 0000000..834c42f --- /dev/null +++ b/templates/partials/app-aside.phtml @@ -0,0 +1,399 @@ + '', + 'visible' => $hasOrganization, + 'items' => [ + [ + 'label' => t('Tenants'), + 'path' => 'admin/tenants', + 'active' => navActive('admin/tenants', true), + 'visible' => $canViewTenants, + 'withTenant' => false, + ], + [ + 'label' => t('Departments'), + 'path' => 'admin/departments', + 'active' => navActive('admin/departments', true), + 'visible' => $canViewDepartments, + 'withTenant' => true, + ], + [ + 'label' => t('Users'), + 'path' => 'admin/users', + 'active' => navActive('admin/users', true), + 'visible' => $canViewUsers, + 'withTenant' => true, + ], + ], + ], + [ + 'label' => t('Roles & permissions'), + 'visible' => $hasUsersSection, + 'items' => [ + [ + 'label' => t('Roles'), + 'path' => 'admin/roles', + 'active' => navActive('admin/roles', true), + 'visible' => $canViewRoles, + 'withTenant' => false, + ], + [ + 'label' => t('Permissions'), + 'path' => 'admin/permissions', + 'active' => navActive('admin/permissions', true), + 'visible' => $canViewPermissions, + 'withTenant' => false, + ], + ], + ], + [ + 'label' => t('System'), + 'visible' => $hasSystemSection, + 'items' => [ + [ + 'label' => t('Statistics'), + 'path' => 'admin/stats', + 'active' => navActive('admin/stats', true), + 'visible' => $canViewStats, + 'withTenant' => false, + ], + [ + 'label' => t('Mail logs'), + 'path' => 'admin/mail-log', + 'active' => navActive('admin/mail-log', true), + 'visible' => $canViewMailLog, + 'withTenant' => false, + ], + [ + 'label' => t('Settings'), + 'path' => 'admin/settings', + 'active' => navActive('admin/settings', true), + 'visible' => $canViewSettings, + 'withTenant' => false, + ], + ], + ], +]; + +// Render helper for admin sections +$renderNavSection = static function (array $section, string $tenantQueryParam): void { + if (empty($section['visible'])) { + return; + } + $items = $section['items'] ?? []; + $items = array_values(array_filter($items, static fn ($item) => !empty($item['visible']))); + if (!$items) { + return; + } + ?> + + + + + '', 'aria' => '']; + ?> +
  • + > + + +
  • + + 1 && !empty($currentTenant['uuid'])) { + $tenantQueryParam = '?tenant=' . urlencode($currentTenant['uuid']); +} + +// Tenant logo display (without switcher - switcher is now in topbar) +$tenantUuid = $currentTenant['uuid'] ?? ''; +$tenantName = $currentTenant['description'] ?? ''; +$hasTenantAvatar = $tenantUuid && TenantAvatarService::hasAvatar($tenantUuid); +$addressBookUrl = lurl('address-book'); +$activeAddressTenants = array_filter(array_map('trim', explode(',', (string) ($_GET['tenants'] ?? '')))); +$activeAddressDepartments = array_filter(array_map('intval', explode(',', (string) ($_GET['departments'] ?? '')))); +$peopleGroups = $_SESSION['available_departments_by_tenant'] ?? []; +?> + diff --git a/templates/partials/app-flash.phtml b/templates/partials/app-flash.phtml new file mode 100644 index 0000000..a6eed46 --- /dev/null +++ b/templates/partials/app-flash.phtml @@ -0,0 +1,34 @@ + 5000, + 'info' => 6000, + 'warning' => 7000, + 'error' => 0, +]; + +?> +
    + + + +
    + +
    + + + +
    +
    + +
    diff --git a/templates/partials/app-footer.phtml b/templates/partials/app-footer.phtml new file mode 100644 index 0000000..5d50d45 --- /dev/null +++ b/templates/partials/app-footer.phtml @@ -0,0 +1,24 @@ + diff --git a/templates/partials/app-language-switcher.phtml b/templates/partials/app-language-switcher.phtml new file mode 100644 index 0000000..d269ffe --- /dev/null +++ b/templates/partials/app-language-switcher.phtml @@ -0,0 +1,66 @@ + + + + + + + + + + + diff --git a/templates/partials/app-tile.phtml b/templates/partials/app-tile.phtml new file mode 100644 index 0000000..467b73f --- /dev/null +++ b/templates/partials/app-tile.phtml @@ -0,0 +1,44 @@ + +style=""> + + + + + + + + + + + diff --git a/templates/partials/app-topbar.phtml b/templates/partials/app-topbar.phtml new file mode 100644 index 0000000..15aa1a8 --- /dev/null +++ b/templates/partials/app-topbar.phtml @@ -0,0 +1,106 @@ + +
    + +
    diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000..ea19a3e Binary files /dev/null and b/tests/.DS_Store differ diff --git a/tests/Http/RequestTest.php b/tests/Http/RequestTest.php new file mode 100644 index 0000000..e5007ac --- /dev/null +++ b/tests/Http/RequestTest.php @@ -0,0 +1,54 @@ +serverBackup = $_SERVER; + Router::$baseUrl = '/'; + } + + protected function tearDown(): void + { + $_SERVER = $this->serverBackup; + parent::tearDown(); + } + + public function testPathWithQuery(): void + { + $_SERVER['REQUEST_URI'] = '/admin/users?active=1'; + + $this->assertSame('admin/users?active=1', Request::pathWithQuery()); + } + + public function testSafeReturnTargetPrefersRelativeParam(): void + { + $this->assertSame( + 'admin/users?active=1', + Request::safeReturnTarget('admin/users?active=1') + ); + } + + public function testWantsJsonFromAcceptHeader(): void + { + $_SERVER['HTTP_ACCEPT'] = 'application/json'; + + $this->assertTrue(Request::wantsJson()); + } + + public function testWantsJsonFalseForHtml(): void + { + $_SERVER['HTTP_ACCEPT'] = 'text/html'; + + $this->assertFalse(Request::wantsJson()); + } +} diff --git a/tests/Service/UserServicePasswordTest.php b/tests/Service/UserServicePasswordTest.php new file mode 100644 index 0000000..3257939 --- /dev/null +++ b/tests/Service/UserServicePasswordTest.php @@ -0,0 +1,49 @@ +invoke(null, $password, $password2, $required, $email); + } + + public function testRequiresPasswordWhenRequired(): void + { + $errors = $this->validate('', '', true, 'test@example.com'); + $this->assertNotEmpty($errors); + } + + public function testRejectsMismatch(): void + { + $errors = $this->validate('Abcd1234$efg', 'Abcd1234$xxx', true, 'test@example.com'); + $this->assertNotEmpty($errors); + } + + public function testAcceptsStrongPassword(): void + { + $errors = $this->validate('StrongPass1!', 'StrongPass1!', true, 'test@example.com'); + $this->assertSame([], $errors); + } + + public function testRejectsPasswordContainingEmail(): void + { + $errors = $this->validate('test@example.comA1!', 'test@example.comA1!', true, 'test@example.com'); + $this->assertNotEmpty($errors); + } +} diff --git a/web/.DS_Store b/web/.DS_Store new file mode 100644 index 0000000..3c9725f Binary files /dev/null and b/web/.DS_Store differ diff --git a/web/.htaccess b/web/.htaccess new file mode 100644 index 0000000..6f3a18a --- /dev/null +++ b/web/.htaccess @@ -0,0 +1,9 @@ +php_flag display_startup_errors on +php_flag display_errors on + + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php [QSA,L] + diff --git a/web/brand/logo.svg b/web/brand/logo.svg new file mode 100644 index 0000000..973159c --- /dev/null +++ b/web/brand/logo.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/css/base/variables.css b/web/css/base/variables.css new file mode 100644 index 0000000..9406177 --- /dev/null +++ b/web/css/base/variables.css @@ -0,0 +1,713 @@ +@charset "UTF-8"; + +:host, +:root { + --app-font-family-emoji: + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --app-font-family-sans-serif: + system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, + "Helvetica Neue", sans-serif, var(--app-font-family-emoji); + --app-font-family-monospace: + ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", + monospace, var(--app-font-family-emoji); + --app-font-family: var(--app-font-family-sans-serif); + --app-line-height: 1.5; + --app-font-weight: 400; + --app-font-size: 100%; + --app-text-underline-offset: 0.1rem; + --app-border-radius: 0.5rem; + --app-border-width: 0.0625rem; + --app-outline-width: 0.125rem; + --app-transition: 0.2s ease-in-out; + --app-spacing: 1rem; + --app-typography-spacing-vertical: 1rem; + --app-block-spacing-vertical: 1.5rem; + --app-block-spacing-horizontal: 1.5rem; + --app-grid-column-gap: var(--app-spacing); + --app-grid-row-gap: var(--app-spacing); + --app-flex-gap: var(--app-spacing); + --app-form-element-spacing-vertical: .5rem; + --app-form-element-spacing-horizontal: .7rem; + --app-button-padding-vertical: var(--app-form-element-spacing-vertical); + --app-button-padding-horizontal: var(--app-form-element-spacing-horizontal); + --app-button-padding-vertical-small: calc( + var(--app-form-element-spacing-vertical) * 0.3 + ); + --app-button-padding-horizontal-small: calc( + var(--app-form-element-spacing-horizontal) * 0.5 + ); + --app-group-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-group-box-shadow-focus-with-button: 0 0 0 var(--app-outline-width) + var(--app-primary-focus); + --app-group-box-shadow-focus-with-input: 0 0 0 0.0625rem + var(--app-form-element-border-color); + --app-modal-overlay-backdrop-filter: blur(0.375rem); + --app-nav-element-spacing-vertical: 1rem; + --app-nav-element-spacing-horizontal: 0.5rem; + --app-nav-link-spacing-vertical: 0.5rem; + --app-nav-link-spacing-horizontal: 0.5rem; + --app-nav-breadcrumb-divider: ">"; + --app-primary-h-base: 180.75; + --app-primary-s-base: 37.38%; + --app-primary-l-base: 58.04%; + --app-secondary-h-base: 223; + --app-secondary-s-base: 19%; + --app-secondary-l-base: 45%; + --app-badge-background-color: coral; +} + +/* Icons */ +:host, +:root { + --app-icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); + --app-icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E"); + --app-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); + --app-icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); + --app-icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E"); + --app-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); + --app-icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E"); + --app-icon-loading: url("data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E"); +} + +@media (min-width: 576px) { + :host, + :root { + --app-font-size: 100%; + } +} + +@media (min-width: 768px) { + :host, + :root { + --app-font-size: 100%; + } +} + +@media (min-width: 1024px) { + :host, + :root { + --app-font-size: 100%; + } +} + +@media (min-width: 1280px) { + :host, + :root { + --app-font-size: 100%; + } +} + +@media (min-width: 1536px) { + :host, + :root { + --app-font-size: 100%; + } +} + +:host(:not([data-theme="dark"])), +:root:not([data-theme="dark"]), +[data-theme="light"] { + color-scheme: light; + --app-background-color: #fff; + --app-sidebar-background-color: rgb(247 250 252); + --app-color: #373c44; + --app-text-selection-color: rgba(2, 154, 232, 0.25); + --app-muted-color: #646b79; + --app-muted-border-color: rgb(231, 234, 239.5); + --app-primary-h-light: 180.75; + --app-primary-s-light: 37.38%; + --app-primary-l-light: 58.04%; + --app-primary-h: var(--app-primary-h-light, var(--app-primary-h-base)); + --app-primary-s: var(--app-primary-s-light, var(--app-primary-s-base)); + --app-primary-l: var(--app-primary-l-light, var(--app-primary-l-base)); + --app-primary-l-hover: -7%; + --app-primary-l-hover-bg: -4%; + --app-primary-l-focus: 16%; + --app-primary: hsl( + var(--app-primary-h) var(--app-primary-s) var(--app-primary-l) + ); + --app-primary-background: var(--app-primary); + --app-primary-border: var(--app-primary-background); + --app-primary-underline: hsl( + var(--app-primary-h) var(--app-primary-s) var(--app-primary-l) / 0.5 + ); + --app-primary-hover: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-hover)) + ); + --app-primary-hover-background: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-hover-bg)) + ); + --app-primary-hover-border: var(--app-primary-hover-background); + --app-primary-hover-underline: var(--app-primary-hover); + --app-primary-focus: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-focus)) / 0.5 + ); + --app-primary-inverse: #fff; + --app-secondary-h-light: 223; + --app-secondary-s-light: 19%; + --app-secondary-l-light: 45%; + --app-secondary-h: var(--app-secondary-h-light, var(--app-secondary-h-base)); + --app-secondary-s: var(--app-secondary-s-light, var(--app-secondary-s-base)); + --app-secondary-l: var(--app-secondary-l-light, var(--app-secondary-l-base)); + --app-secondary-l-hover: -6%; + --app-secondary-l-hover-bg: -6%; + --app-secondary-l-focus: 0%; + --app-secondary: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) + ); + --app-secondary-background: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) - 4%) + ); + --app-secondary-border: var(--app-secondary-background); + --app-secondary-underline: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) / 0.5 + ); + --app-secondary-hover: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) + var(--app-secondary-l-hover)) + ); + --app-secondary-hover-background: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) + var(--app-secondary-l-hover-bg)) + ); + --app-secondary-hover-border: var(--app-secondary-hover-background); + --app-secondary-hover-underline: var(--app-secondary-hover); + --app-secondary-focus: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) / 0.25 + ); + --app-secondary-inverse: #fff; + --app-contrast: #181c25; + --app-contrast-background: #181c25; + --app-contrast-border: var(--app-contrast-background); + --app-contrast-underline: rgba(24, 28, 37, 0.5); + --app-contrast-hover: #000; + --app-contrast-hover-background: #000; + --app-contrast-hover-border: var(--app-contrast-hover-background); + --app-contrast-hover-underline: var(--app-secondary-hover); + --app-contrast-focus: rgba(93, 107, 137, 0.25); + --app-contrast-inverse: #fff; + --app-box-shadow: + 0.0145rem 0.029rem 0.174rem rgba(129, 145, 181, 0.01698), + 0.0335rem 0.067rem 0.402rem rgba(129, 145, 181, 0.024), + 0.0625rem 0.125rem 0.75rem rgba(129, 145, 181, 0.03), + 0.1125rem 0.225rem 1.35rem rgba(129, 145, 181, 0.036), + 0.2085rem 0.417rem 2.502rem rgba(129, 145, 181, 0.04302), + 0.5rem 1rem 6rem rgba(129, 145, 181, 0.06), + 0 0 0 0.0625rem rgba(129, 145, 181, 0.015); + --app-h1-color: #2d3138; + --app-h2-color: #373c44; + --app-h3-color: #424751; + --app-h4-color: #4d535e; + --app-h5-color: #5c6370; + --app-h6-color: #646b79; + --app-mark-background-color: rgb(252.5, 230.5, 191.5); + --app-mark-color: #0f1114; + --app-ins-color: rgb(28.5, 105.5, 84); + --app-del-color: rgb(136, 56.5, 53); + --app-blockquote-border-color: var(--app-muted-border-color); + --app-blockquote-footer-color: var(--app-muted-color); + --app-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-table-border-color: var(--app-muted-border-color); + --app-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); + --app-code-background-color: rgb(243, 244.5, 246.75); + --app-code-color: #646b79; + --app-code-kbd-background-color: var(--app-color); + --app-code-kbd-color: var(--app-background-color); + --app-form-element-background-color: rgb(251, 251.5, 252.25); + --app-form-element-selected-background-color: #dfe3eb; + --app-form-element-border-color: #cfd5e2; + --app-border: var(--app-form-element-border-color); + --app-form-element-color: #23262c; + --app-form-element-placeholder-color: var(--app-muted-color); + --app-form-element-active-background-color: #fff; + --app-form-element-active-border-color: var(--app-primary-border); + --app-form-element-focus-color: var(--app-primary-border); + --app-form-element-disabled-opacity: 0.5; + --app-form-element-invalid-border-color: rgb(183.5, 105.5, 106.5); + --app-form-element-invalid-active-border-color: rgb(200.25, 79.25, 72.25); + --app-form-element-invalid-focus-color: var( + --app-form-element-invalid-active-border-color + ); + --app-form-element-valid-border-color: rgb(76, 154.5, 137.5); + --app-form-element-valid-active-border-color: rgb(39, 152.75, 118.75); + --app-form-element-valid-focus-color: var( + --app-form-element-valid-active-border-color + ); + --app-notice-success-color: var(--app-form-element-valid-border-color); + --app-notice-success-border-color: var(--app-form-element-valid-border-color); + --app-notice-success-background-color: rgba(76, 154.5, 137.5, 0.12); + --app-notice-warning-color: rgb(173, 113, 34); + --app-notice-warning-border-color: rgb(214, 145, 55); + --app-notice-warning-background-color: rgba(214, 145, 55, 0.12); + --app-notice-info-color: var(--app-secondary); + --app-notice-info-border-color: var(--app-secondary); + --app-notice-info-background-color: rgba(96.9, 110.5, 137.7, 0.12); + --app-notice-error-color: var(--app-form-element-invalid-border-color); + --app-notice-error-border-color: var(--app-form-element-invalid-border-color); + --app-notice-error-background-color: rgb(255 0 0 / 13%); + --app-switch-background-color: #bfc7d9; + --app-switch-checked-background-color: var(--app-primary-background); + --app-switch-color: #fff; + --app-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-range-border-color: #dfe3eb; + --app-range-active-border-color: #bfc7d9; + --app-range-thumb-border-color: var(--app-background-color); + --app-range-thumb-color: var(--app-secondary-background); + --app-range-thumb-active-color: var(--app-primary-background); + --app-accordion-border-color: var(--app-muted-border-color); + --app-accordion-active-summary-color: var(--app-primary-hover); + --app-accordion-close-summary-color: var(--app-color); + --app-accordion-open-summary-color: var(--app-muted-color); + --app-card-background-color: var(--app-background-color); + --app-card-border-color: var(--app-muted-border-color); + --app-card-box-shadow: var(--app-box-shadow); + --app-card-sectioning-background-color: rgb(251, 251.5, 252.25); + --app-dropdown-background-color: #fff; + --app-dropdown-border-color: #eff1f4; + --app-dropdown-box-shadow: var(--app-box-shadow); + --app-dropdown-color: var(--app-color); + --app-dropdown-hover-background-color: #eff1f4; + --app-loading-spinner-opacity: 0.5; + --app-modal-overlay-background-color: rgba(232, 234, 237, 0.75); + --app-progress-background-color: #dfe3eb; + --app-progress-color: var(--app-primary-background); + --app-tooltip-background-color: var(--app-contrast-background); + --app-tooltip-color: var(--app-contrast-inverse); + --app-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 154.5, 137.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); + --app-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200.25, 79.25, 72.25)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); +} + +:host(:not([data-theme="dark"])) + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ), +:root:not([data-theme="dark"]) + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ), +[data-theme="light"] + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ) { + --app-form-element-focus-color: var(--app-primary-focus); +} + +@media only screen and (prefers-color-scheme: dark) { + :host(:not([data-theme])), + :root:not([data-theme]) { + color-scheme: dark; + --app-background-color: rgb(19, 22.5, 30.5); + --app-sidebar-background-color: rgb(18 20 27); + --app-color: #c2c7d0; + --app-text-selection-color: rgba(1, 170, 255, 0.1875); + --app-muted-color: #7b8495; + --app-muted-border-color: #202632; + --app-primary-h-dark: var(--app-primary-h-light, var(--app-primary-h-base)); + --app-primary-s-dark: var(--app-primary-s-light, var(--app-primary-s-base)); + --app-primary-l-dark: var(--app-primary-l-light, var(--app-primary-l-base)); + --app-primary-h: var(--app-primary-h-dark, var(--app-primary-h-base)); + --app-primary-s: var(--app-primary-s-dark, var(--app-primary-s-base)); + --app-primary-l: var(--app-primary-l-dark, var(--app-primary-l-base)); + --app-primary-l-hover: -7%; + --app-primary-l-hover-bg: -4%; + --app-primary-l-focus: 16%; + --app-primary: hsl( + var(--app-primary-h) var(--app-primary-s) var(--app-primary-l) + ); + --app-primary-background: var(--app-primary); + --app-primary-border: var(--app-primary-background); + --app-primary-underline: hsl( + var(--app-primary-h) var(--app-primary-s) var(--app-primary-l) / 0.5 + ); + --app-primary-hover: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-hover)) + ); + --app-primary-hover-background: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-hover-bg)) + ); + --app-primary-hover-border: var(--app-primary-hover-background); + --app-primary-hover-underline: var(--app-primary-hover); + --app-primary-focus: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-focus)) / 0.5 + ); + --app-primary-inverse: #fff; + --app-secondary-h-dark: var( + --app-secondary-h-light, + var(--app-secondary-h-base) + ); + --app-secondary-s-dark: var( + --app-secondary-s-light, + var(--app-secondary-s-base) + ); + --app-secondary-l-dark: var( + --app-secondary-l-light, + var(--app-secondary-l-base) + ); + --app-secondary-h: var(--app-secondary-h-dark, var(--app-secondary-h-base)); + --app-secondary-s: var(--app-secondary-s-dark, var(--app-secondary-s-base)); + --app-secondary-l: var(--app-secondary-l-dark, var(--app-secondary-l-base)); + --app-secondary-l-hover: -6%; + --app-secondary-l-hover-bg: -6%; + --app-secondary-l-focus: 0%; + --app-secondary: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) + ); + --app-secondary-background: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) - 4%) + ); + --app-secondary-border: var(--app-secondary-background); + --app-secondary-underline: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) / 0.5 + ); + --app-secondary-hover: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) + var(--app-secondary-l-hover)) + ); + --app-secondary-hover-background: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) + var(--app-secondary-l-hover-bg)) + ); + --app-secondary-hover-border: var(--app-secondary-hover-background); + --app-secondary-hover-underline: var(--app-secondary-hover); + --app-secondary-focus: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) / + 0.25 + ); + --app-secondary-inverse: #fff; + --app-contrast: #dfe3eb; + --app-contrast-background: #eff1f4; + --app-contrast-border: var(--app-contrast-background); + --app-contrast-underline: rgba(223, 227, 235, 0.5); + --app-contrast-hover: #fff; + --app-contrast-hover-background: #fff; + --app-contrast-hover-border: var(--app-contrast-hover-background); + --app-contrast-hover-underline: var(--app-contrast-hover); + --app-contrast-focus: rgba(207, 213, 226, 0.25); + --app-contrast-inverse: #000; + --app-box-shadow: + 0.0145rem 0.029rem 0.174rem rgba(7, 8.5, 12, 0.01698), + 0.0335rem 0.067rem 0.402rem rgba(7, 8.5, 12, 0.024), + 0.0625rem 0.125rem 0.75rem rgba(7, 8.5, 12, 0.03), + 0.1125rem 0.225rem 1.35rem rgba(7, 8.5, 12, 0.036), + 0.2085rem 0.417rem 2.502rem rgba(7, 8.5, 12, 0.04302), + 0.5rem 1rem 6rem rgba(7, 8.5, 12, 0.06), + 0 0 0 0.0625rem rgba(7, 8.5, 12, 0.015); + --app-h1-color: #f0f1f3; + --app-h2-color: #e0e3e7; + --app-h3-color: #c2c7d0; + --app-h4-color: #b3b9c5; + --app-h5-color: #a4acba; + --app-h6-color: #8891a4; + --app-mark-background-color: #014063; + --app-mark-color: #fff; + --app-ins-color: #62af9a; + --app-del-color: rgb(205.5, 126, 123); + --app-blockquote-border-color: var(--app-muted-border-color); + --app-blockquote-footer-color: var(--app-muted-color); + --app-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-table-border-color: var(--app-muted-border-color); + --app-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); + --app-code-background-color: rgb(26, 30.5, 40.25); + --app-code-color: #8891a4; + --app-code-kbd-background-color: var(--app-color); + --app-code-kbd-color: var(--app-background-color); + --app-form-element-background-color: rgb(28, 33, 43.5); + --app-form-element-selected-background-color: #2a3140; + --app-form-element-border-color: #2a3140; + --app-border: var(--app-form-element-border-color); + --app-form-element-color: #e0e3e7; + --app-form-element-placeholder-color: #8891a4; + --app-form-element-active-background-color: rgb(26, 30.5, 40.25); + --app-form-element-active-border-color: var(--app-primary-border); + --app-form-element-focus-color: var(--app-primary-border); + --app-form-element-disabled-opacity: 0.5; + --app-form-element-invalid-border-color: rgb(149.5, 74, 80); + --app-form-element-invalid-active-border-color: rgb(183.25, 63.5, 59); + --app-form-element-invalid-focus-color: var( + --app-form-element-invalid-active-border-color + ); + --app-form-element-valid-border-color: #2a7b6f; + --app-form-element-valid-active-border-color: rgb(22, 137, 105.5); + --app-form-element-valid-focus-color: var( + --app-form-element-valid-active-border-color + ); + --app-switch-background-color: #333c4e; + --app-switch-checked-background-color: var(--app-primary-background); + --app-switch-color: #fff; + --app-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-range-border-color: #202632; + --app-range-active-border-color: #2a3140; + --app-range-thumb-border-color: var(--app-background-color); + --app-range-thumb-color: var(--app-secondary-background); + --app-range-thumb-active-color: var(--app-primary-background); + --app-accordion-border-color: var(--app-muted-border-color); + --app-accordion-active-summary-color: var(--app-primary-hover); + --app-accordion-close-summary-color: var(--app-color); + --app-accordion-open-summary-color: var(--app-muted-color); + --app-card-background-color: #181c25; + --app-card-border-color: var(--app-card-background-color); + --app-card-box-shadow: var(--app-box-shadow); + --app-card-sectioning-background-color: rgb(26, 30.5, 40.25); + --app-dropdown-background-color: #181c25; + --app-dropdown-border-color: #202632; + --app-dropdown-box-shadow: var(--app-box-shadow); + --app-dropdown-color: var(--app-color); + --app-dropdown-hover-background-color: #202632; + --app-loading-spinner-opacity: 0.5; + --app-modal-overlay-background-color: rgba(7.5, 8.5, 10, 0.75); + --app-progress-background-color: #202632; + --app-progress-color: var(--app-primary-background); + --app-tooltip-background-color: var(--app-contrast-background); + --app-tooltip-color: var(--app-contrast-inverse); + --app-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); + --app-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); + } + + :host(:not([data-theme])) + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ), + :root:not([data-theme]) + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ) { + --app-form-element-focus-color: var(--app-primary-focus); + } +} + +[data-theme="dark"] { + color-scheme: dark; + --app-background-color: rgb(19, 22.5, 30.5); + --app-sidebar-background-color: rgb(18 20 27); + --app-color: #c2c7d0; + --app-text-selection-color: rgba(1, 170, 255, 0.1875); + --app-muted-color: #7b8495; + --app-muted-border-color: #202632; + --app-primary-h-dark: var(--app-primary-h-light, var(--app-primary-h-base)); + --app-primary-s-dark: var(--app-primary-s-light, var(--app-primary-s-base)); + --app-primary-l-dark: var(--app-primary-l-light, var(--app-primary-l-base)); + --app-primary-h: var(--app-primary-h-dark, var(--app-primary-h-base)); + --app-primary-s: var(--app-primary-s-dark, var(--app-primary-s-base)); + --app-primary-l: var(--app-primary-l-dark, var(--app-primary-l-base)); + --app-primary-l-hover: -7%; + --app-primary-l-hover-bg: -4%; + --app-primary-l-focus: 16%; + --app-primary: hsl( + var(--app-primary-h) var(--app-primary-s) var(--app-primary-l) + ); + --app-primary-background: var(--app-primary); + --app-primary-border: var(--app-primary-background); + --app-primary-underline: hsl( + var(--app-primary-h) var(--app-primary-s) var(--app-primary-l) / 0.5 + ); + --app-primary-hover: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-hover)) + ); + --app-primary-hover-background: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-hover-bg)) + ); + --app-primary-hover-border: var(--app-primary-hover-background); + --app-primary-hover-underline: var(--app-primary-hover); + --app-primary-focus: hsl( + var(--app-primary-h) var(--app-primary-s) + calc(var(--app-primary-l) + var(--app-primary-l-focus)) / 0.5 + ); + --app-primary-inverse: #fff; + --app-secondary-h-dark: var( + --app-secondary-h-light, + var(--app-secondary-h-base) + ); + --app-secondary-s-dark: var( + --app-secondary-s-light, + var(--app-secondary-s-base) + ); + --app-secondary-l-dark: var( + --app-secondary-l-light, + var(--app-secondary-l-base) + ); + --app-secondary-h: var(--app-secondary-h-dark, var(--app-secondary-h-base)); + --app-secondary-s: var(--app-secondary-s-dark, var(--app-secondary-s-base)); + --app-secondary-l: var(--app-secondary-l-dark, var(--app-secondary-l-base)); + --app-secondary-l-hover: -6%; + --app-secondary-l-hover-bg: -6%; + --app-secondary-l-focus: 0%; + --app-secondary: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) + ); + --app-secondary-background: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) - 4%) + ); + --app-secondary-border: var(--app-secondary-background); + --app-secondary-underline: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) / 0.5 + ); + --app-secondary-hover: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) + var(--app-secondary-l-hover)) + ); + --app-secondary-hover-background: hsl( + var(--app-secondary-h) var(--app-secondary-s) + calc(var(--app-secondary-l) + var(--app-secondary-l-hover-bg)) + ); + --app-secondary-hover-border: var(--app-secondary-hover-background); + --app-secondary-hover-underline: var(--app-secondary-hover); + --app-secondary-focus: hsl( + var(--app-secondary-h) var(--app-secondary-s) var(--app-secondary-l) / 0.25 + ); + --app-secondary-inverse: #fff; + --app-contrast: #dfe3eb; + --app-contrast-background: #eff1f4; + --app-contrast-border: var(--app-contrast-background); + --app-contrast-underline: rgba(223, 227, 235, 0.5); + --app-contrast-hover: #fff; + --app-contrast-hover-background: #fff; + --app-contrast-hover-border: var(--app-contrast-hover-background); + --app-contrast-hover-underline: var(--app-contrast-hover); + --app-contrast-focus: rgba(207, 213, 226, 0.25); + --app-contrast-inverse: #000; + --app-box-shadow: + 0.0145rem 0.029rem 0.174rem rgba(7, 8.5, 12, 0.01698), + 0.0335rem 0.067rem 0.402rem rgba(7, 8.5, 12, 0.024), + 0.0625rem 0.125rem 0.75rem rgba(7, 8.5, 12, 0.03), + 0.1125rem 0.225rem 1.35rem rgba(7, 8.5, 12, 0.036), + 0.2085rem 0.417rem 2.502rem rgba(7, 8.5, 12, 0.04302), + 0.5rem 1rem 6rem rgba(7, 8.5, 12, 0.06), + 0 0 0 0.0625rem rgba(7, 8.5, 12, 0.015); + --app-h1-color: #f0f1f3; + --app-h2-color: #e0e3e7; + --app-h3-color: #c2c7d0; + --app-h4-color: #b3b9c5; + --app-h5-color: #a4acba; + --app-h6-color: #8891a4; + --app-mark-background-color: #014063; + --app-mark-color: #fff; + --app-ins-color: #62af9a; + --app-del-color: rgb(205.5, 126, 123); + --app-blockquote-border-color: var(--app-muted-border-color); + --app-blockquote-footer-color: var(--app-muted-color); + --app-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-table-border-color: var(--app-muted-border-color); + --app-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); + --app-code-background-color: rgb(26, 30.5, 40.25); + --app-code-color: #8891a4; + --app-code-kbd-background-color: var(--app-color); + --app-code-kbd-color: var(--app-background-color); + --app-form-element-background-color: rgb(28, 33, 43.5); + --app-form-element-selected-background-color: #2a3140; + --app-form-element-border-color: #2a3140; + --app-border: var(--app-form-element-border-color); + --app-form-element-color: #e0e3e7; + --app-form-element-placeholder-color: #8891a4; + --app-form-element-active-background-color: rgb(26, 30.5, 40.25); + --app-form-element-active-border-color: var(--app-primary-border); + --app-form-element-focus-color: var(--app-primary-border); + --app-form-element-disabled-opacity: 0.5; + --app-form-element-invalid-border-color: rgb(149.5, 74, 80); + --app-form-element-invalid-active-border-color: rgb(183.25, 63.5, 59); + --app-form-element-invalid-focus-color: var( + --app-form-element-invalid-active-border-color + ); + --app-form-element-valid-border-color: #2a7b6f; + --app-form-element-valid-active-border-color: rgb(22, 137, 105.5); + --app-form-element-valid-focus-color: var( + --app-form-element-valid-active-border-color + ); + --app-notice-success-color: var(--app-form-element-valid-border-color); + --app-notice-success-border-color: var(--app-form-element-valid-border-color); + --app-notice-success-background-color: rgba(42, 123, 111, 0.2); + --app-notice-warning-color: rgb(199, 140, 61); + --app-notice-warning-border-color: rgb(199, 140, 61); + --app-notice-warning-background-color: rgba(199, 140, 61, 0.2); + --app-notice-info-color: var(--app-secondary); + --app-notice-info-border-color: var(--app-secondary); + --app-notice-info-background-color: rgba(114.4, 130.1, 165.1, 0.2); + --app-notice-error-color: var(--app-form-element-invalid-border-color); + --app-notice-error-border-color: var(--app-form-element-invalid-border-color); + --app-notice-error-background-color: rgba(149.5, 74, 80, 0.2); + --app-switch-background-color: #333c4e; + --app-switch-checked-background-color: var(--app-primary-background); + --app-switch-color: #fff; + --app-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --app-range-border-color: #202632; + --app-range-active-border-color: #2a3140; + --app-range-thumb-border-color: var(--app-background-color); + --app-range-thumb-color: var(--app-secondary-background); + --app-range-thumb-active-color: var(--app-primary-background); + --app-accordion-border-color: var(--app-muted-border-color); + --app-accordion-active-summary-color: var(--app-primary-hover); + --app-accordion-close-summary-color: var(--app-color); + --app-accordion-open-summary-color: var(--app-muted-color); + --app-card-background-color: #181c25; + --app-card-border-color: var(--app-card-background-color); + --app-card-box-shadow: var(--app-box-shadow); + --app-card-sectioning-background-color: rgb(26, 30.5, 40.25); + --app-dropdown-background-color: #181c25; + --app-dropdown-border-color: #202632; + --app-dropdown-box-shadow: var(--app-box-shadow); + --app-dropdown-color: var(--app-color); + --app-dropdown-hover-background-color: #202632; + --app-loading-spinner-opacity: 0.5; + --app-modal-overlay-background-color: rgba(7.5, 8.5, 10, 0.75); + --app-progress-background-color: #202632; + --app-progress-color: var(--app-primary-background); + --app-tooltip-background-color: var(--app-contrast-background); + --app-tooltip-color: var(--app-contrast-inverse); + --app-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); + --app-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); +} + +[data-theme="dark"] + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ) { + --app-form-element-focus-color: var(--app-primary-focus); +} + +:host, +:root { + --app-scrollbar-width: 0px; +} diff --git a/web/css/components/app-badges.css b/web/css/components/app-badges.css new file mode 100644 index 0000000..82ef859 --- /dev/null +++ b/web/css/components/app-badges.css @@ -0,0 +1,127 @@ +:root { + --badge-success-bg: #e6f6eb; + --badge-success-color: #1f6a3a; + --badge-success-border: #c9ead3; + --badge-danger-bg: #fdecec; + --badge-danger-color: #8f2c2c; + --badge-danger-border: #f6caca; + --badge-info-bg: #e9f0ff; + --badge-info-color: #264db3; + --badge-info-border: #cbd7ff; + --badge-warn-bg: #fff5e6; + --badge-warn-color: #9a5a00; + --badge-warn-border: #ffd9a6; + --badge-neutral-bg: #f2f4f7; + --badge-neutral-color: #4a5568; + --badge-neutral-border: #e1e6ee; +} + +[data-theme="dark"] { + --badge-success-bg: #163a2a; + --badge-success-color: #8fe0b0; + --badge-success-border: #25523c; + --badge-danger-bg: #3a1b1b; + --badge-danger-color: #f2a3a3; + --badge-danger-border: #5b2c2c; + --badge-info-bg: #1a253f; + --badge-info-color: #9fb6ff; + --badge-info-border: #2a3a66; + --badge-warn-bg: #3a2a14; + --badge-warn-color: #ffd596; + --badge-warn-border: #5b401d; + --badge-neutral-bg: #222831; + --badge-neutral-color: #cbd5e1; + --badge-neutral-border: #2f3945; +} + +.badge[role="button"], +.badge { + display: inline-flex; + align-items: center; + gap: 0.35em; + padding: 0.15em 0.6em; + border-radius: 999px; + font-size: 0.85em; + font-weight: 400; + line-height: 1.2; + border: 1px solid transparent; + white-space: nowrap; +} + +.badge[data-copy="true"] { + gap: 0.45em; + padding-right: 0.4em; + cursor: pointer; + user-select: none; +} + +.badge .badge-copy-icon { + display: inline-flex; + align-items: center; + justify-content: center; + color: inherit; + line-height: 1; + font-size: 10px; +} + +.badge .badge-copy-icon .bi { + font-size: 0.95em; +} + +.badge .badge-copy-icon .bi-check2 { + display: none; +} + +.badge.is-copied .badge-copy-icon .bi-copy { + display: none; +} + +.badge.is-copied .badge-copy-icon .bi-check2 { + display: inline; +} + +.badge[data-copy="true"]:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; + border-radius: 6px; +} + +.badge[data-variant="success"] { + background: var(--badge-success-bg); + color: var(--badge-success-color); + border-color: var(--badge-success-border); +} + +.badge[data-variant="danger"] { + background: var(--badge-danger-bg); + color: var(--badge-danger-color); + border-color: var(--badge-danger-border); +} + +.badge[data-variant="info"] { + background: var(--badge-info-bg); + color: var(--badge-info-color); + border-color: var(--badge-info-border); +} + +.badge[data-variant="warn"] { + background: var(--badge-warn-bg); + color: var(--badge-warn-color); + border-color: var(--badge-warn-border); +} +.badge[data-variant="warning"] { + background: var(--badge-warn-bg); + color: var(--badge-warn-color); + border-color: var(--badge-warn-border); +} + +.badge[data-variant="neutral"] { + background: var(--badge-neutral-bg); + color: var(--badge-neutral-color); + border-color: var(--badge-neutral-border); +} + +.badge-list { + display: flex; + gap: 0.35rem; +} diff --git a/web/css/components/app-blockquote.css b/web/css/components/app-blockquote.css new file mode 100644 index 0000000..6a5b744 --- /dev/null +++ b/web/css/components/app-blockquote.css @@ -0,0 +1,66 @@ +:root { + --app-blockquote-background-color: var( + --app-form-element-active-background-color + ); +} + +blockquote { + --blockquote-bg: var(--app-blockquote-background-color); + --blockquote-color: inherit; + --blockquote-border: var(--app-blockquote-border-color); + --blockquote-footer-color: var(--app-blockquote-footer-color); + + display: block; + margin: var(--app-typography-spacing-vertical) 0; + padding: var(--app-spacing); + border-right: none; + border-left: 0.25rem solid var(--app-blockquote-border-color); + border-inline-start: 0.25rem solid var(--app-blockquote-border-color); + border-inline-end: none; + background-color: var(--blockquote-bg); + color: var(--blockquote-color); + border-left-color: var(--blockquote-border); + border-inline-start-color: var(--blockquote-border); + border-radius: var(--app-border-radius); +} + +blockquote footer { + margin-top: calc(var(--app-typography-spacing-vertical) * 0.5); + color: var(--blockquote-footer-color); +} + +blockquote[data-variant="success"] { + --blockquote-bg: var(--badge-success-bg); + --blockquote-color: var(--badge-success-color); + --blockquote-border: var(--badge-success-border); + --blockquote-footer-color: var(--badge-success-color); +} + +blockquote[data-variant="danger"] { + --blockquote-bg: var(--badge-danger-bg); + --blockquote-color: var(--badge-danger-color); + --blockquote-border: var(--badge-danger-border); + --blockquote-footer-color: var(--badge-danger-color); +} + +blockquote[data-variant="info"] { + --blockquote-bg: var(--badge-info-bg); + --blockquote-color: var(--badge-info-color); + --blockquote-border: var(--badge-info-border); + --blockquote-footer-color: var(--badge-info-color); +} + +blockquote[data-variant="warn"], +blockquote[data-variant="warning"] { + --blockquote-bg: var(--badge-warn-bg); + --blockquote-color: var(--badge-warn-color); + --blockquote-border: var(--badge-warn-border); + --blockquote-footer-color: var(--badge-warn-color); +} + +blockquote[data-variant="neutral"] { + --blockquote-bg: var(--badge-neutral-bg); + --blockquote-color: var(--badge-neutral-color); + --blockquote-border: var(--badge-neutral-border); + --blockquote-footer-color: var(--badge-neutral-color); +} diff --git a/web/css/components/app-brand.css b/web/css/components/app-brand.css new file mode 100644 index 0000000..0d54441 --- /dev/null +++ b/web/css/components/app-brand.css @@ -0,0 +1,31 @@ +.brand { + display: flex; + align-items: center; + gap: 10px; +} + +img.brand-logo { + width: 30px; + height: 30px; + object-fit: contain; + flex-shrink: 0; +} + +.brand-name { + line-height: 1; + font-weight: bold; + color: var(--app-contrast); +} + +.brand-info { + line-height: 1; +} + +.brand-slogan { + font-size: 0.85em; + color: var(--app-muted-color); +} + +.badge:empty { + padding: 0; +} \ No newline at end of file diff --git a/web/css/components/app-breadcrumb.css b/web/css/components/app-breadcrumb.css new file mode 100644 index 0000000..913296c --- /dev/null +++ b/web/css/components/app-breadcrumb.css @@ -0,0 +1,12 @@ +.app-breadcrumb { + margin-bottom: 5px; + display: flex; + align-items: center; + gap: 3px; + font-size: 11px; +} + +.app-breadcrumb a { + color: inherit; + text-decoration: none; +} \ No newline at end of file diff --git a/web/css/components/app-buttons.css b/web/css/components/app-buttons.css new file mode 100644 index 0000000..d8f3d3d --- /dev/null +++ b/web/css/components/app-buttons.css @@ -0,0 +1,44 @@ +.danger:is(button, [type="submit"], [type="button"], [role="button"]) { + --app-background-color: var(--app-notice-error-border-color); + --app-border-color: var(--app-notice-error-border-color); + --app-color: var(--app-secondary-inverse); +} + +.danger:is(button, [type="submit"], [type="button"], [role="button"]):is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --app-background-color: var(--app-form-element-invalid-active-border-color); + --app-border-color: var(--app-form-element-invalid-active-border-color); + --app-color: var(--app-secondary-inverse); +} + +.danger:is(button, [type="submit"], [type="button"], [role="button"]).outline { + --app-background-color: transparent; + --app-border-color: var(--app-notice-error-border-color); + --app-color: var(--app-notice-error-border-color); +} + +.danger:is( + button, + [type="submit"], + [type="button"], + [role="button"] + ).outline:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --app-background-color: var(--app-notice-error-background-color); + --app-border-color: var(--app-notice-error-border-color); + --app-color: var(--app-notice-error-border-color); +} + +.danger:is(button, [type="submit"], [type="button"], [role="button"]):focus { + --app-box-shadow: + var(--app-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), + 0 0 0 var(--app-outline-width) var(--app-form-element-invalid-focus-color); +} diff --git a/web/css/components/app-dashboard-titlebar.css b/web/css/components/app-dashboard-titlebar.css new file mode 100644 index 0000000..b33ada9 --- /dev/null +++ b/web/css/components/app-dashboard-titlebar.css @@ -0,0 +1,55 @@ +.app-dashboard-titlebar { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 10px; + margin-block-end: var(--app-spacing); +} + +.app-dashboard-titlebar h1, +.app-dashboard-titlebar h2, +.app-dashboard-titlebar h3, +.app-dashboard-titlebar h4, +.app-dashboard-titlebar h5, +.app-dashboard-titlebar h6 { + margin: 0; +} + +.app-dashboard-titlebar button, +.app-dashboard-titlebar [role="button"] { + --app-button-padding-horizontal: 10px; + --app-button-padding-vertical: 6px; + font-size: 13px; + margin: 0; +} + +.app-dashboard-titlebar-actions { + display: flex; + align-items: center; + gap: 10px; + white-space: nowrap; +} + +.app-dashboard-titlebar h1 i { + background: var(--app-muted-border-color); + width: 35px; + height: 29px; + font-size: 15px; + border-radius: var(--app-border-radius); + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--app-contrast); +} + +.app-dashboard-titlebar h1:has(i) { + display: flex; + align-items: center; + gap: 7px; + line-height: 1; +} + +.app-dashboard-titlebar h1 a:hover i { + background: var(--app-border); +} \ No newline at end of file diff --git a/web/css/components/app-details-titlebar.css b/web/css/components/app-details-titlebar.css new file mode 100644 index 0000000..203d643 --- /dev/null +++ b/web/css/components/app-details-titlebar.css @@ -0,0 +1,87 @@ +.app-details-titlebar { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 10px; + border-bottom: 1px solid var(--app-border); + padding-block-end: var(--app-spacing); + margin-bottom: calc(var(--app-spacing) * 2); +} + +.app-details-titlebar h1, +.app-details-titlebar h2, +.app-details-titlebar h3, +.app-details-titlebar h4, +.app-details-titlebar h5, +.app-details-titlebar h6 { + margin: 0; +} + +.app-details-titlebar button, +.app-details-titlebar [role="button"] { + --app-button-padding-horizontal: 10px; + --app-button-padding-vertical: 6px; + font-size: 13px; + margin: 0; +} + +.app-details-titlebar-actions { + display: flex; + align-items: center; + gap: 10px; + white-space: nowrap; +} + +.app-details-titlebar h1 i { + background: var(--app-background-color); + width: 35px; + height: 29px; + font-size: 15px; + border-radius: var(--app-border-radius); + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--app-contrast); + border: 1px solid var(--app-border); + color: var(--app-muted-color); + box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px; +} + +.app-details-titlebar h1:has(i) { + display: flex; + align-items: center; + gap: 7px; + line-height: 1; +} +.app-details-titlebar details.dropdown { + margin-bottom: 0; +} + +.app-details-titlebar details.dropdown summary:after { + display: none; +} + +.app-details-titlebar details.dropdown li { + padding: 0!important; + margin: 0!important; + border-bottom: 1px solid var(--app-border); +} + +.app-details-titlebar details.dropdown li:last-child { + border-bottom: none; +} + +.app-details-titlebar details.dropdown ul { + padding: 0!important; + margin: 0!important; +} + +.app-details-titlebar details.dropdown li button, +.app-details-titlebar details.dropdown li [role=button] { + margin: 0!important; + border: none; + border-radius: 0; + width: 100%; + text-align: left; +} \ No newline at end of file diff --git a/web/css/components/app-details.css b/web/css/components/app-details.css new file mode 100644 index 0000000..9ee32d8 --- /dev/null +++ b/web/css/components/app-details.css @@ -0,0 +1,137 @@ +.app-details-container aside { + padding-top: var(--app-spacing); +} + +.user-avatar-block, +.entity-avatar-block { + --avatar-size: 96px; + --avatar-ratio: 1 / 1; + display: grid; + gap: calc(var(--app-spacing) * 0.75); + margin-bottom: calc(var(--app-spacing) * 1.25); + width: var(--avatar-size); + aspect-ratio: var(--avatar-ratio); +} + +.user-avatar-image, +.user-avatar-placeholder, +.entity-avatar-image, +.entity-avatar-placeholder { + width: 100%; + height: 100%; + border: 1px solid var(--app-muted-border-color); + background: var(--app-card-background-color); + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 600; + color: var(--app-muted-color); +} + +.user-avatar-placeholder, +.entity-avatar-placeholder { + font-size: 1.5rem; +} + +.user-avatar-image { + object-fit: cover; +} + +.entity-avatar-image { + object-fit: contain; + padding: 3px; +} + +.avatar-round .user-avatar-image, +.avatar-round .user-avatar-placeholder, +.avatar-round .entity-avatar-image, +.avatar-round .entity-avatar-placeholder { + border-radius: 999px; + aspect-ratio: 1; +} + +.avatar-square .user-avatar-image, +.avatar-square .user-avatar-placeholder, +.avatar-square .entity-avatar-image, +.avatar-square .entity-avatar-placeholder { + border-radius: var(--app-radius); +} + +.avatar-ratio-1-1 { + --avatar-ratio: 1 / 1; +} + +.avatar-ratio-3-2 { + --avatar-ratio: 3 / 2; +} + +.avatar-ratio-4-3 { + --avatar-ratio: 4 / 3; +} + +.avatar-ratio-16-9 { + --avatar-ratio: 16 / 9; +} + +.avatar-borderless .user-avatar-image, +.avatar-borderless .user-avatar-placeholder, +.avatar-borderless .entity-avatar-image, +.avatar-borderless .entity-avatar-placeholder { + border: none; +} + +.avatar-size-auto { + width: auto; + aspect-ratio: auto; +} + +.avatar-size-auto .user-avatar-image, +.avatar-size-auto .user-avatar-placeholder, +.avatar-size-auto .entity-avatar-image, +.avatar-size-auto .entity-avatar-placeholder { + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; +} + +.app-details-container > section > * { + padding-inline: calc(var(--app-spacing) * 2); +} + +.app-details-container .app-breadcrumb { + padding-top: calc(var(--app-spacing) * 2); + position: sticky; + top: 20px; + background: var(--app-background-color); +} + +.app-details-container .app-details-titlebar { + position: sticky; + top: 69px; + background: var(--app-background-color); +} + +@media (min-width: 968px) { + /* .app-details-container > section { + padding: calc(var(--app-spacing) * 2) calc(var(--app-spacing) * 2) + calc(var(--app-spacing) * 2) calc(var(--app-spacing) * 0); + } */ + .app-details-container:has(aside) { + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 300px); + min-height: 82vh; + } + .app-details-container.is-aside-collapsed { + grid-template-columns: minmax(0, 1fr); + } + .app-details-container.is-aside-collapsed aside { + display: none; + } + .app-details-container aside { + border-left: 1px solid var(--app-border); + padding: calc(var(--app-spacing) * 2); + border-top: none; + min-height: 100vh; + } +} diff --git a/web/css/components/app-flash.css b/web/css/components/app-flash.css new file mode 100644 index 0000000..1a518d9 --- /dev/null +++ b/web/css/components/app-flash.css @@ -0,0 +1,119 @@ +.notice { + --app-notice-border-color: var(--app-muted-border-color); + --app-notice-background-color: var(--app-card-background-color); + --app-notice-accent-color: var(--app-color); + border: var(--app-border-width) solid var(--app-notice-border-color); + border-radius: var(--app-border-radius); + background: var(--app-notice-background-color); + padding: calc(var(--app-spacing) * .5) calc(var(--app-spacing) * 1); + padding-right: calc(var(--app-spacing) * .5); + margin-bottom: var(--app-spacing); + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--app-spacing); + position: relative; + overflow: hidden; + color: var(--app-notice-border-color); +} + +#async-messages .notice { + border: none; + border-radius: 0; + padding: 0; + padding-right: 5px; + padding-left: 15px; + margin-bottom: 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 5px; + position: relative; + overflow: hidden; + color: var(--app-notice-border-color); +} + + +.notice strong { + color: var(--app-notice-accent-color); +} + +.notice[data-variant=success] { + --app-notice-border-color: var(--app-notice-success-border-color); + --app-notice-background-color: var(--app-notice-success-background-color); + --app-notice-accent-color: var(--app-notice-success-color); +} + +.notice[data-variant=warning] { + --app-notice-border-color: var(--app-notice-warning-border-color); + --app-notice-background-color: var(--app-notice-warning-background-color); + --app-notice-accent-color: var(--app-notice-warning-color); +} + +.notice[data-variant=info] { + --app-notice-border-color: var(--app-notice-info-border-color); + --app-notice-background-color: var(--app-notice-info-background-color); + --app-notice-accent-color: var(--app-notice-info-color); +} + +.notice[data-variant=error] { + --app-notice-border-color: var(--app-notice-error-border-color); + --app-notice-background-color: var(--app-notice-error-background-color); + --app-notice-accent-color: var(--app-notice-error-color); +} + +.notice.flash-timed::after { + content: ""; + position: absolute; + left: 0; + bottom: 0; + height: 3px; + width: 100%; + background: var(--app-notice-border-color); + transform-origin: left; + transform: scaleX(0); + animation: flash-progress var(--flash-timeout, 4000ms) linear forwards; + opacity: 0.9; +} + +.notice.flash-timed:hover::after { + animation-play-state: paused; +} + +@keyframes flash-progress { + from { + transform: scaleX(0); + } + to { + transform: scaleX(1); + } +} + +@media (prefers-reduced-motion: reduce) { + .notice.flash-timed::after { + animation: none; + } +} + +.notice button[type=submit] { + border: 1px solid var(--app-notice-border-color) !important; + background: var(--app-notice-background-color); + color: var(--app-notice-border-color); +} + +.notice ul { + margin: 0; + padding-left: 20px; +} + +.notice li:last-child { + margin: 0; +} + +.app-details-errors { + padding-block-start: 1rem; +} + +.flash-stack:has(.notice) { + margin-top: var(--app-spacing); +} \ No newline at end of file diff --git a/web/css/components/app-forms.css b/web/css/components/app-forms.css new file mode 100644 index 0000000..4d7a5a2 --- /dev/null +++ b/web/css/components/app-forms.css @@ -0,0 +1,62 @@ +.form-hint { + font-size: small; +} + +ul.form-hint-list { + margin: 0; + padding-left: var(--app-spacing); +} + +ul.form-hint-list li { + margin: 0; +} + +ul.form-hint-list li.is-valid { + color: var(--app-form-element-valid-border-color); +} + +ul.form-hint-list li.is-invalid { + color: var(--app-form-element-invalid-border-color); +} + +ul.form-hint-list li.is-valid:after { + content: "\F26B"; + font-family: "Bootstrap-icons"; + margin-left: 4px; + font-size: 10px; +} + +ul.form-hint-list li.is-invalid:after { + content: "\F33A"; + font-family: "Bootstrap-icons"; + margin-left: 4px; + font-size: 10px; +} + +label:has([type="checkbox"], [type="radio"]) { + display: flex; + line-height: 1; + align-items: center; +} + +input[type="color"] { + padding: 0; + width: 42px; + border: 0; + margin: 0; + height: 42px; + flex-shrink: 0; +} + +label:has(input[type="color"]) { + display: flex; + align-items: center; + gap: 10px; +} + +small.muted { + display: block; + width: 100%; + color: var(--app-muted-color); +} + diff --git a/web/css/components/app-list-table.css b/web/css/components/app-list-table.css new file mode 100644 index 0000000..3ff8ae8 --- /dev/null +++ b/web/css/components/app-list-table.css @@ -0,0 +1,7 @@ +.app-list-table table { + margin: 0; +} + +.app-list-table { + white-space: nowrap; +} \ No newline at end of file diff --git a/web/css/components/app-list-tabs.css b/web/css/components/app-list-tabs.css new file mode 100644 index 0000000..5e5bcde --- /dev/null +++ b/web/css/components/app-list-tabs.css @@ -0,0 +1,37 @@ +:root { + --list-tabs-border: var(--app-border); + --list-tabs-active: var(--app-primary); + --list-tabs-muted: var(--app-muted-color); +} + +.app-list-tabs { + display: flex; + flex-wrap: wrap; + margin-bottom: var(--app-spacing); + border-bottom: 1px solid var(--list-tabs-border); +} + +.app-list-tabs button, +.app-list-tabs a { + background: transparent; + border: 0; + padding: 5px 10px 5px 10px; + color: var(--list-tabs-muted); + cursor: pointer; + font-weight: 500; + border-bottom: 2px solid transparent; + text-decoration: none; +} + +.app-list-tabs button:hover, +.app-list-tabs button:focus-visible, +.app-list-tabs a:hover, +.app-list-tabs a:focus-visible { + color: var(--app-color); +} + +.app-list-tabs button.is-active, +.app-list-tabs a.is-active { + color: var(--app-color); + border-color: var(--list-tabs-active); +} diff --git a/web/css/components/app-list-titlebar.css b/web/css/components/app-list-titlebar.css new file mode 100644 index 0000000..0c86fad --- /dev/null +++ b/web/css/components/app-list-titlebar.css @@ -0,0 +1,79 @@ +.app-list-titlebar { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 10px; + margin-bottom: calc(var(--app-spacing) * 1); +} + +.app-list-titlebar h1, +.app-list-titlebar h2, +.app-list-titlebar h3, +.app-list-titlebar h4, +.app-list-titlebar h5, +.app-list-titlebar h6 { + margin: 0; +} + +.app-list-titlebar button, +.app-list-titlebar [role="button"] { + --app-button-padding-horizontal: 10px; + --app-button-padding-vertical: 6px; + font-size: 13px; + margin: 0; +} + +.app-list-titlebar-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.app-list-titlebar h1 i { + background: var(--app-muted-border-color); + width: 35px; + height: 35px; + font-size: 16px; + border-radius: var(--app-border-radius); + display: inline-flex; + align-items: center; + justify-content: center; +} + +.app-list-titlebar h1:has(i) { + display: flex; + align-items: center; + gap: 7px; +} + +.app-list-titlebar details.dropdown { + margin-bottom: 0; +} + +.app-list-titlebar details.dropdown summary:after { + display: none; +} + +.app-list-titlebar details.dropdown li { + padding: 0!important; + margin: 0!important; + border-bottom: 1px solid var(--app-border); +} + +.app-list-titlebar details.dropdown li:last-child { + border-bottom: none; +} + +.app-list-titlebar details.dropdown ul { + padding: 0!important; + margin: 0!important; +} + +.app-list-titlebar details.dropdown li button, .app-list-titlebar details.dropdown li [role=button] { + margin: 0 !important; + border: none; + border-radius: 0; + width: 100%; + text-align: left; +} \ No newline at end of file diff --git a/web/css/components/app-list-toolbar.css b/web/css/components/app-list-toolbar.css new file mode 100644 index 0000000..fb7198b --- /dev/null +++ b/web/css/components/app-list-toolbar.css @@ -0,0 +1,69 @@ +.app-list-toolbar { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 10px; + margin-bottom: calc(var(--app-spacing) * 1); +} + +.app-list-toolbar > * { + margin: 0 !important; + width: auto !important; + --app-form-element-spacing-horizontal: 10px; + --app-form-element-spacing-vertical: 6px; + font-size: 13px !important; + height: auto !important; +} +.app-list-toolbar > * > * { + margin: 0 !important; + width: auto !important; + --app-form-element-spacing-horizontal: 10px; + --app-form-element-spacing-vertical: 6px; + font-size: 13px !important; + height: auto !important; +} + +.app-list-toolbar + input:not( + [type="checkbox"], + .app-list-toolbar [type="radio"], + .app-list-toolbar [type="range"], + .app-list-toolbar [type="file"] + ):is( + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"] + ) { + width: auto; +} + +.app-list-toolbar > label > span { + display: block; + font-size: 11px !important; +} + +.app-list-toolbar + .multi-select + .multi-select-header + .multi-select-header-placeholder { + font-size: 13px; +} + +.app-list-toolbar .multi-select .multi-select-header, .app-list-toolbar .multi-select .multi-select-option, .app-list-toolbar .multi-select .multi-select-all { + min-height: unset; + padding-left: 5px; +} + +.app-list-toolbar .multi-select { + width: 200px !important; +} + +.app-list-toolbar span.multi-select-header-option { + background: transparent !important; + border: 0 !important; + padding: 0 !important; + padding-left: 3px !important; + font-size: 11px !important; +} \ No newline at end of file diff --git a/web/css/components/app-page-copy.css b/web/css/components/app-page-copy.css new file mode 100644 index 0000000..c06755e --- /dev/null +++ b/web/css/components/app-page-copy.css @@ -0,0 +1,10 @@ + + +.app-page-copy .app-field { + min-width: 200px; +} + + +.app-page-copy .app-field [role="group"] { + margin-bottom: 0; +} \ No newline at end of file diff --git a/web/css/components/app-page-editor.css b/web/css/components/app-page-editor.css new file mode 100644 index 0000000..b3c4798 --- /dev/null +++ b/web/css/components/app-page-editor.css @@ -0,0 +1,74 @@ +.app-page-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--app-spacing); + margin-bottom: var(--app-spacing); +} + +.app-page-header > div { + display: flex; + align-items: center; + gap: var(--app-spacing); +} + +.app-page-header > div > * { + margin-bottom: 0; + width: auto; +} + +.app-page-header button, +.app-page-header input { + margin-bottom: 0; + width: auto; +} + +.app-page-header h1 { + margin: 0; +} + + + +label.app-field > span { + display: block; +} + +label.app-field > small { + display: block; +} + + +.page-editor-actions { + margin-top: var(--app-spacing); + display: flex; + gap: var(--app-spacing); + justify-content: flex-end; +} + +.page-editor[data-edit-mode="view"] .page-editor-actions { + display: none; +} + +.page-editor[data-edit-mode="view"] .ce-toolbar, +.page-editor[data-edit-mode="view"] .ce-inline-toolbar, +.page-editor[data-edit-mode="view"] .ce-popover { + display: none !important; +} + +.ce-block__content, .ce-toolbar__content { + max-width: 90%!important; +} + +.ce-editorjsColumns_wrapper .ce-toolbar__content, +.ce-editorjsColumns_wrapper .ce-block__content { + max-width: 100% ! Important; +} + +.ce-popover-item[data-item-name="columns"] svg { + width: 14px; + height: 14px; +} + +.app-details-aside-section button { + margin-bottom: 0; +} \ No newline at end of file diff --git a/web/css/components/app-profile.css b/web/css/components/app-profile.css new file mode 100644 index 0000000..41a466e --- /dev/null +++ b/web/css/components/app-profile.css @@ -0,0 +1,570 @@ +.app-profile-card { + border: 1px solid var(--app-border); + background: var(--app-card-background-color); + overflow: hidden; + border-radius: var(--app-border-radius); + margin-bottom: var(--app-spacing); +} + +.app-profile-header { + display: flex; + align-items: flex-start; + padding: 0 calc(var(--app-spacing) * 1) 0 calc(var(--app-spacing) * 1); + flex-direction: column; + justify-content: center; + margin-top: -42px; + z-index: 1; + position: relative; +} + +.app-profile-meta h2 { + margin: 0; +} + +.app-profile-meta p { + margin: 0.25rem 0 0; + color: var(--app-muted-color); +} + +.app-profile-body { + border: 1px solid var(--app-border); + background: var(--app-card-background-color); + overflow: hidden; + border-radius: var(--app-border-radius); + margin-bottom: var(--app-spacing); +} + +.app-profile-body [data-tab-panel] { + padding-inline: var(--app-spacing); +} + +.app-profile-body [data-tab-panel]:not(:has(p)) { + padding-block-end: var(--app-spacing); +} + +.app-profile-table { + width: 100%; + border: 1px solid var(--app-border); + border-radius: var(--app-border-radius); + border-collapse: separate; + border-spacing: 0; + background: transparent; + overflow: hidden; +} + +.app-profile-table th, +.app-profile-table td { + padding: 0.55rem 0.75rem; + background: transparent; + border-bottom: 1px solid var(--app-border); + text-align: left; + vertical-align: top; +} + +.app-profile-table thead th { + font-weight: 600; +} + +.app-profile-table tr:last-child td { + border-bottom: 0; +} + +@keyframes ani-app-profile-banner { + 0% { + --c-0: hsla(212, 0%, 0%, 1); + --s-start-0: 14.489998991212337%; + --s-end-0: 72%; + --y-0: 93%; + --x-0: 93%; + --c-1: hsla(212, 0%, 0%, 1); + --s-start-1: 0%; + --s-end-1: 45%; + --y-1: 9%; + --x-1: 26%; + --x-2: 15%; + --s-start-2: 2.9253667596993065%; + --s-end-2: 22.388851682060018%; + --c-2: hsla(257, 91%, 27%, 0.35); + --y-2: 79%; + --x-3: 40%; + --s-start-3: 3.985353824694249%; + --s-end-3: 47.580278608924694%; + --y-3: 104%; + --c-3: hsla(212, 100%, 50%, 0.5); + --y-4: 60%; + --x-4: 0%; + --c-4: hsla(224, 72%, 36%, 1); + --s-start-4: 2.391200382592061%; + --s-end-4: 29.307684556768592%; + --s-start-5: 2.9253667596993065%; + --s-end-5: 22.388851682060018%; + --y-5: 37%; + --c-5: hsla(248, 52%, 24%, 1); + --x-5: 92%; + --x-6: 101%; + --y-6: 16%; + --s-start-6: 13.173642363290591%; + --s-end-6: 31.747336520355095%; + --c-6: hsla(212, 100%, 50%, 0.19); + --y-7: 13%; + --s-start-7: 1%; + --s-end-7: 31%; + --x-7: 90%; + --c-7: hsla(227, 98%, 53%, 1); + --y-8: 56%; + --s-start-8: 3.985353824694249%; + --s-end-8: 13.103042116379756%; + --c-8: hsla(166, 71%, 60%, 0.32); + --x-8: 104%; + --c-9: hsla(219, 83%, 23%, 0.18); + --s-start-9: 18.597054544690312%; + --s-end-9: 31%; + --x-9: 97%; + --y-9: 19%; + } + + 100% { + --c-0: hsla(306, 0%, 0%, 1); + --s-start-0: 2.391200382592061%; + --s-end-0: 43.902064173373226%; + --y-0: 9%; + --x-0: 7%; + --c-1: hsla(306, 0%, 0%, 1); + --s-start-1: 9%; + --s-end-1: 54.805582404585024%; + --y-1: 93%; + --x-1: 96%; + --x-2: -2%; + --s-start-2: 3%; + --s-end-2: 26.722813338714598%; + --c-2: hsla(166, 72%, 60%, 1); + --y-2: 103%; + --x-3: 33%; + --s-start-3: 2.391200382592061%; + --s-end-3: 32.0689540200964%; + --y-3: 82%; + --c-3: hsla(180, 100%, 50%, 0.26); + --y-4: 81%; + --x-4: 37%; + --c-4: hsla(212, 88%, 26%, 0.58); + --s-start-4: 4.40642490323111%; + --s-end-4: 37.23528104246256%; + --s-start-5: 3%; + --s-end-5: 32.537089799783296%; + --y-5: 99%; + --c-5: hsla(271, 98%, 53%, 0.31); + --x-5: 54%; + --x-6: 104%; + --y-6: 43%; + --s-start-6: 6%; + --s-end-6: 42.501105312974815%; + --c-6: hsla(262, 100%, 50%, 0.15); + --y-7: -16%; + --s-start-7: 5%; + --s-end-7: 13.10107024898374%; + --x-7: 104%; + --c-7: hsla(298, 36%, 23%, 1); + --y-8: 30%; + --s-start-8: 2.391200382592061%; + --s-end-8: 27.141813016850573%; + --c-8: hsla(180, 100%, 50%, 0.11); + --x-8: 97%; + --c-9: hsla(219, 83%, 23%, 0.59); + --s-start-9: 5%; + --s-end-9: 21.32164536610654%; + --x-9: 78%; + --y-9: 4%; + } +} + +@property --c-0 { + syntax: ""; + inherits: false; + initial-value: hsla(212, 0%, 0%, 1); +} + +@property --s-start-0 { + syntax: ""; + inherits: false; + initial-value: 14.489998991212337%; +} + +@property --s-end-0 { + syntax: ""; + inherits: false; + initial-value: 72%; +} + +@property --y-0 { + syntax: ""; + inherits: false; + initial-value: 93%; +} + +@property --x-0 { + syntax: ""; + inherits: false; + initial-value: 93%; +} + +@property --c-1 { + syntax: ""; + inherits: false; + initial-value: hsla(212, 0%, 0%, 1); +} + +@property --s-start-1 { + syntax: ""; + inherits: false; + initial-value: 0%; +} + +@property --s-end-1 { + syntax: ""; + inherits: false; + initial-value: 45%; +} + +@property --y-1 { + syntax: ""; + inherits: false; + initial-value: 9%; +} + +@property --x-1 { + syntax: ""; + inherits: false; + initial-value: 26%; +} + +@property --x-2 { + syntax: ""; + inherits: false; + initial-value: 15%; +} + +@property --s-start-2 { + syntax: ""; + inherits: false; + initial-value: 2.9253667596993065%; +} + +@property --s-end-2 { + syntax: ""; + inherits: false; + initial-value: 22.388851682060018%; +} + +@property --c-2 { + syntax: ""; + inherits: false; + initial-value: hsla(257, 91%, 27%, 0.35); +} + +@property --y-2 { + syntax: ""; + inherits: false; + initial-value: 79%; +} + +@property --x-3 { + syntax: ""; + inherits: false; + initial-value: 40%; +} + +@property --s-start-3 { + syntax: ""; + inherits: false; + initial-value: 3.985353824694249%; +} + +@property --s-end-3 { + syntax: ""; + inherits: false; + initial-value: 47.580278608924694%; +} + +@property --y-3 { + syntax: ""; + inherits: false; + initial-value: 104%; +} + +@property --c-3 { + syntax: ""; + inherits: false; + initial-value: hsla(212, 100%, 50%, 0.5); +} + +@property --y-4 { + syntax: ""; + inherits: false; + initial-value: 60%; +} + +@property --x-4 { + syntax: ""; + inherits: false; + initial-value: 0%; +} + +@property --c-4 { + syntax: ""; + inherits: false; + initial-value: hsla(224, 72%, 36%, 1); +} + +@property --s-start-4 { + syntax: ""; + inherits: false; + initial-value: 2.391200382592061%; +} + +@property --s-end-4 { + syntax: ""; + inherits: false; + initial-value: 29.307684556768592%; +} + +@property --s-start-5 { + syntax: ""; + inherits: false; + initial-value: 2.9253667596993065%; +} + +@property --s-end-5 { + syntax: ""; + inherits: false; + initial-value: 22.388851682060018%; +} + +@property --y-5 { + syntax: ""; + inherits: false; + initial-value: 37%; +} + +@property --c-5 { + syntax: ""; + inherits: false; + initial-value: hsla(248, 52%, 24%, 1); +} + +@property --x-5 { + syntax: ""; + inherits: false; + initial-value: 92%; +} + +@property --x-6 { + syntax: ""; + inherits: false; + initial-value: 101%; +} + +@property --y-6 { + syntax: ""; + inherits: false; + initial-value: 16%; +} + +@property --s-start-6 { + syntax: ""; + inherits: false; + initial-value: 13.173642363290591%; +} + +@property --s-end-6 { + syntax: ""; + inherits: false; + initial-value: 31.747336520355095%; +} + +@property --c-6 { + syntax: ""; + inherits: false; + initial-value: hsla(212, 100%, 50%, 0.19); +} + +@property --y-7 { + syntax: ""; + inherits: false; + initial-value: 13%; +} + +@property --s-start-7 { + syntax: ""; + inherits: false; + initial-value: 1%; +} + +@property --s-end-7 { + syntax: ""; + inherits: false; + initial-value: 31%; +} + +@property --x-7 { + syntax: ""; + inherits: false; + initial-value: 90%; +} + +@property --c-7 { + syntax: ""; + inherits: false; + initial-value: hsla(227, 98%, 53%, 1); +} + +@property --y-8 { + syntax: ""; + inherits: false; + initial-value: 56%; +} + +@property --s-start-8 { + syntax: ""; + inherits: false; + initial-value: 3.985353824694249%; +} + +@property --s-end-8 { + syntax: ""; + inherits: false; + initial-value: 13.103042116379756%; +} + +@property --c-8 { + syntax: ""; + inherits: false; + initial-value: hsla(166, 71%, 60%, 0.32); +} + +@property --x-8 { + syntax: ""; + inherits: false; + initial-value: 104%; +} + +@property --c-9 { + syntax: ""; + inherits: false; + initial-value: hsla(219, 83%, 23%, 0.18); +} + +@property --s-start-9 { + syntax: ""; + inherits: false; + initial-value: 18.597054544690312%; +} + +@property --s-end-9 { + syntax: ""; + inherits: false; + initial-value: 31%; +} + +@property --x-9 { + syntax: ""; + inherits: false; + initial-value: 97%; +} + +@property --y-9 { + syntax: ""; + inherits: false; + initial-value: 19%; +} + +.app-profile-banner { + --c-0: hsla(212, 0%, 0%, 1); + --y-0: 93%; + --x-0: 93%; + --c-1: hsla(212, 0%, 0%, 1); + --y-1: 9%; + --x-1: 26%; + --x-2: 15%; + --c-2: hsla(257, 91%, 27%, 0.35); + --y-2: 79%; + --x-3: 40%; + --y-3: 104%; + --c-3: hsla(212, 100%, 50%, 0.5); + --y-4: 60%; + --x-4: 0%; + --c-4: hsla(224, 72%, 36%, 1); + --y-5: 37%; + --c-5: hsla(248, 52%, 24%, 1); + --x-5: 92%; + --x-6: 101%; + --y-6: 16%; + --c-6: hsla(212, 100%, 50%, 0.19); + --y-7: 13%; + --x-7: 90%; + --c-7: hsla(227, 98%, 53%, 1); + --y-8: 56%; + --c-8: hsla(166, 71%, 60%, 0.32); + --x-8: 104%; + --c-9: hsla(219, 83%, 23%, 0.18); + --x-9: 97%; + --y-9: 19%; + background-color: hsla(305, 0%, 0%, 1); + background-image: + radial-gradient( + circle at var(--x-0) var(--y-0), + var(--c-0) var(--s-start-0), + transparent var(--s-end-0) + ), + radial-gradient( + circle at var(--x-1) var(--y-1), + var(--c-1) var(--s-start-1), + transparent var(--s-end-1) + ), + radial-gradient( + circle at var(--x-2) var(--y-2), + var(--c-2) var(--s-start-2), + transparent var(--s-end-2) + ), + radial-gradient( + circle at var(--x-3) var(--y-3), + var(--c-3) var(--s-start-3), + transparent var(--s-end-3) + ), + radial-gradient( + circle at var(--x-4) var(--y-4), + var(--c-4) var(--s-start-4), + transparent var(--s-end-4) + ), + radial-gradient( + circle at var(--x-5) var(--y-5), + var(--c-5) var(--s-start-5), + transparent var(--s-end-5) + ), + radial-gradient( + circle at var(--x-6) var(--y-6), + var(--c-6) var(--s-start-6), + transparent var(--s-end-6) + ), + radial-gradient( + circle at var(--x-7) var(--y-7), + var(--c-7) var(--s-start-7), + transparent var(--s-end-7) + ), + radial-gradient( + circle at var(--x-8) var(--y-8), + var(--c-8) var(--s-start-8), + transparent var(--s-end-8) + ), + radial-gradient( + circle at var(--x-9) var(--y-9), + var(--c-9) var(--s-start-9), + transparent var(--s-end-9) + ); + animation: ani-app-profile-banner 10s linear infinite alternate; + background-blend-mode: + normal, normal, normal, normal, normal, normal, normal, normal, normal, + normal; + will-change: transform, opacity; + contain: paint; + height: 160px; +} diff --git a/web/css/components/app-search.css b/web/css/components/app-search.css new file mode 100644 index 0000000..cfb21f5 --- /dev/null +++ b/web/css/components/app-search.css @@ -0,0 +1,137 @@ +.app-search { + padding: 0 var(--app-spacing); +} + +ul.app-search-results { + padding: 0; + margin: 0; +} + +ul.app-search-results > li > a { + justify-content: space-between; +} + +ul.app-search-results .badge { + font-size: 10px; + padding: 1px; + min-width: 20px; + text-align: center; + border-radius: 999px; + background: var(--app-accordion-border-color); + display: inline-flex; + justify-content: center; + align-items: center; + height: 20px; +} + +ul.app-search-preview { + position: relative; + font-size: 11px; + padding: 0 0 0 20px; + margin: 6px 0 1.5rem 10px; + opacity: 0.75; + list-style: none; +} + +/* Hide preview rows (detail results) */ +.search-details-hidden ul.app-search-preview { + display: none; +} +ul.app-search-preview::before { + content: ""; + position: absolute; + left: 9px; + top: 2px; + bottom: 20px; + width: 1px; + background: var(--app-border); + border-radius: 999px; +} + +ul.app-search-preview li { + position: relative; + padding: 0; +} + +ul.app-search-preview li::before { + content: ""; + position: absolute; + left: -11px; + bottom: 11px; + width: 8px; + height: 10px; + background: transparent; + border-left: 1px solid var(--app-border); + border-bottom: 1px solid var(--app-border); + border-bottom-left-radius: 6px; +} + +ul.app-search-preview a { + padding-inline: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; + border: none; +} + +ul.app-search-preview a:hover { + border: none; +} + +input#side-search { + --app-form-element-spacing-horizontal: 10px; + --app-form-element-spacing-vertical: 6px; + height: auto; + font-size: 13px; +} + +ul.app-search-preview:has(li) { + margin-bottom: 1rem!important; +} + +.search-result-row { + display: flex; + gap: 12px; + align-items: flex-start; +} + +.search-reuslt-title-container { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.search-result-avatar, +.search-result-avatar-placeholder { + width: 40px; + height: 40px; + border-radius: 10px; + border: 1px solid var(--app-border); + background: var(--app-card-background-color); + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--app-muted-color); + flex-shrink: 0; + object-fit: cover; +} + + + +.search-result-title { + font-weight: 600; + text-decoration: none; +} + +.search-result-desc { + margin: 0; + color: var(--app-muted-color); + font-size: small; +} + +.search-result-type { + color: var(--app-muted-color); + font-weight: 600; +} diff --git a/web/css/components/app-tabs.css b/web/css/components/app-tabs.css new file mode 100644 index 0000000..9eddcb2 --- /dev/null +++ b/web/css/components/app-tabs.css @@ -0,0 +1,53 @@ +:root { + --tabs-border: var(--app-border); + --tabs-active: var(--app-primary); + --tabs-muted: var(--app-muted-color); +} + +.app-tabs { + display: flex; + flex-direction: column; + gap: var(--app-spacing); +} + +.app-tabs-nav { + display: flex; + border-bottom: 1px solid var(--tabs-border); + overflow-x: auto; + -webkit-overflow-scrolling: touch; + padding-block-start: 2px; + padding-left: 2px; +} + +.app-tabs-nav button, +.app-tabs-nav a { + background: transparent; + border: 0; + padding: 5px 10px 5px 10px; + color: var(--tabs-muted) !important; + cursor: pointer; + font-weight: 500; + border-bottom: 2px solid transparent; + text-decoration: none; + border-radius: 0; + margin-bottom: 0; +} + +.app-tabs-nav button:hover, +.app-tabs-nav button:focus-visible, +.app-tabs-nav a:hover, +.app-tabs-nav a:focus-visible { + --app-color: inherit; + color: var(--app-color) !important; +} + +.app-tabs-nav button.is-active, +.app-tabs-nav a.is-active { + --app-color: inherit; + color: var(--app-color) !important; + border-color: var(--tabs-active); +} + +[data-tab-panel][hidden] { + display: none; +} diff --git a/web/css/components/app-tile.css b/web/css/components/app-tile.css new file mode 100644 index 0000000..f80c3f6 --- /dev/null +++ b/web/css/components/app-tile.css @@ -0,0 +1,111 @@ +:root { + --app-tile-bg: var(--app-card-background-color); + --app-tile-border: var(--app-muted-border-color); + --app-tile-shadow: var(--app-box-shadow); + --app-tile-icon-bg: rgba(0, 0, 0, 0.04); + --app-tile-icon-color: var(--app-contrast); + --app-tile-count-color: var(--app-contrast); + --app-tile-label-color: var(--app-color); + --app-tile-link-color: var(--app-muted-color); +} + +[data-theme="dark"] { + --app-tile-bg: rgba(20, 24, 32, 0.6); + --app-tile-border: rgba(255, 255, 255, 0.08); + --app-tile-shadow: 0 10px 30px rgba(0, 0, 0, 0.25); + --app-tile-icon-bg: rgba(255, 255, 255, 0.08); + --app-tile-icon-color: #e6ecff; + --app-tile-count-color: #f1f5ff; + --app-tile-label-color: #d1d7e3; + --app-tile-link-color: #98a2b3; +} + + +.app-tiles { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: calc(var(--app-spacing) * 1); + margin-bottom: calc(var(--app-spacing) * 2); +} + + +.app-stats-table { + overflow: auto; + border: 1px solid var(--app-border); + border-radius: var(--app-border-radius); + margin-bottom: calc(var(--app-spacing) * 1); +} + +.app-stats-table th { + border-top: 0; +} + +.app-stats-table table { + margin: 0; +} + +.app-stats-table table tr:last-child td { + border-bottom: 0; +} + +.app-tile { + --tile-icon-bg: var(--app-tile-icon-bg); + --tile-icon-color: var(--app-tile-icon-color); + --tile-count-color: var(--app-tile-count-color); + --tile-label-color: var(--app-tile-label-color); + --tile-link-color: var(--app-tile-link-color); + position: relative; + display: block; + padding: calc(var(--app-spacing) * 1); + border: 1px solid var(--app-tile-border); + border-radius: calc(var(--app-border-radius) * 1.1); + background: var(--app-tile-bg); + color: var(--app-tile-label-color); + text-decoration: none; + transition: transform var(--app-transition), box-shadow var(--app-transition), border-color var(--app-transition); +} + +.app-tile:hover { + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.08); +} + +.app-tile-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 10px; + background: var(--tile-icon-bg); + color: var(--tile-icon-color); + font-size: 1.1rem; +} + +.app-tile-count { + position: absolute; + top: calc(var(--app-spacing) * 1); + right: calc(var(--app-spacing) * 1); + font-weight: 700; + color: var(--tile-count-color); +} + +.app-tile-label { + display: block; + margin-top: calc(var(--app-spacing) * 1); + font-weight: 600; + color: var(--tile-label-color); +} + +.app-tile-link { + position: absolute; + right: calc(var(--app-spacing) * 1); + bottom: calc(var(--app-spacing) * 1); + color: var(--tile-link-color); + font-size: 0.9rem; +} + +@media (max-width: 640px) { + .app-tiles { + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + } +} diff --git a/web/css/components/app-tooltips.css b/web/css/components/app-tooltips.css new file mode 100644 index 0000000..cd0d1ac --- /dev/null +++ b/web/css/components/app-tooltips.css @@ -0,0 +1,108 @@ +:root { + --tooltip-bg: var(--app-card-background-color); + --tooltip-color: var(--app-color); + --tooltip-border: var(--app-border); + --tooltip-shadow: 0 6px 18px rgba(0, 0, 0, 0.12); + --tooltip-radius: 8px; + --tooltip-offset: 8px; +} + +[data-tooltip] { + --app-card-background-color: inherit; + position: relative; +} + +[data-tooltip]::after, +[data-tooltip]::before { + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease, transform 0.15s ease; + position: absolute; + z-index: 40; + text-decoration: none; +} + +[data-tooltip]::after { + content: attr(data-tooltip); + background: var(--tooltip-bg); + color: var(--tooltip-color); + border: 1px solid var(--tooltip-border); + border-radius: var(--tooltip-radius); + box-shadow: var(--tooltip-shadow); + font-size: 0.75rem; + padding: 0.35rem 0.6rem; + white-space: nowrap; +} + +[data-tooltip]::before { + content: ''; + width: 8px; + height: 8px; + background: var(--tooltip-bg); + border-left: 1px solid var(--tooltip-border); + border-top: 1px solid var(--tooltip-border); + transform: rotate(45deg); +} + +[data-tooltip]:hover::after, +[data-tooltip]:hover::before, +[data-tooltip]:focus-visible::after, +[data-tooltip]:focus-visible::before { + opacity: 1; +} + +[data-tooltip]::after, +[data-tooltip]::before { + top: 50%; + left: 100%; +} + +[data-tooltip]::after { + transform: translate(var(--tooltip-offset), -50%); +} + +[data-tooltip]::before { + transform: translate(calc(var(--tooltip-offset) - 4px), -50%) rotate(45deg); +} + +[data-tooltip][data-tooltip-pos="top"]::after, +[data-tooltip][data-tooltip-pos="center"]::after { + bottom: 100%; + left: 50%; + top: auto; + transform: translate(-50%, calc(-1 * var(--tooltip-offset))); +} + +[data-tooltip][data-tooltip-pos="top"]::before, +[data-tooltip][data-tooltip-pos="center"]::before { + bottom: 100%; + left: 50%; + top: auto; + transform: translate(-50%, calc(-1 * (var(--tooltip-offset) - 2px))) rotate(45deg); +} + +[data-tooltip][data-tooltip-pos="bottom"]::after { + top: 100%; + left: 50%; + transform: translate(-50%, var(--tooltip-offset)); +} + +[data-tooltip][data-tooltip-pos="bottom"]::before { + top: 100%; + left: 50%; + transform: translate(-50%, calc(var(--tooltip-offset) - 2px)) rotate(45deg); +} + +[data-tooltip][data-tooltip-pos="left"]::after { + top: 50%; + right: 100%; + left: auto; + transform: translate(calc(-1 * var(--tooltip-offset)), -50%); +} + +[data-tooltip][data-tooltip-pos="left"]::before { + top: 50%; + right: 100%; + left: auto; + transform: translate(calc(-1 * (var(--tooltip-offset) - 2px)), -50%) rotate(45deg); +} diff --git a/web/css/components/vendor-editorjs.css b/web/css/components/vendor-editorjs.css new file mode 100644 index 0000000..0f8a3b6 --- /dev/null +++ b/web/css/components/vendor-editorjs.css @@ -0,0 +1,74 @@ +.codex-editor { + font-family: var(--app-font-family); + color: var(--app-color); +} + +.ce-block__content { + max-width: 90%; +} + +.ce-toolbar__content, +.ce-inline-toolbar { + border-radius: var(--app-border-radius); +} + +.ce-inline-toolbar { + box-shadow: var(--app-box-shadow); +} + +.ce-toolbar__content { + max-width: 90%; +} + +input.cdx-search-field__input { + margin: 0; + padding: 5px; + height: auto; + border-radius: 0; +} + +.cdx-search-field__icon { + width: 36px; + height: 36px; + margin-right: 0; +} + +.ce-popover__items button { + background: transparent; + color: inherit; + padding: inherit; + border: 0; + align-items: center; + justify-content: center; + display: flex; + margin: 0; +} + +input.ce-inline-tool-input.ce-inline-tool-input--showed { + margin: 0; +} + +table.tc-table { + margin: 0; +} + +.tc-cell { + border-left: 1px solid var(--color-border); +} + +.ce-toolbar__actions { + right: calc(100% + 10px)!important; + background-color: white!important; +} + +.ce-block--selected .ce-block__content { + background: var(--app-table-row-stripped-background-color); +} + +.ce-inline-tool svg { + color: black; +} + +.ce-popover--inline .ce-popover-item__icon svg { + color: black; +} \ No newline at end of file diff --git a/web/css/components/vendor-gridjs.css b/web/css/components/vendor-gridjs.css new file mode 100644 index 0000000..d6967b2 --- /dev/null +++ b/web/css/components/vendor-gridjs.css @@ -0,0 +1,207 @@ +.gridjs-container { + color: var(--app-color); + width: 100%; + padding: 0; +} + +.gridjs-wrapper { + border: 0; + border-radius: 0; + box-shadow: none; + background-color: var(--app-background-color); + border-top: var(--app-border-width) solid var(--app-table-border-color); + padding-block-end: 1rem; + min-height: 515px; +} + +.gridjs-footer { + background-color: var(--app-background-color); + border: 0; + border-top: 0; + box-shadow: none; + color: var(--app-muted-color); + padding: 1rem 0 0; +} + +.gridjs-table { + color: var(--app-color); +} + +th.gridjs-th { + padding: calc(var(--app-spacing) / 2) var(--app-spacing); + border: none; + border-bottom: var(--app-border-width) solid var(--app-table-border-color); + background-color: var(--app-background-color); + color: var(--app-color); + font-weight: var(--app-font-weight); + text-align: left; + text-align: start; +} + +button.gridjs-sort { + height: 15px; +} + +th.gridjs-th-sort:hover, +th.gridjs-th-sort:focus { + background-color: var(--app-table-row-stripped-background-color); +} + +td.gridjs-td { + background-color: var(--app-background-color); + border: none; + border-color: var(--app-table-border-color); + padding: calc(var(--app-spacing) / 2) var(--app-spacing); + border-bottom: var(--app-border-width) solid var(--app-table-border-color); + background-color: var(--app-background-color); + color: var(--app-color); + font-weight: var(--app-font-weight); + text-align: left; + text-align: start; +} + +.gridjs-search { + float: none; +} + +.gridjs-search-input { + width: min(320px, 100%); +} + +input.gridjs-input { + background-color: var(--app-background-color); + border: var(--app-border-width) solid var(--app-form-element-border-color); + border-radius: var(--app-border-radius); + color: var(--app-color); + font-size: 0.95rem; + padding: 0.5rem 0.75rem; +} + +input.gridjs-input:focus { + border-color: var(--app-primary); + box-shadow: 0 0 0 var(--app-outline-width) var(--app-primary-focus); +} + +.gridjs-pagination { + color: var(--app-muted-color); +} + +.gridjs-pagination .gridjs-pages button { + background-color: var(--app-background-color); + border: var(--app-border-width) solid var(--app-table-border-color); + color: var(--app-color); + padding: 0.4rem 0.7rem; +} + +.gridjs-pagination .gridjs-pages button:hover { + background-color: var(--app-primary-hover-background); + border-color: var(--app-primary-hover-border); + color: var(--app-primary-inverse); +} + +.gridjs-pagination .gridjs-pages button:focus { + box-shadow: 0 0 0 var(--app-outline-width) var(--app-primary-focus); +} + +.gridjs-pagination .gridjs-pages button.gridjs-currentPage { + background-color: var(--app-primary) !important; + font-weight: 400; + padding: 2px 10px; + color: var(--app-secondary-inverse) !important; +} + +.gridjs-pagination .gridjs-pages button:disabled, .gridjs-pagination .gridjs-pages button[disabled] { + background-color: var(--app-background-color); + color: var(--app-muted-color); + cursor: default; + padding: 2px 10px; + border: 0; +} + +.gridjs-pagination .gridjs-pages { + display: inline-flex; + gap: 5px; +} + +.gridjs-loading-bar { + background-color: var(--app-background-color); + opacity: 0.7; +} + +.grid-avatar { + width: 36px; + height: 36px; + border-radius: 999px; + border: 1px solid var(--app-muted-border-color); + background: var(--app-card-background-color); + object-fit: contain; + display: inline-flex; +} + +.grid-avatar-placeholder { + align-items: center; + justify-content: center; + font-weight: 600; + color: var(--app-muted-color); + font-size: 11px; + background: var(--app-background-color); +} + +.gridjs-loading-bar:after { + background-image: linear-gradient( + 90deg, + transparent, + var(--app-primary-focus), + transparent + ); +} + + /* .gridjs-container:not(.gridjs-has-loaded) .gridjs-message.gridjs-notfound { + display: none; + } */ + +.gridjs-container.gridjs-is-updating .gridjs-message.gridjs-notfound { + display: none; +} + +.gridjs-pages button { + background: var(--app-form-element-background-color) !important; + color: var(--app-contrast) !important; +} + +.gridjs-summary { + font-size: small; +} + +tr.gridjs-tr { + cursor: pointer; +} + +tr.gridjs-tr:hover td { + background: var(--app-table-row-stripped-background-color); +} + +html[data-theme=dark] button.gridjs-sort, +html[data-theme=dark] button.gridjs-sort-neutral { + filter: invert(1); +} + +.gridjs-tbody, td.gridjs-td { + background-color: var(--app-background-color) +} + +.gridjs-table input[data-grid-select-all] { + width: 1em; + height: 1em; + margin: 0; + border-radius: 4px; + border-width: .5px; +} + +.gridjs-th-content:has(input[data-grid-select-all]) { + text-align: center; +} + +.gridjs-pagination .gridjs-pages button:last-child { + border-right: none; +} \ No newline at end of file diff --git a/web/css/components/vendor-multi-select.css b/web/css/components/vendor-multi-select.css new file mode 100644 index 0000000..fc6af21 --- /dev/null +++ b/web/css/components/vendor-multi-select.css @@ -0,0 +1,130 @@ + +:root { + --spacing-smaller: 3px; + --spacing-small: 5px; + --spacing-medium: 7px; + --spacing-large: 12px; + --font-size: 12px; + --font-size-large: 14px; + --font-size-larger: 16px; + --line-height: 16px; + --line-height-larger: 20px; + --primary-color: var(--app-primary); + --text-color-dark: #212529; + --text-color: #585858; + --text-color-light: #65727e; + --border-color: #bebebe; + --border-color-light: #f1f3f5; + --input-placeholder: #65727e; + --input-background: #e9e9ed; + --input-border: #dee2e6; + --input-border-active: #c1c9d0; + --input-border-invalid: #e44e4e; + --input-outline-invalid: rgba(219, 138, 138, 0.5); + --input-color: #e9e9ed; + --input-disabled: #f7f7f7; + --input-min-height: 45px; + --options-height: 40dvh; + --option-background: #f3f4f7; + --border-radius: 5px; + --icon-size: 12px; + --icon-space: 30px; + --checkbox-size: 16px; + --checkbox-radius: 4px; + --checkbox-border: #ced4da; + --checkbox-background: #fff; + --checkbox-active: #fff; + --checkbox-thickness: 2px; +} +/* Overrides for MultiSelect */ +.multi-select { + --spacing-smaller: 3px; + --spacing-small: 5px; + --spacing-medium: 7px; + --spacing-large: 12px; + --font-size: 12px; + --font-size-large: 14px; + --font-size-larger: 16px; + --line-height: 16px; + --line-height-larger: 20px; + --primary-color: var(--app-primary); + --text-color-dark: var(--app-contrast); + --text-color: var(--app-muted-color); + --text-color-light: var(--app-muted-color); + --border-color: var(--app-form-element-border-color); + --border-color-light: var(--app-muted-border-color); + --input-placeholder: var(--app-form-element-placeholder-color); + --input-background: var(--app-form-element-background-color); + --input-border: var(--app-form-element-border-color); + --input-border-active: var(--app-primary-focus); + --input-border-invalid: var(--app-form-element-invalid-border-color); + --input-outline-invalid: var(--app-form-element-invalid-active-shadow); + --input-color: var(--app-form-element-color); + --input-disabled: var(--app-form-element-disabled-background-color); + --input-min-height: calc( + 1rem * var(--app-line-height) + var(--app-form-element-spacing-vertical) * + 2 + var(--app-border-width) * 2 + ); + --options-height: 40dvh; + --option-background: var(--badge-neutral-bg); + --border-radius: var(--app-border-radius); + --icon-size: 12px; + --icon-space: 30px; + --checkbox-size: 16px; + --checkbox-radius: 4px; + --checkbox-border: var(--app-form-element-border-color); + --checkbox-background: var(--app-form-element-background-color); + --checkbox-active: var(--app-form-element-background-color); + --checkbox-thickness: 2px; +} + +.multi-select + .multi-select-options + .multi-select-option + .multi-select-option-radio, +.multi-select + .multi-select-options + .multi-select-all + .multi-select-option-radio { + padding: 2px; +} +.multi-select + .multi-select-options + .multi-select-option + .multi-select-option-radio, +.multi-select + .multi-select-options + .multi-select-all + .multi-select-option-radio { + padding: 2px; +} + +.multi-select { + margin-bottom: var(--app-spacing); +} + +.multi-select.disabled { + opacity: 0.65; + cursor: not-allowed; +} + +.multi-select.disabled .multi-select-header, +.multi-select.disabled .multi-select-options { + pointer-events: none; +} + +.multi-select .multi-select-header { + background: var(--app-form-element-background-color); +} + +.multi-select .multi-select-options { + background: var(--app-form-element-background-color); +} + +.multi-select .multi-select-header .multi-select-header-option { + border: 1px solid var(--app-border); +} + +.multi-select .multi-select-header { + padding-left: 7px; +} diff --git a/web/css/layout/app-aside-icon-bar.css b/web/css/layout/app-aside-icon-bar.css new file mode 100644 index 0000000..91ebaf2 --- /dev/null +++ b/web/css/layout/app-aside-icon-bar.css @@ -0,0 +1,60 @@ +aside.aside-icon-bar { + display: none; +} + +aside.aside-icon-bar ul { + margin: 0; + padding: 0; + display: grid; + gap: 0; +} + +aside.aside-icon-bar li { + margin: 0; + padding: 0; +} +aside.aside-icon-bar li a, +aside.aside-icon-bar li button { + margin: 0; + padding: 0.5rem; + font-size: 20px; + text-align: center; + color: var(--app-muted-color); + border-radius: 0; + background: transparent; + border: 0; + border-left: 3px solid transparent; + display: block; + width: 100%; + cursor: pointer; +} +aside.aside-icon-bar li a:hover, aside.aside-icon-bar li a.active, aside.aside-icon-bar li button:hover, aside.aside-icon-bar li button.active { + border-left: 3px solid var(--app-primary); + color: var(--app-contrast); + background: var(--app-accordion-border-color); +} + +aside.aside-icon-bar nav { + display: flex; + flex-direction: column; + justify-content: space-between; + height: calc(100vh - var(--app-spacing)); +} + +/* aside.aside-icon-bar ul.aside-icon-group li:first-child { + margin-bottom: 1rem; +} */ + +@media (min-width: 768px) { + aside.aside-icon-bar { + border-right: 1px solid var(--app-border); + display: block; + position: sticky; + top: 0; + z-index: 11; + padding-top: var(--app-spacing); + } + .is-sidebar-collapsed aside.aside-icon-bar { + border-right: 0; + } +} diff --git a/web/css/layout/app-shell.css b/web/css/layout/app-shell.css new file mode 100644 index 0000000..9416fe9 --- /dev/null +++ b/web/css/layout/app-shell.css @@ -0,0 +1,2978 @@ +@charset "UTF-8"; + +a { + --app-text-decoration: underline; +} + +a.contrast, +a.secondary { + --app-text-decoration: underline; +} + +small { + --app-font-size: 0.875em; +} + +div#debugger-bar { + display: none; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + --app-font-weight: 700; +} + +h1 { + --app-font-size: 1.8rem; + --app-line-height: 1.125; + --app-typography-spacing-top: 3rem; +} + +h2 { + --app-font-size: 1.6rem; + --app-line-height: 1.15; + --app-typography-spacing-top: 2.625rem; +} + +h3 { + --app-font-size: 1.3rem; + --app-line-height: 1.175; + --app-typography-spacing-top: 2.25rem; +} + +h4 { + --app-font-size: 1.2rem; + --app-line-height: 1.2; + --app-typography-spacing-top: 1.874rem; +} + +h5 { + --app-font-size: 1.1rem; + --app-line-height: 1.225; + --app-typography-spacing-top: 1.6875rem; +} + +h6 { + --app-font-size: 1rem; + --app-line-height: 1.25; + --app-typography-spacing-top: 1.5rem; +} + +tfoot td, +tfoot th, +thead td, +thead th { + --app-font-weight: 600; + --app-border-width: 2px; +} + +code, +kbd, +pre, +samp { + --app-font-family: var(--app-font-family-monospace); +} + +kbd { + --app-font-weight: bolder; +} + +:where(select, textarea), +input:not( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] +) { + --app-outline-width: 0.0625rem; +} + +[type="checkbox"], +[type="radio"] { + --app-border-width: 0.125rem; +} + +[type="checkbox"][role="switch"] { + --app-border-width: 0.1875rem; +} + +details.dropdown summary:not([role="button"]) { + --app-outline-width: 0.0625rem; +} + +nav details.dropdown summary:focus-visible { + --app-outline-width: 0.125rem; +} + +[role="search"] { + --app-border-radius: 5rem; +} + +[role="group"]:has( + button.secondary:focus, + [type="submit"].secondary:focus, + [type="button"].secondary:focus, + [role="button"].secondary:focus +), +[role="search"]:has( + button.secondary:focus, + [type="submit"].secondary:focus, + [type="button"].secondary:focus, + [role="button"].secondary:focus +) { + --app-group-box-shadow-focus-with-button: 0 0 0 var(--app-outline-width) + var(--app-secondary-focus); +} + +[role="group"]:has( + button.contrast:focus, + [type="submit"].contrast:focus, + [type="button"].contrast:focus, + [role="button"].contrast:focus +), +[role="search"]:has( + button.contrast:focus, + [type="submit"].contrast:focus, + [type="button"].contrast:focus, + [role="button"].contrast:focus +) { + --app-group-box-shadow-focus-with-button: 0 0 0 var(--app-outline-width) + var(--app-contrast-focus); +} + +[role="group"] [role="button"], +[role="group"] [type="button"], +[role="group"] [type="submit"], +[role="group"] button, +[role="search"] [role="button"], +[role="search"] [type="button"], +[role="search"] [type="submit"], +[role="search"] button { + --app-form-element-spacing-horizontal: 2rem; +} + +[role="group"] form:last-child button { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +table td [role="group"] { + margin: 0; +} + +th { + text-transform: uppercase; + font-size: 11px; + letter-spacing: 0.4px; + border-top: 1px solid var(--app-border); +} + +table td .grid-actions button, +table td .grid-actions [role="button"] { + padding: 4px 10px; + color: var(--app-muted-color); + border: 1px solid var(--app-border); + font-size: small; + background: var(--app-form-element-active-background-color); + box-shadow: var(--app-group-box-shadow); +} + +table td .grid-actions button:has(i), +table td .grid-actions [role="button"]:has(i) { + max-width: 42px; +} + +table td .grid-actions button:hover, +table td .grid-actions [role="button"]:hover { + color: var(--app-contrast); +} + +details summary[role="button"]:not(.outline)::after { + filter: brightness(0) invert(1); +} + +[aria-busy="true"]:not(input, select, textarea):is( + button, + [type="submit"], + [type="button"], + [type="reset"], + [role="button"] + ):not(.outline)::before { + filter: brightness(0) invert(1); +} + +@media only screen and (prefers-color-scheme: dark) { + :host(:not([data-theme])) + details + summary[role="button"].contrast:not(.outline)::after, + :root:not([data-theme]) + details + summary[role="button"].contrast:not(.outline)::after { + filter: brightness(0); + } + + :host(:not([data-theme])) + [aria-busy="true"]:not(input, select, textarea).contrast:is( + button, + [type="submit"], + [type="button"], + [type="reset"], + [role="button"] + ):not(.outline)::before, + :root:not([data-theme]) + [aria-busy="true"]:not(input, select, textarea).contrast:is( + button, + [type="submit"], + [type="button"], + [type="reset"], + [role="button"] + ):not(.outline)::before { + filter: brightness(0); + } +} + +[data-theme="dark"] + details + summary[role="button"].contrast:not(.outline)::after { + filter: brightness(0); +} + +[data-theme="dark"] + [aria-busy="true"]:not(input, select, textarea).contrast:is( + button, + [type="submit"], + [type="button"], + [type="reset"], + [role="button"] + ):not(.outline)::before { + filter: brightness(0); +} + +[type="checkbox"], +[type="radio"], +[type="range"], +progress { + accent-color: var(--app-primary); +} + +*, +::after, +::before { + box-sizing: border-box; + background-repeat: no-repeat; +} + +::after, +::before { + text-decoration: inherit; + vertical-align: inherit; +} + +:where(:host), +:where(:root) { + -webkit-tap-highlight-color: transparent; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + text-size-adjust: 100%; + background-color: var(--app-background-color); + color: var(--app-color); + font-weight: var(--app-font-weight); + font-size: var(--app-font-size); + line-height: var(--app-line-height); + font-family: var(--app-font-family); + text-underline-offset: var(--app-text-underline-offset); + text-rendering: optimizeLegibility; + overflow-wrap: break-word; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; +} + +body { + width: 100%; + margin: 0; +} + +main { + display: block; +} + +body > footer, +body > main { + padding-block: var(--app-block-spacing-vertical); +} + +body > footer { + padding-inline: calc(var(--app-spacing) * 2); +} + +footer.site-footer { + padding-inline: calc(var(--app-spacing) * 2); + border-top: 1px solid var(--app-border); +} + +footer.site-footer a { + color: inherit; +} + +body > footer a { + color: inherit; +} +header[data-align="center"], +hgroup[data-align="center"] > h1, +hgroup[data-align="center"] > h2, +hgroup[data-align="center"] > h3, +hgroup[data-align="center"] > p, +hgroup[data-align="center"] > *, +hgroup[data-align="center"] { + text-align: center; + justify-content: center; +} + +section { + margin-bottom: var(--app-block-spacing-vertical); +} + +.container-small { + max-width: 420px; + margin-right: auto; + margin-left: auto; +} + +.container, +.container-fluid { + width: 100%; + margin-right: auto; + margin-left: auto; + padding-right: var(--app-spacing); + padding-left: var(--app-spacing); +} + +.app-main-content { + padding-inline: calc(var(--app-spacing) * 1); +} + +@media (min-width: 576px) { + .container { + max-width: 510px; + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 768px) { + .container { + max-width: 700px; + } + .app-container:has(.aside-icon-bar):has(.app-sidebar) { + display: grid; + min-height: 100dvh; + align-items: flex-start; + grid-template-columns: minmax(0, 60px) minmax(0, 220px) minmax(0, 1fr); + } + .app-container.is-sidebar-collapsed:has(.aside-icon-bar):has(.app-sidebar) { + display: grid; + min-height: 100dvh; + align-items: flex-start; + grid-template-columns: minmax(0, 60px) minmax(0, 1fr); + } + + .app-container.is-sidebar-collapsed { + grid-template-columns: minmax(0, 60px) minmax(0, 1fr); + } + + .sidebar-collapsed .app-container { + grid-template-columns: minmax(0, 60px) minmax(0, 1fr); + } + + .app-main { + border-left: 1px solid var(--app-border); + min-height: 100dvh; + background: var(--app-background-color); + } + + .app-main-content { + padding-inline: calc(var(--app-spacing) * 2); + margin-bottom: calc(var(--app-spacing) * 2); + } + .app-main-content:has(.app-details-container) { + margin-bottom: 0; + padding: 0; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 950px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1200px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1450px; + } +} + +.grid { + grid-column-gap: var(--app-grid-column-gap); + grid-row-gap: var(--app-grid-row-gap); + display: grid; + --app-grid-template-columns: 1fr; + grid-template-columns: var(--app-grid-template-columns); +} + +.flex { + display: flex; + flex-wrap: wrap; + gap: var(--app-flex-gap); +} + +.flex h1, +.flex h2, +.flex h3, +.flex h4, +.flex h5, +.flex h6 { + margin-bottom: 0; +} + +.align-center { + align-items: center; +} + +.align-flex-start { + align-items: flex-start; +} + +.justify-space-between { + justify-content: space-between; +} + +@media (min-width: 768px) { + .grid { + --app-grid-template-columns: repeat(auto-fit, minmax(0%, 1fr)); + } + + .grid.grid-2, + .grid.grid-1-1 { + --app-grid-template-columns: repeat(2, minmax(0%, 1fr)); + } + + .grid.grid-3 { + --app-grid-template-columns: repeat(3, minmax(0%, 1fr)); + } + + .grid.grid-4 { + --app-grid-template-columns: repeat(4, minmax(0%, 1fr)); + } + + .grid.grid-5 { + --app-grid-template-columns: repeat(5, minmax(0%, 1fr)); + } + + .grid.grid-6 { + --app-grid-template-columns: repeat(6, minmax(0%, 1fr)); + } + + .grid.grid-1-2 { + --app-grid-template-columns: minmax(0%, 1fr) minmax(0%, 2fr); + } + + .grid.grid-2-1 { + --app-grid-template-columns: minmax(0%, 2fr) minmax(0%, 1fr); + } + + .grid.grid-1-3 { + --app-grid-template-columns: minmax(0%, 1fr) minmax(0%, 3fr); + } + + .grid.grid-3-1 { + --app-grid-template-columns: minmax(0%, 3fr) minmax(0%, 1fr); + } +} + +.grid > * { + min-width: 0; +} + +.overflow-auto { + overflow: auto; +} + +b, +strong { + font-weight: bolder; +} + +sub, +sup { + position: relative; + font-size: 0.75em; + line-height: 0; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +address, +blockquote, +dl, +ol, +p, +pre, +table, +ul { + margin-top: 0; + margin-bottom: var(--app-typography-spacing-vertical); + color: var(--app-color); + font-style: normal; + font-weight: var(--app-font-weight); +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-top: 0; + margin-bottom: var(--app-typography-spacing-vertical); + color: var(--app-color); + font-weight: var(--app-font-weight); + font-size: var(--app-font-size); + line-height: var(--app-line-height); + font-family: var(--app-font-family); +} + +h1 { + --app-color: var(--app-h1-color); +} + +h2 { + --app-color: var(--app-h2-color); +} + +h3 { + --app-color: var(--app-h3-color); +} + +h4 { + --app-color: var(--app-h4-color); +} + +h5 { + --app-color: var(--app-h5-color); +} + +h6 { + --app-color: var(--app-h6-color); +} + +:where(article, address, blockquote, dl, figure, form, ol, p, pre, table, ul) + ~ :is(h1, h2, h3, h4, h5, h6) { + margin-top: var(--app-typography-spacing-top); +} + +p { + margin-bottom: var(--app-typography-spacing-vertical); +} + +hgroup { + margin-bottom: var(--app-typography-spacing-vertical); +} + +.flex hgroup { + margin-bottom: 0; +} + +hgroup > * { + margin-top: 0; + margin-bottom: 0; +} + +hgroup > :not(:first-child):last-child { + --app-color: var(--app-muted-color); + --app-font-weight: unset; + font-size: 1rem; +} + +:where(ol, ul) li { + margin-bottom: calc(var(--app-typography-spacing-vertical) * 0.25); +} + +:where(dl, ol, ul) :where(dl, ol, ul) { + margin: 0; + margin-top: calc(var(--app-typography-spacing-vertical) * 0.25); +} + +ul li { + list-style: square; +} + +mark { + padding: 0.125rem 0.25rem; + background-color: var(--app-mark-background-color); + color: var(--app-mark-color); + vertical-align: baseline; +} + + +abbr[title] { + border-bottom: 1px dotted; + text-decoration: none; + cursor: help; +} + +ins { + color: var(--app-ins-color); + text-decoration: none; +} + +del { + color: var(--app-del-color); +} + +::-moz-selection { + background-color: var(--app-text-selection-color); +} + +::selection { + background-color: var(--app-text-selection-color); +} + +:where(a:not([role="button"])), +[role="link"] { + --app-color: var(--app-primary); + --app-background-color: transparent; + --app-underline: var(--app-primary-underline); + outline: 0; + background-color: var(--app-background-color); + color: var(--app-color); + -webkit-text-decoration: var(--app-text-decoration); + text-decoration: var(--app-text-decoration); + text-decoration-color: var(--app-underline); + text-underline-offset: 0.125em; + transition: + background-color var(--app-transition), + color var(--app-transition), + box-shadow var(--app-transition), + -webkit-text-decoration var(--app-transition); + transition: + background-color var(--app-transition), + color var(--app-transition), + text-decoration var(--app-transition), + box-shadow var(--app-transition); + transition: + background-color var(--app-transition), + color var(--app-transition), + text-decoration var(--app-transition), + box-shadow var(--app-transition), + -webkit-text-decoration var(--app-transition); +} + +:where(a:not([role="button"])):is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus +), +[role="link"]:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus +) { + --app-color: var(--app-primary-hover); + --app-underline: var(--app-primary-hover-underline); + --app-text-decoration: underline; +} + +:where(a:not([role="button"])):focus-visible, +[role="link"]:focus-visible { + box-shadow: 0 0 0 var(--app-outline-width) var(--app-primary-focus); +} + +:where(a:not([role="button"])).secondary, +[role="link"].secondary { + --app-color: var(--app-secondary); + --app-underline: var(--app-secondary-underline); +} + +:where(a:not([role="button"])).secondary:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +[role="link"].secondary:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --app-color: var(--app-secondary-hover); + --app-underline: var(--app-secondary-hover-underline); +} + +:where(a:not([role="button"])).contrast, +[role="link"].contrast { + --app-color: var(--app-contrast); + --app-underline: var(--app-contrast-underline); +} + +:where(a:not([role="button"])).contrast:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +[role="link"].contrast:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --app-color: var(--app-contrast-hover); + --app-underline: var(--app-contrast-hover-underline); +} + +a[role="button"] { + display: inline-block; +} + +button { + margin: 0; + overflow: visible; + font-family: inherit; + text-transform: none; +} + +a.transparent[role="button"], +button.transparent, +.transparent[role="button"] { + background: transparent; + border: none; + box-shadow: none; + color: var(--app-muted-color); +} +a.transparent[role="button"]:hover, +button.transparent:hover, +.transparent[role="button"]:hover { + background: var(--app-muted-border-color); + color: var(--app-contrast); +} + +[type="button"], +[type="reset"], +[type="submit"], +button { + -webkit-appearance: button; +} + +[role="button"], +[type="button"], +[type="file"]::file-selector-button, +[type="reset"], +[type="submit"], +button { + --app-background-color: var(--app-primary-background); + --app-border-color: var(--app-primary-border); + --app-color: var(--app-primary-inverse); + --app-box-shadow: var(--app-button-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); + padding: var(--app-button-padding-vertical) + var(--app-button-padding-horizontal); + border: var(--app-border-width) solid var(--app-border-color); + border-radius: var(--app-border-radius); + outline: 0; + background-color: var(--app-background-color); + box-shadow: var(--app-box-shadow); + color: var(--app-color); + font-weight: var(--app-font-weight); + font-size: 1rem; + line-height: var(--app-line-height); + text-align: center; + text-decoration: none; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + transition: + background-color var(--app-transition), + border-color var(--app-transition), + color var(--app-transition), + box-shadow var(--app-transition); +} + +:where( + button, + [type="submit"], + [type="reset"], + [type="button"], + [role="button"] +).small { + --app-button-padding-vertical: var(--app-button-padding-vertical-small); + --app-button-padding-horizontal: var(--app-button-padding-horizontal-small); +} + +[role="button"]:is(:hover, :active, :focus), +[role="button"]:is([aria-current]:not([aria-current="false"])), +[type="button"]:is(:hover, :active, :focus), +[type="button"]:is([aria-current]:not([aria-current="false"])), +[type="file"]::file-selector-button:is(:hover, :active, :focus), +[type="file"]::file-selector-button:is( + [aria-current]:not([aria-current="false"]) + ), +[type="reset"]:is(:hover, :active, :focus), +[type="reset"]:is([aria-current]:not([aria-current="false"])), +[type="submit"]:is(:hover, :active, :focus), +[type="submit"]:is([aria-current]:not([aria-current="false"])), +button:is(:hover, :active, :focus), +button:is([aria-current]:not([aria-current="false"])) { + --app-background-color: var(--app-primary-hover-background); + --app-border-color: var(--app-primary-hover-border); + --app-box-shadow: var(--app-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); + --app-color: var(--app-primary-inverse); +} + +[role="button"]:focus, +[role="button"]:is([aria-current]:not([aria-current="false"])):focus, +[type="button"]:focus, +[type="button"]:is([aria-current]:not([aria-current="false"])):focus, +[type="file"]::file-selector-button:focus, +[type="file"]::file-selector-button:is( + [aria-current]:not([aria-current="false"]) + ):focus, +[type="reset"]:focus, +[type="reset"]:is([aria-current]:not([aria-current="false"])):focus, +[type="submit"]:focus, +[type="submit"]:is([aria-current]:not([aria-current="false"])):focus, +button:focus, +button:is([aria-current]:not([aria-current="false"])):focus { + --app-box-shadow: + var(--app-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), + 0 0 0 var(--app-outline-width) var(--app-primary-focus); +} + +[type="button"], +[type="reset"] { + margin-bottom: var(--app-spacing); +} + +:is(button, [type="submit"], [type="button"], [role="button"]).secondary, +[type="file"]::file-selector-button, +[type="reset"] { + --app-background-color: var(--app-secondary-background); + --app-border-color: var(--app-secondary-border); + --app-color: var(--app-secondary-inverse); + cursor: pointer; +} + +:is(button, [type="submit"], [type="button"], [role="button"]).secondary:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +[type="file"]::file-selector-button:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +[type="reset"]:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus +) { + --app-background-color: var(--app-secondary-hover-background); + --app-border-color: var(--app-secondary-hover-border); + --app-color: var(--app-secondary-inverse); +} + +:is(button, [type="submit"], [type="button"], [role="button"]).secondary:focus, +:is(button, [type="submit"], [type="button"], [role="button"]).secondary:is( + [aria-current]:not([aria-current="false"]) + ):focus, +[type="file"]::file-selector-button:focus, +[type="file"]::file-selector-button:is( + [aria-current]:not([aria-current="false"]) + ):focus, +[type="reset"]:focus, +[type="reset"]:is([aria-current]:not([aria-current="false"])):focus { + --app-box-shadow: + var(--app-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), + 0 0 0 var(--app-outline-width) var(--app-secondary-focus); +} + +:is(button, [type="submit"], [type="button"], [role="button"]).contrast { + --app-background-color: var(--app-contrast-background); + --app-border-color: var(--app-contrast-border); + --app-color: var(--app-contrast-inverse); +} + +:is(button, [type="submit"], [type="button"], [role="button"]).contrast:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --app-background-color: var(--app-contrast-hover-background); + --app-border-color: var(--app-contrast-hover-border); + --app-color: var(--app-contrast-inverse); +} + +:is(button, [type="submit"], [type="button"], [role="button"]).contrast:focus, +:is(button, [type="submit"], [type="button"], [role="button"]).contrast:is( + [aria-current]:not([aria-current="false"]) + ):focus { + --app-box-shadow: + var(--app-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), + 0 0 0 var(--app-outline-width) var(--app-contrast-focus); +} + +:is(button, [type="submit"], [type="button"], [role="button"]).outline, +[type="reset"].outline { + --app-background-color: transparent; + --app-color: var(--app-primary); + --app-border-color: var(--app-primary); +} + +:is(button, [type="submit"], [type="button"], [role="button"]).outline:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +[type="reset"].outline:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --app-background-color: transparent; + --app-color: var(--app-primary-hover); + --app-border-color: var(--app-primary-hover); +} + +:is( + button, + [type="submit"], + [type="button"], + [role="button"] + ).outline.secondary, +[type="reset"].outline { + --app-color: var(--app-secondary); + --app-border-color: var(--app-secondary); +} + +:is( + button, + [type="submit"], + [type="button"], + [role="button"] + ).outline.secondary:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +[type="reset"].outline:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --app-color: var(--app-secondary-hover); + --app-border-color: var(--app-secondary-hover); +} + +:is( + button, + [type="submit"], + [type="button"], + [role="button"] + ).outline.contrast { + --app-color: var(--app-contrast); + --app-border-color: var(--app-contrast); +} + +:is( + button, + [type="submit"], + [type="button"], + [role="button"] + ).outline.contrast:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --app-color: var(--app-contrast-hover); + --app-border-color: var(--app-contrast-hover); +} + +:where( + button, + [type="submit"], + [type="reset"], + [type="button"], + [role="button"] +)[disabled], +:where(fieldset[disabled]) + :is( + button, + [type="submit"], + [type="button"], + [type="reset"], + [role="button"] + ) { + opacity: 0.5; + pointer-events: none; +} + +:where(table) { + width: 100%; + border-collapse: collapse; + border-spacing: 0; + text-indent: 0; +} + +td, +th { + padding: calc(var(--app-spacing) / 2) var(--app-spacing); + border-bottom: var(--app-border-width) solid var(--app-table-border-color); + background-color: var(--app-background-color); + color: var(--app-color); + font-weight: var(--app-font-weight); + text-align: left; + text-align: start; +} + +tfoot td, +tfoot th { + border-top: var(--app-border-width) solid var(--app-table-border-color); + border-bottom: 0; +} + +table.striped tbody tr:nth-child(odd) td, +table.striped tbody tr:nth-child(odd) th { + background-color: var(--app-table-row-stripped-background-color); +} + +:where(audio, canvas, iframe, img, svg, video) { + vertical-align: middle; +} + +audio, +video { + display: inline-block; +} + +audio:not([controls]) { + display: none; + height: 0; +} + +:where(iframe) { + border-style: none; +} + +img { + max-width: 100%; + height: auto; + border-style: none; +} + +img.rounded { + border-radius: 0.5rem; +} + +:where(svg:not([fill])) { + fill: currentColor; +} + +svg:not(:host), +svg:not(:root) { + overflow: hidden; +} + +code, +kbd, +pre, +samp { + font-size: 0.875em; + font-family: var(--app-font-family); +} + +pre code, +pre samp { + font-size: inherit; + font-family: inherit; +} + +pre { + -ms-overflow-style: scrollbar; + overflow: auto; +} + +code, +kbd, +pre, +samp { + border-radius: var(--app-border-radius); + background: var(--app-code-background-color); + color: var(--app-code-color); + font-weight: var(--app-font-weight); + line-height: initial; +} + +code, +kbd, +samp { + display: inline-block; + padding: 0.375rem; +} + +pre { + display: block; + margin-bottom: var(--app-spacing); + overflow-x: auto; +} + +pre > code, +pre > samp { + display: block; + padding: var(--app-spacing); + background: 0 0; + line-height: var(--app-line-height); +} + +.code-toolbar { + position: relative; + margin-block: var(--app-spacing); +} + +.code-toolbar > .toolbar { + position: absolute; + top: 0.5rem; + right: 0.5rem; + z-index: 1; +} + +.code-toolbar > .toolbar .toolbar-item { + display: inline-flex; +} + +.code-toolbar > .toolbar .copy-to-clipboard-button { + --app-background-color: transparent; + --app-border-color: var(--app-muted-border-color); + --app-color: var(--app-muted-color); + --app-box-shadow: none; + --app-button-padding-vertical: var(--app-button-padding-vertical-small); + --app-button-padding-horizontal: var(--app-button-padding-horizontal-small); + font-size: 0.75rem; +} + +.code-toolbar > .toolbar .copy-to-clipboard-button:is(:hover, :focus) { + --app-background-color: var(--app-card-sectioning-background-color); + --app-border-color: var(--app-muted-border-color); +} + +.code-toolbar > pre > code { + padding-top: calc(var(--app-spacing) * 2); +} + +kbd { + background-color: var(--app-code-kbd-background-color); + color: var(--app-code-kbd-color); + vertical-align: baseline; +} + +figure { + display: block; + margin: 0; + padding: 0; +} + +figure figcaption { + padding: calc(var(--app-spacing) * 0.5) 0; + color: var(--app-muted-color); +} + +hr { + height: 0; + margin: var(--app-typography-spacing-vertical) 0; + border: 0; + border-top: 1px solid var(--app-muted-border-color); + color: inherit; +} + +[hidden], +template { + display: none !important; +} + +canvas { + display: inline-block; +} + +input, +optgroup, +select, +textarea { + margin: 0; + font-size: 1rem; + line-height: var(--app-line-height); + font-family: inherit; + letter-spacing: inherit; +} + +input { + overflow: visible; +} + +select { + text-transform: none; +} + +legend { + max-width: 100%; + white-space: normal; + margin: 0; + display: block; + color: var(--app-color); + font-weight: var(--app-form-label-font-weight, var(--app-font-weight)); +} + +textarea { + overflow: auto; +} + +[type="checkbox"], +[type="radio"] { + padding: 0; +} + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; +} + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} + +::-moz-focus-inner { + padding: 0; + border-style: none; +} + +:-moz-focusring { + outline: 0; +} + +:-moz-ui-invalid { + box-shadow: none; +} + +::-ms-expand { + display: none; +} + +[type="file"], +[type="range"] { + padding: 0; + border-width: 0; +} + +input:not([type="checkbox"], [type="radio"], [type="range"]) { + height: calc( + 1rem * var(--app-line-height) + var(--app-form-element-spacing-vertical) * + 2 + var(--app-border-width) * 2 + ); +} + +fieldset { + width: 100%; + margin-bottom: var(--app-spacing); + border: 1px solid var(--app-form-element-border-color); + padding: var(--app-spacing); + border-radius: var(--app-border-radius); +} + +fieldset[role="group"] { + padding: 0; + border: 0; + border-radius: 0; +} + +label { + display: block; + margin-bottom: calc(var(--app-spacing) * 0.375); + color: var(--app-color); + font-weight: var(--app-form-label-font-weight, var(--app-font-weight)); + font-size: small; +} + +label:has(input[required]) span:after { + content: " *"; +} + +button[type="submit"], +input:not([type="checkbox"], [type="radio"]), +select, +textarea { + width: 100%; +} + +input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]), +select, +textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: var(--app-form-element-spacing-vertical) + var(--app-form-element-spacing-horizontal); +} + +input, +select, +textarea { + --app-background-color: var(--app-form-element-background-color); + --app-border-color: var(--app-form-element-border-color); + --app-color: var(--app-form-element-color); + --app-box-shadow: none; + border: var(--app-border-width) solid var(--app-border-color); + border-radius: var(--app-border-radius); + outline: 0; + background-color: var(--app-background-color); + box-shadow: var(--app-box-shadow); + color: var(--app-color); + font-weight: var(--app-font-weight); + transition: + background-color var(--app-transition), + border-color var(--app-transition), + color var(--app-transition), + box-shadow var(--app-transition); +} + +:where(select, textarea):not([readonly]):is(:active, :focus), +input:not( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [readonly] + ):is(:active, :focus) { + --app-background-color: var(--app-form-element-active-background-color); +} + +:where(select, textarea):not([readonly]):is(:active, :focus), +input:not( + [type="submit"], + [type="button"], + [type="reset"], + [role="switch"], + [readonly] + ):is(:active, :focus) { + --app-border-color: var(--app-form-element-active-border-color); +} + +:where(select, textarea):not([readonly]):focus, +input:not( + [type="submit"], + [type="button"], + [type="reset"], + [type="range"], + [type="file"], + [readonly] + ):focus { + --app-box-shadow: 0 0 0 var(--app-outline-width) + var(--app-form-element-focus-color); +} + +:where(fieldset[disabled]) + :is( + input:not([type="submit"], [type="button"], [type="reset"]), + select, + textarea + ), +input:not([type="submit"], [type="button"], [type="reset"])[disabled], +label[aria-disabled="true"], +select[disabled], +textarea[disabled] { + opacity: var(--app-form-element-disabled-opacity); + pointer-events: none; +} + +label[aria-disabled="true"] input[disabled] { + opacity: 1; +} + +:where(input, select, textarea):not( + [type="checkbox"], + [type="radio"], + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"], + [type="range"] + )[aria-invalid] { + padding-right: calc( + var(--app-form-element-spacing-horizontal) + 1.5rem + ) !important; + padding-left: var(--app-form-element-spacing-horizontal); + padding-inline-start: var(--app-form-element-spacing-horizontal) !important; + padding-inline-end: calc( + var(--app-form-element-spacing-horizontal) + 1.5rem + ) !important; + background-position: center right 0.75rem; + background-size: 1rem auto; + background-repeat: no-repeat; +} + +:where(input, select, textarea):not( + [type="checkbox"], + [type="radio"], + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"], + [type="range"] + )[aria-invalid="false"]:not(select) { + background-image: var(--app-icon-valid); +} + +:where(input, select, textarea):not( + [type="checkbox"], + [type="radio"], + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"], + [type="range"] + )[aria-invalid="true"]:not(select) { + background-image: var(--app-icon-invalid); +} + +:where(input, select, textarea)[aria-invalid="false"] { + --app-border-color: var(--app-form-element-valid-border-color); +} + +:where(input, select, textarea)[aria-invalid="false"]:is(:active, :focus) { + --app-border-color: var( + --app-form-element-valid-active-border-color + ) !important +; +} + +:where(input, select, textarea)[aria-invalid="false"]:is(:active, :focus):not( + [type="checkbox"], + [type="radio"] + ) { + --app-box-shadow: 0 0 0 var(--app-outline-width) + var(--app-form-element-valid-focus-color) !important +; +} + +:where(input, select, textarea)[aria-invalid="true"] { + --app-border-color: var(--app-form-element-invalid-border-color); +} + +:where(input, select, textarea)[aria-invalid="true"]:is(:active, :focus) { + --app-border-color: var( + --app-form-element-invalid-active-border-color + ) !important +; +} + +:where(input, select, textarea)[aria-invalid="true"]:is(:active, :focus):not( + [type="checkbox"], + [type="radio"] + ) { + --app-box-shadow: 0 0 0 var(--app-outline-width) + var(--app-form-element-invalid-focus-color) !important +; +} + +[dir="rtl"] + :where(input, select, textarea):not([type="checkbox"], [type="radio"]):is( + [aria-invalid], + [aria-invalid="true"], + [aria-invalid="false"] + ) { + background-position: center left 0.75rem; +} + +input::-webkit-input-placeholder, +input::placeholder, +select:invalid, +textarea::-webkit-input-placeholder, +textarea::placeholder { + color: var(--app-form-element-placeholder-color); + opacity: 1; +} + +input:not([type="checkbox"], [type="radio"]), +select, +textarea { + margin-bottom: var(--app-spacing); +} + +select::-ms-expand { + border: 0; + background-color: transparent; +} + +select:not([multiple], [size]) { + padding-right: calc(var(--app-form-element-spacing-horizontal) + 1.5rem); + padding-left: var(--app-form-element-spacing-horizontal); + padding-inline-start: var(--app-form-element-spacing-horizontal); + padding-inline-end: calc(var(--app-form-element-spacing-horizontal) + 1.5rem); + background-image: var(--app-icon-chevron); + background-position: center right 0.75rem; + background-size: 1rem auto; + background-repeat: no-repeat; +} + +select[multiple] option:checked { + background: var(--app-form-element-selected-background-color); + color: var(--app-form-element-color); +} + +[dir="rtl"] select:not([multiple], [size]) { + background-position: center left 0.75rem; +} + +textarea { + display: block; + resize: vertical; +} + +textarea[aria-invalid] { + --app-icon-height: calc( + 1rem * var(--app-line-height) + var(--app-form-element-spacing-vertical) * + 2 + var(--app-border-width) * 2 + ); + background-position: top right 0.75rem !important; + background-size: 1rem var(--app-icon-height) !important; +} + +:where(input, select, textarea, fieldset, .grid) + small { + display: block; + width: 100%; + margin-top: calc(var(--app-spacing) * -0.75); + margin-bottom: var(--app-spacing); + color: var(--app-muted-color); +} + +:where(input, select, textarea, fieldset, .grid)[aria-invalid="false"] + small { + color: var(--app-ins-color); +} + +:where(input, select, textarea, fieldset, .grid)[aria-invalid="true"] + small { + color: var(--app-del-color); +} + +label > :where(input, select, textarea) { + margin-top: calc(var(--app-spacing) * 0.25); +} + +label:has([type="checkbox"], [type="radio"]) { + width: -moz-fit-content; + width: fit-content; + cursor: pointer; +} + +[type="checkbox"], +[type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 1.25em; + height: 1.25em; + margin-top: -0.125em; + margin-inline-end: 0.5em; + border-width: var(--app-border-width); + vertical-align: middle; + cursor: pointer; +} + +[type="checkbox"]::-ms-check, +[type="radio"]::-ms-check { + display: none; +} + +[type="checkbox"]:checked, +[type="checkbox"]:checked:active, +[type="checkbox"]:checked:focus, +[type="radio"]:checked, +[type="radio"]:checked:active, +[type="radio"]:checked:focus { + --app-background-color: var(--app-primary-background); + --app-border-color: var(--app-primary-border); + background-image: var(--app-icon-checkbox); + background-position: center; + background-size: 0.75em auto; + background-repeat: no-repeat; +} + +[type="checkbox"] ~ label, +[type="radio"] ~ label { + display: inline-block; + margin-bottom: 0; + cursor: pointer; +} + +[type="checkbox"] ~ label:not(:last-of-type), +[type="radio"] ~ label:not(:last-of-type) { + margin-inline-end: 1em; +} + +[type="checkbox"]:indeterminate { + --app-background-color: var(--app-primary-background); + --app-border-color: var(--app-primary-border); + background-image: var(--app-icon-minus); + background-position: center; + background-size: 0.75em auto; + background-repeat: no-repeat; +} + +[type="radio"] { + border-radius: 50%; +} + +[type="radio"]:checked, +[type="radio"]:checked:active, +[type="radio"]:checked:focus { + --app-background-color: var(--app-primary-inverse); + border-width: 0.35em; + background-image: none; +} + +[type="checkbox"][role="switch"] { + --app-background-color: var(--app-switch-background-color); + --app-color: var(--app-switch-color); + width: 2.25em; + height: 1.25em; + border: var(--app-border-width) solid var(--app-border-color); + border-radius: 1.25em; + background-color: var(--app-background-color); + line-height: 1.25em; +} + +[type="checkbox"][role="switch"]:not([aria-invalid]) { + --app-border-color: var(--app-switch-background-color); +} + +[type="checkbox"][role="switch"]:before { + display: block; + aspect-ratio: 1; + height: 100%; + border-radius: 50%; + background-color: var(--app-color); + box-shadow: var(--app-switch-thumb-box-shadow); + content: ""; + transition: margin 0.1s ease-in-out; +} + +[type="checkbox"][role="switch"]:focus { + --app-background-color: var(--app-switch-background-color); + --app-border-color: var(--app-switch-background-color); +} + +[type="checkbox"][role="switch"]:checked { + --app-background-color: var(--app-switch-checked-background-color); + --app-border-color: var(--app-switch-checked-background-color); + background-image: none; +} + +[type="checkbox"][role="switch"]:checked::before { + margin-inline-start: calc(2.25em - 1.25em); +} + +[type="checkbox"][role="switch"][disabled] { + --app-background-color: var(--app-border-color); +} + +[type="checkbox"][aria-invalid="false"]:checked, +[type="checkbox"][aria-invalid="false"]:checked:active, +[type="checkbox"][aria-invalid="false"]:checked:focus, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked:active, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked:focus { + --app-background-color: var(--app-form-element-valid-border-color); +} + +[type="checkbox"]:checked:active[aria-invalid="true"], +[type="checkbox"]:checked:focus[aria-invalid="true"], +[type="checkbox"]:checked[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked:active[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked:focus[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked[aria-invalid="true"] { + --app-background-color: var(--app-form-element-invalid-border-color); +} + +[type="checkbox"][aria-invalid="false"]:checked, +[type="checkbox"][aria-invalid="false"]:checked:active, +[type="checkbox"][aria-invalid="false"]:checked:focus, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked:active, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked:focus, +[type="radio"][aria-invalid="false"]:checked, +[type="radio"][aria-invalid="false"]:checked:active, +[type="radio"][aria-invalid="false"]:checked:focus { + --app-border-color: var(--app-form-element-valid-border-color); +} + +[type="checkbox"]:checked:active[aria-invalid="true"], +[type="checkbox"]:checked:focus[aria-invalid="true"], +[type="checkbox"]:checked[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked:active[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked:focus[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked[aria-invalid="true"], +[type="radio"]:checked:active[aria-invalid="true"], +[type="radio"]:checked:focus[aria-invalid="true"], +[type="radio"]:checked[aria-invalid="true"] { + --app-border-color: var(--app-form-element-invalid-border-color); +} + +[type="color"]::-webkit-color-swatch-wrapper { + padding: 0; +} + +[type="color"]::-moz-focus-inner { + padding: 0; +} + +[type="color"]::-webkit-color-swatch { + border: 0; + border-radius: calc(var(--app-border-radius) * 0.5); +} + +[type="color"]::-moz-color-swatch { + border: 0; + border-radius: calc(var(--app-border-radius) * 0.5); +} + +input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]):is( + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"] + ) { + --app-icon-position: 0.75rem; + --app-icon-width: 1rem; + padding-right: calc(var(--app-icon-width) + var(--app-icon-position)); + background-image: var(--app-icon-date); + background-position: center right var(--app-icon-position); + background-size: var(--app-icon-width) auto; + background-repeat: no-repeat; +} + +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="time"] { + background-image: var(--app-icon-time); +} + +[type="date"]::-webkit-calendar-picker-indicator, +[type="datetime-local"]::-webkit-calendar-picker-indicator, +[type="month"]::-webkit-calendar-picker-indicator, +[type="time"]::-webkit-calendar-picker-indicator, +[type="week"]::-webkit-calendar-picker-indicator { + width: var(--app-icon-width); + margin-right: calc(var(--app-icon-width) * -1); + margin-left: var(--app-icon-position); + opacity: 0; +} + +@-moz-document url-prefix() { + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"] { + padding-right: var(--app-form-element-spacing-horizontal) !important; + background-image: none !important; + } +} + +[dir="rtl"] + :is( + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"] + ) { + text-align: right; +} + +[type="file"] { + --app-color: var(--app-muted-color); + margin-left: calc(var(--app-outline-width) * -1); + background: 0 0; + border: 2px dashed var(--app-form-element-border-color); + padding: 1.5rem; + height: auto !important; +} + +[type="file"]::file-selector-button { + margin-right: calc(var(--app-spacing) / 2); + padding: calc(var(--app-form-element-spacing-vertical) * 0.5) + var(--app-form-element-spacing-horizontal); +} + +[type="file"]:is(:hover, :active, :focus)::file-selector-button { + --app-background-color: var(--app-secondary-hover-background); + --app-border-color: var(--app-secondary-hover-border); +} + +[type="file"]:focus::file-selector-button { + --app-box-shadow: + var(--app-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), + 0 0 0 var(--app-outline-width) var(--app-secondary-focus); +} + +[type="range"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 100%; + height: 1.25rem; + background: 0 0; +} + +[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 0.375rem; + border-radius: var(--app-border-radius); + background-color: var(--app-range-border-color); + -webkit-transition: + background-color var(--app-transition), + box-shadow var(--app-transition); + transition: + background-color var(--app-transition), + box-shadow var(--app-transition); +} + +[type="range"]::-moz-range-track { + width: 100%; + height: 0.375rem; + border-radius: var(--app-border-radius); + background-color: var(--app-range-border-color); + -moz-transition: + background-color var(--app-transition), + box-shadow var(--app-transition); + transition: + background-color var(--app-transition), + box-shadow var(--app-transition); +} + +[type="range"]::-ms-track { + width: 100%; + height: 0.375rem; + border-radius: var(--app-border-radius); + background-color: var(--app-range-border-color); + -ms-transition: + background-color var(--app-transition), + box-shadow var(--app-transition); + transition: + background-color var(--app-transition), + box-shadow var(--app-transition); +} + +[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + width: 1.25rem; + height: 1.25rem; + margin-top: -0.4375rem; + border: 2px solid var(--app-range-thumb-border-color); + border-radius: 50%; + background-color: var(--app-range-thumb-color); + cursor: pointer; + -webkit-transition: + background-color var(--app-transition), + transform var(--app-transition); + transition: + background-color var(--app-transition), + transform var(--app-transition); +} + +[type="range"]::-moz-range-thumb { + -webkit-appearance: none; + width: 1.25rem; + height: 1.25rem; + margin-top: -0.4375rem; + border: 2px solid var(--app-range-thumb-border-color); + border-radius: 50%; + background-color: var(--app-range-thumb-color); + cursor: pointer; + -moz-transition: + background-color var(--app-transition), + transform var(--app-transition); + transition: + background-color var(--app-transition), + transform var(--app-transition); +} + +[type="range"]::-ms-thumb { + -webkit-appearance: none; + width: 1.25rem; + height: 1.25rem; + margin-top: -0.4375rem; + border: 2px solid var(--app-range-thumb-border-color); + border-radius: 50%; + background-color: var(--app-range-thumb-color); + cursor: pointer; + -ms-transition: + background-color var(--app-transition), + transform var(--app-transition); + transition: + background-color var(--app-transition), + transform var(--app-transition); +} + +[type="range"]:active, +[type="range"]:focus-within { + --app-range-border-color: var(--app-range-active-border-color); + --app-range-thumb-color: var(--app-range-thumb-active-color); +} + +[type="range"]:active::-webkit-slider-thumb { + transform: scale(1.25); +} + +[type="range"]:active::-moz-range-thumb { + transform: scale(1.25); +} + +[type="range"]:active::-ms-thumb { + transform: scale(1.25); +} + +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"] { + padding-inline-start: calc( + var(--app-form-element-spacing-horizontal) + 1.75rem + ); + background-image: var(--app-icon-search); + background-position: center left + calc(var(--app-form-element-spacing-horizontal) + 0.125rem); + background-size: 1rem auto; + background-repeat: no-repeat; +} + +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"][aria-invalid] { + padding-inline-start: calc( + var(--app-form-element-spacing-horizontal) + 1.75rem + ) !important; + background-position: + center left 1.125rem, + center right 0.75rem; +} + +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"][aria-invalid="false"] { + background-image: var(--app-icon-search), var(--app-icon-valid); +} + +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"][aria-invalid="true"] { + background-image: var(--app-icon-search), var(--app-icon-invalid); +} + +[dir="rtl"] + :where(input):not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"] { + background-position: center right 1.125rem; +} + +[dir="rtl"] + :where(input):not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"][aria-invalid] { + background-position: + center right 1.125rem, + center left 0.75rem; +} + +details { + display: block; + margin-bottom: var(--app-spacing); +} + +details summary { + line-height: 1rem; + list-style-type: none; + cursor: pointer; + transition: color var(--app-transition); +} + +details summary:not([role]) { + color: var(--app-accordion-close-summary-color); +} + +details summary::-webkit-details-marker { + display: none; +} + +details summary::marker { + display: none; +} + +details summary::-moz-list-bullet { + list-style-type: none; +} + +details summary::after { + display: block; + width: 1rem; + height: 1rem; + margin-inline-start: calc(var(--app-spacing, 1rem) * 0.5); + float: right; + transform: rotate(-90deg); + background-image: var(--app-icon-chevron); + background-position: right center; + background-size: 1rem auto; + background-repeat: no-repeat; + content: ""; + transition: transform var(--app-transition); +} + +details summary:focus { + outline: 0; +} + +details summary:focus:not([role]) { + color: var(--app-accordion-active-summary-color); +} + +details summary:focus-visible:not([role]) { + outline: var(--app-outline-width) solid var(--app-primary-focus); + outline-offset: calc(var(--app-spacing, 1rem) * 0.5); + color: var(--app-primary); +} + +details summary[role="button"] { + width: 100%; + text-align: left; +} + +details summary[role="button"]::after { + height: calc(1rem * var(--app-line-height, 1.5)); +} + +details[open] > summary { + margin-bottom: calc(var(--app-spacing) * 0.5); +} + +details[open] > summary:not([role]):not(:focus) { + color: var(--app-accordion-close-summary-color); +} + +details[open] > summary::after { + transform: rotate(0); +} + +[dir="rtl"] details summary { + text-align: right; +} + +[dir="rtl"] details summary::after { + float: left; + background-position: left center; +} + +article { + margin-bottom: var(--app-block-spacing-vertical); + padding: var(--app-block-spacing-vertical) var(--app-block-spacing-horizontal); + border-radius: var(--app-border-radius); + background: var(--app-card-background-color); + box-shadow: var(--app-card-box-shadow); +} + +article:not(:has(footer)) p:last-child { + margin: 0; +} + +article > footer, +article > header { + margin-right: calc(var(--app-block-spacing-horizontal) * -1); + margin-left: calc(var(--app-block-spacing-horizontal) * -1); + padding: calc(var(--app-block-spacing-vertical) * 1) + var(--app-block-spacing-horizontal); + background-color: var(--app-card-sectioning-background-color); +} + +article > header { + margin-top: calc(var(--app-block-spacing-vertical) * -1); + margin-bottom: var(--app-block-spacing-vertical); + border-bottom: var(--app-border-width) solid var(--app-card-border-color); + border-top-right-radius: var(--app-border-radius); + border-top-left-radius: var(--app-border-radius); +} + +article > header > hgroup { + margin: 0; +} + +article > footer { + margin-top: var(--app-block-spacing-vertical); + margin-bottom: calc(var(--app-block-spacing-vertical) * -1); + border-top: var(--app-border-width) solid var(--app-card-border-color); + border-bottom-right-radius: var(--app-border-radius); + border-bottom-left-radius: var(--app-border-radius); +} + +details.dropdown { + position: relative; + border-bottom: none; +} + +details.dropdown > a::after, +details.dropdown > button::after, +details.dropdown > summary::after { + display: block; + width: 1rem; + height: calc(1rem * var(--app-line-height, 1.5)); + margin-inline-start: 0.25rem; + float: right; + transform: rotate(0) translateX(0.2rem); + background-image: var(--app-icon-chevron); + background-position: right center; + background-size: 1rem auto; + background-repeat: no-repeat; + content: ""; +} + +nav details.dropdown { + margin-bottom: 0; +} + +details.dropdown > summary:not([role]) { + height: calc( + 1rem * var(--app-line-height) + var(--app-form-element-spacing-vertical) * + 2 + var(--app-border-width) * 2 + ); + padding: var(--app-form-element-spacing-vertical) + var(--app-form-element-spacing-horizontal); + border: var(--app-border-width) solid var(--app-form-element-border-color); + border-radius: var(--app-border-radius); + background-color: var(--app-form-element-background-color); + color: var(--app-form-element-placeholder-color); + line-height: inherit; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + transition: + background-color var(--app-transition), + border-color var(--app-transition), + color var(--app-transition), + box-shadow var(--app-transition); +} + +details.dropdown > summary:not([role]):active, +details.dropdown > summary:not([role]):focus { + border-color: var(--app-form-element-active-border-color); + background-color: var(--app-form-element-active-background-color); +} + +details.dropdown > summary:not([role]):focus { + box-shadow: 0 0 0 var(--app-outline-width) var(--app-form-element-focus-color); +} + +details.dropdown > summary:not([role]):focus-visible { + outline: 0; +} + +details.dropdown > summary:not([role])[aria-invalid="false"] { + --app-form-element-border-color: var(--app-form-element-valid-border-color); + --app-form-element-active-border-color: var( + --app-form-element-valid-focus-color + ); + --app-form-element-focus-color: var(--app-form-element-valid-focus-color); +} + +details.dropdown > summary:not([role])[aria-invalid="true"] { + --app-form-element-border-color: var(--app-form-element-invalid-border-color); + --app-form-element-active-border-color: var( + --app-form-element-invalid-focus-color + ); + --app-form-element-focus-color: var(--app-form-element-invalid-focus-color); +} + +nav details.dropdown { + display: inline; + margin: calc(var(--app-nav-element-spacing-vertical) * -1) 0; +} + +nav details.dropdown > summary::after { + transform: rotate(0) translateX(0); +} + +nav details.dropdown > summary:not([role]) { + height: calc( + 1rem * var(--app-line-height) + var(--app-nav-link-spacing-vertical) * 2 + ); + padding: calc( + var(--app-nav-link-spacing-vertical) - var(--app-border-width) * 2 + ) + var(--app-nav-link-spacing-horizontal); +} + +nav details.dropdown > summary:not([role]):focus-visible { + box-shadow: 0 0 0 var(--app-outline-width) var(--app-primary-focus); +} + +details.dropdown > summary + ul { + display: flex; + z-index: 99; + position: absolute; + left: 0; + flex-direction: column; + width: 100%; + min-width: -moz-fit-content; + min-width: fit-content; + margin: 0; + margin-top: var(--app-outline-width); + padding: 0; + border: var(--app-border-width) solid var(--app-dropdown-border-color); + border-radius: var(--app-border-radius); + background-color: var(--app-dropdown-background-color); + box-shadow: var(--app-dropdown-box-shadow); + color: var(--app-dropdown-color); + white-space: nowrap; + opacity: 0; + transition: + opacity var(--app-transition), + transform 0s ease-in-out 1s; +} + +details.dropdown > summary + ul[dir="rtl"] { + right: 0; + left: auto; +} + +details.dropdown > summary + ul li { + width: 100%; + margin-bottom: 0; + padding: calc(var(--app-form-element-spacing-vertical) * 0.5) + var(--app-form-element-spacing-horizontal); + list-style: none; +} + +details.dropdown > summary + ul li:first-of-type { + margin-top: calc(var(--app-form-element-spacing-vertical) * 0.5); +} + +details.dropdown > summary + ul li:last-of-type { + margin-bottom: calc(var(--app-form-element-spacing-vertical) * 0.5); +} + +details.dropdown > summary + ul li a { + display: block; + margin: calc(var(--app-form-element-spacing-vertical) * -0.5) + calc(var(--app-form-element-spacing-horizontal) * -1); + padding: calc(var(--app-form-element-spacing-vertical) * 0.5) + var(--app-form-element-spacing-horizontal); + overflow: hidden; + border-radius: 0; + color: var(--app-dropdown-color); + text-decoration: none; + text-overflow: ellipsis; +} + +details.dropdown > summary + ul li a:active, +details.dropdown > summary + ul li a:focus, +details.dropdown > summary + ul li a:focus-visible, +details.dropdown > summary + ul li a:hover, +details.dropdown > summary + ul li a[aria-current]:not([aria-current="false"]) { + background-color: var(--app-dropdown-hover-background-color); +} + +details.dropdown > summary + ul li label { + width: 100%; +} + +details.dropdown > summary + ul li:has(label):hover { + background-color: var(--app-dropdown-hover-background-color); +} + +details.dropdown[open] > summary { + margin-bottom: 0; +} + +details.dropdown[open] > summary + ul { + transform: scaleY(1); + opacity: 1; + transition: + opacity var(--app-transition), + transform 0s ease-in-out 0s; +} + +details.dropdown[open] > summary::before { + display: block; + z-index: 1; + position: fixed; + width: 100vw; + height: 100vh; + inset: 0; + background: 0 0; + content: ""; + cursor: default; +} + +label > details.dropdown { + margin-top: calc(var(--app-spacing) * 0.25); +} + +[role="group"], +[role="search"] { + display: inline-flex; + position: relative; + width: 100%; + margin-bottom: var(--app-spacing); + border-radius: var(--app-border-radius); + box-shadow: var(--app-group-box-shadow, 0 0 0 transparent); + vertical-align: middle; + transition: box-shadow var(--app-transition); +} + +[role="group"] input:not([type="checkbox"], [type="radio"]), +[role="group"] select, +[role="group"] > *, +[role="search"] input:not([type="checkbox"], [type="radio"]), +[role="search"] select, +[role="search"] > * { + position: relative; + flex: 1 1 auto; + margin-bottom: 0; +} + +[role="group"] input:not([type="checkbox"], [type="radio"]):not(:first-child), +[role="group"] select:not(:first-child), +[role="group"] > :not(:first-child), +[role="search"] input:not([type="checkbox"], [type="radio"]):not(:first-child), +[role="search"] select:not(:first-child), +[role="search"] > :not(:first-child) { + margin-left: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +[role="group"] input:not([type="checkbox"], [type="radio"]):not(:last-child), +[role="group"] select:not(:last-child), +[role="group"] > :not(:last-child), +[role="search"] input:not([type="checkbox"], [type="radio"]):not(:last-child), +[role="search"] select:not(:last-child), +[role="search"] > :not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +[role="group"] input:not([type="checkbox"], [type="radio"]):focus, +[role="group"] select:focus, +[role="group"] > :focus, +[role="search"] input:not([type="checkbox"], [type="radio"]):focus, +[role="search"] select:focus, +[role="search"] > :focus { + z-index: 2; +} + +[role="group"] [role="button"]:not(:first-child), +[role="group"] [type="button"]:not(:first-child), +[role="group"] [type="reset"]:not(:first-child), +[role="group"] [type="submit"]:not(:first-child), +[role="group"] button:not(:first-child), +[role="group"] input:not([type="checkbox"], [type="radio"]):not(:first-child), +[role="group"] select:not(:first-child), +[role="search"] [role="button"]:not(:first-child), +[role="search"] [type="button"]:not(:first-child), +[role="search"] [type="reset"]:not(:first-child), +[role="search"] [type="submit"]:not(:first-child), +[role="search"] button:not(:first-child), +[role="search"] input:not([type="checkbox"], [type="radio"]):not(:first-child), +[role="search"] select:not(:first-child) { + margin-left: calc(var(--app-border-width) * -1); +} + +[role="group"] [role="button"], +[role="group"] [type="button"], +[role="group"] [type="reset"], +[role="group"] [type="submit"], +[role="group"] button, +[role="search"] [role="button"], +[role="search"] [type="button"], +[role="search"] [type="reset"], +[role="search"] [type="submit"], +[role="search"] button { + width: auto; +} + +@supports selector(:has(*)) { + [role="group"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ), + [role="search"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) { + --app-group-box-shadow: var(--app-group-box-shadow-focus-with-button); + } + + [role="group"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) + input:not([type="checkbox"], [type="radio"]), + [role="group"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) + select, + [role="search"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) + input:not([type="checkbox"], [type="radio"]), + [role="search"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) + select { + border-color: transparent; + } + + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ), + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) { + --app-group-box-shadow: var(--app-group-box-shadow-focus-with-input); + } + + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [role="button"], + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [type="button"], + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [type="submit"], + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + button, + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [role="button"], + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [type="button"], + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [type="submit"], + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + button { + --app-button-box-shadow: 0 0 0 var(--app-border-width) + var(--app-primary-border); + --app-button-hover-box-shadow: 0 0 0 var(--app-border-width) + var(--app-primary-hover-border); + } + + [role="group"] [role="button"]:focus, + [role="group"] [type="button"]:focus, + [role="group"] [type="reset"]:focus, + [role="group"] [type="submit"]:focus, + [role="group"] button:focus, + [role="search"] [role="button"]:focus, + [role="search"] [type="button"]:focus, + [role="search"] [type="reset"]:focus, + [role="search"] [type="submit"]:focus, + [role="search"] button:focus { + box-shadow: none; + } +} + +[role="search"] > :first-child { + border-top-left-radius: 5rem; + border-bottom-left-radius: 5rem; +} + +[role="search"] > :last-child { + border-top-right-radius: 5rem; + border-bottom-right-radius: 5rem; +} + +html { + scroll-behavior: smooth; + background: var(--app-sidebar-background-color); +} + +.white-space-nowrap { + white-space: nowrap; +} + +[aria-busy="true"]:not(input, select, textarea, html, form) { + white-space: nowrap; +} + +[aria-busy="true"]:not(input, select, textarea, html, form)::before { + display: inline-block; + width: 1em; + height: 1em; + background-image: var(--app-icon-loading); + background-size: 1em auto; + background-repeat: no-repeat; + content: ""; + vertical-align: -0.125em; +} + +[aria-busy="true"]:not(input, select, textarea, html, form):not( + :empty + )::before { + margin-inline-end: calc(var(--app-spacing) * 0.5); +} + +[aria-busy="true"]:not(input, select, textarea, html, form):empty { + text-align: center; +} + +[role="button"][aria-busy="true"], +[type="button"][aria-busy="true"], +[type="reset"][aria-busy="true"], +[type="submit"][aria-busy="true"], +a[aria-busy="true"], +button[aria-busy="true"] { + pointer-events: none; +} + +dialog { + display: flex; + z-index: 999; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + align-items: center; + justify-content: center; + width: inherit; + min-width: 100%; + height: inherit; + min-height: 100%; + padding: 0; + border: 0; + -webkit-backdrop-filter: var(--app-modal-overlay-backdrop-filter); + backdrop-filter: var(--app-modal-overlay-backdrop-filter); + background-color: var(--app-modal-overlay-background-color); + color: var(--app-color); +} + +dialog > article { + width: 100%; + max-height: calc(100vh - var(--app-spacing) * 2); + margin: var(--app-spacing); + overflow: auto; +} + +@media (min-width: 576px) { + dialog > article { + max-width: 510px; + } +} + +@media (min-width: 768px) { + dialog > article { + max-width: 700px; + } +} + +dialog > article > header > * { + margin-bottom: 0; +} + +dialog > article > header .close, +dialog > article > header :is(a, button)[rel="prev"] { + margin: 0; + margin-left: var(--app-spacing); + padding: 0; + float: right; +} + +dialog > article > footer { + text-align: right; +} + +dialog > article > footer [role="button"], +dialog > article > footer button { + margin-bottom: 0; +} + +dialog > article > footer [role="button"]:not(:first-of-type), +dialog > article > footer button:not(:first-of-type) { + margin-left: calc(var(--app-spacing) * 0.5); +} + +dialog > article .close, +dialog > article :is(a, button)[rel="prev"] { + display: block; + width: 1rem; + height: 1rem; + margin-top: calc(var(--app-spacing) * -1); + margin-bottom: var(--app-spacing); + margin-left: auto; + border: none; + background-image: var(--app-icon-close); + background-position: center; + background-size: auto 1rem; + background-repeat: no-repeat; + background-color: transparent; + opacity: 0.5; + transition: opacity var(--app-transition); +} + +dialog + > article + .close:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +dialog + > article + :is(a, button)[rel="prev"]:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + opacity: 1; +} + +dialog:not([open]), +dialog[open="false"] { + display: none; +} + +.modal-is-open { + padding-right: var(--app-scrollbar-width, 0); + overflow: hidden; + pointer-events: none; + touch-action: none; +} + +.modal-is-open dialog { + pointer-events: auto; + touch-action: auto; +} + +:where(.modal-is-opening, .modal-is-closing) dialog, +:where(.modal-is-opening, .modal-is-closing) dialog > article { + animation-duration: 0.2s; + animation-timing-function: ease-in-out; + animation-fill-mode: both; +} + +:where(.modal-is-opening, .modal-is-closing) dialog { + animation-duration: 0.8s; + animation-name: modal-overlay; +} + +:where(.modal-is-opening, .modal-is-closing) dialog > article { + animation-delay: 0.2s; + animation-name: modal; +} + +.modal-is-closing dialog, +.modal-is-closing dialog > article { + animation-delay: 0s; + animation-direction: reverse; +} + +@keyframes modal-overlay { + from { + -webkit-backdrop-filter: none; + backdrop-filter: none; + background-color: transparent; + } +} + +@keyframes modal { + from { + transform: translateY(-100%); + opacity: 0; + } +} + +:where(nav li)::before { + float: left; + content: "​"; +} + +nav, +nav ul { + display: flex; +} + +nav { + justify-content: space-between; + overflow: visible; +} + +nav ol, +nav ul { + align-items: center; + margin-bottom: 0; + padding: 0; + list-style: none; +} + +nav ol:first-of-type, +nav ul:first-of-type { + margin-left: calc(var(--app-nav-element-spacing-horizontal) * -1); +} + +nav ol:last-of-type, +nav ul:last-of-type { + margin-right: calc(var(--app-nav-element-spacing-horizontal) * -1); +} + +nav li { + display: inline-block; + margin: 0; + padding: var(--app-nav-element-spacing-vertical) + var(--app-nav-element-spacing-horizontal); +} + +nav li :where(a, [role="link"]) { + display: inline-block; + margin: calc(var(--app-nav-link-spacing-vertical) * -1) + calc(var(--app-nav-link-spacing-horizontal) * -1); + padding: var(--app-nav-link-spacing-vertical) + var(--app-nav-link-spacing-horizontal); + border-radius: var(--app-border-radius); +} + +nav li :where(a, [role="link"]):not(:hover) { + text-decoration: none; +} + +nav li [role="button"], +nav li [type="button"], +nav li button, +nav + li + input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]), +nav li select { + height: auto; + margin-right: inherit; + margin-bottom: 0; + margin-left: inherit; + padding: calc( + var(--app-nav-link-spacing-vertical) - var(--app-border-width) * 2 + ) + var(--app-nav-link-spacing-horizontal); +} + +nav[aria-label="breadcrumb"] { + --app-breadcrumb-separator-size: 0.75rem; + --app-nav-element-spacing-vertical: 0.25rem; + --app-nav-element-spacing-horizontal: 0.25rem; + --app-nav-link-spacing-vertical: 0.25rem; + --app-nav-link-spacing-horizontal: 0.25rem; + align-items: center; + justify-content: start; + font-size: 0.875em; +} + +nav[aria-label="breadcrumb"] ol, +nav[aria-label="breadcrumb"] ul { + margin: 0; +} + +nav[aria-label="breadcrumb"] :is(ol, ul) li { + padding: 0; +} + +nav[aria-label="breadcrumb"] :is(ol, ul) li:not(:first-child) { + margin-inline-start: 0; +} + +nav[aria-label="breadcrumb"] :is(ol, ul) li a { + margin: calc(var(--app-nav-link-spacing-vertical) * -1) 0; + margin-inline-start: calc(var(--app-nav-link-spacing-horizontal) * -1); +} + +nav[aria-label="breadcrumb"] :is(ol, ul) li:not(:last-child)::after { + display: inline-block; + width: var(--app-breadcrumb-separator-size); + height: var(--app-breadcrumb-separator-size); + margin-inline: calc(var(--app-nav-link-spacing-horizontal) * 0.75); + content: ""; + background-image: var(--app-icon-chevron); + background-size: var(--app-breadcrumb-separator-size) auto; + background-repeat: no-repeat; + opacity: 0.7; + transform: rotate(-90deg); +} + +nav[aria-label="breadcrumb"] a[aria-current]:not([aria-current="false"]) { + background-color: transparent; + color: inherit; + text-decoration: none; + pointer-events: none; +} + +aside li, +aside nav, +aside ol, +aside ul { + display: block; +} + +aside li { + padding: calc(var(--app-nav-element-spacing-vertical) * 0.5) + var(--app-nav-element-spacing-horizontal); +} + +aside li a { + display: block; +} + +aside li [role="button"] { + margin: inherit; +} + +.text-align-center { + text-align: center; +} + +.primary { + box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px; +} + +.secondary { + border: 1px solid var(--app-border); + color: var(--app-muted-color); + box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px; +} +.secondary:hover { + border: 1px solid var(--app-contrast); + color: var(--app-contrast); +} + +/* Loading state - global cursor and interaction blocking */ +body.is-loading { + cursor: wait; +} + +body.is-loading * { + cursor: wait !important; +} + +body.is-loading button, +body.is-loading a, +body.is-loading input, +body.is-loading select, +body.is-loading textarea { + pointer-events: none; +} diff --git a/web/css/layout/app-sidebar.css b/web/css/layout/app-sidebar.css new file mode 100644 index 0000000..52bc886 --- /dev/null +++ b/web/css/layout/app-sidebar.css @@ -0,0 +1,208 @@ +.app-sidebar { + background: var(--app-sidebar-background-color); +} + +.app-sidebar .brand { + padding: var(--app-spacing); +} + +.app-sidebar-titlebar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + padding: 0px var(--app-spacing) 0px var(--app-spacing); + margin-bottom: 0; + min-height: 30px; +} + +.app-sidebar .app-sidebar-panel { + display: block; +} + +.app-sidebar .app-sidebar-panel[hidden] { + display: none !important; +} + +.app-sidebar.is-collapsed { + display: none; +} + +.sidebar-collapsed .app-sidebar { + display: none; +} + +.app-sidebar-tenant-logo { + display: flex; + align-items: center; + justify-content: space-between; + gap: 5px; + padding: var(--app-spacing) var(--app-spacing) 10px var(--app-spacing); +} + + +.app-sidebar .app-sidebar-tenant-logo a:hover { + background: transparent; + border: 0; +} + +.app-sidebar .app-sidebar-tenant-logo > a { + padding: 0; + flex: 1; + border: 0; +} + + + + + +.app-sidebar-tenant-name { + flex: 1; + font-weight: 600; + font-size: 14px; + color: var(--app-contrast); + padding: 8px 12px; + border-radius: var(--app-border-radius); + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + border: 1px solid var(--app-border); +} + +.app-sidebar-title { + text-transform: uppercase; + font-size: 10px; + color: var(--app-muted-color); + display: none; +} + +.app-sidebar-tools { + display: flex; + align-items: center; + gap: 0.35rem; +} + +.app-sidebar-tools .icon-button { + border: 0; + background: transparent; + color: var(--app-muted-color); + padding: 0.25rem; + font-size: 0.9rem; + cursor: pointer; + margin-bottom: 0; +} + +.app-sidebar-tools .icon-button:hover { + color: var(--app-contrast); +} + +@media (min-width: 768px) { + .app-sidebar .brand { + margin-bottom: 1rem; + padding-block-start: 0.5rem; + } + .app-sidebar-title { + display: block; + } + + .app-sidebar { + position: sticky; + top: 0; + z-index: 1; + } +} + +.app-sidebar a { + color: inherit; + display: flex; + align-items: center; + gap: 10px; + text-decoration: none; + padding: 5px var(--app-spacing); + border-radius: 0; + margin: 0; + border-left: 2px solid transparent; + width: 100%; +} + +.app-sidebar a:hover, +.app-sidebar .active { + color: var(--app-primary); + --app-background-color: inherit; + background: var(--app-background-color); + border-left: 2px solid var(--app-primary); +} + +aside.app-sidebar ul { + margin: 0; +} + +.app-sidebar small { + margin-top: 2rem; + margin-bottom: 5px; + display: block; + padding-inline: var(--app-spacing); + opacity: 0.7; +} +.app-sidebar .app-sidebar-group small { + margin-top: 0; + margin-bottom: 0; + display: block; + padding-inline: 0; + opacity: 1; +} + +.app-sidebar li { + padding-block: 0; + padding: 0; +} + +.app-sidebar .app-sidebar-group { + list-style: none; +} + +.app-sidebar .app-sidebar-group details { + padding: 0; + margin: 0; +} + +.app-sidebar .app-sidebar-group summary { + padding: 0 var(--app-spacing); + font-size: 12px; + color: var(--app-muted-color); + line-height: 1; +} + +nav#aside-panel-people { + margin-top: var(--app-spacing); +} + +.app-sidebar .app-sidebar-group summary::-webkit-details-marker { + display: none; +} + +.app-sidebar .app-sidebar-group details > ul { + margin: 0; + padding: 0; +} + +.app-sidebar .app-sidebar-group summary > span { + text-overflow: ellipsis; + display: inline-block; + max-width: 157px; + overflow: hidden; + white-space: nowrap; +} + +.app-sidebar .app-sidebar-group details > ul li:first-child { + margin-top: 10px; +} + +.app-sidebar .app-sidebar-group details > ul li a { + padding-block: 2px; + opacity: .7; +} +.app-sidebar .app-sidebar-group details > ul li a:hover { + opacity: 1; +} \ No newline at end of file diff --git a/web/css/layout/app-topbar.css b/web/css/layout/app-topbar.css new file mode 100644 index 0000000..31cc406 --- /dev/null +++ b/web/css/layout/app-topbar.css @@ -0,0 +1,87 @@ +.app-header nav li { + padding: 0; +} + +form:has(input#app-search) { + flex: 1; +} + +.app-header nav { + gap: var(--app-spacing); +} + +.app-header { + padding-block-start: 0; + padding-left: var(--app-spacing); + padding-right: var(--app-spacing); + margin-bottom: calc(var(--app-spacing) * 2); + color: var(--app-contrast); + border-bottom: 1px solid var(--app-border); +} + +main:has(.app-details-container) .app-header { + margin-bottom: 0; +} + +li:has(#toggle-main-content-aside) { + display: none; +} + +main:has(#app-details-aside-section) li:has(#toggle-main-content-aside) { + display: block; +} + +.app-header nav > ul { + display: flex; + gap: 8px; + align-items: center; + color: var(--app-muted-color); +} + +.app-header details.dropdown > summary:not([role]) { + border: none; + height: auto; + padding: 5px; + border-radius: 0; + background: transparent; +} + +.app-header details.dropdown > summary:not([role]):after { + display: none; +} + +.app-header nav > ul li a { + padding: 5px; + color: var(--app-form-element-placeholder-color); + display: block; + margin: 0; +} + +.app-header button { + padding: 5px; + color: var(--app-form-element-placeholder-color); + display: block; + margin: 0; + background: transparent; + border: none; + border-radius: 0; +} +.app-header #global-back.is-disabled, +.app-header #global-forward.is-disabled { + opacity: 0.4; + cursor: not-allowed; + pointer-events: none; +} + +@media (min-width: 768px) { + header.app-header { + position: sticky; + top: 0; + background: var(--app-background-color); + z-index: 10; + } +} + +.app-header details.dropdown[open] > summary::before { + display: none; +} \ No newline at end of file diff --git a/web/css/pages/app-login.css b/web/css/pages/app-login.css new file mode 100644 index 0000000..ca961f4 --- /dev/null +++ b/web/css/pages/app-login.css @@ -0,0 +1,10 @@ +.back_to_login { + margin-bottom: 1rem; +} + +@media (min-width: 600px) { + main { + min-height: 90vh; + place-content: center; + } +} diff --git a/web/favicon/apple-touch-icon.png b/web/favicon/apple-touch-icon.png new file mode 100644 index 0000000..aaa57dc Binary files /dev/null and b/web/favicon/apple-touch-icon.png differ diff --git a/web/favicon/favicon-16x16.png b/web/favicon/favicon-16x16.png new file mode 100644 index 0000000..9aa0ec3 Binary files /dev/null and b/web/favicon/favicon-16x16.png differ diff --git a/web/favicon/favicon-32x32.png b/web/favicon/favicon-32x32.png new file mode 100644 index 0000000..9213347 Binary files /dev/null and b/web/favicon/favicon-32x32.png differ diff --git a/web/favicon/favicon-96x96.png b/web/favicon/favicon-96x96.png new file mode 100644 index 0000000..c2ae6e4 Binary files /dev/null and b/web/favicon/favicon-96x96.png differ diff --git a/web/favicon/favicon.ico b/web/favicon/favicon.ico new file mode 100644 index 0000000..b2fdcb0 Binary files /dev/null and b/web/favicon/favicon.ico differ diff --git a/web/favicon/favicon.svg b/web/favicon/favicon.svg new file mode 100644 index 0000000..e8e86a6 --- /dev/null +++ b/web/favicon/favicon.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/favicon/site.webmanifest b/web/favicon/site.webmanifest new file mode 100644 index 0000000..2a08603 --- /dev/null +++ b/web/favicon/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "IVMS", + "short_name": "IVMS", + "icons": [ + { + "src": "/favicon/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/favicon/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/apple-touch-icon.png b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/apple-touch-icon.png new file mode 100644 index 0000000..5578415 Binary files /dev/null and b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/apple-touch-icon.png differ diff --git a/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-16x16.png b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-16x16.png new file mode 100644 index 0000000..c12919a Binary files /dev/null and b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-16x16.png differ diff --git a/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-32x32.png b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-32x32.png new file mode 100644 index 0000000..c51bd49 Binary files /dev/null and b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-32x32.png differ diff --git a/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-96x96.png b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-96x96.png new file mode 100644 index 0000000..74a164f Binary files /dev/null and b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/favicon-96x96.png differ diff --git a/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/web-app-manifest-192x192.png b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/web-app-manifest-192x192.png new file mode 100644 index 0000000..9b311fe Binary files /dev/null and b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/web-app-manifest-192x192.png differ diff --git a/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/web-app-manifest-512x512.png b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/web-app-manifest-512x512.png new file mode 100644 index 0000000..255b2ba Binary files /dev/null and b/web/favicon/tenants/21a17705-d67e-4e20-8cc3-cf994bcfc7bb/web-app-manifest-512x512.png differ diff --git a/web/favicon/web-app-manifest-192x192.png b/web/favicon/web-app-manifest-192x192.png new file mode 100644 index 0000000..4b8dd40 Binary files /dev/null and b/web/favicon/web-app-manifest-192x192.png differ diff --git a/web/favicon/web-app-manifest-512x512.png b/web/favicon/web-app-manifest-512x512.png new file mode 100644 index 0000000..8d7c28f Binary files /dev/null and b/web/favicon/web-app-manifest-512x512.png differ diff --git a/web/img/favicon.ico b/web/img/favicon.ico new file mode 100644 index 0000000..f9c244c Binary files /dev/null and b/web/img/favicon.ico differ diff --git a/web/img/forkme_right_red_aa0000.png b/web/img/forkme_right_red_aa0000.png new file mode 100644 index 0000000..1e19c21 Binary files /dev/null and b/web/img/forkme_right_red_aa0000.png differ diff --git a/web/img/minty_square.png b/web/img/minty_square.png new file mode 100644 index 0000000..250db3a Binary files /dev/null and b/web/img/minty_square.png differ diff --git a/web/img/mintyphp_logo.png b/web/img/mintyphp_logo.png new file mode 100644 index 0000000..a5a2631 Binary files /dev/null and b/web/img/mintyphp_logo.png differ diff --git a/web/index.php b/web/index.php new file mode 100644 index 0000000..cd2113a --- /dev/null +++ b/web/index.php @@ -0,0 +1,159 @@ +parseUri($originalUri); +$pathWithoutLocale = $parsedUri['pathWithoutLocale']; +$requestedLocale = $parsedUri['locale']; +$_SERVER['MINTY_LOCALE'] = $requestedLocale; + +// Rewrite REQUEST_URI without locale segment for router +if ($parsedUri['hadLocaleInUrl']) { + $_SERVER['REQUEST_URI'] = $localeResolver->buildUriWithoutLocale( + $pathWithoutLocale, + $parsedUri['query'] + ); +} + +// Resolve effective locale from URL > Session > Cookie > Default +I18n::$locale = $localeResolver->resolveLocale( + $requestedLocale, + $_SESSION['user']['locale'] ?? null, + $_COOKIE['locale'] ?? null +); + +$isAssetRequest = AssetDetector::isAssetRequest($pathWithoutLocale); + +// Refresh tenant context if stale or missing (skip for assets) +if (!empty($_SESSION['user']['id']) && !$isAssetRequest) { + $userId = (int) ($_SESSION['user']['id'] ?? 0); + if ($userId > 0) { + $ttl = defined('TENANT_SNAPSHOT_TTL') ? (int) TENANT_SNAPSHOT_TTL : 120; + $lastRefresh = (int) ($_SESSION['tenant_snapshot_at'] ?? 0); + $needsRefresh = empty($_SESSION['current_tenant']) || empty($_SESSION['available_tenants']); + if ($ttl > 0 && (time() - $lastRefresh) >= $ttl) { + $needsRefresh = true; + } + if ($needsRefresh) { + \MintyPHP\Service\AuthService::loadTenantDataIntoSession($userId); + $_SESSION['tenant_snapshot_at'] = time(); + } + } +} + +if ($requestedLocale === '' && !$isAssetRequest) { + $method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')); + if (in_array($method, ['GET', 'HEAD'], true)) { + $target = Request::withLocale($pathWithoutLocale, I18n::$locale ?? I18n::$defaultLocale); + $target .= $parsedUri['query'] !== '' ? '?' . $parsedUri['query'] : ''; + Router::redirect($target); + } +} + +// Guard: restrict non-public pages to logged-in users +$accessControl = new AccessControl(); +$accessControl->requireAuthOrRedirect($pathWithoutLocale, !empty($_SESSION['user']['id'])); + +// Analyze the PHP code +if (Debugger::$enabled) { + Analyzer::execute(); +} + +// Load the action into body +ob_start(); +if (Router::getTemplateAction()) { + require Router::getTemplateAction(); +} +if (ob_get_contents()) { + ob_end_flush(); + trigger_error('MintyPHP template action"' . Router::getTemplateAction() . '" should not send output. Error raised ', E_USER_WARNING); +} else { + ob_end_clean(); +} + +ob_start(); +if (Router::getAction()) { + extract(Router::getParameters()); + require Router::getAction(); +} +if (ob_get_contents()) { + ob_end_flush(); + trigger_error('MintyPHP action "' . Router::getAction() . '" should not send output. Error raised ', E_USER_WARNING); +} else { + ob_end_clean(); +} + +// End the session +Session::end(); + +// Close the database connection +DB::close(); + +if (Router::getTemplateView()) { + Buffer::start('html'); + if (Router::getView()) { + require Router::getView(); + } + + // Show developer toolbar + if (Debugger::$enabled) { + Debugger::toolbar(); + } + + Buffer::end('html'); + // Load body into template + require Router::getTemplateView(); +} else { // Handle the 'none' template case + if (Router::getView()) { + require Router::getView(); + } +} diff --git a/web/js/app-init.js b/web/js/app-init.js new file mode 100644 index 0000000..902fc25 --- /dev/null +++ b/web/js/app-init.js @@ -0,0 +1,21 @@ +import './components/app-flash-auto-dismiss.js'; +import './components/app-fslightbox-refresh.js'; +import './components/app-password-hints.js'; +import './components/app-copy-badge.js'; +import './components/app-multiselect-init.js'; +import './components/app-nav-history.js'; +import './components/app-toggle-sidebar.js'; +import './components/app-toggle-aside-panels.js'; +import './components/app-toggle-details-aside.js'; +import './components/app-global-search.js'; +import './components/app-theme-toggle.js'; +import './components/app-tenant-switcher.js'; +import './components/app-tabs.js'; +import './components/app-color-default-toggle.js'; + +// Ensure fslightbox is refreshed once the vendor script is loaded. +if (typeof window.refreshFsLightbox === 'function') { + window.refreshFsLightbox(); +} else { + window.__fsLightboxRefreshPending = true; +} diff --git a/web/js/app-login-init.js b/web/js/app-login-init.js new file mode 100644 index 0000000..e483ae8 --- /dev/null +++ b/web/js/app-login-init.js @@ -0,0 +1,2 @@ +import './components/app-flash-auto-dismiss.js'; +import './components/app-password-hints.js'; diff --git a/web/js/components/app-async-flash.js b/web/js/components/app-async-flash.js new file mode 100644 index 0000000..71c5375 --- /dev/null +++ b/web/js/components/app-async-flash.js @@ -0,0 +1,89 @@ +/** + * Async Flash Messages & Loading State + * Renders flash messages into #async-messages for JS-triggered notifications + * Provides loading cursor state management + */ + +/** + * Set global loading state (cursor + pointer-events) + * @param {boolean} loading - Whether to show loading state + */ +export function setLoading(loading) { + if (loading) { + document.body.classList.add('is-loading'); + } else { + document.body.classList.remove('is-loading'); + } +} + +/** + * Execute an async function with loading state + * @param {Function} fn - Async function to execute + * @returns {Promise} - Result of the function + */ +export async function withLoading(fn) { + setLoading(true); + try { + return await fn(); + } finally { + setLoading(false); + } +} + +const defaultTimeouts = { + success: 10000, + info: 10000, + warning: 10000, + error: 0 +}; + +/** + * Show an async flash message + * @param {string} type - Message type: 'success', 'info', 'warning', 'error' + * @param {string} message - The message text + * @param {number} [timeout] - Auto-dismiss timeout in ms (0 = no auto-dismiss) + */ +export function showAsyncFlash(type, message, timeout) { + const container = document.getElementById('async-messages'); + if (!container) { + console.warn('async-messages container not found'); + return null; + } + + const effectiveTimeout = timeout ?? defaultTimeouts[type] ?? 5000; + + const notice = document.createElement('div'); + notice.className = 'notice'; + notice.setAttribute('data-variant', type); + + const messageSpan = document.createElement('span'); + messageSpan.textContent = message; + notice.appendChild(messageSpan); + + const closeButton = document.createElement('button'); + closeButton.type = 'button'; + closeButton.className = 'contrast outline small'; + closeButton.innerHTML = ''; + closeButton.addEventListener('click', () => notice.remove()); + notice.appendChild(closeButton); + + container.appendChild(notice); + + if (effectiveTimeout > 0) { + notice.style.setProperty('--flash-timeout', `${effectiveTimeout}ms`); + notice.classList.add('flash-timed'); + setTimeout(() => notice.remove(), effectiveTimeout); + } + + return notice; +} + +/** + * Clear all async flash messages + */ +export function clearAsyncFlash() { + const container = document.getElementById('async-messages'); + if (container) { + container.innerHTML = ''; + } +} diff --git a/web/js/components/app-bulk-selection.js b/web/js/components/app-bulk-selection.js new file mode 100644 index 0000000..13bcc59 --- /dev/null +++ b/web/js/components/app-bulk-selection.js @@ -0,0 +1,26 @@ +export function bindBulkVisibility({ + containerSelector, + buttonSelector, + selectedRowSelector = '.gridjs-tr-selected' +} = {}) { + const container = document.querySelector(containerSelector); + const buttons = Array.from(document.querySelectorAll(buttonSelector)); + if (!container || !buttons.length) return { update: () => {} }; + + const update = () => { + const selected = container.querySelectorAll(selectedRowSelector).length > 0; + buttons.forEach((button) => { + button.hidden = !selected; + }); + }; + + const observer = new MutationObserver(() => update()); + observer.observe(container, { + subtree: true, + attributes: true, + attributeFilter: ['class'] + }); + + update(); + return { update, destroy: () => observer.disconnect() }; +} diff --git a/web/js/components/app-color-default-toggle.js b/web/js/components/app-color-default-toggle.js new file mode 100644 index 0000000..01e6a86 --- /dev/null +++ b/web/js/components/app-color-default-toggle.js @@ -0,0 +1,43 @@ +const toggles = document.querySelectorAll('[data-color-default-toggle]'); + +const syncToggle = (toggle) => { + const targetSelector = toggle.dataset.colorTarget || ''; + if (!targetSelector) { + return; + } + const target = document.querySelector(targetSelector); + if (!target) { + return; + } + const defaultValue = toggle.dataset.colorDefault || '#2fa4a4'; + if (toggle.checked) { + if (!target.dataset.customValue) { + target.dataset.customValue = target.value; + } + target.value = defaultValue; + target.setAttribute('disabled', 'disabled'); + } else { + target.removeAttribute('disabled'); + if (target.dataset.customValue) { + target.value = target.dataset.customValue; + } + } +}; + +toggles.forEach((toggle) => { + const targetSelector = toggle.dataset.colorTarget || ''; + const target = targetSelector ? document.querySelector(targetSelector) : null; + + toggle.addEventListener('change', () => syncToggle(toggle)); + + if (target) { + target.addEventListener('input', () => { + if (toggle.checked) { + toggle.checked = false; + target.removeAttribute('disabled'); + } + }); + } + + syncToggle(toggle); +}); diff --git a/web/js/components/app-copy-badge.js b/web/js/components/app-copy-badge.js new file mode 100644 index 0000000..b6ceab1 --- /dev/null +++ b/web/js/components/app-copy-badge.js @@ -0,0 +1,66 @@ +const COPY_SELECTOR = '.badge[data-copy="true"]'; +const COPIED_CLASS = 'is-copied'; + +const getCopyValue = (badge) => { + const explicit = badge.getAttribute('data-copy-value'); + if (explicit !== null && explicit !== '') { + return explicit; + } + return (badge.textContent || '').trim(); +}; + +const copyText = async (value) => { + if (!value) return false; + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(value); + return true; + } + const textarea = document.createElement('textarea'); + textarea.value = value; + textarea.setAttribute('readonly', 'true'); + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + document.body.appendChild(textarea); + textarea.select(); + const ok = document.execCommand('copy'); + document.body.removeChild(textarea); + return ok; +}; + +const enhanceBadge = (badge) => { + if (badge.querySelector('.badge-copy-icon')) return; + const icon = document.createElement('span'); + icon.className = 'badge-copy-icon'; + icon.setAttribute('aria-hidden', 'true'); + icon.innerHTML = ''; + badge.appendChild(icon); + badge.setAttribute('role', 'button'); + badge.setAttribute('tabindex', '0'); + if (!badge.hasAttribute('aria-label')) { + badge.setAttribute('aria-label', 'Copy to clipboard'); + } + const handler = async (event) => { + event.preventDefault(); + const value = getCopyValue(badge); + const ok = await copyText(value); + if (!ok) return; + badge.classList.add(COPIED_CLASS); + window.setTimeout(() => badge.classList.remove(COPIED_CLASS), 1200); + }; + badge.addEventListener('click', handler); + badge.addEventListener('keydown', (event) => { + if (event.key === 'Enter' || event.key === ' ') { + handler(event); + } + }); +}; + +const initBadgeCopy = () => { + document.querySelectorAll(COPY_SELECTOR).forEach(enhanceBadge); +}; + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initBadgeCopy); +} else { + initBadgeCopy(); +} diff --git a/web/js/components/app-flash-auto-dismiss.js b/web/js/components/app-flash-auto-dismiss.js new file mode 100644 index 0000000..0a64ddc --- /dev/null +++ b/web/js/components/app-flash-auto-dismiss.js @@ -0,0 +1,53 @@ +export function initFlashAutoDismiss(options = {}) { + const { + selector = '.flash-stack .notice[data-flash-timeout]', + defaultTimeout = 0 + } = options; + + const notices = document.querySelectorAll(selector); + if (!notices.length) { + return; + } + + const postForm = async (form) => { + const action = form.getAttribute('action'); + if (!action) return null; + const body = new URLSearchParams(new FormData(form)); + return fetch(action, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'fetch' + }, + body + }); + }; + + notices.forEach((notice) => { + const timeout = Number.parseInt( + notice.dataset.flashTimeout || `${defaultTimeout}`, + 10 + ); + if (!timeout || timeout <= 0) { + return; + } + notice.style.setProperty('--flash-timeout', `${timeout}ms`); + notice.classList.add('flash-timed'); + window.setTimeout(async () => { + const form = notice.querySelector('form'); + if (form) { + const response = await postForm(form); + if (!response || !response.ok) { + return; + } + } + notice.remove(); + }, timeout); + }); +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => initFlashAutoDismiss()); +} else { + initFlashAutoDismiss(); +} diff --git a/web/js/components/app-fslightbox-refresh.js b/web/js/components/app-fslightbox-refresh.js new file mode 100644 index 0000000..f4e415a --- /dev/null +++ b/web/js/components/app-fslightbox-refresh.js @@ -0,0 +1,48 @@ +const refreshFsLightbox = () => { + if (typeof window.refreshFsLightbox === 'function') { + window.refreshFsLightbox(); + return true; + } + return false; +}; + +let refreshTimer = null; +const scheduleRefresh = () => { + if (refreshTimer) return; + refreshTimer = window.setTimeout(() => { + refreshTimer = null; + refreshFsLightbox(); + }, 30); +}; + +const initObserver = () => { + if (!document.body || typeof MutationObserver === 'undefined') { + return; + } + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (!(node instanceof HTMLElement)) continue; + if (node.matches?.('[data-fslightbox]') || node.querySelector?.('[data-fslightbox]')) { + scheduleRefresh(); + return; + } + } + } + }); + observer.observe(document.body, { childList: true, subtree: true }); +}; + +if (!refreshFsLightbox()) { + scheduleRefresh(); +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + refreshFsLightbox(); + initObserver(); + }); +} else { + refreshFsLightbox(); + initObserver(); +} diff --git a/web/js/components/app-global-search.js b/web/js/components/app-global-search.js new file mode 100644 index 0000000..ba34865 --- /dev/null +++ b/web/js/components/app-global-search.js @@ -0,0 +1,168 @@ +const input = document.querySelector('#side-search'); +const resultsEl = document.querySelector('[data-global-search-results]'); + +if (!input || !resultsEl) { + if (!input) console.warn('[GlobalSearch] #side-search input not found'); + if (!resultsEl) console.warn('[GlobalSearch] [data-global-search-results] container not found'); +} else { + const panel = document.querySelector('#aside-panel-search'); + const toggleButton = document.querySelector('[data-search-details-toggle]'); + const toggleIcon = document.querySelector('[data-search-details-icon]'); + const appBase = window.APP_BASE || document.baseURI; + const endpoint = new URL('admin/search/data', appBase); + const resultsPage = new URL('search', appBase); + const minLength = 2; + let pending = null; + + const itemsByKey = new Map(); + resultsEl.querySelectorAll('[data-search-key]').forEach((item) => { + const key = item.getAttribute('data-search-key'); + if (key) itemsByKey.set(key, item); + }); + if (itemsByKey.size === 0) { + console.warn('[GlobalSearch] No [data-search-key] elements found in results container'); + } + + const setLoadingState = (value) => { + resultsEl.querySelectorAll('[data-search-count]').forEach((el) => { + el.textContent = value; + }); + }; + + const render = (items, query) => { + items.forEach((item) => { + const li = itemsByKey.get(item.key); + if (!li) { + console.warn('[GlobalSearch] No element found for key:', item.key); + return; + } + const countEl = li.querySelector('[data-search-count]'); + const link = li.querySelector('a'); + const previewEl = li.querySelector('[data-search-preview]'); + const base = item.key === 'pages' && item.url ? item.url : (li.getAttribute('data-search-base') || item.url); + const url = new URL(base, appBase); + if (item.key !== 'pages') { + url.searchParams.set('search', query); + } + if (countEl) countEl.textContent = item.count.toString(); + if (link) { + link.href = item.count > 0 ? url.toString() : base; + link.setAttribute('aria-disabled', item.count > 0 ? 'false' : 'true'); + } + if (previewEl) { + previewEl.innerHTML = ''; + if (Array.isArray(item.items) && item.items.length > 0) { + item.items.forEach((previewItem, idx) => { + if (!previewItem || !previewItem.url) { + console.warn('[GlobalSearch] Preview item missing url at index', idx, 'for key:', item.key, previewItem); + return; + } + const previewLi = document.createElement('li'); + const previewLink = document.createElement('a'); + previewLink.href = previewItem.url; + previewLink.textContent = previewItem.label || ''; + previewLi.appendChild(previewLink); + previewEl.appendChild(previewLi); + }); + } + } + if (item.count > 0) { + li.hidden = false; + } else { + li.hidden = true; + } + }); + }; + + const clear = () => { + setLoadingState('0'); + resultsEl.querySelectorAll('a').forEach((link) => { + link.setAttribute('aria-disabled', 'true'); + const base = link.closest('[data-search-base]')?.getAttribute('data-search-base'); + link.setAttribute('href', base || '#'); + }); + resultsEl.querySelectorAll('[data-search-preview]').forEach((list) => { + list.innerHTML = ''; + }); + resultsEl.querySelectorAll('[data-search-key]').forEach((li) => { + li.hidden = false; + }); + }; + + const fetchCounts = async (value) => { + const url = new URL(endpoint.toString()); + url.searchParams.set('q', value); + setLoadingState('…'); + const response = await fetch(url.toString(), { headers: { 'Accept': 'application/json' } }); + if (!response.ok) { + console.warn('[GlobalSearch] Fetch failed with status:', response.status, response.statusText); + clear(); + return; + } + const data = await response.json(); + if (Array.isArray(data)) { + render(data, value); + } else { + console.warn('[GlobalSearch] Invalid response format, expected array but got:', typeof data, data); + clear(); + } + }; + + const onInput = () => { + const value = input.value.trim(); + if (value.length < minLength) { + clear(); + return; + } + if (pending) window.clearTimeout(pending); + pending = window.setTimeout(() => { + fetchCounts(value).catch((err) => { + console.warn('[GlobalSearch] Fetch error:', err); + clear(); + }); + }, 300); + }; + + input.addEventListener('input', onInput); + const submitSearch = () => { + const value = input.value.trim(); + if (value.length < minLength) return; + resultsPage.searchParams.set('search', value); + window.location.href = resultsPage.toString(); + }; + + input.addEventListener('keydown', (event) => { + if (event.key !== 'Enter') return; + event.preventDefault(); + submitSearch(); + }); + + const form = input.closest('form'); + if (form) { + form.addEventListener('submit', (event) => { + event.preventDefault(); + submitSearch(); + }); + } + + if (toggleButton && panel) { + toggleButton.addEventListener('click', () => { + panel.classList.toggle('search-details-hidden'); + if (toggleIcon) { + toggleIcon.classList.toggle('bi-dash-square'); + toggleIcon.classList.toggle('bi-plus-square'); + } + }); + } else if (toggleButton || panel) { + if (!toggleButton) console.warn('[GlobalSearch] [data-search-details-toggle] button not found'); + if (!panel) console.warn('[GlobalSearch] #aside-panel-search panel not found'); + } + + // Seed from current URL search param if present + const currentUrl = new URL(window.location.href); + const preset = currentUrl.searchParams.get('search'); + if (preset && !input.value) { + input.value = preset; + onInput(); + } +} diff --git a/web/js/components/app-multiselect-init.js b/web/js/components/app-multiselect-init.js new file mode 100644 index 0000000..d6a8b8e --- /dev/null +++ b/web/js/components/app-multiselect-init.js @@ -0,0 +1,109 @@ +let multiSelectLoader = null; + +const resolveScriptUrl = (script) => { + if (script) return script; + const assetBase = window.APP_ASSET_BASE || document.baseURI; + return new URL('vendor/multi-select/MultiSelect.js', assetBase).toString(); +}; + +const ensureMultiSelect = async (script) => { + if (typeof window.MultiSelect !== 'undefined') { + return window.MultiSelect; + } + if (!multiSelectLoader) { + multiSelectLoader = new Promise((resolve, reject) => { + const tag = document.createElement('script'); + tag.src = resolveScriptUrl(script); + tag.async = true; + tag.onload = () => resolve(window.MultiSelect); + tag.onerror = (event) => reject(event); + document.head.appendChild(tag); + }); + } + return multiSelectLoader; +}; + +export const initMultiSelect = async (target = '[data-multi-select]') => { + const elements = typeof target === 'string' ? document.querySelectorAll(target) : [target]; + if (!elements.length) return []; + + const MultiSelect = await ensureMultiSelect(); + const instances = []; + elements.forEach((element) => { + if (!element || element.dataset.multiSelectInit === '1') return; + const syncTarget = element.dataset.syncTarget; + const syncInput = syncTarget ? document.querySelector(syncTarget) : null; + const options = {}; + const placeholder = element.dataset.placeholder; + if (placeholder) options.placeholder = placeholder; + if (element.dataset.search !== undefined) { + options.search = element.dataset.search === 'true'; + } + if (element.dataset.selectAll !== undefined) { + options.selectAll = element.dataset.selectAll === 'true'; + } + if (element.dataset.disabled !== undefined) { + options.disabled = element.dataset.disabled === 'true'; + } + if (element.disabled) { + options.disabled = true; + } + if (element.dataset.listAll !== undefined) { + options.listAll = element.dataset.listAll === 'true'; + } + if (element.dataset.selectedLabel) { + options.selectedLabel = element.dataset.selectedLabel; + } + if (element.dataset.searchPlaceholder) { + options.searchPlaceholder = element.dataset.searchPlaceholder; + } + if (element.dataset.selectAllLabel) { + options.selectAllLabel = element.dataset.selectAllLabel; + } + let isSyncing = false; + let instance; + try { + instance = new MultiSelect(element, options); + } catch (error) { + console.error('[multi-select] init failed', element, error); + return; + } + if (element.dataset.disabled === 'true' || element.disabled) { + instance.disable(); + } + element.dataset.multiSelectInit = '1'; + instances.push(instance); + if (syncInput) { + syncInput._multiSelectInstance = instance; + const syncValues = () => { + if (!syncInput || isSyncing) return; + isSyncing = true; + const values = instance.selectedValues || []; + syncInput.value = Array.isArray(values) ? values.join(',') : ''; + syncInput.dispatchEvent(new Event('change', { bubbles: true })); + isSyncing = false; + }; + instance.element.addEventListener('multi-select:change', syncValues); + const values = instance.selectedValues || []; + syncInput.value = Array.isArray(values) ? values.join(',') : ''; + syncInput.addEventListener('change', () => { + if (isSyncing) return; + const nextValues = syncInput.value + ? syncInput.value.split(',').map((item) => item.trim()).filter(Boolean) + : []; + instance.setValues(nextValues); + }); + } + }); + return instances; +}; + +const autoInit = () => { + initMultiSelect().catch(() => {}); +}; + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', autoInit); +} else { + autoInit(); +} diff --git a/web/js/components/app-nav-history.js b/web/js/components/app-nav-history.js new file mode 100644 index 0000000..0fbd1ad --- /dev/null +++ b/web/js/components/app-nav-history.js @@ -0,0 +1,48 @@ +const updateHistoryButtons = () => { + const back = document.querySelector('#global-back'); + const forward = document.querySelector('#global-forward'); + if (!back && !forward) return; + + const hasHistory = window.history.length > 1; + + if (back) { + back.setAttribute('aria-disabled', hasHistory ? 'false' : 'true'); + back.classList.toggle('is-disabled', !hasHistory); + } + if (forward) { + forward.setAttribute('aria-disabled', hasHistory ? 'false' : 'true'); + forward.classList.toggle('is-disabled', !hasHistory); + } +}; + +const initHistoryButtons = () => { + const back = document.querySelector('#global-back'); + const forward = document.querySelector('#global-forward'); + + if (back) { + back.addEventListener('click', (event) => { + if (window.history.length > 1) { + event.preventDefault(); + window.history.back(); + } + }); + } + + if (forward) { + forward.addEventListener('click', (event) => { + if (window.history.length > 1) { + event.preventDefault(); + window.history.forward(); + } + }); + } + + updateHistoryButtons(); + window.addEventListener('popstate', updateHistoryButtons); +}; + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initHistoryButtons); +} else { + initHistoryButtons(); +} diff --git a/web/js/components/app-page-editor.js b/web/js/components/app-page-editor.js new file mode 100644 index 0000000..c720c4c --- /dev/null +++ b/web/js/components/app-page-editor.js @@ -0,0 +1,185 @@ +import EditorJS from '../../vendor/editorjs/editorjs.mjs'; + +const form = document.querySelector('[data-page-editor]'); +if (form) { + const holder = form.querySelector('[data-editor-holder]'); + if (!holder) { + // No holder available, skip initialization. + } else { + const contentField = form.querySelector('#page-content'); + const toggleButton = document.querySelector('[data-editor-toggle]'); + const saveButton = document.querySelector('[data-editor-save]'); + const canEdit = form.dataset.canEdit === '1'; + + const parseData = (value) => { + if (!value) { + return { blocks: [] }; + } + try { + const parsed = JSON.parse(value); + if (parsed && typeof parsed === 'object') { + if (!Array.isArray(parsed.blocks)) { + parsed.blocks = []; + } + return parsed; + } + } catch (error) { + // ignore invalid JSON + } + return { blocks: [] }; + }; + + const setToggleLabel = (mode) => { + if (!toggleButton) { + return; + } + const viewLabel = toggleButton.dataset.viewLabel || ''; + const editLabel = toggleButton.dataset.editLabel || ''; + const text = mode === 'edit' ? viewLabel : editLabel; + const icon = toggleButton.querySelector('[data-editor-icon]'); + + if (icon) { + icon.className = mode === 'edit' ? 'bi bi-eye-fill' : 'bi bi-pencil-square'; + } + toggleButton.setAttribute('aria-label', text); + toggleButton.dataset.tooltip = text; + }; + + let mode = form.dataset.editMode === 'edit' && canEdit ? 'edit' : 'view'; + form.dataset.editMode = mode; + setToggleLabel(mode); + + const HeaderTool = window.Header || window.EditorJSHeader || null; + const ListTool = window.EditorjsList || window.List || null; + const ChecklistTool = window.Checklist || window.EditorJSChecklist || null; + const TableTool = window.Table || window.EditorJSTable || null; + const ColumnsTool = window.editorjsColumns || window.Columns || null; + const MarkerTool = window.Marker || null; + + const tools = {}; + if (HeaderTool) { + tools.header = { + class: HeaderTool, + inlineToolbar: true, + config: { + placeholder: form.dataset.headerPlaceholder || 'Heading', + levels: [1, 2, 3, 4], + defaultLevel: 1, + }, + }; + } + if (ListTool) { + tools.list = { + class: ListTool, + inlineToolbar: true, + config: { + defaultStyle: 'unordered', + }, + }; + } + if (ChecklistTool) { + tools.checklist = { + class: ChecklistTool, + inlineToolbar: true, + }; + } + if (TableTool) { + tools.table = { + class: TableTool, + inlineToolbar: true, + config: { + rows: 2, + cols: 2, + }, + }; + } + if (ColumnsTool) { + const innerTools = { ...tools }; + tools.columns = { + class: ColumnsTool, + config: { + EditorJsLibrary: EditorJS, + tools: innerTools, + }, + }; + } + if (MarkerTool) { + tools.marker = { + class: MarkerTool, + shortcut: 'CMD+SHIFT+M', + }; + } + + const inlineTools = ['bold', 'italic', 'link']; + if (MarkerTool) { + inlineTools.unshift('marker'); + } + + const editor = new EditorJS({ + holder, + data: parseData(contentField ? contentField.value : ''), + readOnly: mode !== 'edit', + autofocus: mode === 'edit', + placeholder: form.dataset.placeholder || undefined, + tools, + inlineToolbar: inlineTools, + }); + + const toggleMode = async (nextMode) => { + if (!canEdit || mode === nextMode) { + return; + } + mode = nextMode; + form.dataset.editMode = mode; + setToggleLabel(mode); + if (editor.readOnly && editor.readOnly.toggle) { + await editor.readOnly.toggle(mode !== 'edit'); + } + if (saveButton) { + saveButton.disabled = mode !== 'edit'; + } + }; + + if (toggleButton && canEdit) { + toggleButton.addEventListener('click', () => { + toggleMode(mode === 'edit' ? 'view' : 'edit'); + }); + } + + if (form && contentField && canEdit) { + form.addEventListener('submit', async (event) => { + if (form.dataset.submitting === '1') { + return; + } + event.preventDefault(); + const output = await editor.save(); + contentField.value = JSON.stringify(output); + form.dataset.submitting = '1'; + + try { + const formData = new FormData(form); + const response = await fetch(form.action || window.location.pathname, { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + Accept: 'application/json', + }, + body: formData, + }); + if (response.redirected) { + window.location.href = response.url; + return; + } + const data = await response.json().catch(() => null); + if (data && data.redirect) { + window.location.href = data.redirect; + return; + } + window.location.reload(); + } catch (error) { + form.submit(); + } + }); + } + } +} diff --git a/web/js/components/app-password-hints.js b/web/js/components/app-password-hints.js new file mode 100644 index 0000000..88afa2d --- /dev/null +++ b/web/js/components/app-password-hints.js @@ -0,0 +1,76 @@ +const rules = { + min: (value, min) => value.length >= min, + upper: (value) => /[A-Z]/.test(value), + lower: (value) => /[a-z]/.test(value), + number: (value) => /\d/.test(value), + symbol: (value) => /[^a-zA-Z0-9]/.test(value), + email: (value, _min, email) => { + if (!email) return true; + return !value.toLowerCase().includes(email.toLowerCase()); + }, + match: (_value, _min, _email, confirm) => { + if (!confirm) return false; + return true; + } +}; + +export function initPasswordHints() { + const containers = document.querySelectorAll('[data-password-hints]'); + if (!containers.length) return; + + containers.forEach((container) => { + const passwordSelector = container.dataset.passwordInput; + const confirmSelector = container.dataset.confirmInput; + const emailSelector = container.dataset.emailInput; + const passwordInput = passwordSelector ? document.querySelector(passwordSelector) : null; + const confirmInput = confirmSelector ? document.querySelector(confirmSelector) : null; + const emailInput = emailSelector ? document.querySelector(emailSelector) : null; + const minLength = Number.parseInt(container.dataset.minLength || '12', 10); + const items = container.querySelectorAll('[data-rule]'); + + const setState = (item, state) => { + item.classList.remove('is-valid', 'is-invalid'); + if (state === 'valid') item.classList.add('is-valid'); + if (state === 'invalid') item.classList.add('is-invalid'); + }; + + const evaluate = () => { + const password = passwordInput?.value ?? ''; + const confirm = confirmInput?.value ?? ''; + const email = emailInput?.value ?? ''; + const hasValue = password.length > 0 || confirm.length > 0; + + items.forEach((item) => { + const rule = item.dataset.rule; + if (!rule) return; + if (!hasValue) { + setState(item, 'neutral'); + return; + } + if (rule === 'match') { + if (password === '' || confirm === '') { + setState(item, 'neutral'); + return; + } + setState(item, password === confirm ? 'valid' : 'invalid'); + return; + } + const check = rules[rule]; + if (!check) return; + const ok = check(password, minLength, email, confirm); + setState(item, ok ? 'valid' : 'invalid'); + }); + }; + + if (passwordInput) passwordInput.addEventListener('input', evaluate); + if (confirmInput) confirmInput.addEventListener('input', evaluate); + if (emailInput) emailInput.addEventListener('input', evaluate); + evaluate(); + }); +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initPasswordHints); +} else { + initPasswordHints(); +} diff --git a/web/js/components/app-tabs.js b/web/js/components/app-tabs.js new file mode 100644 index 0000000..f1b12cb --- /dev/null +++ b/web/js/components/app-tabs.js @@ -0,0 +1,118 @@ +/** + * Initializes tab navigation + * Usage: + * - Tab buttons: + * - Tab panels:
    ...
    + * - Container:
    ...
    + * - URL param: ?tab=system + */ +export function initTabs(root = document) { + const containers = root.querySelectorAll('[data-tabs]'); + + containers.forEach((container) => { + if (container.dataset.tabsBound === '1') return; + container.dataset.tabsBound = '1'; + + const tabs = Array.from(container.querySelectorAll('[data-tab]')); + const panels = Array.from(container.querySelectorAll('[data-tab-panel]')); + + if (!tabs.length || !panels.length) return; + + const storageKey = container.dataset.tabsStorageKey || (container.id ? `tabs:${container.id}` : null); + const urlParamName = container.dataset.tabsParam || 'tab'; + + // Helper to get URL parameter + const getUrlParam = (name) => { + const params = new URLSearchParams(window.location.search); + return params.get(name); + }; + + // Helper to set URL parameter + const setUrlParam = (name, value) => { + const url = new URL(window.location); + url.searchParams.set(name, value); + window.history.replaceState({}, '', url); + }; + + // Get initial active tab from URL param, localStorage, or data-tab-default + let activeTab = null; + const urlTab = getUrlParam(urlParamName); + if (urlTab && panels.some(p => p.dataset.tabPanel === urlTab)) { + activeTab = urlTab; + } else if (storageKey) { + try { + const stored = localStorage.getItem(storageKey); + if (stored && panels.some(p => p.dataset.tabPanel === stored)) { + activeTab = stored; + } + } catch (e) { + // localStorage not available + } + } + if (!activeTab) { + const defaultTab = container.querySelector('[data-tab-default]'); + activeTab = defaultTab?.dataset.tab || tabs[0]?.dataset.tab; + } + + const activateTab = (tabName) => { + tabs.forEach(tab => { + const isActive = tab.dataset.tab === tabName; + tab.classList.toggle('is-active', isActive); + tab.setAttribute('aria-selected', isActive ? 'true' : 'false'); + }); + + panels.forEach(panel => { + const isVisible = panel.dataset.tabPanel === tabName; + panel.hidden = !isVisible; + panel.setAttribute('aria-hidden', isVisible ? 'false' : 'true'); + }); + + // Update URL parameter + setUrlParam(urlParamName, tabName); + + // Save to localStorage + if (storageKey) { + try { + localStorage.setItem(storageKey, tabName); + } catch (e) { + // localStorage not available + } + } + }; + + // Initialize tabs + tabs.forEach(tab => { + tab.setAttribute('role', 'tab'); + tab.addEventListener('click', (e) => { + e.preventDefault(); + activateTab(tab.dataset.tab); + }); + }); + + panels.forEach(panel => { + panel.setAttribute('role', 'tabpanel'); + }); + + container.setAttribute('role', 'tablist'); + + // Activate initial tab + if (activeTab) { + activateTab(activeTab); + } + + // Listen for popstate (back/forward navigation) + window.addEventListener('popstate', () => { + const newTab = getUrlParam(urlParamName); + if (newTab && panels.some(p => p.dataset.tabPanel === newTab)) { + activateTab(newTab); + } + }); + }); +} + +// Auto-initialize on load +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => initTabs()); +} else { + initTabs(); +} diff --git a/web/js/components/app-tenant-switcher.js b/web/js/components/app-tenant-switcher.js new file mode 100644 index 0000000..765b6af --- /dev/null +++ b/web/js/components/app-tenant-switcher.js @@ -0,0 +1,71 @@ +const switchTenant = async (link) => { + const tenantId = link.dataset.switchTenant; + const csrfKey = link.dataset.csrfKey; + const csrfToken = link.dataset.csrfToken; + const errorMessage = link.dataset.errorMessage || 'Failed to switch tenant'; + + if (!tenantId || !csrfKey || !csrfToken) { + console.error('Missing tenant ID or CSRF token'); + return; + } + + // Disable link during request + link.style.pointerEvents = 'none'; + link.style.opacity = '0.5'; + + try { + const body = new URLSearchParams({ + tenant_id: tenantId, + [csrfKey]: csrfToken + }); + + const response = await fetch('admin/users/switch-tenant', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json', + 'X-Requested-With': 'fetch' + }, + body + }); + + if (response.ok) { + const data = await response.json(); + if (data.ok) { + window.location.reload(); + } else { + console.error('Tenant switch failed:', data.error); + alert(errorMessage); + link.style.pointerEvents = ''; + link.style.opacity = ''; + } + } else { + console.error('Server error:', response.status); + alert(errorMessage); + link.style.pointerEvents = ''; + link.style.opacity = ''; + } + } catch (error) { + console.error('Network error:', error); + alert(errorMessage); + link.style.pointerEvents = ''; + link.style.opacity = ''; + } +}; + +const initTenantSwitcher = () => { + const tenantLinks = document.querySelectorAll('[data-switch-tenant]'); + + tenantLinks.forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault(); + switchTenant(link); + }); + }); +}; + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initTenantSwitcher); +} else { + initTenantSwitcher(); +} diff --git a/web/js/components/app-theme-toggle.js b/web/js/components/app-theme-toggle.js new file mode 100644 index 0000000..012edfd --- /dev/null +++ b/web/js/components/app-theme-toggle.js @@ -0,0 +1,57 @@ +const setTheme = (theme) => { + const root = document.documentElement; + root.dataset.theme = theme; +}; + +const setIcon = (button, theme) => { + const icon = button.querySelector('i'); + if (!icon) return; + icon.classList.remove('bi-sun-fill', 'bi-moon-stars-fill'); + icon.classList.add(theme === 'dark' ? 'bi-moon-stars-fill' : 'bi-sun-fill'); +}; + +const updateTheme = async (button, theme) => { + const url = button.dataset.themeUrl; + const csrfKey = button.dataset.csrfKey; + const csrfToken = button.dataset.csrfToken; + if (!url || !csrfKey || !csrfToken) { + return true; + } + const body = new URLSearchParams({ theme, [csrfKey]: csrfToken }); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json', + 'X-Requested-With': 'fetch' + }, + body + }); + return response.ok; +}; + +const initThemeToggle = () => { + const button = document.querySelector('[data-theme-toggle]'); + if (!button) return; + const root = document.documentElement; + const getCurrent = () => (root.dataset.theme === 'dark' ? 'dark' : 'light'); + setIcon(button, getCurrent()); + + button.addEventListener('click', async () => { + const current = getCurrent(); + const next = current === 'dark' ? 'light' : 'dark'; + setTheme(next); + setIcon(button, next); + const ok = await updateTheme(button, next); + if (!ok) { + setTheme(current); + setIcon(button, current); + } + }); +}; + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initThemeToggle); +} else { + initThemeToggle(); +} diff --git a/web/js/components/app-toggle-aside-panels.js b/web/js/components/app-toggle-aside-panels.js new file mode 100644 index 0000000..bc17e42 --- /dev/null +++ b/web/js/components/app-toggle-aside-panels.js @@ -0,0 +1,191 @@ +export function initAsidePanels(options = {}) { + const { + barSelector = '.aside-icon-bar', + panelSelector = '.app-sidebar-panel', + titleSelector = '.app-sidebar-title', + toolsSelector = '[data-aside-tools]', + revealSidebarOnActivate = true, + persistSidebarStateOnActivate = true + } = options; + + const bar = document.querySelector(barSelector); + if (!bar) return; + + const tabs = Array.from(bar.querySelectorAll('[data-aside-target]')); + if (!tabs.length) return; + + const panels = Array.from(document.querySelectorAll(panelSelector)); + const titleEl = document.querySelector(titleSelector); + const toolsHost = document.querySelector(toolsSelector); + const sidebarController = window.AppSidebar || null; + const defaultTitle = titleEl?.dataset.asideTitleDefault || titleEl?.textContent || ''; + const storageKey = bar.dataset.asideStorage || ''; + + const getPanelKey = (panel) => panel?.dataset?.asidePanel || ''; + const getTabKey = (tab) => tab?.dataset?.asideTarget || ''; + const getPanelTools = (panel) => panel?.querySelector('[data-aside-panel-tools]') || null; + const getTabHref = (tab) => tab?.dataset?.asideHref || ''; + + const initPanelDetails = (panel) => { + if (!panel || panel.dataset.detailsBound === '1') return; + const storageKey = panel.dataset.asideDetailsStorage || ''; + if (!storageKey) return; + const detailsEls = Array.from(panel.querySelectorAll('details[data-details-key]')); + if (!detailsEls.length) return; + + const readStored = () => { + try { + const raw = window.localStorage.getItem(storageKey); + return raw ? JSON.parse(raw) : []; + } catch (error) { + return []; + } + }; + + const writeStored = (value) => { + try { + const list = Array.isArray(value) ? value : []; + window.localStorage.setItem(storageKey, JSON.stringify(list)); + } catch (error) { + // ignore storage errors + } + }; + + const applyStored = () => { + const stored = readStored(); + if (!stored.length) return; + detailsEls.forEach((details) => { + details.open = stored.includes(details.dataset.detailsKey || ''); + }); + }; + + detailsEls.forEach((details) => { + details.addEventListener('toggle', () => { + const openKeys = detailsEls + .filter((item) => item.open) + .map((item) => item.dataset.detailsKey || '') + .filter(Boolean); + writeStored(openKeys); + }); + }); + + applyStored(); + panel.dataset.detailsBound = '1'; + }; + + const readStored = () => { + if (!storageKey) return null; + try { + return window.localStorage.getItem(storageKey); + } catch (error) { + return null; + } + }; + + const writeStored = (key) => { + if (!storageKey) return; + try { + window.localStorage.setItem(storageKey, key); + } catch (error) { + // ignore storage errors + } + }; + + const syncTools = (panel) => { + if (!toolsHost) return; + toolsHost.innerHTML = ''; + const tools = getPanelTools(panel); + if (!tools) return; + toolsHost.innerHTML = tools.innerHTML; + }; + + const openSidebar = (persist) => { + if (!revealSidebarOnActivate || !sidebarController) return; + if (!sidebarController.isCollapsed()) return; + sidebarController.open({ persist }); + }; + + const setActive = (key, { fromUser = false } = {}) => { + if (!key) return; + + let activePanel = null; + panels.forEach((panel) => { + const isActive = getPanelKey(panel) === key; + if (isActive) activePanel = panel; + panel.hidden = !isActive; + panel.setAttribute('aria-hidden', isActive ? 'false' : 'true'); + }); + + tabs.forEach((tab) => { + const isActive = getTabKey(tab) === key; + tab.classList.toggle('active', isActive); + tab.setAttribute('aria-selected', isActive ? 'true' : 'false'); + tab.setAttribute('tabindex', isActive ? '0' : '-1'); + }); + + if (titleEl) { + const nextTitle = activePanel?.dataset?.asideTitle || defaultTitle; + if (nextTitle) { + titleEl.textContent = nextTitle; + } + } + if (activePanel) { + syncTools(activePanel); + initPanelDetails(activePanel); + } + writeStored(key); + if (fromUser) { + openSidebar(persistSidebarStateOnActivate); + } + }; + + const getInitialKey = () => { + const stored = readStored(); + if (stored && panels.some((panel) => getPanelKey(panel) === stored)) { + return stored; + } + const activeTab = tabs.find((tab) => tab.classList.contains('active')); + if (activeTab) return getTabKey(activeTab); + const visiblePanel = panels.find((panel) => !panel.hidden); + if (visiblePanel) return getPanelKey(visiblePanel); + return getTabKey(tabs[0]); + }; + + setActive(getInitialKey(), { fromUser: false }); + + tabs.forEach((tab) => { + tab.addEventListener('click', (event) => { + event.preventDefault(); + const key = getTabKey(tab); + const isActive = tab.classList.contains('active'); + const href = getTabHref(tab); + if (href) { + const base = document.baseURI || window.location.origin; + const targetUrl = new URL(href, base); + const currentUrl = new URL(window.location.href); + const isSame = targetUrl.pathname === currentUrl.pathname && targetUrl.search === currentUrl.search; + if (!isSame) { + setActive(key, { fromUser: true }); + window.location.href = targetUrl.toString(); + return; + } + } + if (sidebarController && sidebarController.isCollapsed()) { + sidebarController.open({ persist: persistSidebarStateOnActivate }); + setActive(key, { fromUser: true }); + return; + } + if (isActive && sidebarController) { + sidebarController.close({ persist: true }); + return; + } + setActive(key, { fromUser: true }); + }); + }); +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => initAsidePanels()); +} else { + initAsidePanels(); +} diff --git a/web/js/components/app-toggle-details-aside.js b/web/js/components/app-toggle-details-aside.js new file mode 100644 index 0000000..9f402d9 --- /dev/null +++ b/web/js/components/app-toggle-details-aside.js @@ -0,0 +1,66 @@ +export function initDetailsAsideToggle(options = {}) { + const { + buttonSelector = '#toggle-main-content-aside', + asideSelector = '#app-details-aside-section', + containerSelector = '.app-details-container', + storageKey = 'app-details-aside-collapsed' + } = options; + + const button = document.querySelector(buttonSelector); + const aside = document.querySelector(asideSelector); + if (!button || !aside) return; + + const container = aside.closest(containerSelector) || document.querySelector(containerSelector); + if (!container) return; + + if (!button.getAttribute('aria-controls')) { + button.setAttribute('aria-controls', aside.id || 'app-details-aside-section'); + } + + const readStored = () => { + try { + return window.localStorage.getItem(storageKey); + } catch (error) { + return null; + } + }; + + const writeStored = (collapsed) => { + try { + window.localStorage.setItem(storageKey, collapsed ? '1' : '0'); + } catch (error) { + // ignore storage errors + } + }; + + const applyState = (collapsed, persist = false) => { + container.classList.toggle('is-aside-collapsed', collapsed); + aside.hidden = collapsed; + button.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + if (persist) { + writeStored(collapsed); + } + }; + + const getInitialState = () => { + const stored = readStored(); + if (stored === '1' || stored === '0') { + return stored === '1'; + } + return false; + }; + + applyState(getInitialState(), false); + + button.addEventListener('click', (event) => { + event.preventDefault(); + const collapsed = !container.classList.contains('is-aside-collapsed'); + applyState(collapsed, true); + }); +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => initDetailsAsideToggle()); +} else { + initDetailsAsideToggle(); +} diff --git a/web/js/components/app-toggle-sidebar.js b/web/js/components/app-toggle-sidebar.js new file mode 100644 index 0000000..65cfeae --- /dev/null +++ b/web/js/components/app-toggle-sidebar.js @@ -0,0 +1,95 @@ +export function initSidebarToggle(options = {}) { + const { + buttonSelector = '#toggle-aside', + asideSelector = '#app-sidebar', + containerSelector = '.app-container', + storageKey = 'app-sidebar-collapsed' + } = options; + + const aside = document.querySelector(asideSelector); + if (!aside) return null; + + const buttons = Array.from(document.querySelectorAll(buttonSelector)); + const container = document.querySelector(containerSelector); + const root = document.documentElement; + + const readStored = () => { + try { + return window.localStorage.getItem(storageKey); + } catch (error) { + return null; + } + }; + + const writeStored = (collapsed) => { + try { + window.localStorage.setItem(storageKey, collapsed ? '1' : '0'); + } catch (error) { + // ignore storage errors + } + }; + + const applyState = (collapsed, { persist = false } = {}) => { + aside.classList.toggle('is-collapsed', collapsed); + if (container) { + container.classList.toggle('is-sidebar-collapsed', collapsed); + } + if (root) { + root.classList.toggle('sidebar-collapsed', collapsed); + } + buttons.forEach((button) => { + if (!button.getAttribute('aria-controls')) { + button.setAttribute('aria-controls', aside.id || 'app-sidebar'); + } + button.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + }); + if (persist) { + writeStored(collapsed); + } + }; + + const isCollapsed = () => aside.classList.contains('is-collapsed'); + + const getInitialState = () => { + const stored = readStored(); + if (stored === '1' || stored === '0') { + return stored === '1'; + } + return root?.classList.contains('sidebar-collapsed') ?? false; + }; + + const controller = { + isCollapsed, + open: ({ persist = true } = {}) => applyState(false, { persist }), + close: ({ persist = true } = {}) => applyState(true, { persist }), + toggle: ({ persist = true } = {}) => applyState(!isCollapsed(), { persist }), + apply: (collapsed, { persist = false } = {}) => applyState(!!collapsed, { persist }) + }; + + applyState(getInitialState(), { persist: false }); + + buttons.forEach((button) => { + button.addEventListener('click', (event) => { + event.preventDefault(); + controller.toggle({ persist: true }); + }); + }); + + document.addEventListener('app:sidebar', (event) => { + const detail = event.detail || {}; + const action = detail.action; + const persist = detail.persist ?? false; + if (action === 'open') controller.open({ persist }); + if (action === 'close') controller.close({ persist }); + if (action === 'toggle') controller.toggle({ persist }); + }); + + window.AppSidebar = controller; + return controller; +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => initSidebarToggle()); +} else { + initSidebarToggle(); +} diff --git a/web/js/components/app-toggle-toolbar.js b/web/js/components/app-toggle-toolbar.js new file mode 100644 index 0000000..5a3a3ea --- /dev/null +++ b/web/js/components/app-toggle-toolbar.js @@ -0,0 +1,86 @@ +export function initToolbarToggles(root = document) { + const toggles = root.querySelectorAll('[data-toolbar-toggle]'); + toggles.forEach((toggle) => { + if (toggle.dataset.toolbarBound === '1') { + return; + } + toggle.dataset.toolbarBound = '1'; + if (!toggle.hasAttribute('type')) { + toggle.setAttribute('type', 'button'); + } + + const targetSelector = toggle.dataset.toolbarTarget || toggle.getAttribute('aria-controls'); + if (!targetSelector) { + return; + } + + const targets = Array.from(root.querySelectorAll(targetSelector)).filter(Boolean); + if (!targets.length) { + return; + } + + const labelTarget = toggle.querySelector('[data-toolbar-label]'); + const showLabel = toggle.dataset.toolbarTextShow; + const hideLabel = toggle.dataset.toolbarTextHide; + const storageKey = + toggle.dataset.toolbarStorageKey || + (targets.length === 1 && targets[0].id + ? `toolbar:${targets[0].id}` + : `toolbar:${targetSelector}`); + + if (!toggle.getAttribute('aria-controls') && targets.length === 1 && targets[0].id) { + toggle.setAttribute('aria-controls', targets[0].id); + } + + targets.forEach((target) => { + if (target.dataset.toolbarInitialized === '1') { + return; + } + const initial = target.dataset.toolbarInitial; + if (initial === 'hidden') { + target.hidden = true; + } else if (initial === 'shown') { + target.hidden = false; + } + target.dataset.toolbarInitialized = '1'; + }); + + if (storageKey) { + try { + const stored = localStorage.getItem(storageKey); + if (stored === 'hidden' || stored === 'shown') { + const shouldShow = stored === 'shown'; + targets.forEach((target) => { + target.hidden = !shouldShow; + }); + } + } catch (error) { + // localStorage not available + } + } + + const getExpanded = () => targets.every((target) => !target.hidden); + const setExpanded = (expanded) => { + targets.forEach((target) => { + target.hidden = !expanded; + }); + toggle.setAttribute('aria-expanded', expanded ? 'true' : 'false'); + if (labelTarget && (showLabel || hideLabel)) { + labelTarget.textContent = expanded ? (hideLabel || labelTarget.textContent) : (showLabel || labelTarget.textContent); + } + if (storageKey) { + try { + localStorage.setItem(storageKey, expanded ? 'shown' : 'hidden'); + } catch (error) { + // localStorage not available + } + } + }; + + setExpanded(getExpanded()); + + toggle.addEventListener('click', () => { + setExpanded(!getExpanded()); + }); + }); +} diff --git a/web/js/core/app-grid-factory.js b/web/js/core/app-grid-factory.js new file mode 100644 index 0000000..252c353 --- /dev/null +++ b/web/js/core/app-grid-factory.js @@ -0,0 +1,604 @@ +export function createServerGrid(options) { + const { + container, + dataUrl, + appBase = '/', + columns, + mapData, + total = (data) => data.total, + sortColumns = [], + paginationLimit = 10, + language = {}, + search = null, + filters = [], + urlSync = true, + gridjs, + rowDblClick = null, + actions = null, + rowDataset = null, + selection = null + } = options || {}; + + const gridjsLib = gridjs || window.gridjs; + const containerEl = typeof container === 'string' ? document.querySelector(container) : container; + if (!containerEl || !gridjsLib) { + return null; + } + + const params = new URLSearchParams(window.location.search); + const resolveUrl = (path) => new URL(path, appBase).toString(); + const addParam = (url, key, value) => { + const nextUrl = new URL(url); + nextUrl.searchParams.set(key, value); + return nextUrl.toString(); + }; + + const getInput = (input) => { + if (!input) return null; + return typeof input === 'string' ? document.querySelector(input) : input; + }; + + const getInputValue = (input) => { + if (!input) return ''; + if (input.type === 'checkbox') { + return input.checked ? (input.value || '1') : ''; + } + if (input.tagName === 'SELECT' && input.multiple) { + return Array.from(input.selectedOptions || []).map((option) => option.value).filter(Boolean); + } + return (input.value ?? '').toString(); + }; + + const setInputValue = (input, value) => { + if (!input) return; + if (input.type === 'checkbox') { + input.checked = value === true || value === 'true' || value === '1'; + } else if (input.tagName === 'SELECT' && input.multiple) { + const values = Array.isArray(value) + ? value.map((item) => item.toString()) + : value + ? value.toString().split(',').map((item) => item.trim()).filter(Boolean) + : []; + Array.from(input.options || []).forEach((option) => { + option.selected = values.includes(option.value); + }); + } else { + input.value = value; + } + if (input._multiSelectInstance && typeof input._multiSelectInstance.setValues === 'function') { + const values = Array.isArray(value) + ? value.map((item) => item.toString()) + : value + ? value.toString().split(',').map((item) => item.trim()).filter(Boolean) + : []; + input._multiSelectInstance.setValues(values); + } + }; + + let searchValue = ''; + let searchParam = 'search'; + let searchInput = null; + let searchDebounce = 250; + if (search) { + searchInput = getInput(search.input); + searchParam = search.param || searchParam; + searchDebounce = search.debounce ?? searchDebounce; + const initial = params.get(searchParam) ?? search.default ?? ''; + searchValue = initial || ''; + if (searchInput) { + setInputValue(searchInput, initial); + } + } + + const filterState = filters.map((filter) => { + const input = getInput(filter.input); + const initial = params.get(filter.param); + const value = initial !== null ? initial : (filter.default ?? ''); + if (input) { + const parsed = typeof filter.parse === 'function' ? filter.parse(value) : value; + setInputValue(input, parsed); + } + return { ...filter, input, value }; + }); + + const normalizeValue = (filter, value) => { + if (typeof filter.normalize === 'function') { + return filter.normalize(value); + } + return value; + }; + + const buildParams = () => { + const query = new URLSearchParams(); + if (searchValue) { + query.set(searchParam, searchValue); + } + filterState.forEach((filter) => { + const raw = getInputValue(filter.input) || filter.value || ''; + const normalized = normalizeValue(filter, raw); + if (Array.isArray(normalized)) { + const list = normalized.map((item) => item.toString()).filter(Boolean); + if (list.length) { + query.set(filter.param, list.join(',')); + } + return; + } + if (normalized !== '' && normalized !== null && typeof normalized !== 'undefined') { + query.set(filter.param, normalized); + } + }); + return query; + }; + + const baseUrl = () => { + const url = new URL(dataUrl, appBase); + const query = buildParams(); + query.forEach((value, key) => url.searchParams.set(key, value)); + return url.toString(); + }; + + const updateUrl = () => { + if (!urlSync) return; + const url = new URL(window.location.href); + if (searchParam) { + url.searchParams.delete(searchParam); + } + filterState.forEach((filter) => url.searchParams.delete(filter.param)); + const query = buildParams(); + query.forEach((value, key) => url.searchParams.set(key, value)); + window.history.replaceState({}, '', url.toString()); + }; + + const actionConfig = actions && actions.enabled ? { + label: actions.label || 'Actions', + index: Number.isInteger(actions.index) ? actions.index : null, + items: Array.isArray(actions.items) ? actions.items : [], + getRowId: typeof actions.getRowId === 'function' ? actions.getRowId : null, + wrapperClass: actions.wrapperClass || 'grid-actions', + onAction: typeof actions.onAction === 'function' ? actions.onAction : null + } : null; + const selectionConfig = selection && selection.enabled ? { + id: selection.id || 'selectRow', + index: Number.isInteger(selection.index) ? selection.index : 0, + component: selection.component || null, + props: selection.props || null, + onChange: typeof selection.onChange === 'function' ? selection.onChange : null, + getSelectedIds: typeof selection.getSelectedIds === 'function' ? selection.getSelectedIds : null, + selectAllLabel: selection.selectAllLabel || 'Select all', + selectAllEnabled: selection.selectAllEnabled !== false + } : null; + + const insertActionColumn = (cols) => { + if (!actionConfig) return cols; + const actionColumn = { + name: actionConfig.label, + sort: false, + formatter: (_cell, row) => { + const id = actionConfig.getRowId ? actionConfig.getRowId(row) : null; + if (!id) { + return gridjsLib.html(''); + } + const buttons = actionConfig.items.map((item) => { + if (!item || !item.key) return ''; + const label = typeof item.label === 'function' ? item.label(row, id) : item.label; + if (!label) return ''; + const className = item.className ? ` ${item.className}` : ''; + const type = item.type || 'button'; + if (type === 'link') { + const href = typeof item.href === 'function' ? item.href({ row, id }) : item.href; + const url = href || '#'; + return `${label}`; + } + return ``; + }).join(''); + return gridjsLib.html(`
    ${buttons}
    `); + } + }; + + const next = [...cols]; + const index = actionConfig.index === null ? next.length : Math.min(Math.max(actionConfig.index, 0), next.length); + next.splice(index, 0, actionColumn); + return next; + }; + + const insertSelectionColumn = (cols) => { + if (!selectionConfig) return cols; + const headerLabel = selectionConfig.selectAllLabel || 'Select all'; + const header = selectionConfig.selectAllEnabled + ? gridjsLib.html(``) + : ''; + const selectionColumn = { + id: selectionConfig.id, + name: header, + sort: false, + plugin: { + id: selectionConfig.id + } + }; + + const next = [...cols]; + const index = Math.min(Math.max(selectionConfig.index ?? 0, 0), next.length); + next.splice(index, 0, selectionColumn); + return next; + }; + + const insertActionSort = (cols) => { + if (!actionConfig || !cols.length) return cols; + const next = [...cols]; + const index = actionConfig.index === null ? next.length : Math.min(Math.max(actionConfig.index, 0), next.length); + next.splice(index, 0, null); + return next; + }; + + const insertSelectionSort = (cols) => { + if (!selectionConfig || !cols.length) return cols; + const next = [...cols]; + const index = Math.min(Math.max(selectionConfig.index ?? 0, 0), next.length); + next.splice(index, 0, null); + return next; + }; + + const mapDataWithActions = (data) => { + const rows = mapData ? mapData(data) : []; + if (!actionConfig || !Array.isArray(rows)) { + return rows; + } + const index = actionConfig.index === null ? null : Math.min(Math.max(actionConfig.index, 0), Infinity); + return rows.map((row) => { + if (!Array.isArray(row)) return row; + const next = [...row]; + const insertAt = index === null ? next.length : Math.min(index, next.length); + next.splice(insertAt, 0, null); + return next; + }); + }; + + const serverConfig = { + url: baseUrl(), + then: mapDataWithActions, + total + }; + + let finalSortColumns = sortColumns; + if (selectionConfig) { + finalSortColumns = insertSelectionSort(finalSortColumns); + } + if (actionConfig) { + finalSortColumns = insertActionSort(finalSortColumns); + } + let currentSort = null; + const sortConfig = finalSortColumns.length ? { + enabled: true, + server: { + url: (prev, columns) => { + if (!columns.length) { + currentSort = null; + return prev; + } + const column = columns[0]; + const key = finalSortColumns[column.index]; + if (!key) { + currentSort = null; + return prev; + } + const direction = column.direction === 1 ? 'asc' : 'desc'; + currentSort = { order: key, dir: direction }; + return addParam(addParam(prev, 'order', key), 'dir', direction); + } + } + } : { enabled: false }; + + const paginationConfig = { + limit: paginationLimit, + server: { + url: (prev, page, limit) => { + const offset = page * limit; + return addParam(addParam(prev, 'limit', limit), 'offset', offset); + } + } + }; + + const plugins = []; + if (selectionConfig?.component) { + plugins.push({ + id: selectionConfig.id, + component: selectionConfig.component, + props: selectionConfig.props || {} + }); + } + + const grid = new gridjsLib.Grid({ + columns: insertActionColumn(insertSelectionColumn(columns)), + server: serverConfig, + search: false, + sort: sortConfig, + pagination: paginationConfig, + language, + plugins + }); + + grid.render(containerEl); + setTimeout(() => { + initSelectAll(); + }, 0); + + // Prevent empty-state flash before data arrives. + let hasLoadedOnce = false; + const markLoadedOnce = () => { + if (hasLoadedOnce) return; + hasLoadedOnce = true; + containerEl.classList.add('gridjs-has-loaded'); + }; + const setUpdating = () => containerEl.classList.add('gridjs-is-updating'); + const clearUpdating = () => containerEl.classList.remove('gridjs-is-updating'); + + const store = grid?.config?.store; + if (store?.subscribe) { + store.subscribe((state) => { + if (!state) return; + // Grid.js status: 0=Init, 1=Loading, 2=Loaded, 3=Rendered, 4=Error + if (state.status === 1) { + setUpdating(); + } else { + clearUpdating(); + } + if (state.status >= 2) { + markLoadedOnce(); + } + if (state.status >= 3) { + initSelectAll(); + } + }); + } + + const applyRowDataset = () => { + if (typeof rowDataset !== 'function') return; + const rowEls = containerEl.querySelectorAll('.gridjs-tbody .gridjs-tr'); + const dataRows = grid.config.store.getState().data?.rows || []; + rowEls.forEach((rowEl, index) => { + const rowData = dataRows[index] || null; + if (!rowData) return; + const attrs = rowDataset(rowData, index) || {}; + Object.keys(attrs).forEach((key) => { + const value = attrs[key]; + const attr = `data-${key}`; + if (value === null || typeof value === 'undefined' || value === '') { + rowEl.removeAttribute(attr); + } else { + rowEl.setAttribute(attr, String(value)); + } + }); + }); + }; + + const initSelectAll = () => { + if (!selectionConfig?.component || !selectionConfig.selectAllEnabled) return; + const header = containerEl.querySelector(`th[data-column-id="${selectionConfig.id}"] input[data-grid-select-all]`); + if (!header) return; + if (header.dataset.bound === '1') { + return; + } + header.dataset.bound = '1'; + + const getRowCheckboxes = () => Array.from( + containerEl.querySelectorAll(`td[data-column-id="${selectionConfig.id}"] input[type="checkbox"]`) + ); + + const updateHeaderState = () => { + const boxes = getRowCheckboxes(); + const total = boxes.length; + const checked = boxes.filter((box) => box.checked).length; + header.indeterminate = total > 0 && checked > 0 && checked < total; + header.checked = total > 0 && checked === total; + header.disabled = total === 0; + }; + + header.addEventListener('change', () => { + const boxes = getRowCheckboxes(); + boxes.forEach((box) => { + if (box.checked !== header.checked) { + box.click(); + } + }); + updateHeaderState(); + }); + + updateHeaderState(); + }; + + if (typeof rowDataset === 'function') { + const observer = new MutationObserver(() => applyRowDataset()); + observer.observe(containerEl, { childList: true, subtree: true }); + applyRowDataset(); + } + + const getRowDataByElement = (rowEl) => { + if (!rowEl) return null; + const rows = Array.from(rowEl.parentElement?.children || []); + const rowIndex = rows.indexOf(rowEl); + if (rowIndex < 0) return null; + const dataRows = grid.config.store.getState().data?.rows || []; + const rowData = dataRows[rowIndex] || null; + if (!rowData) return null; + return { rowData, rowIndex }; + }; + + if (rowDblClick) { + const excludeSelector = rowDblClick.exclude || '.grid-actions, button, a, input, select, textarea'; + containerEl.addEventListener('dblclick', (event) => { + if (excludeSelector && event.target.closest(excludeSelector)) { + return; + } + const rowEl = event.target.closest('.gridjs-tr'); + const rowInfo = getRowDataByElement(rowEl); + if (!rowInfo) { + return; + } + const { rowData, rowIndex } = rowInfo; + const getUrl = rowDblClick.getUrl; + if (typeof getUrl !== 'function') { + return; + } + const url = getUrl(rowData, rowIndex); + if (url) { + window.location.href = url; + } + }); + } + + if (actionConfig) { + containerEl.addEventListener('click', async (event) => { + const actionEl = event.target.closest('[data-grid-action]'); + if (!actionEl) { + return; + } + const rowEl = actionEl.closest('.gridjs-tr'); + const rowInfo = getRowDataByElement(rowEl); + if (!rowInfo) { + return; + } + const { rowData } = rowInfo; + const actionKey = actionEl.getAttribute('data-grid-action') || ''; + const actionItem = actionConfig.items.find((item) => item && item.key === actionKey); + if (!actionItem) { + return; + } + const id = actionConfig.getRowId ? actionConfig.getRowId(rowData) : null; + if (!id) { + return; + } + const confirmText = typeof actionItem.confirm === 'function' + ? actionItem.confirm({ row: rowData, id }) + : actionItem.confirm; + if (confirmText && !window.confirm(confirmText)) { + return; + } + if (actionItem.type === 'link') { + const href = typeof actionItem.href === 'function' + ? actionItem.href({ row: rowData, id }) + : actionItem.href; + if (href) { + event.preventDefault(); + window.location.href = href; + } + return; + } + if (actionConfig.onAction) { + event.preventDefault(); + await actionConfig.onAction({ action: actionKey, id, row: rowData, event, grid, container: containerEl }); + } + }); + } + + const updateGrid = () => { + updateUrl(); + grid.updateConfig({ + server: { ...serverConfig, url: baseUrl() } + }).forceRender(); + }; + + const debounce = (fn, delay = 250) => { + let timer; + return (...args) => { + clearTimeout(timer); + timer = setTimeout(() => fn(...args), delay); + }; + }; + + if (searchInput) { + searchInput.addEventListener('input', debounce((event) => { + searchValue = event.target.value.trim(); + updateGrid(); + }, searchDebounce)); + } + + filterState.forEach((filter) => { + if (!filter.input) return; + const eventName = filter.event || 'change'; + filter.input.addEventListener(eventName, () => { + filter.value = getInputValue(filter.input); + updateGrid(); + }); + }); + + const resetFilters = () => { + if (search) { + const defaultValue = search.default ?? ''; + searchValue = defaultValue; + if (searchInput) { + setInputValue(searchInput, defaultValue); + } + } + filterState.forEach((filter) => { + const defaultValue = filter.default ?? ''; + filter.value = defaultValue; + if (filter.input) { + setInputValue(filter.input, defaultValue); + } + }); + updateGrid(); + }; + + const getSortParams = () => (currentSort ? { ...currentSort } : null); + const selectionApi = selectionConfig && selectionConfig.component ? { + getSelectedIds: () => { + if (selectionConfig.getSelectedIds) { + return selectionConfig.getSelectedIds({ grid, container: containerEl }); + } + const plugin = grid?.config?.plugin?.get?.(selectionConfig.id); + const store = plugin?.props?.store; + const state = store?.state ?? store?.getState?.(); + if (!state) return []; + if (Array.isArray(state.selected)) return state.selected; + if (Array.isArray(state.ids)) return state.ids; + if (state.rows && typeof state.rows === 'object') { + return Object.keys(state.rows).filter((key) => state.rows[key]); + } + if (state instanceof Set) return Array.from(state); + if (Array.isArray(state)) return state; + if (typeof state === 'object') { + return Object.keys(state).filter((key) => state[key]); + } + return []; + }, + clear: () => { + const plugin = grid?.config?.plugin?.get?.(selectionConfig.id); + const store = plugin?.props?.store; + if (store?.dispatch) { + store.dispatch('reset'); + } else if (store?.reset) { + store.reset(); + } + } + } : null; + + if (selectionConfig?.onChange && selectionConfig.component) { + grid.on('ready', () => { + const plugin = grid?.config?.plugin?.get?.(selectionConfig.id); + const store = plugin?.props?.store; + const sync = () => { + selectionConfig.onChange(selectionApi?.getSelectedIds?.() || []); + }; + if (store?.on) store.on('updated', sync); + if (store?.subscribe) store.subscribe(sync); + sync(); + }); + } + + grid.on('ready', () => { + initSelectAll(); + setTimeout(() => { + if (typeof window.requestFsLightboxRefresh === 'function') { + window.requestFsLightboxRefresh(); + return; + } + if (typeof window.refreshFsLightbox === 'function') { + window.refreshFsLightbox(); + } else { + window.__fsLightboxRefreshPending = true; + } + }, 0); + }); + + return { grid, container: containerEl, updateGrid, updateUrl, baseUrl, resolveUrl, getSortParams, resetFilters, selection: selectionApi }; +} diff --git a/web/js/pages/app-list-utils.js b/web/js/pages/app-list-utils.js new file mode 100644 index 0000000..5433c97 --- /dev/null +++ b/web/js/pages/app-list-utils.js @@ -0,0 +1,19 @@ +export const buildUrl = (appBase, path) => new URL(path, appBase).toString(); + +export const postAction = async (url, csrf, data = {}) => { + const body = new URLSearchParams({ ...data, [csrf.key]: csrf.token }); + return fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json', + 'X-Requested-With': 'fetch' + }, + body + }); +}; + +export const badgeHtml = (gridjs, value = '') => { + const safe = value ?? ''; + return gridjs.html(`${safe}`); +}; diff --git a/web/js/pages/app-users-list.js b/web/js/pages/app-users-list.js new file mode 100644 index 0000000..eaf6823 --- /dev/null +++ b/web/js/pages/app-users-list.js @@ -0,0 +1,127 @@ +export function initUsersListPage(options = {}) { + const { + gridConfig, + appBase, + csrf, + labels = {}, + selectors = {}, + exportPath = 'admin/users/export', + listPath = 'admin/users', + buildBulkUrl, + showFlash, + withLoading + } = options; + + if (!gridConfig) return; + + const exportSelector = selectors.exportButton || '[data-users-export]'; + const resetSelector = selectors.resetButton || '[data-users-reset]'; + const bulkSelector = selectors.bulkButtons || '[data-users-bulk]'; + + const resolveBulkUrl = buildBulkUrl + ? buildBulkUrl + : (action) => new URL(`admin/users/bulk/${action}`, appBase).toString(); + + const postAction = async (url, data = {}) => { + const body = new URLSearchParams({ ...data, [csrf.key]: csrf.token }); + return fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json', + 'X-Requested-With': 'fetch' + }, + body + }); + }; + + const exportButton = document.querySelector(exportSelector); + if (exportButton) { + exportButton.addEventListener('click', () => { + const exportUrl = new URL(exportPath, appBase); + const dataUrl = new URL(gridConfig.baseUrl()); + dataUrl.searchParams.forEach((value, key) => exportUrl.searchParams.set(key, value)); + const sortParams = gridConfig.getSortParams ? gridConfig.getSortParams() : null; + if (sortParams && sortParams.order) { + exportUrl.searchParams.set('order', sortParams.order); + exportUrl.searchParams.set('dir', sortParams.dir || 'asc'); + } + window.location.href = exportUrl.toString(); + }); + } + + const resetButton = document.querySelector(resetSelector); + if (resetButton) { + resetButton.addEventListener('click', () => { + if (typeof gridConfig.resetFilters === 'function') { + gridConfig.resetFilters(); + } else { + gridConfig.updateGrid(); + } + }); + } + + const bulkButtons = Array.from(document.querySelectorAll(bulkSelector)); + if (bulkButtons.length) { + bulkButtons.forEach((button) => { + button.addEventListener('click', async () => { + const action = button.getAttribute('data-users-bulk') || ''; + const ids = gridConfig.selection?.getSelectedIds?.() || []; + const validActions = ['activate', 'deactivate', 'delete', 'send-access']; + if (!ids.length || !validActions.includes(action)) { + return; + } + const confirmTexts = { + 'activate': labels.bulkActivateConfirm, + 'deactivate': labels.bulkDeactivateConfirm, + 'delete': labels.bulkDeleteConfirm, + 'send-access': labels.bulkSendAccessConfirm + }; + const confirmText = confirmTexts[action]; + if (confirmText && !window.confirm(confirmText)) { + return; + } + + const executeBulk = async () => { + const url = resolveBulkUrl(action); + const response = await postAction(url, { uuids: ids.join(',') }); + const data = await response.json().catch(() => null); + if (response.ok && data && data.ok) { + gridConfig.selection?.clear?.(); + gridConfig.grid?.forceRender(); + + if (showFlash && labels.bulkMessages) { + const msg = labels.bulkMessages[action]; + if (msg) { + const count = data.count ?? 0; + const failed = data.failed ?? 0; + let text = msg.success.replace('%d', count); + if (failed > 0 && msg.partial) { + text = msg.partial.replace('%d', count).replace('%f', failed); + } + showFlash('success', text); + } + } + } else { + if (showFlash && labels.bulkMessages) { + const msg = labels.bulkMessages[action]; + if (msg?.error) { + showFlash('error', msg.error); + } else { + window.location.href = new URL(listPath, appBase).toString(); + } + } else { + window.location.href = new URL(listPath, appBase).toString(); + } + } + }; + + if (withLoading) { + await withLoading(executeBulk); + } else { + await executeBulk(); + } + }); + }); + } +} diff --git a/web/vendor/bootstrap-icons/0-circle-fill.svg b/web/vendor/bootstrap-icons/0-circle-fill.svg new file mode 100644 index 0000000..2f5f026 --- /dev/null +++ b/web/vendor/bootstrap-icons/0-circle-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/0-circle.svg b/web/vendor/bootstrap-icons/0-circle.svg new file mode 100644 index 0000000..5e84c8c --- /dev/null +++ b/web/vendor/bootstrap-icons/0-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/0-square-fill.svg b/web/vendor/bootstrap-icons/0-square-fill.svg new file mode 100644 index 0000000..cca049b --- /dev/null +++ b/web/vendor/bootstrap-icons/0-square-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/0-square.svg b/web/vendor/bootstrap-icons/0-square.svg new file mode 100644 index 0000000..73b7bc1 --- /dev/null +++ b/web/vendor/bootstrap-icons/0-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/1-circle-fill.svg b/web/vendor/bootstrap-icons/1-circle-fill.svg new file mode 100644 index 0000000..b10d0f3 --- /dev/null +++ b/web/vendor/bootstrap-icons/1-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/1-circle.svg b/web/vendor/bootstrap-icons/1-circle.svg new file mode 100644 index 0000000..2aa21f9 --- /dev/null +++ b/web/vendor/bootstrap-icons/1-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/1-square-fill.svg b/web/vendor/bootstrap-icons/1-square-fill.svg new file mode 100644 index 0000000..2ce792f --- /dev/null +++ b/web/vendor/bootstrap-icons/1-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/1-square.svg b/web/vendor/bootstrap-icons/1-square.svg new file mode 100644 index 0000000..773534a --- /dev/null +++ b/web/vendor/bootstrap-icons/1-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/123.svg b/web/vendor/bootstrap-icons/123.svg new file mode 100644 index 0000000..277d405 --- /dev/null +++ b/web/vendor/bootstrap-icons/123.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/2-circle-fill.svg b/web/vendor/bootstrap-icons/2-circle-fill.svg new file mode 100644 index 0000000..169df3a --- /dev/null +++ b/web/vendor/bootstrap-icons/2-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/2-circle.svg b/web/vendor/bootstrap-icons/2-circle.svg new file mode 100644 index 0000000..00260b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/2-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/2-square-fill.svg b/web/vendor/bootstrap-icons/2-square-fill.svg new file mode 100644 index 0000000..2760ca7 --- /dev/null +++ b/web/vendor/bootstrap-icons/2-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/2-square.svg b/web/vendor/bootstrap-icons/2-square.svg new file mode 100644 index 0000000..4a79ed6 --- /dev/null +++ b/web/vendor/bootstrap-icons/2-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/3-circle-fill.svg b/web/vendor/bootstrap-icons/3-circle-fill.svg new file mode 100644 index 0000000..9aedc47 --- /dev/null +++ b/web/vendor/bootstrap-icons/3-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/3-circle.svg b/web/vendor/bootstrap-icons/3-circle.svg new file mode 100644 index 0000000..c2fc517 --- /dev/null +++ b/web/vendor/bootstrap-icons/3-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/3-square-fill.svg b/web/vendor/bootstrap-icons/3-square-fill.svg new file mode 100644 index 0000000..20c71a7 --- /dev/null +++ b/web/vendor/bootstrap-icons/3-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/3-square.svg b/web/vendor/bootstrap-icons/3-square.svg new file mode 100644 index 0000000..f0907cc --- /dev/null +++ b/web/vendor/bootstrap-icons/3-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/4-circle-fill.svg b/web/vendor/bootstrap-icons/4-circle-fill.svg new file mode 100644 index 0000000..51dc8a5 --- /dev/null +++ b/web/vendor/bootstrap-icons/4-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/4-circle.svg b/web/vendor/bootstrap-icons/4-circle.svg new file mode 100644 index 0000000..e25df51 --- /dev/null +++ b/web/vendor/bootstrap-icons/4-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/4-square-fill.svg b/web/vendor/bootstrap-icons/4-square-fill.svg new file mode 100644 index 0000000..ce3aa94 --- /dev/null +++ b/web/vendor/bootstrap-icons/4-square-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/4-square.svg b/web/vendor/bootstrap-icons/4-square.svg new file mode 100644 index 0000000..769102d --- /dev/null +++ b/web/vendor/bootstrap-icons/4-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/5-circle-fill.svg b/web/vendor/bootstrap-icons/5-circle-fill.svg new file mode 100644 index 0000000..915462d --- /dev/null +++ b/web/vendor/bootstrap-icons/5-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/5-circle.svg b/web/vendor/bootstrap-icons/5-circle.svg new file mode 100644 index 0000000..5ef620d --- /dev/null +++ b/web/vendor/bootstrap-icons/5-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/5-square-fill.svg b/web/vendor/bootstrap-icons/5-square-fill.svg new file mode 100644 index 0000000..2b066bb --- /dev/null +++ b/web/vendor/bootstrap-icons/5-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/5-square.svg b/web/vendor/bootstrap-icons/5-square.svg new file mode 100644 index 0000000..ad3e281 --- /dev/null +++ b/web/vendor/bootstrap-icons/5-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/6-circle-fill.svg b/web/vendor/bootstrap-icons/6-circle-fill.svg new file mode 100644 index 0000000..21b601a --- /dev/null +++ b/web/vendor/bootstrap-icons/6-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/6-circle.svg b/web/vendor/bootstrap-icons/6-circle.svg new file mode 100644 index 0000000..fc036b0 --- /dev/null +++ b/web/vendor/bootstrap-icons/6-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/6-square-fill.svg b/web/vendor/bootstrap-icons/6-square-fill.svg new file mode 100644 index 0000000..908706d --- /dev/null +++ b/web/vendor/bootstrap-icons/6-square-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/6-square.svg b/web/vendor/bootstrap-icons/6-square.svg new file mode 100644 index 0000000..ce28209 --- /dev/null +++ b/web/vendor/bootstrap-icons/6-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/7-circle-fill.svg b/web/vendor/bootstrap-icons/7-circle-fill.svg new file mode 100644 index 0000000..0f4f6d9 --- /dev/null +++ b/web/vendor/bootstrap-icons/7-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/7-circle.svg b/web/vendor/bootstrap-icons/7-circle.svg new file mode 100644 index 0000000..796f45c --- /dev/null +++ b/web/vendor/bootstrap-icons/7-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/7-square-fill.svg b/web/vendor/bootstrap-icons/7-square-fill.svg new file mode 100644 index 0000000..604186d --- /dev/null +++ b/web/vendor/bootstrap-icons/7-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/7-square.svg b/web/vendor/bootstrap-icons/7-square.svg new file mode 100644 index 0000000..f269032 --- /dev/null +++ b/web/vendor/bootstrap-icons/7-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/8-circle-fill.svg b/web/vendor/bootstrap-icons/8-circle-fill.svg new file mode 100644 index 0000000..579846b --- /dev/null +++ b/web/vendor/bootstrap-icons/8-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/8-circle.svg b/web/vendor/bootstrap-icons/8-circle.svg new file mode 100644 index 0000000..2dbb93e --- /dev/null +++ b/web/vendor/bootstrap-icons/8-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/8-square-fill.svg b/web/vendor/bootstrap-icons/8-square-fill.svg new file mode 100644 index 0000000..f8efe93 --- /dev/null +++ b/web/vendor/bootstrap-icons/8-square-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/8-square.svg b/web/vendor/bootstrap-icons/8-square.svg new file mode 100644 index 0000000..f29d225 --- /dev/null +++ b/web/vendor/bootstrap-icons/8-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/9-circle-fill.svg b/web/vendor/bootstrap-icons/9-circle-fill.svg new file mode 100644 index 0000000..b19b3f5 --- /dev/null +++ b/web/vendor/bootstrap-icons/9-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/9-circle.svg b/web/vendor/bootstrap-icons/9-circle.svg new file mode 100644 index 0000000..ff91949 --- /dev/null +++ b/web/vendor/bootstrap-icons/9-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/9-square-fill.svg b/web/vendor/bootstrap-icons/9-square-fill.svg new file mode 100644 index 0000000..e2ca2c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/9-square-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/9-square.svg b/web/vendor/bootstrap-icons/9-square.svg new file mode 100644 index 0000000..3410f7b --- /dev/null +++ b/web/vendor/bootstrap-icons/9-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/activity.svg b/web/vendor/bootstrap-icons/activity.svg new file mode 100644 index 0000000..1ca946e --- /dev/null +++ b/web/vendor/bootstrap-icons/activity.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/airplane-engines-fill.svg b/web/vendor/bootstrap-icons/airplane-engines-fill.svg new file mode 100644 index 0000000..3d8f185 --- /dev/null +++ b/web/vendor/bootstrap-icons/airplane-engines-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/airplane-engines.svg b/web/vendor/bootstrap-icons/airplane-engines.svg new file mode 100644 index 0000000..2efc31e --- /dev/null +++ b/web/vendor/bootstrap-icons/airplane-engines.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/airplane-fill.svg b/web/vendor/bootstrap-icons/airplane-fill.svg new file mode 100644 index 0000000..b1e4fa3 --- /dev/null +++ b/web/vendor/bootstrap-icons/airplane-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/airplane.svg b/web/vendor/bootstrap-icons/airplane.svg new file mode 100644 index 0000000..5f937e1 --- /dev/null +++ b/web/vendor/bootstrap-icons/airplane.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/alarm-fill.svg b/web/vendor/bootstrap-icons/alarm-fill.svg new file mode 100644 index 0000000..a53c88f --- /dev/null +++ b/web/vendor/bootstrap-icons/alarm-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/alarm.svg b/web/vendor/bootstrap-icons/alarm.svg new file mode 100644 index 0000000..27160b3 --- /dev/null +++ b/web/vendor/bootstrap-icons/alarm.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/alexa.svg b/web/vendor/bootstrap-icons/alexa.svg new file mode 100644 index 0000000..0827c41 --- /dev/null +++ b/web/vendor/bootstrap-icons/alexa.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/align-bottom.svg b/web/vendor/bootstrap-icons/align-bottom.svg new file mode 100644 index 0000000..5c2569f --- /dev/null +++ b/web/vendor/bootstrap-icons/align-bottom.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/align-center.svg b/web/vendor/bootstrap-icons/align-center.svg new file mode 100644 index 0000000..ea4290b --- /dev/null +++ b/web/vendor/bootstrap-icons/align-center.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/align-end.svg b/web/vendor/bootstrap-icons/align-end.svg new file mode 100644 index 0000000..15429bb --- /dev/null +++ b/web/vendor/bootstrap-icons/align-end.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/align-middle.svg b/web/vendor/bootstrap-icons/align-middle.svg new file mode 100644 index 0000000..82f6801 --- /dev/null +++ b/web/vendor/bootstrap-icons/align-middle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/align-start.svg b/web/vendor/bootstrap-icons/align-start.svg new file mode 100644 index 0000000..75dca50 --- /dev/null +++ b/web/vendor/bootstrap-icons/align-start.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/align-top.svg b/web/vendor/bootstrap-icons/align-top.svg new file mode 100644 index 0000000..f354fc5 --- /dev/null +++ b/web/vendor/bootstrap-icons/align-top.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/alipay.svg b/web/vendor/bootstrap-icons/alipay.svg new file mode 100644 index 0000000..30b6fe7 --- /dev/null +++ b/web/vendor/bootstrap-icons/alipay.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/alphabet-uppercase.svg b/web/vendor/bootstrap-icons/alphabet-uppercase.svg new file mode 100644 index 0000000..d0887b5 --- /dev/null +++ b/web/vendor/bootstrap-icons/alphabet-uppercase.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/alphabet.svg b/web/vendor/bootstrap-icons/alphabet.svg new file mode 100644 index 0000000..5d097d7 --- /dev/null +++ b/web/vendor/bootstrap-icons/alphabet.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/alt.svg b/web/vendor/bootstrap-icons/alt.svg new file mode 100644 index 0000000..2141bcb --- /dev/null +++ b/web/vendor/bootstrap-icons/alt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/amazon.svg b/web/vendor/bootstrap-icons/amazon.svg new file mode 100644 index 0000000..0752545 --- /dev/null +++ b/web/vendor/bootstrap-icons/amazon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/amd.svg b/web/vendor/bootstrap-icons/amd.svg new file mode 100644 index 0000000..ef0757c --- /dev/null +++ b/web/vendor/bootstrap-icons/amd.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/android.svg b/web/vendor/bootstrap-icons/android.svg new file mode 100644 index 0000000..4c1f097 --- /dev/null +++ b/web/vendor/bootstrap-icons/android.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/android2.svg b/web/vendor/bootstrap-icons/android2.svg new file mode 100644 index 0000000..f49c14e --- /dev/null +++ b/web/vendor/bootstrap-icons/android2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/anthropic.svg b/web/vendor/bootstrap-icons/anthropic.svg new file mode 100644 index 0000000..eed7edf --- /dev/null +++ b/web/vendor/bootstrap-icons/anthropic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/app-indicator.svg b/web/vendor/bootstrap-icons/app-indicator.svg new file mode 100644 index 0000000..b28e219 --- /dev/null +++ b/web/vendor/bootstrap-icons/app-indicator.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/app.svg b/web/vendor/bootstrap-icons/app.svg new file mode 100644 index 0000000..b361308 --- /dev/null +++ b/web/vendor/bootstrap-icons/app.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/apple-music.svg b/web/vendor/bootstrap-icons/apple-music.svg new file mode 100644 index 0000000..325e3a7 --- /dev/null +++ b/web/vendor/bootstrap-icons/apple-music.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/apple.svg b/web/vendor/bootstrap-icons/apple.svg new file mode 100644 index 0000000..58235c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/apple.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/archive-fill.svg b/web/vendor/bootstrap-icons/archive-fill.svg new file mode 100644 index 0000000..e5ea32b --- /dev/null +++ b/web/vendor/bootstrap-icons/archive-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/archive.svg b/web/vendor/bootstrap-icons/archive.svg new file mode 100644 index 0000000..7bc5eb2 --- /dev/null +++ b/web/vendor/bootstrap-icons/archive.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-90deg-down.svg b/web/vendor/bootstrap-icons/arrow-90deg-down.svg new file mode 100644 index 0000000..7459597 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-90deg-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-90deg-left.svg b/web/vendor/bootstrap-icons/arrow-90deg-left.svg new file mode 100644 index 0000000..4e17ab4 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-90deg-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-90deg-right.svg b/web/vendor/bootstrap-icons/arrow-90deg-right.svg new file mode 100644 index 0000000..9fb6363 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-90deg-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-90deg-up.svg b/web/vendor/bootstrap-icons/arrow-90deg-up.svg new file mode 100644 index 0000000..11be0de --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-90deg-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-bar-down.svg b/web/vendor/bootstrap-icons/arrow-bar-down.svg new file mode 100644 index 0000000..1b212d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-bar-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-bar-left.svg b/web/vendor/bootstrap-icons/arrow-bar-left.svg new file mode 100644 index 0000000..621bf2a --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-bar-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-bar-right.svg b/web/vendor/bootstrap-icons/arrow-bar-right.svg new file mode 100644 index 0000000..0210410 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-bar-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-bar-up.svg b/web/vendor/bootstrap-icons/arrow-bar-up.svg new file mode 100644 index 0000000..d5510da --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-bar-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-clockwise.svg b/web/vendor/bootstrap-icons/arrow-clockwise.svg new file mode 100644 index 0000000..324d5af --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-clockwise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-counterclockwise.svg b/web/vendor/bootstrap-icons/arrow-counterclockwise.svg new file mode 100644 index 0000000..3d9ff62 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-counterclockwise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-circle-fill.svg b/web/vendor/bootstrap-icons/arrow-down-circle-fill.svg new file mode 100644 index 0000000..242eb58 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-circle.svg b/web/vendor/bootstrap-icons/arrow-down-circle.svg new file mode 100644 index 0000000..42e96b5 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-left-circle-fill.svg b/web/vendor/bootstrap-icons/arrow-down-left-circle-fill.svg new file mode 100644 index 0000000..e7f4735 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-left-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-left-circle.svg b/web/vendor/bootstrap-icons/arrow-down-left-circle.svg new file mode 100644 index 0000000..f67491f --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-left-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-left-square-fill.svg b/web/vendor/bootstrap-icons/arrow-down-left-square-fill.svg new file mode 100644 index 0000000..6e03bf2 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-left-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-left-square.svg b/web/vendor/bootstrap-icons/arrow-down-left-square.svg new file mode 100644 index 0000000..1278d39 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-left-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-left.svg b/web/vendor/bootstrap-icons/arrow-down-left.svg new file mode 100644 index 0000000..4011c77 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-right-circle-fill.svg b/web/vendor/bootstrap-icons/arrow-down-right-circle-fill.svg new file mode 100644 index 0000000..4ff73b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-right-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-right-circle.svg b/web/vendor/bootstrap-icons/arrow-down-right-circle.svg new file mode 100644 index 0000000..054e83f --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-right-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-right-square-fill.svg b/web/vendor/bootstrap-icons/arrow-down-right-square-fill.svg new file mode 100644 index 0000000..a556e06 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-right-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-right-square.svg b/web/vendor/bootstrap-icons/arrow-down-right-square.svg new file mode 100644 index 0000000..4bfb679 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-right-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-right.svg b/web/vendor/bootstrap-icons/arrow-down-right.svg new file mode 100644 index 0000000..08aef16 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-short.svg b/web/vendor/bootstrap-icons/arrow-down-short.svg new file mode 100644 index 0000000..66b7fa1 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-short.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-square-fill.svg b/web/vendor/bootstrap-icons/arrow-down-square-fill.svg new file mode 100644 index 0000000..c9020dc --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-square.svg b/web/vendor/bootstrap-icons/arrow-down-square.svg new file mode 100644 index 0000000..c492b71 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down-up.svg b/web/vendor/bootstrap-icons/arrow-down-up.svg new file mode 100644 index 0000000..04cb3a5 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-down.svg b/web/vendor/bootstrap-icons/arrow-down.svg new file mode 100644 index 0000000..f66f74b --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-left-circle-fill.svg b/web/vendor/bootstrap-icons/arrow-left-circle-fill.svg new file mode 100644 index 0000000..ae19d97 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-left-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-left-circle.svg b/web/vendor/bootstrap-icons/arrow-left-circle.svg new file mode 100644 index 0000000..f3246ea --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-left-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-left-right.svg b/web/vendor/bootstrap-icons/arrow-left-right.svg new file mode 100644 index 0000000..89c4003 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-left-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-left-short.svg b/web/vendor/bootstrap-icons/arrow-left-short.svg new file mode 100644 index 0000000..abb15dd --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-left-short.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-left-square-fill.svg b/web/vendor/bootstrap-icons/arrow-left-square-fill.svg new file mode 100644 index 0000000..3ee717e --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-left-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-left-square.svg b/web/vendor/bootstrap-icons/arrow-left-square.svg new file mode 100644 index 0000000..8f09a48 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-left-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-left.svg b/web/vendor/bootstrap-icons/arrow-left.svg new file mode 100644 index 0000000..587d4fe --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-repeat.svg b/web/vendor/bootstrap-icons/arrow-repeat.svg new file mode 100644 index 0000000..b17dba4 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-repeat.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-return-left.svg b/web/vendor/bootstrap-icons/arrow-return-left.svg new file mode 100644 index 0000000..3c13fc4 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-return-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-return-right.svg b/web/vendor/bootstrap-icons/arrow-return-right.svg new file mode 100644 index 0000000..60d282c --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-return-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-right-circle-fill.svg b/web/vendor/bootstrap-icons/arrow-right-circle-fill.svg new file mode 100644 index 0000000..32c21ea --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-right-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-right-circle.svg b/web/vendor/bootstrap-icons/arrow-right-circle.svg new file mode 100644 index 0000000..ad7293e --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-right-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-right-short.svg b/web/vendor/bootstrap-icons/arrow-right-short.svg new file mode 100644 index 0000000..fa238ff --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-right-short.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-right-square-fill.svg b/web/vendor/bootstrap-icons/arrow-right-square-fill.svg new file mode 100644 index 0000000..c7c8eec --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-right-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-right-square.svg b/web/vendor/bootstrap-icons/arrow-right-square.svg new file mode 100644 index 0000000..7a4b78b --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-right-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-right.svg b/web/vendor/bootstrap-icons/arrow-right.svg new file mode 100644 index 0000000..2362904 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-through-heart-fill.svg b/web/vendor/bootstrap-icons/arrow-through-heart-fill.svg new file mode 100644 index 0000000..e98bce1 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-through-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-through-heart.svg b/web/vendor/bootstrap-icons/arrow-through-heart.svg new file mode 100644 index 0000000..daf397b --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-through-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-circle-fill.svg b/web/vendor/bootstrap-icons/arrow-up-circle-fill.svg new file mode 100644 index 0000000..9e7ef36 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-circle.svg b/web/vendor/bootstrap-icons/arrow-up-circle.svg new file mode 100644 index 0000000..e18a689 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-left-circle-fill.svg b/web/vendor/bootstrap-icons/arrow-up-left-circle-fill.svg new file mode 100644 index 0000000..e1e2e97 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-left-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-left-circle.svg b/web/vendor/bootstrap-icons/arrow-up-left-circle.svg new file mode 100644 index 0000000..3101b65 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-left-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-left-square-fill.svg b/web/vendor/bootstrap-icons/arrow-up-left-square-fill.svg new file mode 100644 index 0000000..e699865 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-left-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-left-square.svg b/web/vendor/bootstrap-icons/arrow-up-left-square.svg new file mode 100644 index 0000000..4f31190 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-left-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-left.svg b/web/vendor/bootstrap-icons/arrow-up-left.svg new file mode 100644 index 0000000..938d0d0 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-right-circle-fill.svg b/web/vendor/bootstrap-icons/arrow-up-right-circle-fill.svg new file mode 100644 index 0000000..0eb9b54 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-right-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-right-circle.svg b/web/vendor/bootstrap-icons/arrow-up-right-circle.svg new file mode 100644 index 0000000..ed6ae41 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-right-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-right-square-fill.svg b/web/vendor/bootstrap-icons/arrow-up-right-square-fill.svg new file mode 100644 index 0000000..c2246ec --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-right-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-right-square.svg b/web/vendor/bootstrap-icons/arrow-up-right-square.svg new file mode 100644 index 0000000..b14940f --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-right-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-right.svg b/web/vendor/bootstrap-icons/arrow-up-right.svg new file mode 100644 index 0000000..7b3794b --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-short.svg b/web/vendor/bootstrap-icons/arrow-up-short.svg new file mode 100644 index 0000000..543089a --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-short.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-square-fill.svg b/web/vendor/bootstrap-icons/arrow-up-square-fill.svg new file mode 100644 index 0000000..9d7f65f --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up-square.svg b/web/vendor/bootstrap-icons/arrow-up-square.svg new file mode 100644 index 0000000..bec15c4 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrow-up.svg b/web/vendor/bootstrap-icons/arrow-up.svg new file mode 100644 index 0000000..951521a --- /dev/null +++ b/web/vendor/bootstrap-icons/arrow-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-angle-contract.svg b/web/vendor/bootstrap-icons/arrows-angle-contract.svg new file mode 100644 index 0000000..1aa7875 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-angle-contract.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-angle-expand.svg b/web/vendor/bootstrap-icons/arrows-angle-expand.svg new file mode 100644 index 0000000..578d3b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-angle-expand.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-collapse-vertical.svg b/web/vendor/bootstrap-icons/arrows-collapse-vertical.svg new file mode 100644 index 0000000..0fc4477 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-collapse-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-collapse.svg b/web/vendor/bootstrap-icons/arrows-collapse.svg new file mode 100644 index 0000000..ca055c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-collapse.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-expand-vertical.svg b/web/vendor/bootstrap-icons/arrows-expand-vertical.svg new file mode 100644 index 0000000..9dbfac4 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-expand-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-expand.svg b/web/vendor/bootstrap-icons/arrows-expand.svg new file mode 100644 index 0000000..99eb276 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-expand.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-fullscreen.svg b/web/vendor/bootstrap-icons/arrows-fullscreen.svg new file mode 100644 index 0000000..7633e3f --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-fullscreen.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-move.svg b/web/vendor/bootstrap-icons/arrows-move.svg new file mode 100644 index 0000000..ef2b885 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-move.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows-vertical.svg b/web/vendor/bootstrap-icons/arrows-vertical.svg new file mode 100644 index 0000000..22f60fd --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/arrows.svg b/web/vendor/bootstrap-icons/arrows.svg new file mode 100644 index 0000000..a6bd3d7 --- /dev/null +++ b/web/vendor/bootstrap-icons/arrows.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/aspect-ratio-fill.svg b/web/vendor/bootstrap-icons/aspect-ratio-fill.svg new file mode 100644 index 0000000..6d6cb8d --- /dev/null +++ b/web/vendor/bootstrap-icons/aspect-ratio-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/aspect-ratio.svg b/web/vendor/bootstrap-icons/aspect-ratio.svg new file mode 100644 index 0000000..ee634b0 --- /dev/null +++ b/web/vendor/bootstrap-icons/aspect-ratio.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/asterisk.svg b/web/vendor/bootstrap-icons/asterisk.svg new file mode 100644 index 0000000..fbc13b7 --- /dev/null +++ b/web/vendor/bootstrap-icons/asterisk.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/at.svg b/web/vendor/bootstrap-icons/at.svg new file mode 100644 index 0000000..3cab29e --- /dev/null +++ b/web/vendor/bootstrap-icons/at.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/award-fill.svg b/web/vendor/bootstrap-icons/award-fill.svg new file mode 100644 index 0000000..f996790 --- /dev/null +++ b/web/vendor/bootstrap-icons/award-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/award.svg b/web/vendor/bootstrap-icons/award.svg new file mode 100644 index 0000000..67c760b --- /dev/null +++ b/web/vendor/bootstrap-icons/award.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/back.svg b/web/vendor/bootstrap-icons/back.svg new file mode 100644 index 0000000..9f55340 --- /dev/null +++ b/web/vendor/bootstrap-icons/back.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backpack-fill.svg b/web/vendor/bootstrap-icons/backpack-fill.svg new file mode 100644 index 0000000..ec737a9 --- /dev/null +++ b/web/vendor/bootstrap-icons/backpack-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backpack.svg b/web/vendor/bootstrap-icons/backpack.svg new file mode 100644 index 0000000..819aa29 --- /dev/null +++ b/web/vendor/bootstrap-icons/backpack.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backpack2-fill.svg b/web/vendor/bootstrap-icons/backpack2-fill.svg new file mode 100644 index 0000000..3c9b4da --- /dev/null +++ b/web/vendor/bootstrap-icons/backpack2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backpack2.svg b/web/vendor/bootstrap-icons/backpack2.svg new file mode 100644 index 0000000..e0e4fe5 --- /dev/null +++ b/web/vendor/bootstrap-icons/backpack2.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backpack3-fill.svg b/web/vendor/bootstrap-icons/backpack3-fill.svg new file mode 100644 index 0000000..4583c4f --- /dev/null +++ b/web/vendor/bootstrap-icons/backpack3-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backpack3.svg b/web/vendor/bootstrap-icons/backpack3.svg new file mode 100644 index 0000000..819f8df --- /dev/null +++ b/web/vendor/bootstrap-icons/backpack3.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backpack4-fill.svg b/web/vendor/bootstrap-icons/backpack4-fill.svg new file mode 100644 index 0000000..a3bba3a --- /dev/null +++ b/web/vendor/bootstrap-icons/backpack4-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backpack4.svg b/web/vendor/bootstrap-icons/backpack4.svg new file mode 100644 index 0000000..140a12d --- /dev/null +++ b/web/vendor/bootstrap-icons/backpack4.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backspace-fill.svg b/web/vendor/bootstrap-icons/backspace-fill.svg new file mode 100644 index 0000000..0787578 --- /dev/null +++ b/web/vendor/bootstrap-icons/backspace-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backspace-reverse-fill.svg b/web/vendor/bootstrap-icons/backspace-reverse-fill.svg new file mode 100644 index 0000000..41c8dbb --- /dev/null +++ b/web/vendor/bootstrap-icons/backspace-reverse-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backspace-reverse.svg b/web/vendor/bootstrap-icons/backspace-reverse.svg new file mode 100644 index 0000000..7b3fafd --- /dev/null +++ b/web/vendor/bootstrap-icons/backspace-reverse.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/backspace.svg b/web/vendor/bootstrap-icons/backspace.svg new file mode 100644 index 0000000..39b688f --- /dev/null +++ b/web/vendor/bootstrap-icons/backspace.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-3d-fill.svg b/web/vendor/bootstrap-icons/badge-3d-fill.svg new file mode 100644 index 0000000..750598c --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-3d-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-3d.svg b/web/vendor/bootstrap-icons/badge-3d.svg new file mode 100644 index 0000000..b3153f2 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-3d.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-4k-fill.svg b/web/vendor/bootstrap-icons/badge-4k-fill.svg new file mode 100644 index 0000000..72f34b9 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-4k-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-4k.svg b/web/vendor/bootstrap-icons/badge-4k.svg new file mode 100644 index 0000000..3dfc9b1 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-4k.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-8k-fill.svg b/web/vendor/bootstrap-icons/badge-8k-fill.svg new file mode 100644 index 0000000..4bd9b80 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-8k-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-8k.svg b/web/vendor/bootstrap-icons/badge-8k.svg new file mode 100644 index 0000000..d11f82d --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-8k.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-ad-fill.svg b/web/vendor/bootstrap-icons/badge-ad-fill.svg new file mode 100644 index 0000000..023f210 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-ad-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-ad.svg b/web/vendor/bootstrap-icons/badge-ad.svg new file mode 100644 index 0000000..616ad74 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-ad.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-ar-fill.svg b/web/vendor/bootstrap-icons/badge-ar-fill.svg new file mode 100644 index 0000000..48aee0f --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-ar-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-ar.svg b/web/vendor/bootstrap-icons/badge-ar.svg new file mode 100644 index 0000000..0fc1975 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-ar.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-cc-fill.svg b/web/vendor/bootstrap-icons/badge-cc-fill.svg new file mode 100644 index 0000000..0f3d1f9 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-cc-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-cc.svg b/web/vendor/bootstrap-icons/badge-cc.svg new file mode 100644 index 0000000..d5f42e1 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-cc.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-hd-fill.svg b/web/vendor/bootstrap-icons/badge-hd-fill.svg new file mode 100644 index 0000000..96f0875 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-hd-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-hd.svg b/web/vendor/bootstrap-icons/badge-hd.svg new file mode 100644 index 0000000..5689042 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-hd.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-sd-fill.svg b/web/vendor/bootstrap-icons/badge-sd-fill.svg new file mode 100644 index 0000000..a37154a --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-sd-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-sd.svg b/web/vendor/bootstrap-icons/badge-sd.svg new file mode 100644 index 0000000..df8d029 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-sd.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-tm-fill.svg b/web/vendor/bootstrap-icons/badge-tm-fill.svg new file mode 100644 index 0000000..632b569 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-tm-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-tm.svg b/web/vendor/bootstrap-icons/badge-tm.svg new file mode 100644 index 0000000..33de5c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-tm.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-vo-fill.svg b/web/vendor/bootstrap-icons/badge-vo-fill.svg new file mode 100644 index 0000000..1d27b07 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-vo-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-vo.svg b/web/vendor/bootstrap-icons/badge-vo.svg new file mode 100644 index 0000000..f5e2ece --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-vo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-vr-fill.svg b/web/vendor/bootstrap-icons/badge-vr-fill.svg new file mode 100644 index 0000000..e614af6 --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-vr-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-vr.svg b/web/vendor/bootstrap-icons/badge-vr.svg new file mode 100644 index 0000000..c1c73dc --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-vr.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-wc-fill.svg b/web/vendor/bootstrap-icons/badge-wc-fill.svg new file mode 100644 index 0000000..d16436a --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-wc-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/badge-wc.svg b/web/vendor/bootstrap-icons/badge-wc.svg new file mode 100644 index 0000000..ea459ba --- /dev/null +++ b/web/vendor/bootstrap-icons/badge-wc.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-check-fill.svg b/web/vendor/bootstrap-icons/bag-check-fill.svg new file mode 100644 index 0000000..9976d5c --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-check.svg b/web/vendor/bootstrap-icons/bag-check.svg new file mode 100644 index 0000000..a4e3278 --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-dash-fill.svg b/web/vendor/bootstrap-icons/bag-dash-fill.svg new file mode 100644 index 0000000..ccb5589 --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-dash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-dash.svg b/web/vendor/bootstrap-icons/bag-dash.svg new file mode 100644 index 0000000..0997f33 --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-fill.svg b/web/vendor/bootstrap-icons/bag-fill.svg new file mode 100644 index 0000000..812fcfc --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-heart-fill.svg b/web/vendor/bootstrap-icons/bag-heart-fill.svg new file mode 100644 index 0000000..4938b83 --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-heart.svg b/web/vendor/bootstrap-icons/bag-heart.svg new file mode 100644 index 0000000..e7b906a --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-plus-fill.svg b/web/vendor/bootstrap-icons/bag-plus-fill.svg new file mode 100644 index 0000000..b98f6b2 --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-plus.svg b/web/vendor/bootstrap-icons/bag-plus.svg new file mode 100644 index 0000000..0d7ddc6 --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-x-fill.svg b/web/vendor/bootstrap-icons/bag-x-fill.svg new file mode 100644 index 0000000..cbe77df --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag-x.svg b/web/vendor/bootstrap-icons/bag-x.svg new file mode 100644 index 0000000..4f38008 --- /dev/null +++ b/web/vendor/bootstrap-icons/bag-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bag.svg b/web/vendor/bootstrap-icons/bag.svg new file mode 100644 index 0000000..acd0287 --- /dev/null +++ b/web/vendor/bootstrap-icons/bag.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/balloon-fill.svg b/web/vendor/bootstrap-icons/balloon-fill.svg new file mode 100644 index 0000000..2d57e2d --- /dev/null +++ b/web/vendor/bootstrap-icons/balloon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/balloon-heart-fill.svg b/web/vendor/bootstrap-icons/balloon-heart-fill.svg new file mode 100644 index 0000000..ab17865 --- /dev/null +++ b/web/vendor/bootstrap-icons/balloon-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/balloon-heart.svg b/web/vendor/bootstrap-icons/balloon-heart.svg new file mode 100644 index 0000000..0e056cb --- /dev/null +++ b/web/vendor/bootstrap-icons/balloon-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/balloon.svg b/web/vendor/bootstrap-icons/balloon.svg new file mode 100644 index 0000000..6906cb3 --- /dev/null +++ b/web/vendor/bootstrap-icons/balloon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ban-fill.svg b/web/vendor/bootstrap-icons/ban-fill.svg new file mode 100644 index 0000000..0bb7df0 --- /dev/null +++ b/web/vendor/bootstrap-icons/ban-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ban.svg b/web/vendor/bootstrap-icons/ban.svg new file mode 100644 index 0000000..5acfd41 --- /dev/null +++ b/web/vendor/bootstrap-icons/ban.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bandaid-fill.svg b/web/vendor/bootstrap-icons/bandaid-fill.svg new file mode 100644 index 0000000..052ad73 --- /dev/null +++ b/web/vendor/bootstrap-icons/bandaid-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bandaid.svg b/web/vendor/bootstrap-icons/bandaid.svg new file mode 100644 index 0000000..e09850e --- /dev/null +++ b/web/vendor/bootstrap-icons/bandaid.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bank.svg b/web/vendor/bootstrap-icons/bank.svg new file mode 100644 index 0000000..2e7f4f0 --- /dev/null +++ b/web/vendor/bootstrap-icons/bank.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bank2.svg b/web/vendor/bootstrap-icons/bank2.svg new file mode 100644 index 0000000..acc8ef9 --- /dev/null +++ b/web/vendor/bootstrap-icons/bank2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bar-chart-fill.svg b/web/vendor/bootstrap-icons/bar-chart-fill.svg new file mode 100644 index 0000000..7e4ebee --- /dev/null +++ b/web/vendor/bootstrap-icons/bar-chart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bar-chart-line-fill.svg b/web/vendor/bootstrap-icons/bar-chart-line-fill.svg new file mode 100644 index 0000000..6808e6f --- /dev/null +++ b/web/vendor/bootstrap-icons/bar-chart-line-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bar-chart-line.svg b/web/vendor/bootstrap-icons/bar-chart-line.svg new file mode 100644 index 0000000..567a808 --- /dev/null +++ b/web/vendor/bootstrap-icons/bar-chart-line.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bar-chart-steps.svg b/web/vendor/bootstrap-icons/bar-chart-steps.svg new file mode 100644 index 0000000..346e97b --- /dev/null +++ b/web/vendor/bootstrap-icons/bar-chart-steps.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bar-chart.svg b/web/vendor/bootstrap-icons/bar-chart.svg new file mode 100644 index 0000000..8e57c80 --- /dev/null +++ b/web/vendor/bootstrap-icons/bar-chart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/basket-fill.svg b/web/vendor/bootstrap-icons/basket-fill.svg new file mode 100644 index 0000000..b2e01f5 --- /dev/null +++ b/web/vendor/bootstrap-icons/basket-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/basket.svg b/web/vendor/bootstrap-icons/basket.svg new file mode 100644 index 0000000..418a5f9 --- /dev/null +++ b/web/vendor/bootstrap-icons/basket.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/basket2-fill.svg b/web/vendor/bootstrap-icons/basket2-fill.svg new file mode 100644 index 0000000..03c7079 --- /dev/null +++ b/web/vendor/bootstrap-icons/basket2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/basket2.svg b/web/vendor/bootstrap-icons/basket2.svg new file mode 100644 index 0000000..9b78be2 --- /dev/null +++ b/web/vendor/bootstrap-icons/basket2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/basket3-fill.svg b/web/vendor/bootstrap-icons/basket3-fill.svg new file mode 100644 index 0000000..e26f0ee --- /dev/null +++ b/web/vendor/bootstrap-icons/basket3-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/basket3.svg b/web/vendor/bootstrap-icons/basket3.svg new file mode 100644 index 0000000..57fa6a0 --- /dev/null +++ b/web/vendor/bootstrap-icons/basket3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/battery-charging.svg b/web/vendor/bootstrap-icons/battery-charging.svg new file mode 100644 index 0000000..4ae74d2 --- /dev/null +++ b/web/vendor/bootstrap-icons/battery-charging.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/battery-full.svg b/web/vendor/bootstrap-icons/battery-full.svg new file mode 100644 index 0000000..bff6a3f --- /dev/null +++ b/web/vendor/bootstrap-icons/battery-full.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/battery-half.svg b/web/vendor/bootstrap-icons/battery-half.svg new file mode 100644 index 0000000..de57848 --- /dev/null +++ b/web/vendor/bootstrap-icons/battery-half.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/battery-low.svg b/web/vendor/bootstrap-icons/battery-low.svg new file mode 100644 index 0000000..b99e758 --- /dev/null +++ b/web/vendor/bootstrap-icons/battery-low.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/battery.svg b/web/vendor/bootstrap-icons/battery.svg new file mode 100644 index 0000000..2bacfa8 --- /dev/null +++ b/web/vendor/bootstrap-icons/battery.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/beaker-fill.svg b/web/vendor/bootstrap-icons/beaker-fill.svg new file mode 100644 index 0000000..3bf39d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/beaker-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/beaker.svg b/web/vendor/bootstrap-icons/beaker.svg new file mode 100644 index 0000000..f1958c6 --- /dev/null +++ b/web/vendor/bootstrap-icons/beaker.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/behance.svg b/web/vendor/bootstrap-icons/behance.svg new file mode 100644 index 0000000..805f142 --- /dev/null +++ b/web/vendor/bootstrap-icons/behance.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bell-fill.svg b/web/vendor/bootstrap-icons/bell-fill.svg new file mode 100644 index 0000000..a537c3a --- /dev/null +++ b/web/vendor/bootstrap-icons/bell-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bell-slash-fill.svg b/web/vendor/bootstrap-icons/bell-slash-fill.svg new file mode 100644 index 0000000..534dd13 --- /dev/null +++ b/web/vendor/bootstrap-icons/bell-slash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bell-slash.svg b/web/vendor/bootstrap-icons/bell-slash.svg new file mode 100644 index 0000000..7817e2b --- /dev/null +++ b/web/vendor/bootstrap-icons/bell-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bell.svg b/web/vendor/bootstrap-icons/bell.svg new file mode 100644 index 0000000..a71eba3 --- /dev/null +++ b/web/vendor/bootstrap-icons/bell.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bezier.svg b/web/vendor/bootstrap-icons/bezier.svg new file mode 100644 index 0000000..075b721 --- /dev/null +++ b/web/vendor/bootstrap-icons/bezier.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bezier2.svg b/web/vendor/bootstrap-icons/bezier2.svg new file mode 100644 index 0000000..8a59238 --- /dev/null +++ b/web/vendor/bootstrap-icons/bezier2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bicycle.svg b/web/vendor/bootstrap-icons/bicycle.svg new file mode 100644 index 0000000..3956545 --- /dev/null +++ b/web/vendor/bootstrap-icons/bicycle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bing.svg b/web/vendor/bootstrap-icons/bing.svg new file mode 100644 index 0000000..9368917 --- /dev/null +++ b/web/vendor/bootstrap-icons/bing.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/binoculars-fill.svg b/web/vendor/bootstrap-icons/binoculars-fill.svg new file mode 100644 index 0000000..d6d6dc0 --- /dev/null +++ b/web/vendor/bootstrap-icons/binoculars-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/binoculars.svg b/web/vendor/bootstrap-icons/binoculars.svg new file mode 100644 index 0000000..015d622 --- /dev/null +++ b/web/vendor/bootstrap-icons/binoculars.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/blockquote-left.svg b/web/vendor/bootstrap-icons/blockquote-left.svg new file mode 100644 index 0000000..f8b6b2d --- /dev/null +++ b/web/vendor/bootstrap-icons/blockquote-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/blockquote-right.svg b/web/vendor/bootstrap-icons/blockquote-right.svg new file mode 100644 index 0000000..afc81c9 --- /dev/null +++ b/web/vendor/bootstrap-icons/blockquote-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bluesky.svg b/web/vendor/bootstrap-icons/bluesky.svg new file mode 100644 index 0000000..a070aea --- /dev/null +++ b/web/vendor/bootstrap-icons/bluesky.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bluetooth.svg b/web/vendor/bootstrap-icons/bluetooth.svg new file mode 100644 index 0000000..8726e22 --- /dev/null +++ b/web/vendor/bootstrap-icons/bluetooth.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/body-text.svg b/web/vendor/bootstrap-icons/body-text.svg new file mode 100644 index 0000000..fd5e435 --- /dev/null +++ b/web/vendor/bootstrap-icons/body-text.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/book-fill.svg b/web/vendor/bootstrap-icons/book-fill.svg new file mode 100644 index 0000000..ddb0000 --- /dev/null +++ b/web/vendor/bootstrap-icons/book-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/book-half.svg b/web/vendor/bootstrap-icons/book-half.svg new file mode 100644 index 0000000..8eabe81 --- /dev/null +++ b/web/vendor/bootstrap-icons/book-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/book.svg b/web/vendor/bootstrap-icons/book.svg new file mode 100644 index 0000000..302acf0 --- /dev/null +++ b/web/vendor/bootstrap-icons/book.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-check-fill.svg b/web/vendor/bootstrap-icons/bookmark-check-fill.svg new file mode 100644 index 0000000..325fbde --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-check.svg b/web/vendor/bootstrap-icons/bookmark-check.svg new file mode 100644 index 0000000..f4c9149 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-dash-fill.svg b/web/vendor/bootstrap-icons/bookmark-dash-fill.svg new file mode 100644 index 0000000..dbf9cc1 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-dash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-dash.svg b/web/vendor/bootstrap-icons/bookmark-dash.svg new file mode 100644 index 0000000..115b448 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-fill.svg b/web/vendor/bootstrap-icons/bookmark-fill.svg new file mode 100644 index 0000000..3c237a9 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-heart-fill.svg b/web/vendor/bootstrap-icons/bookmark-heart-fill.svg new file mode 100644 index 0000000..6647b7c --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-heart.svg b/web/vendor/bootstrap-icons/bookmark-heart.svg new file mode 100644 index 0000000..c368f5d --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-plus-fill.svg b/web/vendor/bootstrap-icons/bookmark-plus-fill.svg new file mode 100644 index 0000000..41e0733 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-plus.svg b/web/vendor/bootstrap-icons/bookmark-plus.svg new file mode 100644 index 0000000..37b137c --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-star-fill.svg b/web/vendor/bootstrap-icons/bookmark-star-fill.svg new file mode 100644 index 0000000..89fd335 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-star-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-star.svg b/web/vendor/bootstrap-icons/bookmark-star.svg new file mode 100644 index 0000000..2f792de --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-star.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-x-fill.svg b/web/vendor/bootstrap-icons/bookmark-x-fill.svg new file mode 100644 index 0000000..acac0cf --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark-x.svg b/web/vendor/bootstrap-icons/bookmark-x.svg new file mode 100644 index 0000000..eb85c76 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmark.svg b/web/vendor/bootstrap-icons/bookmark.svg new file mode 100644 index 0000000..a21b14b --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmarks-fill.svg b/web/vendor/bootstrap-icons/bookmarks-fill.svg new file mode 100644 index 0000000..abf5800 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmarks-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookmarks.svg b/web/vendor/bootstrap-icons/bookmarks.svg new file mode 100644 index 0000000..ceb92bb --- /dev/null +++ b/web/vendor/bootstrap-icons/bookmarks.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bookshelf.svg b/web/vendor/bootstrap-icons/bookshelf.svg new file mode 100644 index 0000000..7f435d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/bookshelf.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/boombox-fill.svg b/web/vendor/bootstrap-icons/boombox-fill.svg new file mode 100644 index 0000000..299e95e --- /dev/null +++ b/web/vendor/bootstrap-icons/boombox-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/boombox.svg b/web/vendor/bootstrap-icons/boombox.svg new file mode 100644 index 0000000..35af807 --- /dev/null +++ b/web/vendor/bootstrap-icons/boombox.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bootstrap-fill.svg b/web/vendor/bootstrap-icons/bootstrap-fill.svg new file mode 100644 index 0000000..21253b0 --- /dev/null +++ b/web/vendor/bootstrap-icons/bootstrap-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bootstrap-icons.css b/web/vendor/bootstrap-icons/bootstrap-icons.css new file mode 100644 index 0000000..5f7ae28 --- /dev/null +++ b/web/vendor/bootstrap-icons/bootstrap-icons.css @@ -0,0 +1,2106 @@ +/*! + * Bootstrap Icons v1.13.1 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ + +@font-face { + font-display: block; + font-family: "bootstrap-icons"; + src: url("./fonts/bootstrap-icons.woff2?e34853135f9e39acf64315236852cd5a") format("woff2"), +url("./fonts/bootstrap-icons.woff?e34853135f9e39acf64315236852cd5a") format("woff"); +} + +.bi::before, +[class^="bi-"]::before, +[class*=" bi-"]::before { + display: inline-block; + font-family: bootstrap-icons !important; + font-style: normal; + font-weight: normal !important; + font-variant: normal; + text-transform: none; + line-height: 1; + vertical-align: -.125em; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.bi-123::before { content: "\f67f"; } +.bi-alarm-fill::before { content: "\f101"; } +.bi-alarm::before { content: "\f102"; } +.bi-align-bottom::before { content: "\f103"; } +.bi-align-center::before { content: "\f104"; } +.bi-align-end::before { content: "\f105"; } +.bi-align-middle::before { content: "\f106"; } +.bi-align-start::before { content: "\f107"; } +.bi-align-top::before { content: "\f108"; } +.bi-alt::before { content: "\f109"; } +.bi-app-indicator::before { content: "\f10a"; } +.bi-app::before { content: "\f10b"; } +.bi-archive-fill::before { content: "\f10c"; } +.bi-archive::before { content: "\f10d"; } +.bi-arrow-90deg-down::before { content: "\f10e"; } +.bi-arrow-90deg-left::before { content: "\f10f"; } +.bi-arrow-90deg-right::before { content: "\f110"; } +.bi-arrow-90deg-up::before { content: "\f111"; } +.bi-arrow-bar-down::before { content: "\f112"; } +.bi-arrow-bar-left::before { content: "\f113"; } +.bi-arrow-bar-right::before { content: "\f114"; } +.bi-arrow-bar-up::before { content: "\f115"; } +.bi-arrow-clockwise::before { content: "\f116"; } +.bi-arrow-counterclockwise::before { content: "\f117"; } +.bi-arrow-down-circle-fill::before { content: "\f118"; } +.bi-arrow-down-circle::before { content: "\f119"; } +.bi-arrow-down-left-circle-fill::before { content: "\f11a"; } +.bi-arrow-down-left-circle::before { content: "\f11b"; } +.bi-arrow-down-left-square-fill::before { content: "\f11c"; } +.bi-arrow-down-left-square::before { content: "\f11d"; } +.bi-arrow-down-left::before { content: "\f11e"; } +.bi-arrow-down-right-circle-fill::before { content: "\f11f"; } +.bi-arrow-down-right-circle::before { content: "\f120"; } +.bi-arrow-down-right-square-fill::before { content: "\f121"; } +.bi-arrow-down-right-square::before { content: "\f122"; } +.bi-arrow-down-right::before { content: "\f123"; } +.bi-arrow-down-short::before { content: "\f124"; } +.bi-arrow-down-square-fill::before { content: "\f125"; } +.bi-arrow-down-square::before { content: "\f126"; } +.bi-arrow-down-up::before { content: "\f127"; } +.bi-arrow-down::before { content: "\f128"; } +.bi-arrow-left-circle-fill::before { content: "\f129"; } +.bi-arrow-left-circle::before { content: "\f12a"; } +.bi-arrow-left-right::before { content: "\f12b"; } +.bi-arrow-left-short::before { content: "\f12c"; } +.bi-arrow-left-square-fill::before { content: "\f12d"; } +.bi-arrow-left-square::before { content: "\f12e"; } +.bi-arrow-left::before { content: "\f12f"; } +.bi-arrow-repeat::before { content: "\f130"; } +.bi-arrow-return-left::before { content: "\f131"; } +.bi-arrow-return-right::before { content: "\f132"; } +.bi-arrow-right-circle-fill::before { content: "\f133"; } +.bi-arrow-right-circle::before { content: "\f134"; } +.bi-arrow-right-short::before { content: "\f135"; } +.bi-arrow-right-square-fill::before { content: "\f136"; } +.bi-arrow-right-square::before { content: "\f137"; } +.bi-arrow-right::before { content: "\f138"; } +.bi-arrow-up-circle-fill::before { content: "\f139"; } +.bi-arrow-up-circle::before { content: "\f13a"; } +.bi-arrow-up-left-circle-fill::before { content: "\f13b"; } +.bi-arrow-up-left-circle::before { content: "\f13c"; } +.bi-arrow-up-left-square-fill::before { content: "\f13d"; } +.bi-arrow-up-left-square::before { content: "\f13e"; } +.bi-arrow-up-left::before { content: "\f13f"; } +.bi-arrow-up-right-circle-fill::before { content: "\f140"; } +.bi-arrow-up-right-circle::before { content: "\f141"; } +.bi-arrow-up-right-square-fill::before { content: "\f142"; } +.bi-arrow-up-right-square::before { content: "\f143"; } +.bi-arrow-up-right::before { content: "\f144"; } +.bi-arrow-up-short::before { content: "\f145"; } +.bi-arrow-up-square-fill::before { content: "\f146"; } +.bi-arrow-up-square::before { content: "\f147"; } +.bi-arrow-up::before { content: "\f148"; } +.bi-arrows-angle-contract::before { content: "\f149"; } +.bi-arrows-angle-expand::before { content: "\f14a"; } +.bi-arrows-collapse::before { content: "\f14b"; } +.bi-arrows-expand::before { content: "\f14c"; } +.bi-arrows-fullscreen::before { content: "\f14d"; } +.bi-arrows-move::before { content: "\f14e"; } +.bi-aspect-ratio-fill::before { content: "\f14f"; } +.bi-aspect-ratio::before { content: "\f150"; } +.bi-asterisk::before { content: "\f151"; } +.bi-at::before { content: "\f152"; } +.bi-award-fill::before { content: "\f153"; } +.bi-award::before { content: "\f154"; } +.bi-back::before { content: "\f155"; } +.bi-backspace-fill::before { content: "\f156"; } +.bi-backspace-reverse-fill::before { content: "\f157"; } +.bi-backspace-reverse::before { content: "\f158"; } +.bi-backspace::before { content: "\f159"; } +.bi-badge-3d-fill::before { content: "\f15a"; } +.bi-badge-3d::before { content: "\f15b"; } +.bi-badge-4k-fill::before { content: "\f15c"; } +.bi-badge-4k::before { content: "\f15d"; } +.bi-badge-8k-fill::before { content: "\f15e"; } +.bi-badge-8k::before { content: "\f15f"; } +.bi-badge-ad-fill::before { content: "\f160"; } +.bi-badge-ad::before { content: "\f161"; } +.bi-badge-ar-fill::before { content: "\f162"; } +.bi-badge-ar::before { content: "\f163"; } +.bi-badge-cc-fill::before { content: "\f164"; } +.bi-badge-cc::before { content: "\f165"; } +.bi-badge-hd-fill::before { content: "\f166"; } +.bi-badge-hd::before { content: "\f167"; } +.bi-badge-tm-fill::before { content: "\f168"; } +.bi-badge-tm::before { content: "\f169"; } +.bi-badge-vo-fill::before { content: "\f16a"; } +.bi-badge-vo::before { content: "\f16b"; } +.bi-badge-vr-fill::before { content: "\f16c"; } +.bi-badge-vr::before { content: "\f16d"; } +.bi-badge-wc-fill::before { content: "\f16e"; } +.bi-badge-wc::before { content: "\f16f"; } +.bi-bag-check-fill::before { content: "\f170"; } +.bi-bag-check::before { content: "\f171"; } +.bi-bag-dash-fill::before { content: "\f172"; } +.bi-bag-dash::before { content: "\f173"; } +.bi-bag-fill::before { content: "\f174"; } +.bi-bag-plus-fill::before { content: "\f175"; } +.bi-bag-plus::before { content: "\f176"; } +.bi-bag-x-fill::before { content: "\f177"; } +.bi-bag-x::before { content: "\f178"; } +.bi-bag::before { content: "\f179"; } +.bi-bar-chart-fill::before { content: "\f17a"; } +.bi-bar-chart-line-fill::before { content: "\f17b"; } +.bi-bar-chart-line::before { content: "\f17c"; } +.bi-bar-chart-steps::before { content: "\f17d"; } +.bi-bar-chart::before { content: "\f17e"; } +.bi-basket-fill::before { content: "\f17f"; } +.bi-basket::before { content: "\f180"; } +.bi-basket2-fill::before { content: "\f181"; } +.bi-basket2::before { content: "\f182"; } +.bi-basket3-fill::before { content: "\f183"; } +.bi-basket3::before { content: "\f184"; } +.bi-battery-charging::before { content: "\f185"; } +.bi-battery-full::before { content: "\f186"; } +.bi-battery-half::before { content: "\f187"; } +.bi-battery::before { content: "\f188"; } +.bi-bell-fill::before { content: "\f189"; } +.bi-bell::before { content: "\f18a"; } +.bi-bezier::before { content: "\f18b"; } +.bi-bezier2::before { content: "\f18c"; } +.bi-bicycle::before { content: "\f18d"; } +.bi-binoculars-fill::before { content: "\f18e"; } +.bi-binoculars::before { content: "\f18f"; } +.bi-blockquote-left::before { content: "\f190"; } +.bi-blockquote-right::before { content: "\f191"; } +.bi-book-fill::before { content: "\f192"; } +.bi-book-half::before { content: "\f193"; } +.bi-book::before { content: "\f194"; } +.bi-bookmark-check-fill::before { content: "\f195"; } +.bi-bookmark-check::before { content: "\f196"; } +.bi-bookmark-dash-fill::before { content: "\f197"; } +.bi-bookmark-dash::before { content: "\f198"; } +.bi-bookmark-fill::before { content: "\f199"; } +.bi-bookmark-heart-fill::before { content: "\f19a"; } +.bi-bookmark-heart::before { content: "\f19b"; } +.bi-bookmark-plus-fill::before { content: "\f19c"; } +.bi-bookmark-plus::before { content: "\f19d"; } +.bi-bookmark-star-fill::before { content: "\f19e"; } +.bi-bookmark-star::before { content: "\f19f"; } +.bi-bookmark-x-fill::before { content: "\f1a0"; } +.bi-bookmark-x::before { content: "\f1a1"; } +.bi-bookmark::before { content: "\f1a2"; } +.bi-bookmarks-fill::before { content: "\f1a3"; } +.bi-bookmarks::before { content: "\f1a4"; } +.bi-bookshelf::before { content: "\f1a5"; } +.bi-bootstrap-fill::before { content: "\f1a6"; } +.bi-bootstrap-reboot::before { content: "\f1a7"; } +.bi-bootstrap::before { content: "\f1a8"; } +.bi-border-all::before { content: "\f1a9"; } +.bi-border-bottom::before { content: "\f1aa"; } +.bi-border-center::before { content: "\f1ab"; } +.bi-border-inner::before { content: "\f1ac"; } +.bi-border-left::before { content: "\f1ad"; } +.bi-border-middle::before { content: "\f1ae"; } +.bi-border-outer::before { content: "\f1af"; } +.bi-border-right::before { content: "\f1b0"; } +.bi-border-style::before { content: "\f1b1"; } +.bi-border-top::before { content: "\f1b2"; } +.bi-border-width::before { content: "\f1b3"; } +.bi-border::before { content: "\f1b4"; } +.bi-bounding-box-circles::before { content: "\f1b5"; } +.bi-bounding-box::before { content: "\f1b6"; } +.bi-box-arrow-down-left::before { content: "\f1b7"; } +.bi-box-arrow-down-right::before { content: "\f1b8"; } +.bi-box-arrow-down::before { content: "\f1b9"; } +.bi-box-arrow-in-down-left::before { content: "\f1ba"; } +.bi-box-arrow-in-down-right::before { content: "\f1bb"; } +.bi-box-arrow-in-down::before { content: "\f1bc"; } +.bi-box-arrow-in-left::before { content: "\f1bd"; } +.bi-box-arrow-in-right::before { content: "\f1be"; } +.bi-box-arrow-in-up-left::before { content: "\f1bf"; } +.bi-box-arrow-in-up-right::before { content: "\f1c0"; } +.bi-box-arrow-in-up::before { content: "\f1c1"; } +.bi-box-arrow-left::before { content: "\f1c2"; } +.bi-box-arrow-right::before { content: "\f1c3"; } +.bi-box-arrow-up-left::before { content: "\f1c4"; } +.bi-box-arrow-up-right::before { content: "\f1c5"; } +.bi-box-arrow-up::before { content: "\f1c6"; } +.bi-box-seam::before { content: "\f1c7"; } +.bi-box::before { content: "\f1c8"; } +.bi-braces::before { content: "\f1c9"; } +.bi-bricks::before { content: "\f1ca"; } +.bi-briefcase-fill::before { content: "\f1cb"; } +.bi-briefcase::before { content: "\f1cc"; } +.bi-brightness-alt-high-fill::before { content: "\f1cd"; } +.bi-brightness-alt-high::before { content: "\f1ce"; } +.bi-brightness-alt-low-fill::before { content: "\f1cf"; } +.bi-brightness-alt-low::before { content: "\f1d0"; } +.bi-brightness-high-fill::before { content: "\f1d1"; } +.bi-brightness-high::before { content: "\f1d2"; } +.bi-brightness-low-fill::before { content: "\f1d3"; } +.bi-brightness-low::before { content: "\f1d4"; } +.bi-broadcast-pin::before { content: "\f1d5"; } +.bi-broadcast::before { content: "\f1d6"; } +.bi-brush-fill::before { content: "\f1d7"; } +.bi-brush::before { content: "\f1d8"; } +.bi-bucket-fill::before { content: "\f1d9"; } +.bi-bucket::before { content: "\f1da"; } +.bi-bug-fill::before { content: "\f1db"; } +.bi-bug::before { content: "\f1dc"; } +.bi-building::before { content: "\f1dd"; } +.bi-bullseye::before { content: "\f1de"; } +.bi-calculator-fill::before { content: "\f1df"; } +.bi-calculator::before { content: "\f1e0"; } +.bi-calendar-check-fill::before { content: "\f1e1"; } +.bi-calendar-check::before { content: "\f1e2"; } +.bi-calendar-date-fill::before { content: "\f1e3"; } +.bi-calendar-date::before { content: "\f1e4"; } +.bi-calendar-day-fill::before { content: "\f1e5"; } +.bi-calendar-day::before { content: "\f1e6"; } +.bi-calendar-event-fill::before { content: "\f1e7"; } +.bi-calendar-event::before { content: "\f1e8"; } +.bi-calendar-fill::before { content: "\f1e9"; } +.bi-calendar-minus-fill::before { content: "\f1ea"; } +.bi-calendar-minus::before { content: "\f1eb"; } +.bi-calendar-month-fill::before { content: "\f1ec"; } +.bi-calendar-month::before { content: "\f1ed"; } +.bi-calendar-plus-fill::before { content: "\f1ee"; } +.bi-calendar-plus::before { content: "\f1ef"; } +.bi-calendar-range-fill::before { content: "\f1f0"; } +.bi-calendar-range::before { content: "\f1f1"; } +.bi-calendar-week-fill::before { content: "\f1f2"; } +.bi-calendar-week::before { content: "\f1f3"; } +.bi-calendar-x-fill::before { content: "\f1f4"; } +.bi-calendar-x::before { content: "\f1f5"; } +.bi-calendar::before { content: "\f1f6"; } +.bi-calendar2-check-fill::before { content: "\f1f7"; } +.bi-calendar2-check::before { content: "\f1f8"; } +.bi-calendar2-date-fill::before { content: "\f1f9"; } +.bi-calendar2-date::before { content: "\f1fa"; } +.bi-calendar2-day-fill::before { content: "\f1fb"; } +.bi-calendar2-day::before { content: "\f1fc"; } +.bi-calendar2-event-fill::before { content: "\f1fd"; } +.bi-calendar2-event::before { content: "\f1fe"; } +.bi-calendar2-fill::before { content: "\f1ff"; } +.bi-calendar2-minus-fill::before { content: "\f200"; } +.bi-calendar2-minus::before { content: "\f201"; } +.bi-calendar2-month-fill::before { content: "\f202"; } +.bi-calendar2-month::before { content: "\f203"; } +.bi-calendar2-plus-fill::before { content: "\f204"; } +.bi-calendar2-plus::before { content: "\f205"; } +.bi-calendar2-range-fill::before { content: "\f206"; } +.bi-calendar2-range::before { content: "\f207"; } +.bi-calendar2-week-fill::before { content: "\f208"; } +.bi-calendar2-week::before { content: "\f209"; } +.bi-calendar2-x-fill::before { content: "\f20a"; } +.bi-calendar2-x::before { content: "\f20b"; } +.bi-calendar2::before { content: "\f20c"; } +.bi-calendar3-event-fill::before { content: "\f20d"; } +.bi-calendar3-event::before { content: "\f20e"; } +.bi-calendar3-fill::before { content: "\f20f"; } +.bi-calendar3-range-fill::before { content: "\f210"; } +.bi-calendar3-range::before { content: "\f211"; } +.bi-calendar3-week-fill::before { content: "\f212"; } +.bi-calendar3-week::before { content: "\f213"; } +.bi-calendar3::before { content: "\f214"; } +.bi-calendar4-event::before { content: "\f215"; } +.bi-calendar4-range::before { content: "\f216"; } +.bi-calendar4-week::before { content: "\f217"; } +.bi-calendar4::before { content: "\f218"; } +.bi-camera-fill::before { content: "\f219"; } +.bi-camera-reels-fill::before { content: "\f21a"; } +.bi-camera-reels::before { content: "\f21b"; } +.bi-camera-video-fill::before { content: "\f21c"; } +.bi-camera-video-off-fill::before { content: "\f21d"; } +.bi-camera-video-off::before { content: "\f21e"; } +.bi-camera-video::before { content: "\f21f"; } +.bi-camera::before { content: "\f220"; } +.bi-camera2::before { content: "\f221"; } +.bi-capslock-fill::before { content: "\f222"; } +.bi-capslock::before { content: "\f223"; } +.bi-card-checklist::before { content: "\f224"; } +.bi-card-heading::before { content: "\f225"; } +.bi-card-image::before { content: "\f226"; } +.bi-card-list::before { content: "\f227"; } +.bi-card-text::before { content: "\f228"; } +.bi-caret-down-fill::before { content: "\f229"; } +.bi-caret-down-square-fill::before { content: "\f22a"; } +.bi-caret-down-square::before { content: "\f22b"; } +.bi-caret-down::before { content: "\f22c"; } +.bi-caret-left-fill::before { content: "\f22d"; } +.bi-caret-left-square-fill::before { content: "\f22e"; } +.bi-caret-left-square::before { content: "\f22f"; } +.bi-caret-left::before { content: "\f230"; } +.bi-caret-right-fill::before { content: "\f231"; } +.bi-caret-right-square-fill::before { content: "\f232"; } +.bi-caret-right-square::before { content: "\f233"; } +.bi-caret-right::before { content: "\f234"; } +.bi-caret-up-fill::before { content: "\f235"; } +.bi-caret-up-square-fill::before { content: "\f236"; } +.bi-caret-up-square::before { content: "\f237"; } +.bi-caret-up::before { content: "\f238"; } +.bi-cart-check-fill::before { content: "\f239"; } +.bi-cart-check::before { content: "\f23a"; } +.bi-cart-dash-fill::before { content: "\f23b"; } +.bi-cart-dash::before { content: "\f23c"; } +.bi-cart-fill::before { content: "\f23d"; } +.bi-cart-plus-fill::before { content: "\f23e"; } +.bi-cart-plus::before { content: "\f23f"; } +.bi-cart-x-fill::before { content: "\f240"; } +.bi-cart-x::before { content: "\f241"; } +.bi-cart::before { content: "\f242"; } +.bi-cart2::before { content: "\f243"; } +.bi-cart3::before { content: "\f244"; } +.bi-cart4::before { content: "\f245"; } +.bi-cash-stack::before { content: "\f246"; } +.bi-cash::before { content: "\f247"; } +.bi-cast::before { content: "\f248"; } +.bi-chat-dots-fill::before { content: "\f249"; } +.bi-chat-dots::before { content: "\f24a"; } +.bi-chat-fill::before { content: "\f24b"; } +.bi-chat-left-dots-fill::before { content: "\f24c"; } +.bi-chat-left-dots::before { content: "\f24d"; } +.bi-chat-left-fill::before { content: "\f24e"; } +.bi-chat-left-quote-fill::before { content: "\f24f"; } +.bi-chat-left-quote::before { content: "\f250"; } +.bi-chat-left-text-fill::before { content: "\f251"; } +.bi-chat-left-text::before { content: "\f252"; } +.bi-chat-left::before { content: "\f253"; } +.bi-chat-quote-fill::before { content: "\f254"; } +.bi-chat-quote::before { content: "\f255"; } +.bi-chat-right-dots-fill::before { content: "\f256"; } +.bi-chat-right-dots::before { content: "\f257"; } +.bi-chat-right-fill::before { content: "\f258"; } +.bi-chat-right-quote-fill::before { content: "\f259"; } +.bi-chat-right-quote::before { content: "\f25a"; } +.bi-chat-right-text-fill::before { content: "\f25b"; } +.bi-chat-right-text::before { content: "\f25c"; } +.bi-chat-right::before { content: "\f25d"; } +.bi-chat-square-dots-fill::before { content: "\f25e"; } +.bi-chat-square-dots::before { content: "\f25f"; } +.bi-chat-square-fill::before { content: "\f260"; } +.bi-chat-square-quote-fill::before { content: "\f261"; } +.bi-chat-square-quote::before { content: "\f262"; } +.bi-chat-square-text-fill::before { content: "\f263"; } +.bi-chat-square-text::before { content: "\f264"; } +.bi-chat-square::before { content: "\f265"; } +.bi-chat-text-fill::before { content: "\f266"; } +.bi-chat-text::before { content: "\f267"; } +.bi-chat::before { content: "\f268"; } +.bi-check-all::before { content: "\f269"; } +.bi-check-circle-fill::before { content: "\f26a"; } +.bi-check-circle::before { content: "\f26b"; } +.bi-check-square-fill::before { content: "\f26c"; } +.bi-check-square::before { content: "\f26d"; } +.bi-check::before { content: "\f26e"; } +.bi-check2-all::before { content: "\f26f"; } +.bi-check2-circle::before { content: "\f270"; } +.bi-check2-square::before { content: "\f271"; } +.bi-check2::before { content: "\f272"; } +.bi-chevron-bar-contract::before { content: "\f273"; } +.bi-chevron-bar-down::before { content: "\f274"; } +.bi-chevron-bar-expand::before { content: "\f275"; } +.bi-chevron-bar-left::before { content: "\f276"; } +.bi-chevron-bar-right::before { content: "\f277"; } +.bi-chevron-bar-up::before { content: "\f278"; } +.bi-chevron-compact-down::before { content: "\f279"; } +.bi-chevron-compact-left::before { content: "\f27a"; } +.bi-chevron-compact-right::before { content: "\f27b"; } +.bi-chevron-compact-up::before { content: "\f27c"; } +.bi-chevron-contract::before { content: "\f27d"; } +.bi-chevron-double-down::before { content: "\f27e"; } +.bi-chevron-double-left::before { content: "\f27f"; } +.bi-chevron-double-right::before { content: "\f280"; } +.bi-chevron-double-up::before { content: "\f281"; } +.bi-chevron-down::before { content: "\f282"; } +.bi-chevron-expand::before { content: "\f283"; } +.bi-chevron-left::before { content: "\f284"; } +.bi-chevron-right::before { content: "\f285"; } +.bi-chevron-up::before { content: "\f286"; } +.bi-circle-fill::before { content: "\f287"; } +.bi-circle-half::before { content: "\f288"; } +.bi-circle-square::before { content: "\f289"; } +.bi-circle::before { content: "\f28a"; } +.bi-clipboard-check::before { content: "\f28b"; } +.bi-clipboard-data::before { content: "\f28c"; } +.bi-clipboard-minus::before { content: "\f28d"; } +.bi-clipboard-plus::before { content: "\f28e"; } +.bi-clipboard-x::before { content: "\f28f"; } +.bi-clipboard::before { content: "\f290"; } +.bi-clock-fill::before { content: "\f291"; } +.bi-clock-history::before { content: "\f292"; } +.bi-clock::before { content: "\f293"; } +.bi-cloud-arrow-down-fill::before { content: "\f294"; } +.bi-cloud-arrow-down::before { content: "\f295"; } +.bi-cloud-arrow-up-fill::before { content: "\f296"; } +.bi-cloud-arrow-up::before { content: "\f297"; } +.bi-cloud-check-fill::before { content: "\f298"; } +.bi-cloud-check::before { content: "\f299"; } +.bi-cloud-download-fill::before { content: "\f29a"; } +.bi-cloud-download::before { content: "\f29b"; } +.bi-cloud-drizzle-fill::before { content: "\f29c"; } +.bi-cloud-drizzle::before { content: "\f29d"; } +.bi-cloud-fill::before { content: "\f29e"; } +.bi-cloud-fog-fill::before { content: "\f29f"; } +.bi-cloud-fog::before { content: "\f2a0"; } +.bi-cloud-fog2-fill::before { content: "\f2a1"; } +.bi-cloud-fog2::before { content: "\f2a2"; } +.bi-cloud-hail-fill::before { content: "\f2a3"; } +.bi-cloud-hail::before { content: "\f2a4"; } +.bi-cloud-haze-fill::before { content: "\f2a6"; } +.bi-cloud-haze::before { content: "\f2a7"; } +.bi-cloud-haze2-fill::before { content: "\f2a8"; } +.bi-cloud-lightning-fill::before { content: "\f2a9"; } +.bi-cloud-lightning-rain-fill::before { content: "\f2aa"; } +.bi-cloud-lightning-rain::before { content: "\f2ab"; } +.bi-cloud-lightning::before { content: "\f2ac"; } +.bi-cloud-minus-fill::before { content: "\f2ad"; } +.bi-cloud-minus::before { content: "\f2ae"; } +.bi-cloud-moon-fill::before { content: "\f2af"; } +.bi-cloud-moon::before { content: "\f2b0"; } +.bi-cloud-plus-fill::before { content: "\f2b1"; } +.bi-cloud-plus::before { content: "\f2b2"; } +.bi-cloud-rain-fill::before { content: "\f2b3"; } +.bi-cloud-rain-heavy-fill::before { content: "\f2b4"; } +.bi-cloud-rain-heavy::before { content: "\f2b5"; } +.bi-cloud-rain::before { content: "\f2b6"; } +.bi-cloud-slash-fill::before { content: "\f2b7"; } +.bi-cloud-slash::before { content: "\f2b8"; } +.bi-cloud-sleet-fill::before { content: "\f2b9"; } +.bi-cloud-sleet::before { content: "\f2ba"; } +.bi-cloud-snow-fill::before { content: "\f2bb"; } +.bi-cloud-snow::before { content: "\f2bc"; } +.bi-cloud-sun-fill::before { content: "\f2bd"; } +.bi-cloud-sun::before { content: "\f2be"; } +.bi-cloud-upload-fill::before { content: "\f2bf"; } +.bi-cloud-upload::before { content: "\f2c0"; } +.bi-cloud::before { content: "\f2c1"; } +.bi-clouds-fill::before { content: "\f2c2"; } +.bi-clouds::before { content: "\f2c3"; } +.bi-cloudy-fill::before { content: "\f2c4"; } +.bi-cloudy::before { content: "\f2c5"; } +.bi-code-slash::before { content: "\f2c6"; } +.bi-code-square::before { content: "\f2c7"; } +.bi-code::before { content: "\f2c8"; } +.bi-collection-fill::before { content: "\f2c9"; } +.bi-collection-play-fill::before { content: "\f2ca"; } +.bi-collection-play::before { content: "\f2cb"; } +.bi-collection::before { content: "\f2cc"; } +.bi-columns-gap::before { content: "\f2cd"; } +.bi-columns::before { content: "\f2ce"; } +.bi-command::before { content: "\f2cf"; } +.bi-compass-fill::before { content: "\f2d0"; } +.bi-compass::before { content: "\f2d1"; } +.bi-cone-striped::before { content: "\f2d2"; } +.bi-cone::before { content: "\f2d3"; } +.bi-controller::before { content: "\f2d4"; } +.bi-cpu-fill::before { content: "\f2d5"; } +.bi-cpu::before { content: "\f2d6"; } +.bi-credit-card-2-back-fill::before { content: "\f2d7"; } +.bi-credit-card-2-back::before { content: "\f2d8"; } +.bi-credit-card-2-front-fill::before { content: "\f2d9"; } +.bi-credit-card-2-front::before { content: "\f2da"; } +.bi-credit-card-fill::before { content: "\f2db"; } +.bi-credit-card::before { content: "\f2dc"; } +.bi-crop::before { content: "\f2dd"; } +.bi-cup-fill::before { content: "\f2de"; } +.bi-cup-straw::before { content: "\f2df"; } +.bi-cup::before { content: "\f2e0"; } +.bi-cursor-fill::before { content: "\f2e1"; } +.bi-cursor-text::before { content: "\f2e2"; } +.bi-cursor::before { content: "\f2e3"; } +.bi-dash-circle-dotted::before { content: "\f2e4"; } +.bi-dash-circle-fill::before { content: "\f2e5"; } +.bi-dash-circle::before { content: "\f2e6"; } +.bi-dash-square-dotted::before { content: "\f2e7"; } +.bi-dash-square-fill::before { content: "\f2e8"; } +.bi-dash-square::before { content: "\f2e9"; } +.bi-dash::before { content: "\f2ea"; } +.bi-diagram-2-fill::before { content: "\f2eb"; } +.bi-diagram-2::before { content: "\f2ec"; } +.bi-diagram-3-fill::before { content: "\f2ed"; } +.bi-diagram-3::before { content: "\f2ee"; } +.bi-diamond-fill::before { content: "\f2ef"; } +.bi-diamond-half::before { content: "\f2f0"; } +.bi-diamond::before { content: "\f2f1"; } +.bi-dice-1-fill::before { content: "\f2f2"; } +.bi-dice-1::before { content: "\f2f3"; } +.bi-dice-2-fill::before { content: "\f2f4"; } +.bi-dice-2::before { content: "\f2f5"; } +.bi-dice-3-fill::before { content: "\f2f6"; } +.bi-dice-3::before { content: "\f2f7"; } +.bi-dice-4-fill::before { content: "\f2f8"; } +.bi-dice-4::before { content: "\f2f9"; } +.bi-dice-5-fill::before { content: "\f2fa"; } +.bi-dice-5::before { content: "\f2fb"; } +.bi-dice-6-fill::before { content: "\f2fc"; } +.bi-dice-6::before { content: "\f2fd"; } +.bi-disc-fill::before { content: "\f2fe"; } +.bi-disc::before { content: "\f2ff"; } +.bi-discord::before { content: "\f300"; } +.bi-display-fill::before { content: "\f301"; } +.bi-display::before { content: "\f302"; } +.bi-distribute-horizontal::before { content: "\f303"; } +.bi-distribute-vertical::before { content: "\f304"; } +.bi-door-closed-fill::before { content: "\f305"; } +.bi-door-closed::before { content: "\f306"; } +.bi-door-open-fill::before { content: "\f307"; } +.bi-door-open::before { content: "\f308"; } +.bi-dot::before { content: "\f309"; } +.bi-download::before { content: "\f30a"; } +.bi-droplet-fill::before { content: "\f30b"; } +.bi-droplet-half::before { content: "\f30c"; } +.bi-droplet::before { content: "\f30d"; } +.bi-earbuds::before { content: "\f30e"; } +.bi-easel-fill::before { content: "\f30f"; } +.bi-easel::before { content: "\f310"; } +.bi-egg-fill::before { content: "\f311"; } +.bi-egg-fried::before { content: "\f312"; } +.bi-egg::before { content: "\f313"; } +.bi-eject-fill::before { content: "\f314"; } +.bi-eject::before { content: "\f315"; } +.bi-emoji-angry-fill::before { content: "\f316"; } +.bi-emoji-angry::before { content: "\f317"; } +.bi-emoji-dizzy-fill::before { content: "\f318"; } +.bi-emoji-dizzy::before { content: "\f319"; } +.bi-emoji-expressionless-fill::before { content: "\f31a"; } +.bi-emoji-expressionless::before { content: "\f31b"; } +.bi-emoji-frown-fill::before { content: "\f31c"; } +.bi-emoji-frown::before { content: "\f31d"; } +.bi-emoji-heart-eyes-fill::before { content: "\f31e"; } +.bi-emoji-heart-eyes::before { content: "\f31f"; } +.bi-emoji-laughing-fill::before { content: "\f320"; } +.bi-emoji-laughing::before { content: "\f321"; } +.bi-emoji-neutral-fill::before { content: "\f322"; } +.bi-emoji-neutral::before { content: "\f323"; } +.bi-emoji-smile-fill::before { content: "\f324"; } +.bi-emoji-smile-upside-down-fill::before { content: "\f325"; } +.bi-emoji-smile-upside-down::before { content: "\f326"; } +.bi-emoji-smile::before { content: "\f327"; } +.bi-emoji-sunglasses-fill::before { content: "\f328"; } +.bi-emoji-sunglasses::before { content: "\f329"; } +.bi-emoji-wink-fill::before { content: "\f32a"; } +.bi-emoji-wink::before { content: "\f32b"; } +.bi-envelope-fill::before { content: "\f32c"; } +.bi-envelope-open-fill::before { content: "\f32d"; } +.bi-envelope-open::before { content: "\f32e"; } +.bi-envelope::before { content: "\f32f"; } +.bi-eraser-fill::before { content: "\f330"; } +.bi-eraser::before { content: "\f331"; } +.bi-exclamation-circle-fill::before { content: "\f332"; } +.bi-exclamation-circle::before { content: "\f333"; } +.bi-exclamation-diamond-fill::before { content: "\f334"; } +.bi-exclamation-diamond::before { content: "\f335"; } +.bi-exclamation-octagon-fill::before { content: "\f336"; } +.bi-exclamation-octagon::before { content: "\f337"; } +.bi-exclamation-square-fill::before { content: "\f338"; } +.bi-exclamation-square::before { content: "\f339"; } +.bi-exclamation-triangle-fill::before { content: "\f33a"; } +.bi-exclamation-triangle::before { content: "\f33b"; } +.bi-exclamation::before { content: "\f33c"; } +.bi-exclude::before { content: "\f33d"; } +.bi-eye-fill::before { content: "\f33e"; } +.bi-eye-slash-fill::before { content: "\f33f"; } +.bi-eye-slash::before { content: "\f340"; } +.bi-eye::before { content: "\f341"; } +.bi-eyedropper::before { content: "\f342"; } +.bi-eyeglasses::before { content: "\f343"; } +.bi-facebook::before { content: "\f344"; } +.bi-file-arrow-down-fill::before { content: "\f345"; } +.bi-file-arrow-down::before { content: "\f346"; } +.bi-file-arrow-up-fill::before { content: "\f347"; } +.bi-file-arrow-up::before { content: "\f348"; } +.bi-file-bar-graph-fill::before { content: "\f349"; } +.bi-file-bar-graph::before { content: "\f34a"; } +.bi-file-binary-fill::before { content: "\f34b"; } +.bi-file-binary::before { content: "\f34c"; } +.bi-file-break-fill::before { content: "\f34d"; } +.bi-file-break::before { content: "\f34e"; } +.bi-file-check-fill::before { content: "\f34f"; } +.bi-file-check::before { content: "\f350"; } +.bi-file-code-fill::before { content: "\f351"; } +.bi-file-code::before { content: "\f352"; } +.bi-file-diff-fill::before { content: "\f353"; } +.bi-file-diff::before { content: "\f354"; } +.bi-file-earmark-arrow-down-fill::before { content: "\f355"; } +.bi-file-earmark-arrow-down::before { content: "\f356"; } +.bi-file-earmark-arrow-up-fill::before { content: "\f357"; } +.bi-file-earmark-arrow-up::before { content: "\f358"; } +.bi-file-earmark-bar-graph-fill::before { content: "\f359"; } +.bi-file-earmark-bar-graph::before { content: "\f35a"; } +.bi-file-earmark-binary-fill::before { content: "\f35b"; } +.bi-file-earmark-binary::before { content: "\f35c"; } +.bi-file-earmark-break-fill::before { content: "\f35d"; } +.bi-file-earmark-break::before { content: "\f35e"; } +.bi-file-earmark-check-fill::before { content: "\f35f"; } +.bi-file-earmark-check::before { content: "\f360"; } +.bi-file-earmark-code-fill::before { content: "\f361"; } +.bi-file-earmark-code::before { content: "\f362"; } +.bi-file-earmark-diff-fill::before { content: "\f363"; } +.bi-file-earmark-diff::before { content: "\f364"; } +.bi-file-earmark-easel-fill::before { content: "\f365"; } +.bi-file-earmark-easel::before { content: "\f366"; } +.bi-file-earmark-excel-fill::before { content: "\f367"; } +.bi-file-earmark-excel::before { content: "\f368"; } +.bi-file-earmark-fill::before { content: "\f369"; } +.bi-file-earmark-font-fill::before { content: "\f36a"; } +.bi-file-earmark-font::before { content: "\f36b"; } +.bi-file-earmark-image-fill::before { content: "\f36c"; } +.bi-file-earmark-image::before { content: "\f36d"; } +.bi-file-earmark-lock-fill::before { content: "\f36e"; } +.bi-file-earmark-lock::before { content: "\f36f"; } +.bi-file-earmark-lock2-fill::before { content: "\f370"; } +.bi-file-earmark-lock2::before { content: "\f371"; } +.bi-file-earmark-medical-fill::before { content: "\f372"; } +.bi-file-earmark-medical::before { content: "\f373"; } +.bi-file-earmark-minus-fill::before { content: "\f374"; } +.bi-file-earmark-minus::before { content: "\f375"; } +.bi-file-earmark-music-fill::before { content: "\f376"; } +.bi-file-earmark-music::before { content: "\f377"; } +.bi-file-earmark-person-fill::before { content: "\f378"; } +.bi-file-earmark-person::before { content: "\f379"; } +.bi-file-earmark-play-fill::before { content: "\f37a"; } +.bi-file-earmark-play::before { content: "\f37b"; } +.bi-file-earmark-plus-fill::before { content: "\f37c"; } +.bi-file-earmark-plus::before { content: "\f37d"; } +.bi-file-earmark-post-fill::before { content: "\f37e"; } +.bi-file-earmark-post::before { content: "\f37f"; } +.bi-file-earmark-ppt-fill::before { content: "\f380"; } +.bi-file-earmark-ppt::before { content: "\f381"; } +.bi-file-earmark-richtext-fill::before { content: "\f382"; } +.bi-file-earmark-richtext::before { content: "\f383"; } +.bi-file-earmark-ruled-fill::before { content: "\f384"; } +.bi-file-earmark-ruled::before { content: "\f385"; } +.bi-file-earmark-slides-fill::before { content: "\f386"; } +.bi-file-earmark-slides::before { content: "\f387"; } +.bi-file-earmark-spreadsheet-fill::before { content: "\f388"; } +.bi-file-earmark-spreadsheet::before { content: "\f389"; } +.bi-file-earmark-text-fill::before { content: "\f38a"; } +.bi-file-earmark-text::before { content: "\f38b"; } +.bi-file-earmark-word-fill::before { content: "\f38c"; } +.bi-file-earmark-word::before { content: "\f38d"; } +.bi-file-earmark-x-fill::before { content: "\f38e"; } +.bi-file-earmark-x::before { content: "\f38f"; } +.bi-file-earmark-zip-fill::before { content: "\f390"; } +.bi-file-earmark-zip::before { content: "\f391"; } +.bi-file-earmark::before { content: "\f392"; } +.bi-file-easel-fill::before { content: "\f393"; } +.bi-file-easel::before { content: "\f394"; } +.bi-file-excel-fill::before { content: "\f395"; } +.bi-file-excel::before { content: "\f396"; } +.bi-file-fill::before { content: "\f397"; } +.bi-file-font-fill::before { content: "\f398"; } +.bi-file-font::before { content: "\f399"; } +.bi-file-image-fill::before { content: "\f39a"; } +.bi-file-image::before { content: "\f39b"; } +.bi-file-lock-fill::before { content: "\f39c"; } +.bi-file-lock::before { content: "\f39d"; } +.bi-file-lock2-fill::before { content: "\f39e"; } +.bi-file-lock2::before { content: "\f39f"; } +.bi-file-medical-fill::before { content: "\f3a0"; } +.bi-file-medical::before { content: "\f3a1"; } +.bi-file-minus-fill::before { content: "\f3a2"; } +.bi-file-minus::before { content: "\f3a3"; } +.bi-file-music-fill::before { content: "\f3a4"; } +.bi-file-music::before { content: "\f3a5"; } +.bi-file-person-fill::before { content: "\f3a6"; } +.bi-file-person::before { content: "\f3a7"; } +.bi-file-play-fill::before { content: "\f3a8"; } +.bi-file-play::before { content: "\f3a9"; } +.bi-file-plus-fill::before { content: "\f3aa"; } +.bi-file-plus::before { content: "\f3ab"; } +.bi-file-post-fill::before { content: "\f3ac"; } +.bi-file-post::before { content: "\f3ad"; } +.bi-file-ppt-fill::before { content: "\f3ae"; } +.bi-file-ppt::before { content: "\f3af"; } +.bi-file-richtext-fill::before { content: "\f3b0"; } +.bi-file-richtext::before { content: "\f3b1"; } +.bi-file-ruled-fill::before { content: "\f3b2"; } +.bi-file-ruled::before { content: "\f3b3"; } +.bi-file-slides-fill::before { content: "\f3b4"; } +.bi-file-slides::before { content: "\f3b5"; } +.bi-file-spreadsheet-fill::before { content: "\f3b6"; } +.bi-file-spreadsheet::before { content: "\f3b7"; } +.bi-file-text-fill::before { content: "\f3b8"; } +.bi-file-text::before { content: "\f3b9"; } +.bi-file-word-fill::before { content: "\f3ba"; } +.bi-file-word::before { content: "\f3bb"; } +.bi-file-x-fill::before { content: "\f3bc"; } +.bi-file-x::before { content: "\f3bd"; } +.bi-file-zip-fill::before { content: "\f3be"; } +.bi-file-zip::before { content: "\f3bf"; } +.bi-file::before { content: "\f3c0"; } +.bi-files-alt::before { content: "\f3c1"; } +.bi-files::before { content: "\f3c2"; } +.bi-film::before { content: "\f3c3"; } +.bi-filter-circle-fill::before { content: "\f3c4"; } +.bi-filter-circle::before { content: "\f3c5"; } +.bi-filter-left::before { content: "\f3c6"; } +.bi-filter-right::before { content: "\f3c7"; } +.bi-filter-square-fill::before { content: "\f3c8"; } +.bi-filter-square::before { content: "\f3c9"; } +.bi-filter::before { content: "\f3ca"; } +.bi-flag-fill::before { content: "\f3cb"; } +.bi-flag::before { content: "\f3cc"; } +.bi-flower1::before { content: "\f3cd"; } +.bi-flower2::before { content: "\f3ce"; } +.bi-flower3::before { content: "\f3cf"; } +.bi-folder-check::before { content: "\f3d0"; } +.bi-folder-fill::before { content: "\f3d1"; } +.bi-folder-minus::before { content: "\f3d2"; } +.bi-folder-plus::before { content: "\f3d3"; } +.bi-folder-symlink-fill::before { content: "\f3d4"; } +.bi-folder-symlink::before { content: "\f3d5"; } +.bi-folder-x::before { content: "\f3d6"; } +.bi-folder::before { content: "\f3d7"; } +.bi-folder2-open::before { content: "\f3d8"; } +.bi-folder2::before { content: "\f3d9"; } +.bi-fonts::before { content: "\f3da"; } +.bi-forward-fill::before { content: "\f3db"; } +.bi-forward::before { content: "\f3dc"; } +.bi-front::before { content: "\f3dd"; } +.bi-fullscreen-exit::before { content: "\f3de"; } +.bi-fullscreen::before { content: "\f3df"; } +.bi-funnel-fill::before { content: "\f3e0"; } +.bi-funnel::before { content: "\f3e1"; } +.bi-gear-fill::before { content: "\f3e2"; } +.bi-gear-wide-connected::before { content: "\f3e3"; } +.bi-gear-wide::before { content: "\f3e4"; } +.bi-gear::before { content: "\f3e5"; } +.bi-gem::before { content: "\f3e6"; } +.bi-geo-alt-fill::before { content: "\f3e7"; } +.bi-geo-alt::before { content: "\f3e8"; } +.bi-geo-fill::before { content: "\f3e9"; } +.bi-geo::before { content: "\f3ea"; } +.bi-gift-fill::before { content: "\f3eb"; } +.bi-gift::before { content: "\f3ec"; } +.bi-github::before { content: "\f3ed"; } +.bi-globe::before { content: "\f3ee"; } +.bi-globe2::before { content: "\f3ef"; } +.bi-google::before { content: "\f3f0"; } +.bi-graph-down::before { content: "\f3f1"; } +.bi-graph-up::before { content: "\f3f2"; } +.bi-grid-1x2-fill::before { content: "\f3f3"; } +.bi-grid-1x2::before { content: "\f3f4"; } +.bi-grid-3x2-gap-fill::before { content: "\f3f5"; } +.bi-grid-3x2-gap::before { content: "\f3f6"; } +.bi-grid-3x2::before { content: "\f3f7"; } +.bi-grid-3x3-gap-fill::before { content: "\f3f8"; } +.bi-grid-3x3-gap::before { content: "\f3f9"; } +.bi-grid-3x3::before { content: "\f3fa"; } +.bi-grid-fill::before { content: "\f3fb"; } +.bi-grid::before { content: "\f3fc"; } +.bi-grip-horizontal::before { content: "\f3fd"; } +.bi-grip-vertical::before { content: "\f3fe"; } +.bi-hammer::before { content: "\f3ff"; } +.bi-hand-index-fill::before { content: "\f400"; } +.bi-hand-index-thumb-fill::before { content: "\f401"; } +.bi-hand-index-thumb::before { content: "\f402"; } +.bi-hand-index::before { content: "\f403"; } +.bi-hand-thumbs-down-fill::before { content: "\f404"; } +.bi-hand-thumbs-down::before { content: "\f405"; } +.bi-hand-thumbs-up-fill::before { content: "\f406"; } +.bi-hand-thumbs-up::before { content: "\f407"; } +.bi-handbag-fill::before { content: "\f408"; } +.bi-handbag::before { content: "\f409"; } +.bi-hash::before { content: "\f40a"; } +.bi-hdd-fill::before { content: "\f40b"; } +.bi-hdd-network-fill::before { content: "\f40c"; } +.bi-hdd-network::before { content: "\f40d"; } +.bi-hdd-rack-fill::before { content: "\f40e"; } +.bi-hdd-rack::before { content: "\f40f"; } +.bi-hdd-stack-fill::before { content: "\f410"; } +.bi-hdd-stack::before { content: "\f411"; } +.bi-hdd::before { content: "\f412"; } +.bi-headphones::before { content: "\f413"; } +.bi-headset::before { content: "\f414"; } +.bi-heart-fill::before { content: "\f415"; } +.bi-heart-half::before { content: "\f416"; } +.bi-heart::before { content: "\f417"; } +.bi-heptagon-fill::before { content: "\f418"; } +.bi-heptagon-half::before { content: "\f419"; } +.bi-heptagon::before { content: "\f41a"; } +.bi-hexagon-fill::before { content: "\f41b"; } +.bi-hexagon-half::before { content: "\f41c"; } +.bi-hexagon::before { content: "\f41d"; } +.bi-hourglass-bottom::before { content: "\f41e"; } +.bi-hourglass-split::before { content: "\f41f"; } +.bi-hourglass-top::before { content: "\f420"; } +.bi-hourglass::before { content: "\f421"; } +.bi-house-door-fill::before { content: "\f422"; } +.bi-house-door::before { content: "\f423"; } +.bi-house-fill::before { content: "\f424"; } +.bi-house::before { content: "\f425"; } +.bi-hr::before { content: "\f426"; } +.bi-hurricane::before { content: "\f427"; } +.bi-image-alt::before { content: "\f428"; } +.bi-image-fill::before { content: "\f429"; } +.bi-image::before { content: "\f42a"; } +.bi-images::before { content: "\f42b"; } +.bi-inbox-fill::before { content: "\f42c"; } +.bi-inbox::before { content: "\f42d"; } +.bi-inboxes-fill::before { content: "\f42e"; } +.bi-inboxes::before { content: "\f42f"; } +.bi-info-circle-fill::before { content: "\f430"; } +.bi-info-circle::before { content: "\f431"; } +.bi-info-square-fill::before { content: "\f432"; } +.bi-info-square::before { content: "\f433"; } +.bi-info::before { content: "\f434"; } +.bi-input-cursor-text::before { content: "\f435"; } +.bi-input-cursor::before { content: "\f436"; } +.bi-instagram::before { content: "\f437"; } +.bi-intersect::before { content: "\f438"; } +.bi-journal-album::before { content: "\f439"; } +.bi-journal-arrow-down::before { content: "\f43a"; } +.bi-journal-arrow-up::before { content: "\f43b"; } +.bi-journal-bookmark-fill::before { content: "\f43c"; } +.bi-journal-bookmark::before { content: "\f43d"; } +.bi-journal-check::before { content: "\f43e"; } +.bi-journal-code::before { content: "\f43f"; } +.bi-journal-medical::before { content: "\f440"; } +.bi-journal-minus::before { content: "\f441"; } +.bi-journal-plus::before { content: "\f442"; } +.bi-journal-richtext::before { content: "\f443"; } +.bi-journal-text::before { content: "\f444"; } +.bi-journal-x::before { content: "\f445"; } +.bi-journal::before { content: "\f446"; } +.bi-journals::before { content: "\f447"; } +.bi-joystick::before { content: "\f448"; } +.bi-justify-left::before { content: "\f449"; } +.bi-justify-right::before { content: "\f44a"; } +.bi-justify::before { content: "\f44b"; } +.bi-kanban-fill::before { content: "\f44c"; } +.bi-kanban::before { content: "\f44d"; } +.bi-key-fill::before { content: "\f44e"; } +.bi-key::before { content: "\f44f"; } +.bi-keyboard-fill::before { content: "\f450"; } +.bi-keyboard::before { content: "\f451"; } +.bi-ladder::before { content: "\f452"; } +.bi-lamp-fill::before { content: "\f453"; } +.bi-lamp::before { content: "\f454"; } +.bi-laptop-fill::before { content: "\f455"; } +.bi-laptop::before { content: "\f456"; } +.bi-layer-backward::before { content: "\f457"; } +.bi-layer-forward::before { content: "\f458"; } +.bi-layers-fill::before { content: "\f459"; } +.bi-layers-half::before { content: "\f45a"; } +.bi-layers::before { content: "\f45b"; } +.bi-layout-sidebar-inset-reverse::before { content: "\f45c"; } +.bi-layout-sidebar-inset::before { content: "\f45d"; } +.bi-layout-sidebar-reverse::before { content: "\f45e"; } +.bi-layout-sidebar::before { content: "\f45f"; } +.bi-layout-split::before { content: "\f460"; } +.bi-layout-text-sidebar-reverse::before { content: "\f461"; } +.bi-layout-text-sidebar::before { content: "\f462"; } +.bi-layout-text-window-reverse::before { content: "\f463"; } +.bi-layout-text-window::before { content: "\f464"; } +.bi-layout-three-columns::before { content: "\f465"; } +.bi-layout-wtf::before { content: "\f466"; } +.bi-life-preserver::before { content: "\f467"; } +.bi-lightbulb-fill::before { content: "\f468"; } +.bi-lightbulb-off-fill::before { content: "\f469"; } +.bi-lightbulb-off::before { content: "\f46a"; } +.bi-lightbulb::before { content: "\f46b"; } +.bi-lightning-charge-fill::before { content: "\f46c"; } +.bi-lightning-charge::before { content: "\f46d"; } +.bi-lightning-fill::before { content: "\f46e"; } +.bi-lightning::before { content: "\f46f"; } +.bi-link-45deg::before { content: "\f470"; } +.bi-link::before { content: "\f471"; } +.bi-linkedin::before { content: "\f472"; } +.bi-list-check::before { content: "\f473"; } +.bi-list-nested::before { content: "\f474"; } +.bi-list-ol::before { content: "\f475"; } +.bi-list-stars::before { content: "\f476"; } +.bi-list-task::before { content: "\f477"; } +.bi-list-ul::before { content: "\f478"; } +.bi-list::before { content: "\f479"; } +.bi-lock-fill::before { content: "\f47a"; } +.bi-lock::before { content: "\f47b"; } +.bi-mailbox::before { content: "\f47c"; } +.bi-mailbox2::before { content: "\f47d"; } +.bi-map-fill::before { content: "\f47e"; } +.bi-map::before { content: "\f47f"; } +.bi-markdown-fill::before { content: "\f480"; } +.bi-markdown::before { content: "\f481"; } +.bi-mask::before { content: "\f482"; } +.bi-megaphone-fill::before { content: "\f483"; } +.bi-megaphone::before { content: "\f484"; } +.bi-menu-app-fill::before { content: "\f485"; } +.bi-menu-app::before { content: "\f486"; } +.bi-menu-button-fill::before { content: "\f487"; } +.bi-menu-button-wide-fill::before { content: "\f488"; } +.bi-menu-button-wide::before { content: "\f489"; } +.bi-menu-button::before { content: "\f48a"; } +.bi-menu-down::before { content: "\f48b"; } +.bi-menu-up::before { content: "\f48c"; } +.bi-mic-fill::before { content: "\f48d"; } +.bi-mic-mute-fill::before { content: "\f48e"; } +.bi-mic-mute::before { content: "\f48f"; } +.bi-mic::before { content: "\f490"; } +.bi-minecart-loaded::before { content: "\f491"; } +.bi-minecart::before { content: "\f492"; } +.bi-moisture::before { content: "\f493"; } +.bi-moon-fill::before { content: "\f494"; } +.bi-moon-stars-fill::before { content: "\f495"; } +.bi-moon-stars::before { content: "\f496"; } +.bi-moon::before { content: "\f497"; } +.bi-mouse-fill::before { content: "\f498"; } +.bi-mouse::before { content: "\f499"; } +.bi-mouse2-fill::before { content: "\f49a"; } +.bi-mouse2::before { content: "\f49b"; } +.bi-mouse3-fill::before { content: "\f49c"; } +.bi-mouse3::before { content: "\f49d"; } +.bi-music-note-beamed::before { content: "\f49e"; } +.bi-music-note-list::before { content: "\f49f"; } +.bi-music-note::before { content: "\f4a0"; } +.bi-music-player-fill::before { content: "\f4a1"; } +.bi-music-player::before { content: "\f4a2"; } +.bi-newspaper::before { content: "\f4a3"; } +.bi-node-minus-fill::before { content: "\f4a4"; } +.bi-node-minus::before { content: "\f4a5"; } +.bi-node-plus-fill::before { content: "\f4a6"; } +.bi-node-plus::before { content: "\f4a7"; } +.bi-nut-fill::before { content: "\f4a8"; } +.bi-nut::before { content: "\f4a9"; } +.bi-octagon-fill::before { content: "\f4aa"; } +.bi-octagon-half::before { content: "\f4ab"; } +.bi-octagon::before { content: "\f4ac"; } +.bi-option::before { content: "\f4ad"; } +.bi-outlet::before { content: "\f4ae"; } +.bi-paint-bucket::before { content: "\f4af"; } +.bi-palette-fill::before { content: "\f4b0"; } +.bi-palette::before { content: "\f4b1"; } +.bi-palette2::before { content: "\f4b2"; } +.bi-paperclip::before { content: "\f4b3"; } +.bi-paragraph::before { content: "\f4b4"; } +.bi-patch-check-fill::before { content: "\f4b5"; } +.bi-patch-check::before { content: "\f4b6"; } +.bi-patch-exclamation-fill::before { content: "\f4b7"; } +.bi-patch-exclamation::before { content: "\f4b8"; } +.bi-patch-minus-fill::before { content: "\f4b9"; } +.bi-patch-minus::before { content: "\f4ba"; } +.bi-patch-plus-fill::before { content: "\f4bb"; } +.bi-patch-plus::before { content: "\f4bc"; } +.bi-patch-question-fill::before { content: "\f4bd"; } +.bi-patch-question::before { content: "\f4be"; } +.bi-pause-btn-fill::before { content: "\f4bf"; } +.bi-pause-btn::before { content: "\f4c0"; } +.bi-pause-circle-fill::before { content: "\f4c1"; } +.bi-pause-circle::before { content: "\f4c2"; } +.bi-pause-fill::before { content: "\f4c3"; } +.bi-pause::before { content: "\f4c4"; } +.bi-peace-fill::before { content: "\f4c5"; } +.bi-peace::before { content: "\f4c6"; } +.bi-pen-fill::before { content: "\f4c7"; } +.bi-pen::before { content: "\f4c8"; } +.bi-pencil-fill::before { content: "\f4c9"; } +.bi-pencil-square::before { content: "\f4ca"; } +.bi-pencil::before { content: "\f4cb"; } +.bi-pentagon-fill::before { content: "\f4cc"; } +.bi-pentagon-half::before { content: "\f4cd"; } +.bi-pentagon::before { content: "\f4ce"; } +.bi-people-fill::before { content: "\f4cf"; } +.bi-people::before { content: "\f4d0"; } +.bi-percent::before { content: "\f4d1"; } +.bi-person-badge-fill::before { content: "\f4d2"; } +.bi-person-badge::before { content: "\f4d3"; } +.bi-person-bounding-box::before { content: "\f4d4"; } +.bi-person-check-fill::before { content: "\f4d5"; } +.bi-person-check::before { content: "\f4d6"; } +.bi-person-circle::before { content: "\f4d7"; } +.bi-person-dash-fill::before { content: "\f4d8"; } +.bi-person-dash::before { content: "\f4d9"; } +.bi-person-fill::before { content: "\f4da"; } +.bi-person-lines-fill::before { content: "\f4db"; } +.bi-person-plus-fill::before { content: "\f4dc"; } +.bi-person-plus::before { content: "\f4dd"; } +.bi-person-square::before { content: "\f4de"; } +.bi-person-x-fill::before { content: "\f4df"; } +.bi-person-x::before { content: "\f4e0"; } +.bi-person::before { content: "\f4e1"; } +.bi-phone-fill::before { content: "\f4e2"; } +.bi-phone-landscape-fill::before { content: "\f4e3"; } +.bi-phone-landscape::before { content: "\f4e4"; } +.bi-phone-vibrate-fill::before { content: "\f4e5"; } +.bi-phone-vibrate::before { content: "\f4e6"; } +.bi-phone::before { content: "\f4e7"; } +.bi-pie-chart-fill::before { content: "\f4e8"; } +.bi-pie-chart::before { content: "\f4e9"; } +.bi-pin-angle-fill::before { content: "\f4ea"; } +.bi-pin-angle::before { content: "\f4eb"; } +.bi-pin-fill::before { content: "\f4ec"; } +.bi-pin::before { content: "\f4ed"; } +.bi-pip-fill::before { content: "\f4ee"; } +.bi-pip::before { content: "\f4ef"; } +.bi-play-btn-fill::before { content: "\f4f0"; } +.bi-play-btn::before { content: "\f4f1"; } +.bi-play-circle-fill::before { content: "\f4f2"; } +.bi-play-circle::before { content: "\f4f3"; } +.bi-play-fill::before { content: "\f4f4"; } +.bi-play::before { content: "\f4f5"; } +.bi-plug-fill::before { content: "\f4f6"; } +.bi-plug::before { content: "\f4f7"; } +.bi-plus-circle-dotted::before { content: "\f4f8"; } +.bi-plus-circle-fill::before { content: "\f4f9"; } +.bi-plus-circle::before { content: "\f4fa"; } +.bi-plus-square-dotted::before { content: "\f4fb"; } +.bi-plus-square-fill::before { content: "\f4fc"; } +.bi-plus-square::before { content: "\f4fd"; } +.bi-plus::before { content: "\f4fe"; } +.bi-power::before { content: "\f4ff"; } +.bi-printer-fill::before { content: "\f500"; } +.bi-printer::before { content: "\f501"; } +.bi-puzzle-fill::before { content: "\f502"; } +.bi-puzzle::before { content: "\f503"; } +.bi-question-circle-fill::before { content: "\f504"; } +.bi-question-circle::before { content: "\f505"; } +.bi-question-diamond-fill::before { content: "\f506"; } +.bi-question-diamond::before { content: "\f507"; } +.bi-question-octagon-fill::before { content: "\f508"; } +.bi-question-octagon::before { content: "\f509"; } +.bi-question-square-fill::before { content: "\f50a"; } +.bi-question-square::before { content: "\f50b"; } +.bi-question::before { content: "\f50c"; } +.bi-rainbow::before { content: "\f50d"; } +.bi-receipt-cutoff::before { content: "\f50e"; } +.bi-receipt::before { content: "\f50f"; } +.bi-reception-0::before { content: "\f510"; } +.bi-reception-1::before { content: "\f511"; } +.bi-reception-2::before { content: "\f512"; } +.bi-reception-3::before { content: "\f513"; } +.bi-reception-4::before { content: "\f514"; } +.bi-record-btn-fill::before { content: "\f515"; } +.bi-record-btn::before { content: "\f516"; } +.bi-record-circle-fill::before { content: "\f517"; } +.bi-record-circle::before { content: "\f518"; } +.bi-record-fill::before { content: "\f519"; } +.bi-record::before { content: "\f51a"; } +.bi-record2-fill::before { content: "\f51b"; } +.bi-record2::before { content: "\f51c"; } +.bi-reply-all-fill::before { content: "\f51d"; } +.bi-reply-all::before { content: "\f51e"; } +.bi-reply-fill::before { content: "\f51f"; } +.bi-reply::before { content: "\f520"; } +.bi-rss-fill::before { content: "\f521"; } +.bi-rss::before { content: "\f522"; } +.bi-rulers::before { content: "\f523"; } +.bi-save-fill::before { content: "\f524"; } +.bi-save::before { content: "\f525"; } +.bi-save2-fill::before { content: "\f526"; } +.bi-save2::before { content: "\f527"; } +.bi-scissors::before { content: "\f528"; } +.bi-screwdriver::before { content: "\f529"; } +.bi-search::before { content: "\f52a"; } +.bi-segmented-nav::before { content: "\f52b"; } +.bi-server::before { content: "\f52c"; } +.bi-share-fill::before { content: "\f52d"; } +.bi-share::before { content: "\f52e"; } +.bi-shield-check::before { content: "\f52f"; } +.bi-shield-exclamation::before { content: "\f530"; } +.bi-shield-fill-check::before { content: "\f531"; } +.bi-shield-fill-exclamation::before { content: "\f532"; } +.bi-shield-fill-minus::before { content: "\f533"; } +.bi-shield-fill-plus::before { content: "\f534"; } +.bi-shield-fill-x::before { content: "\f535"; } +.bi-shield-fill::before { content: "\f536"; } +.bi-shield-lock-fill::before { content: "\f537"; } +.bi-shield-lock::before { content: "\f538"; } +.bi-shield-minus::before { content: "\f539"; } +.bi-shield-plus::before { content: "\f53a"; } +.bi-shield-shaded::before { content: "\f53b"; } +.bi-shield-slash-fill::before { content: "\f53c"; } +.bi-shield-slash::before { content: "\f53d"; } +.bi-shield-x::before { content: "\f53e"; } +.bi-shield::before { content: "\f53f"; } +.bi-shift-fill::before { content: "\f540"; } +.bi-shift::before { content: "\f541"; } +.bi-shop-window::before { content: "\f542"; } +.bi-shop::before { content: "\f543"; } +.bi-shuffle::before { content: "\f544"; } +.bi-signpost-2-fill::before { content: "\f545"; } +.bi-signpost-2::before { content: "\f546"; } +.bi-signpost-fill::before { content: "\f547"; } +.bi-signpost-split-fill::before { content: "\f548"; } +.bi-signpost-split::before { content: "\f549"; } +.bi-signpost::before { content: "\f54a"; } +.bi-sim-fill::before { content: "\f54b"; } +.bi-sim::before { content: "\f54c"; } +.bi-skip-backward-btn-fill::before { content: "\f54d"; } +.bi-skip-backward-btn::before { content: "\f54e"; } +.bi-skip-backward-circle-fill::before { content: "\f54f"; } +.bi-skip-backward-circle::before { content: "\f550"; } +.bi-skip-backward-fill::before { content: "\f551"; } +.bi-skip-backward::before { content: "\f552"; } +.bi-skip-end-btn-fill::before { content: "\f553"; } +.bi-skip-end-btn::before { content: "\f554"; } +.bi-skip-end-circle-fill::before { content: "\f555"; } +.bi-skip-end-circle::before { content: "\f556"; } +.bi-skip-end-fill::before { content: "\f557"; } +.bi-skip-end::before { content: "\f558"; } +.bi-skip-forward-btn-fill::before { content: "\f559"; } +.bi-skip-forward-btn::before { content: "\f55a"; } +.bi-skip-forward-circle-fill::before { content: "\f55b"; } +.bi-skip-forward-circle::before { content: "\f55c"; } +.bi-skip-forward-fill::before { content: "\f55d"; } +.bi-skip-forward::before { content: "\f55e"; } +.bi-skip-start-btn-fill::before { content: "\f55f"; } +.bi-skip-start-btn::before { content: "\f560"; } +.bi-skip-start-circle-fill::before { content: "\f561"; } +.bi-skip-start-circle::before { content: "\f562"; } +.bi-skip-start-fill::before { content: "\f563"; } +.bi-skip-start::before { content: "\f564"; } +.bi-slack::before { content: "\f565"; } +.bi-slash-circle-fill::before { content: "\f566"; } +.bi-slash-circle::before { content: "\f567"; } +.bi-slash-square-fill::before { content: "\f568"; } +.bi-slash-square::before { content: "\f569"; } +.bi-slash::before { content: "\f56a"; } +.bi-sliders::before { content: "\f56b"; } +.bi-smartwatch::before { content: "\f56c"; } +.bi-snow::before { content: "\f56d"; } +.bi-snow2::before { content: "\f56e"; } +.bi-snow3::before { content: "\f56f"; } +.bi-sort-alpha-down-alt::before { content: "\f570"; } +.bi-sort-alpha-down::before { content: "\f571"; } +.bi-sort-alpha-up-alt::before { content: "\f572"; } +.bi-sort-alpha-up::before { content: "\f573"; } +.bi-sort-down-alt::before { content: "\f574"; } +.bi-sort-down::before { content: "\f575"; } +.bi-sort-numeric-down-alt::before { content: "\f576"; } +.bi-sort-numeric-down::before { content: "\f577"; } +.bi-sort-numeric-up-alt::before { content: "\f578"; } +.bi-sort-numeric-up::before { content: "\f579"; } +.bi-sort-up-alt::before { content: "\f57a"; } +.bi-sort-up::before { content: "\f57b"; } +.bi-soundwave::before { content: "\f57c"; } +.bi-speaker-fill::before { content: "\f57d"; } +.bi-speaker::before { content: "\f57e"; } +.bi-speedometer::before { content: "\f57f"; } +.bi-speedometer2::before { content: "\f580"; } +.bi-spellcheck::before { content: "\f581"; } +.bi-square-fill::before { content: "\f582"; } +.bi-square-half::before { content: "\f583"; } +.bi-square::before { content: "\f584"; } +.bi-stack::before { content: "\f585"; } +.bi-star-fill::before { content: "\f586"; } +.bi-star-half::before { content: "\f587"; } +.bi-star::before { content: "\f588"; } +.bi-stars::before { content: "\f589"; } +.bi-stickies-fill::before { content: "\f58a"; } +.bi-stickies::before { content: "\f58b"; } +.bi-sticky-fill::before { content: "\f58c"; } +.bi-sticky::before { content: "\f58d"; } +.bi-stop-btn-fill::before { content: "\f58e"; } +.bi-stop-btn::before { content: "\f58f"; } +.bi-stop-circle-fill::before { content: "\f590"; } +.bi-stop-circle::before { content: "\f591"; } +.bi-stop-fill::before { content: "\f592"; } +.bi-stop::before { content: "\f593"; } +.bi-stoplights-fill::before { content: "\f594"; } +.bi-stoplights::before { content: "\f595"; } +.bi-stopwatch-fill::before { content: "\f596"; } +.bi-stopwatch::before { content: "\f597"; } +.bi-subtract::before { content: "\f598"; } +.bi-suit-club-fill::before { content: "\f599"; } +.bi-suit-club::before { content: "\f59a"; } +.bi-suit-diamond-fill::before { content: "\f59b"; } +.bi-suit-diamond::before { content: "\f59c"; } +.bi-suit-heart-fill::before { content: "\f59d"; } +.bi-suit-heart::before { content: "\f59e"; } +.bi-suit-spade-fill::before { content: "\f59f"; } +.bi-suit-spade::before { content: "\f5a0"; } +.bi-sun-fill::before { content: "\f5a1"; } +.bi-sun::before { content: "\f5a2"; } +.bi-sunglasses::before { content: "\f5a3"; } +.bi-sunrise-fill::before { content: "\f5a4"; } +.bi-sunrise::before { content: "\f5a5"; } +.bi-sunset-fill::before { content: "\f5a6"; } +.bi-sunset::before { content: "\f5a7"; } +.bi-symmetry-horizontal::before { content: "\f5a8"; } +.bi-symmetry-vertical::before { content: "\f5a9"; } +.bi-table::before { content: "\f5aa"; } +.bi-tablet-fill::before { content: "\f5ab"; } +.bi-tablet-landscape-fill::before { content: "\f5ac"; } +.bi-tablet-landscape::before { content: "\f5ad"; } +.bi-tablet::before { content: "\f5ae"; } +.bi-tag-fill::before { content: "\f5af"; } +.bi-tag::before { content: "\f5b0"; } +.bi-tags-fill::before { content: "\f5b1"; } +.bi-tags::before { content: "\f5b2"; } +.bi-telegram::before { content: "\f5b3"; } +.bi-telephone-fill::before { content: "\f5b4"; } +.bi-telephone-forward-fill::before { content: "\f5b5"; } +.bi-telephone-forward::before { content: "\f5b6"; } +.bi-telephone-inbound-fill::before { content: "\f5b7"; } +.bi-telephone-inbound::before { content: "\f5b8"; } +.bi-telephone-minus-fill::before { content: "\f5b9"; } +.bi-telephone-minus::before { content: "\f5ba"; } +.bi-telephone-outbound-fill::before { content: "\f5bb"; } +.bi-telephone-outbound::before { content: "\f5bc"; } +.bi-telephone-plus-fill::before { content: "\f5bd"; } +.bi-telephone-plus::before { content: "\f5be"; } +.bi-telephone-x-fill::before { content: "\f5bf"; } +.bi-telephone-x::before { content: "\f5c0"; } +.bi-telephone::before { content: "\f5c1"; } +.bi-terminal-fill::before { content: "\f5c2"; } +.bi-terminal::before { content: "\f5c3"; } +.bi-text-center::before { content: "\f5c4"; } +.bi-text-indent-left::before { content: "\f5c5"; } +.bi-text-indent-right::before { content: "\f5c6"; } +.bi-text-left::before { content: "\f5c7"; } +.bi-text-paragraph::before { content: "\f5c8"; } +.bi-text-right::before { content: "\f5c9"; } +.bi-textarea-resize::before { content: "\f5ca"; } +.bi-textarea-t::before { content: "\f5cb"; } +.bi-textarea::before { content: "\f5cc"; } +.bi-thermometer-half::before { content: "\f5cd"; } +.bi-thermometer-high::before { content: "\f5ce"; } +.bi-thermometer-low::before { content: "\f5cf"; } +.bi-thermometer-snow::before { content: "\f5d0"; } +.bi-thermometer-sun::before { content: "\f5d1"; } +.bi-thermometer::before { content: "\f5d2"; } +.bi-three-dots-vertical::before { content: "\f5d3"; } +.bi-three-dots::before { content: "\f5d4"; } +.bi-toggle-off::before { content: "\f5d5"; } +.bi-toggle-on::before { content: "\f5d6"; } +.bi-toggle2-off::before { content: "\f5d7"; } +.bi-toggle2-on::before { content: "\f5d8"; } +.bi-toggles::before { content: "\f5d9"; } +.bi-toggles2::before { content: "\f5da"; } +.bi-tools::before { content: "\f5db"; } +.bi-tornado::before { content: "\f5dc"; } +.bi-trash-fill::before { content: "\f5dd"; } +.bi-trash::before { content: "\f5de"; } +.bi-trash2-fill::before { content: "\f5df"; } +.bi-trash2::before { content: "\f5e0"; } +.bi-tree-fill::before { content: "\f5e1"; } +.bi-tree::before { content: "\f5e2"; } +.bi-triangle-fill::before { content: "\f5e3"; } +.bi-triangle-half::before { content: "\f5e4"; } +.bi-triangle::before { content: "\f5e5"; } +.bi-trophy-fill::before { content: "\f5e6"; } +.bi-trophy::before { content: "\f5e7"; } +.bi-tropical-storm::before { content: "\f5e8"; } +.bi-truck-flatbed::before { content: "\f5e9"; } +.bi-truck::before { content: "\f5ea"; } +.bi-tsunami::before { content: "\f5eb"; } +.bi-tv-fill::before { content: "\f5ec"; } +.bi-tv::before { content: "\f5ed"; } +.bi-twitch::before { content: "\f5ee"; } +.bi-twitter::before { content: "\f5ef"; } +.bi-type-bold::before { content: "\f5f0"; } +.bi-type-h1::before { content: "\f5f1"; } +.bi-type-h2::before { content: "\f5f2"; } +.bi-type-h3::before { content: "\f5f3"; } +.bi-type-italic::before { content: "\f5f4"; } +.bi-type-strikethrough::before { content: "\f5f5"; } +.bi-type-underline::before { content: "\f5f6"; } +.bi-type::before { content: "\f5f7"; } +.bi-ui-checks-grid::before { content: "\f5f8"; } +.bi-ui-checks::before { content: "\f5f9"; } +.bi-ui-radios-grid::before { content: "\f5fa"; } +.bi-ui-radios::before { content: "\f5fb"; } +.bi-umbrella-fill::before { content: "\f5fc"; } +.bi-umbrella::before { content: "\f5fd"; } +.bi-union::before { content: "\f5fe"; } +.bi-unlock-fill::before { content: "\f5ff"; } +.bi-unlock::before { content: "\f600"; } +.bi-upc-scan::before { content: "\f601"; } +.bi-upc::before { content: "\f602"; } +.bi-upload::before { content: "\f603"; } +.bi-vector-pen::before { content: "\f604"; } +.bi-view-list::before { content: "\f605"; } +.bi-view-stacked::before { content: "\f606"; } +.bi-vinyl-fill::before { content: "\f607"; } +.bi-vinyl::before { content: "\f608"; } +.bi-voicemail::before { content: "\f609"; } +.bi-volume-down-fill::before { content: "\f60a"; } +.bi-volume-down::before { content: "\f60b"; } +.bi-volume-mute-fill::before { content: "\f60c"; } +.bi-volume-mute::before { content: "\f60d"; } +.bi-volume-off-fill::before { content: "\f60e"; } +.bi-volume-off::before { content: "\f60f"; } +.bi-volume-up-fill::before { content: "\f610"; } +.bi-volume-up::before { content: "\f611"; } +.bi-vr::before { content: "\f612"; } +.bi-wallet-fill::before { content: "\f613"; } +.bi-wallet::before { content: "\f614"; } +.bi-wallet2::before { content: "\f615"; } +.bi-watch::before { content: "\f616"; } +.bi-water::before { content: "\f617"; } +.bi-whatsapp::before { content: "\f618"; } +.bi-wifi-1::before { content: "\f619"; } +.bi-wifi-2::before { content: "\f61a"; } +.bi-wifi-off::before { content: "\f61b"; } +.bi-wifi::before { content: "\f61c"; } +.bi-wind::before { content: "\f61d"; } +.bi-window-dock::before { content: "\f61e"; } +.bi-window-sidebar::before { content: "\f61f"; } +.bi-window::before { content: "\f620"; } +.bi-wrench::before { content: "\f621"; } +.bi-x-circle-fill::before { content: "\f622"; } +.bi-x-circle::before { content: "\f623"; } +.bi-x-diamond-fill::before { content: "\f624"; } +.bi-x-diamond::before { content: "\f625"; } +.bi-x-octagon-fill::before { content: "\f626"; } +.bi-x-octagon::before { content: "\f627"; } +.bi-x-square-fill::before { content: "\f628"; } +.bi-x-square::before { content: "\f629"; } +.bi-x::before { content: "\f62a"; } +.bi-youtube::before { content: "\f62b"; } +.bi-zoom-in::before { content: "\f62c"; } +.bi-zoom-out::before { content: "\f62d"; } +.bi-bank::before { content: "\f62e"; } +.bi-bank2::before { content: "\f62f"; } +.bi-bell-slash-fill::before { content: "\f630"; } +.bi-bell-slash::before { content: "\f631"; } +.bi-cash-coin::before { content: "\f632"; } +.bi-check-lg::before { content: "\f633"; } +.bi-coin::before { content: "\f634"; } +.bi-currency-bitcoin::before { content: "\f635"; } +.bi-currency-dollar::before { content: "\f636"; } +.bi-currency-euro::before { content: "\f637"; } +.bi-currency-exchange::before { content: "\f638"; } +.bi-currency-pound::before { content: "\f639"; } +.bi-currency-yen::before { content: "\f63a"; } +.bi-dash-lg::before { content: "\f63b"; } +.bi-exclamation-lg::before { content: "\f63c"; } +.bi-file-earmark-pdf-fill::before { content: "\f63d"; } +.bi-file-earmark-pdf::before { content: "\f63e"; } +.bi-file-pdf-fill::before { content: "\f63f"; } +.bi-file-pdf::before { content: "\f640"; } +.bi-gender-ambiguous::before { content: "\f641"; } +.bi-gender-female::before { content: "\f642"; } +.bi-gender-male::before { content: "\f643"; } +.bi-gender-trans::before { content: "\f644"; } +.bi-headset-vr::before { content: "\f645"; } +.bi-info-lg::before { content: "\f646"; } +.bi-mastodon::before { content: "\f647"; } +.bi-messenger::before { content: "\f648"; } +.bi-piggy-bank-fill::before { content: "\f649"; } +.bi-piggy-bank::before { content: "\f64a"; } +.bi-pin-map-fill::before { content: "\f64b"; } +.bi-pin-map::before { content: "\f64c"; } +.bi-plus-lg::before { content: "\f64d"; } +.bi-question-lg::before { content: "\f64e"; } +.bi-recycle::before { content: "\f64f"; } +.bi-reddit::before { content: "\f650"; } +.bi-safe-fill::before { content: "\f651"; } +.bi-safe2-fill::before { content: "\f652"; } +.bi-safe2::before { content: "\f653"; } +.bi-sd-card-fill::before { content: "\f654"; } +.bi-sd-card::before { content: "\f655"; } +.bi-skype::before { content: "\f656"; } +.bi-slash-lg::before { content: "\f657"; } +.bi-translate::before { content: "\f658"; } +.bi-x-lg::before { content: "\f659"; } +.bi-safe::before { content: "\f65a"; } +.bi-apple::before { content: "\f65b"; } +.bi-microsoft::before { content: "\f65d"; } +.bi-windows::before { content: "\f65e"; } +.bi-behance::before { content: "\f65c"; } +.bi-dribbble::before { content: "\f65f"; } +.bi-line::before { content: "\f660"; } +.bi-medium::before { content: "\f661"; } +.bi-paypal::before { content: "\f662"; } +.bi-pinterest::before { content: "\f663"; } +.bi-signal::before { content: "\f664"; } +.bi-snapchat::before { content: "\f665"; } +.bi-spotify::before { content: "\f666"; } +.bi-stack-overflow::before { content: "\f667"; } +.bi-strava::before { content: "\f668"; } +.bi-wordpress::before { content: "\f669"; } +.bi-vimeo::before { content: "\f66a"; } +.bi-activity::before { content: "\f66b"; } +.bi-easel2-fill::before { content: "\f66c"; } +.bi-easel2::before { content: "\f66d"; } +.bi-easel3-fill::before { content: "\f66e"; } +.bi-easel3::before { content: "\f66f"; } +.bi-fan::before { content: "\f670"; } +.bi-fingerprint::before { content: "\f671"; } +.bi-graph-down-arrow::before { content: "\f672"; } +.bi-graph-up-arrow::before { content: "\f673"; } +.bi-hypnotize::before { content: "\f674"; } +.bi-magic::before { content: "\f675"; } +.bi-person-rolodex::before { content: "\f676"; } +.bi-person-video::before { content: "\f677"; } +.bi-person-video2::before { content: "\f678"; } +.bi-person-video3::before { content: "\f679"; } +.bi-person-workspace::before { content: "\f67a"; } +.bi-radioactive::before { content: "\f67b"; } +.bi-webcam-fill::before { content: "\f67c"; } +.bi-webcam::before { content: "\f67d"; } +.bi-yin-yang::before { content: "\f67e"; } +.bi-bandaid-fill::before { content: "\f680"; } +.bi-bandaid::before { content: "\f681"; } +.bi-bluetooth::before { content: "\f682"; } +.bi-body-text::before { content: "\f683"; } +.bi-boombox::before { content: "\f684"; } +.bi-boxes::before { content: "\f685"; } +.bi-dpad-fill::before { content: "\f686"; } +.bi-dpad::before { content: "\f687"; } +.bi-ear-fill::before { content: "\f688"; } +.bi-ear::before { content: "\f689"; } +.bi-envelope-check-fill::before { content: "\f68b"; } +.bi-envelope-check::before { content: "\f68c"; } +.bi-envelope-dash-fill::before { content: "\f68e"; } +.bi-envelope-dash::before { content: "\f68f"; } +.bi-envelope-exclamation-fill::before { content: "\f691"; } +.bi-envelope-exclamation::before { content: "\f692"; } +.bi-envelope-plus-fill::before { content: "\f693"; } +.bi-envelope-plus::before { content: "\f694"; } +.bi-envelope-slash-fill::before { content: "\f696"; } +.bi-envelope-slash::before { content: "\f697"; } +.bi-envelope-x-fill::before { content: "\f699"; } +.bi-envelope-x::before { content: "\f69a"; } +.bi-explicit-fill::before { content: "\f69b"; } +.bi-explicit::before { content: "\f69c"; } +.bi-git::before { content: "\f69d"; } +.bi-infinity::before { content: "\f69e"; } +.bi-list-columns-reverse::before { content: "\f69f"; } +.bi-list-columns::before { content: "\f6a0"; } +.bi-meta::before { content: "\f6a1"; } +.bi-nintendo-switch::before { content: "\f6a4"; } +.bi-pc-display-horizontal::before { content: "\f6a5"; } +.bi-pc-display::before { content: "\f6a6"; } +.bi-pc-horizontal::before { content: "\f6a7"; } +.bi-pc::before { content: "\f6a8"; } +.bi-playstation::before { content: "\f6a9"; } +.bi-plus-slash-minus::before { content: "\f6aa"; } +.bi-projector-fill::before { content: "\f6ab"; } +.bi-projector::before { content: "\f6ac"; } +.bi-qr-code-scan::before { content: "\f6ad"; } +.bi-qr-code::before { content: "\f6ae"; } +.bi-quora::before { content: "\f6af"; } +.bi-quote::before { content: "\f6b0"; } +.bi-robot::before { content: "\f6b1"; } +.bi-send-check-fill::before { content: "\f6b2"; } +.bi-send-check::before { content: "\f6b3"; } +.bi-send-dash-fill::before { content: "\f6b4"; } +.bi-send-dash::before { content: "\f6b5"; } +.bi-send-exclamation-fill::before { content: "\f6b7"; } +.bi-send-exclamation::before { content: "\f6b8"; } +.bi-send-fill::before { content: "\f6b9"; } +.bi-send-plus-fill::before { content: "\f6ba"; } +.bi-send-plus::before { content: "\f6bb"; } +.bi-send-slash-fill::before { content: "\f6bc"; } +.bi-send-slash::before { content: "\f6bd"; } +.bi-send-x-fill::before { content: "\f6be"; } +.bi-send-x::before { content: "\f6bf"; } +.bi-send::before { content: "\f6c0"; } +.bi-steam::before { content: "\f6c1"; } +.bi-terminal-dash::before { content: "\f6c3"; } +.bi-terminal-plus::before { content: "\f6c4"; } +.bi-terminal-split::before { content: "\f6c5"; } +.bi-ticket-detailed-fill::before { content: "\f6c6"; } +.bi-ticket-detailed::before { content: "\f6c7"; } +.bi-ticket-fill::before { content: "\f6c8"; } +.bi-ticket-perforated-fill::before { content: "\f6c9"; } +.bi-ticket-perforated::before { content: "\f6ca"; } +.bi-ticket::before { content: "\f6cb"; } +.bi-tiktok::before { content: "\f6cc"; } +.bi-window-dash::before { content: "\f6cd"; } +.bi-window-desktop::before { content: "\f6ce"; } +.bi-window-fullscreen::before { content: "\f6cf"; } +.bi-window-plus::before { content: "\f6d0"; } +.bi-window-split::before { content: "\f6d1"; } +.bi-window-stack::before { content: "\f6d2"; } +.bi-window-x::before { content: "\f6d3"; } +.bi-xbox::before { content: "\f6d4"; } +.bi-ethernet::before { content: "\f6d5"; } +.bi-hdmi-fill::before { content: "\f6d6"; } +.bi-hdmi::before { content: "\f6d7"; } +.bi-usb-c-fill::before { content: "\f6d8"; } +.bi-usb-c::before { content: "\f6d9"; } +.bi-usb-fill::before { content: "\f6da"; } +.bi-usb-plug-fill::before { content: "\f6db"; } +.bi-usb-plug::before { content: "\f6dc"; } +.bi-usb-symbol::before { content: "\f6dd"; } +.bi-usb::before { content: "\f6de"; } +.bi-boombox-fill::before { content: "\f6df"; } +.bi-displayport::before { content: "\f6e1"; } +.bi-gpu-card::before { content: "\f6e2"; } +.bi-memory::before { content: "\f6e3"; } +.bi-modem-fill::before { content: "\f6e4"; } +.bi-modem::before { content: "\f6e5"; } +.bi-motherboard-fill::before { content: "\f6e6"; } +.bi-motherboard::before { content: "\f6e7"; } +.bi-optical-audio-fill::before { content: "\f6e8"; } +.bi-optical-audio::before { content: "\f6e9"; } +.bi-pci-card::before { content: "\f6ea"; } +.bi-router-fill::before { content: "\f6eb"; } +.bi-router::before { content: "\f6ec"; } +.bi-thunderbolt-fill::before { content: "\f6ef"; } +.bi-thunderbolt::before { content: "\f6f0"; } +.bi-usb-drive-fill::before { content: "\f6f1"; } +.bi-usb-drive::before { content: "\f6f2"; } +.bi-usb-micro-fill::before { content: "\f6f3"; } +.bi-usb-micro::before { content: "\f6f4"; } +.bi-usb-mini-fill::before { content: "\f6f5"; } +.bi-usb-mini::before { content: "\f6f6"; } +.bi-cloud-haze2::before { content: "\f6f7"; } +.bi-device-hdd-fill::before { content: "\f6f8"; } +.bi-device-hdd::before { content: "\f6f9"; } +.bi-device-ssd-fill::before { content: "\f6fa"; } +.bi-device-ssd::before { content: "\f6fb"; } +.bi-displayport-fill::before { content: "\f6fc"; } +.bi-mortarboard-fill::before { content: "\f6fd"; } +.bi-mortarboard::before { content: "\f6fe"; } +.bi-terminal-x::before { content: "\f6ff"; } +.bi-arrow-through-heart-fill::before { content: "\f700"; } +.bi-arrow-through-heart::before { content: "\f701"; } +.bi-badge-sd-fill::before { content: "\f702"; } +.bi-badge-sd::before { content: "\f703"; } +.bi-bag-heart-fill::before { content: "\f704"; } +.bi-bag-heart::before { content: "\f705"; } +.bi-balloon-fill::before { content: "\f706"; } +.bi-balloon-heart-fill::before { content: "\f707"; } +.bi-balloon-heart::before { content: "\f708"; } +.bi-balloon::before { content: "\f709"; } +.bi-box2-fill::before { content: "\f70a"; } +.bi-box2-heart-fill::before { content: "\f70b"; } +.bi-box2-heart::before { content: "\f70c"; } +.bi-box2::before { content: "\f70d"; } +.bi-braces-asterisk::before { content: "\f70e"; } +.bi-calendar-heart-fill::before { content: "\f70f"; } +.bi-calendar-heart::before { content: "\f710"; } +.bi-calendar2-heart-fill::before { content: "\f711"; } +.bi-calendar2-heart::before { content: "\f712"; } +.bi-chat-heart-fill::before { content: "\f713"; } +.bi-chat-heart::before { content: "\f714"; } +.bi-chat-left-heart-fill::before { content: "\f715"; } +.bi-chat-left-heart::before { content: "\f716"; } +.bi-chat-right-heart-fill::before { content: "\f717"; } +.bi-chat-right-heart::before { content: "\f718"; } +.bi-chat-square-heart-fill::before { content: "\f719"; } +.bi-chat-square-heart::before { content: "\f71a"; } +.bi-clipboard-check-fill::before { content: "\f71b"; } +.bi-clipboard-data-fill::before { content: "\f71c"; } +.bi-clipboard-fill::before { content: "\f71d"; } +.bi-clipboard-heart-fill::before { content: "\f71e"; } +.bi-clipboard-heart::before { content: "\f71f"; } +.bi-clipboard-minus-fill::before { content: "\f720"; } +.bi-clipboard-plus-fill::before { content: "\f721"; } +.bi-clipboard-pulse::before { content: "\f722"; } +.bi-clipboard-x-fill::before { content: "\f723"; } +.bi-clipboard2-check-fill::before { content: "\f724"; } +.bi-clipboard2-check::before { content: "\f725"; } +.bi-clipboard2-data-fill::before { content: "\f726"; } +.bi-clipboard2-data::before { content: "\f727"; } +.bi-clipboard2-fill::before { content: "\f728"; } +.bi-clipboard2-heart-fill::before { content: "\f729"; } +.bi-clipboard2-heart::before { content: "\f72a"; } +.bi-clipboard2-minus-fill::before { content: "\f72b"; } +.bi-clipboard2-minus::before { content: "\f72c"; } +.bi-clipboard2-plus-fill::before { content: "\f72d"; } +.bi-clipboard2-plus::before { content: "\f72e"; } +.bi-clipboard2-pulse-fill::before { content: "\f72f"; } +.bi-clipboard2-pulse::before { content: "\f730"; } +.bi-clipboard2-x-fill::before { content: "\f731"; } +.bi-clipboard2-x::before { content: "\f732"; } +.bi-clipboard2::before { content: "\f733"; } +.bi-emoji-kiss-fill::before { content: "\f734"; } +.bi-emoji-kiss::before { content: "\f735"; } +.bi-envelope-heart-fill::before { content: "\f736"; } +.bi-envelope-heart::before { content: "\f737"; } +.bi-envelope-open-heart-fill::before { content: "\f738"; } +.bi-envelope-open-heart::before { content: "\f739"; } +.bi-envelope-paper-fill::before { content: "\f73a"; } +.bi-envelope-paper-heart-fill::before { content: "\f73b"; } +.bi-envelope-paper-heart::before { content: "\f73c"; } +.bi-envelope-paper::before { content: "\f73d"; } +.bi-filetype-aac::before { content: "\f73e"; } +.bi-filetype-ai::before { content: "\f73f"; } +.bi-filetype-bmp::before { content: "\f740"; } +.bi-filetype-cs::before { content: "\f741"; } +.bi-filetype-css::before { content: "\f742"; } +.bi-filetype-csv::before { content: "\f743"; } +.bi-filetype-doc::before { content: "\f744"; } +.bi-filetype-docx::before { content: "\f745"; } +.bi-filetype-exe::before { content: "\f746"; } +.bi-filetype-gif::before { content: "\f747"; } +.bi-filetype-heic::before { content: "\f748"; } +.bi-filetype-html::before { content: "\f749"; } +.bi-filetype-java::before { content: "\f74a"; } +.bi-filetype-jpg::before { content: "\f74b"; } +.bi-filetype-js::before { content: "\f74c"; } +.bi-filetype-jsx::before { content: "\f74d"; } +.bi-filetype-key::before { content: "\f74e"; } +.bi-filetype-m4p::before { content: "\f74f"; } +.bi-filetype-md::before { content: "\f750"; } +.bi-filetype-mdx::before { content: "\f751"; } +.bi-filetype-mov::before { content: "\f752"; } +.bi-filetype-mp3::before { content: "\f753"; } +.bi-filetype-mp4::before { content: "\f754"; } +.bi-filetype-otf::before { content: "\f755"; } +.bi-filetype-pdf::before { content: "\f756"; } +.bi-filetype-php::before { content: "\f757"; } +.bi-filetype-png::before { content: "\f758"; } +.bi-filetype-ppt::before { content: "\f75a"; } +.bi-filetype-psd::before { content: "\f75b"; } +.bi-filetype-py::before { content: "\f75c"; } +.bi-filetype-raw::before { content: "\f75d"; } +.bi-filetype-rb::before { content: "\f75e"; } +.bi-filetype-sass::before { content: "\f75f"; } +.bi-filetype-scss::before { content: "\f760"; } +.bi-filetype-sh::before { content: "\f761"; } +.bi-filetype-svg::before { content: "\f762"; } +.bi-filetype-tiff::before { content: "\f763"; } +.bi-filetype-tsx::before { content: "\f764"; } +.bi-filetype-ttf::before { content: "\f765"; } +.bi-filetype-txt::before { content: "\f766"; } +.bi-filetype-wav::before { content: "\f767"; } +.bi-filetype-woff::before { content: "\f768"; } +.bi-filetype-xls::before { content: "\f76a"; } +.bi-filetype-xml::before { content: "\f76b"; } +.bi-filetype-yml::before { content: "\f76c"; } +.bi-heart-arrow::before { content: "\f76d"; } +.bi-heart-pulse-fill::before { content: "\f76e"; } +.bi-heart-pulse::before { content: "\f76f"; } +.bi-heartbreak-fill::before { content: "\f770"; } +.bi-heartbreak::before { content: "\f771"; } +.bi-hearts::before { content: "\f772"; } +.bi-hospital-fill::before { content: "\f773"; } +.bi-hospital::before { content: "\f774"; } +.bi-house-heart-fill::before { content: "\f775"; } +.bi-house-heart::before { content: "\f776"; } +.bi-incognito::before { content: "\f777"; } +.bi-magnet-fill::before { content: "\f778"; } +.bi-magnet::before { content: "\f779"; } +.bi-person-heart::before { content: "\f77a"; } +.bi-person-hearts::before { content: "\f77b"; } +.bi-phone-flip::before { content: "\f77c"; } +.bi-plugin::before { content: "\f77d"; } +.bi-postage-fill::before { content: "\f77e"; } +.bi-postage-heart-fill::before { content: "\f77f"; } +.bi-postage-heart::before { content: "\f780"; } +.bi-postage::before { content: "\f781"; } +.bi-postcard-fill::before { content: "\f782"; } +.bi-postcard-heart-fill::before { content: "\f783"; } +.bi-postcard-heart::before { content: "\f784"; } +.bi-postcard::before { content: "\f785"; } +.bi-search-heart-fill::before { content: "\f786"; } +.bi-search-heart::before { content: "\f787"; } +.bi-sliders2-vertical::before { content: "\f788"; } +.bi-sliders2::before { content: "\f789"; } +.bi-trash3-fill::before { content: "\f78a"; } +.bi-trash3::before { content: "\f78b"; } +.bi-valentine::before { content: "\f78c"; } +.bi-valentine2::before { content: "\f78d"; } +.bi-wrench-adjustable-circle-fill::before { content: "\f78e"; } +.bi-wrench-adjustable-circle::before { content: "\f78f"; } +.bi-wrench-adjustable::before { content: "\f790"; } +.bi-filetype-json::before { content: "\f791"; } +.bi-filetype-pptx::before { content: "\f792"; } +.bi-filetype-xlsx::before { content: "\f793"; } +.bi-1-circle-fill::before { content: "\f796"; } +.bi-1-circle::before { content: "\f797"; } +.bi-1-square-fill::before { content: "\f798"; } +.bi-1-square::before { content: "\f799"; } +.bi-2-circle-fill::before { content: "\f79c"; } +.bi-2-circle::before { content: "\f79d"; } +.bi-2-square-fill::before { content: "\f79e"; } +.bi-2-square::before { content: "\f79f"; } +.bi-3-circle-fill::before { content: "\f7a2"; } +.bi-3-circle::before { content: "\f7a3"; } +.bi-3-square-fill::before { content: "\f7a4"; } +.bi-3-square::before { content: "\f7a5"; } +.bi-4-circle-fill::before { content: "\f7a8"; } +.bi-4-circle::before { content: "\f7a9"; } +.bi-4-square-fill::before { content: "\f7aa"; } +.bi-4-square::before { content: "\f7ab"; } +.bi-5-circle-fill::before { content: "\f7ae"; } +.bi-5-circle::before { content: "\f7af"; } +.bi-5-square-fill::before { content: "\f7b0"; } +.bi-5-square::before { content: "\f7b1"; } +.bi-6-circle-fill::before { content: "\f7b4"; } +.bi-6-circle::before { content: "\f7b5"; } +.bi-6-square-fill::before { content: "\f7b6"; } +.bi-6-square::before { content: "\f7b7"; } +.bi-7-circle-fill::before { content: "\f7ba"; } +.bi-7-circle::before { content: "\f7bb"; } +.bi-7-square-fill::before { content: "\f7bc"; } +.bi-7-square::before { content: "\f7bd"; } +.bi-8-circle-fill::before { content: "\f7c0"; } +.bi-8-circle::before { content: "\f7c1"; } +.bi-8-square-fill::before { content: "\f7c2"; } +.bi-8-square::before { content: "\f7c3"; } +.bi-9-circle-fill::before { content: "\f7c6"; } +.bi-9-circle::before { content: "\f7c7"; } +.bi-9-square-fill::before { content: "\f7c8"; } +.bi-9-square::before { content: "\f7c9"; } +.bi-airplane-engines-fill::before { content: "\f7ca"; } +.bi-airplane-engines::before { content: "\f7cb"; } +.bi-airplane-fill::before { content: "\f7cc"; } +.bi-airplane::before { content: "\f7cd"; } +.bi-alexa::before { content: "\f7ce"; } +.bi-alipay::before { content: "\f7cf"; } +.bi-android::before { content: "\f7d0"; } +.bi-android2::before { content: "\f7d1"; } +.bi-box-fill::before { content: "\f7d2"; } +.bi-box-seam-fill::before { content: "\f7d3"; } +.bi-browser-chrome::before { content: "\f7d4"; } +.bi-browser-edge::before { content: "\f7d5"; } +.bi-browser-firefox::before { content: "\f7d6"; } +.bi-browser-safari::before { content: "\f7d7"; } +.bi-c-circle-fill::before { content: "\f7da"; } +.bi-c-circle::before { content: "\f7db"; } +.bi-c-square-fill::before { content: "\f7dc"; } +.bi-c-square::before { content: "\f7dd"; } +.bi-capsule-pill::before { content: "\f7de"; } +.bi-capsule::before { content: "\f7df"; } +.bi-car-front-fill::before { content: "\f7e0"; } +.bi-car-front::before { content: "\f7e1"; } +.bi-cassette-fill::before { content: "\f7e2"; } +.bi-cassette::before { content: "\f7e3"; } +.bi-cc-circle-fill::before { content: "\f7e6"; } +.bi-cc-circle::before { content: "\f7e7"; } +.bi-cc-square-fill::before { content: "\f7e8"; } +.bi-cc-square::before { content: "\f7e9"; } +.bi-cup-hot-fill::before { content: "\f7ea"; } +.bi-cup-hot::before { content: "\f7eb"; } +.bi-currency-rupee::before { content: "\f7ec"; } +.bi-dropbox::before { content: "\f7ed"; } +.bi-escape::before { content: "\f7ee"; } +.bi-fast-forward-btn-fill::before { content: "\f7ef"; } +.bi-fast-forward-btn::before { content: "\f7f0"; } +.bi-fast-forward-circle-fill::before { content: "\f7f1"; } +.bi-fast-forward-circle::before { content: "\f7f2"; } +.bi-fast-forward-fill::before { content: "\f7f3"; } +.bi-fast-forward::before { content: "\f7f4"; } +.bi-filetype-sql::before { content: "\f7f5"; } +.bi-fire::before { content: "\f7f6"; } +.bi-google-play::before { content: "\f7f7"; } +.bi-h-circle-fill::before { content: "\f7fa"; } +.bi-h-circle::before { content: "\f7fb"; } +.bi-h-square-fill::before { content: "\f7fc"; } +.bi-h-square::before { content: "\f7fd"; } +.bi-indent::before { content: "\f7fe"; } +.bi-lungs-fill::before { content: "\f7ff"; } +.bi-lungs::before { content: "\f800"; } +.bi-microsoft-teams::before { content: "\f801"; } +.bi-p-circle-fill::before { content: "\f804"; } +.bi-p-circle::before { content: "\f805"; } +.bi-p-square-fill::before { content: "\f806"; } +.bi-p-square::before { content: "\f807"; } +.bi-pass-fill::before { content: "\f808"; } +.bi-pass::before { content: "\f809"; } +.bi-prescription::before { content: "\f80a"; } +.bi-prescription2::before { content: "\f80b"; } +.bi-r-circle-fill::before { content: "\f80e"; } +.bi-r-circle::before { content: "\f80f"; } +.bi-r-square-fill::before { content: "\f810"; } +.bi-r-square::before { content: "\f811"; } +.bi-repeat-1::before { content: "\f812"; } +.bi-repeat::before { content: "\f813"; } +.bi-rewind-btn-fill::before { content: "\f814"; } +.bi-rewind-btn::before { content: "\f815"; } +.bi-rewind-circle-fill::before { content: "\f816"; } +.bi-rewind-circle::before { content: "\f817"; } +.bi-rewind-fill::before { content: "\f818"; } +.bi-rewind::before { content: "\f819"; } +.bi-train-freight-front-fill::before { content: "\f81a"; } +.bi-train-freight-front::before { content: "\f81b"; } +.bi-train-front-fill::before { content: "\f81c"; } +.bi-train-front::before { content: "\f81d"; } +.bi-train-lightrail-front-fill::before { content: "\f81e"; } +.bi-train-lightrail-front::before { content: "\f81f"; } +.bi-truck-front-fill::before { content: "\f820"; } +.bi-truck-front::before { content: "\f821"; } +.bi-ubuntu::before { content: "\f822"; } +.bi-unindent::before { content: "\f823"; } +.bi-unity::before { content: "\f824"; } +.bi-universal-access-circle::before { content: "\f825"; } +.bi-universal-access::before { content: "\f826"; } +.bi-virus::before { content: "\f827"; } +.bi-virus2::before { content: "\f828"; } +.bi-wechat::before { content: "\f829"; } +.bi-yelp::before { content: "\f82a"; } +.bi-sign-stop-fill::before { content: "\f82b"; } +.bi-sign-stop-lights-fill::before { content: "\f82c"; } +.bi-sign-stop-lights::before { content: "\f82d"; } +.bi-sign-stop::before { content: "\f82e"; } +.bi-sign-turn-left-fill::before { content: "\f82f"; } +.bi-sign-turn-left::before { content: "\f830"; } +.bi-sign-turn-right-fill::before { content: "\f831"; } +.bi-sign-turn-right::before { content: "\f832"; } +.bi-sign-turn-slight-left-fill::before { content: "\f833"; } +.bi-sign-turn-slight-left::before { content: "\f834"; } +.bi-sign-turn-slight-right-fill::before { content: "\f835"; } +.bi-sign-turn-slight-right::before { content: "\f836"; } +.bi-sign-yield-fill::before { content: "\f837"; } +.bi-sign-yield::before { content: "\f838"; } +.bi-ev-station-fill::before { content: "\f839"; } +.bi-ev-station::before { content: "\f83a"; } +.bi-fuel-pump-diesel-fill::before { content: "\f83b"; } +.bi-fuel-pump-diesel::before { content: "\f83c"; } +.bi-fuel-pump-fill::before { content: "\f83d"; } +.bi-fuel-pump::before { content: "\f83e"; } +.bi-0-circle-fill::before { content: "\f83f"; } +.bi-0-circle::before { content: "\f840"; } +.bi-0-square-fill::before { content: "\f841"; } +.bi-0-square::before { content: "\f842"; } +.bi-rocket-fill::before { content: "\f843"; } +.bi-rocket-takeoff-fill::before { content: "\f844"; } +.bi-rocket-takeoff::before { content: "\f845"; } +.bi-rocket::before { content: "\f846"; } +.bi-stripe::before { content: "\f847"; } +.bi-subscript::before { content: "\f848"; } +.bi-superscript::before { content: "\f849"; } +.bi-trello::before { content: "\f84a"; } +.bi-envelope-at-fill::before { content: "\f84b"; } +.bi-envelope-at::before { content: "\f84c"; } +.bi-regex::before { content: "\f84d"; } +.bi-text-wrap::before { content: "\f84e"; } +.bi-sign-dead-end-fill::before { content: "\f84f"; } +.bi-sign-dead-end::before { content: "\f850"; } +.bi-sign-do-not-enter-fill::before { content: "\f851"; } +.bi-sign-do-not-enter::before { content: "\f852"; } +.bi-sign-intersection-fill::before { content: "\f853"; } +.bi-sign-intersection-side-fill::before { content: "\f854"; } +.bi-sign-intersection-side::before { content: "\f855"; } +.bi-sign-intersection-t-fill::before { content: "\f856"; } +.bi-sign-intersection-t::before { content: "\f857"; } +.bi-sign-intersection-y-fill::before { content: "\f858"; } +.bi-sign-intersection-y::before { content: "\f859"; } +.bi-sign-intersection::before { content: "\f85a"; } +.bi-sign-merge-left-fill::before { content: "\f85b"; } +.bi-sign-merge-left::before { content: "\f85c"; } +.bi-sign-merge-right-fill::before { content: "\f85d"; } +.bi-sign-merge-right::before { content: "\f85e"; } +.bi-sign-no-left-turn-fill::before { content: "\f85f"; } +.bi-sign-no-left-turn::before { content: "\f860"; } +.bi-sign-no-parking-fill::before { content: "\f861"; } +.bi-sign-no-parking::before { content: "\f862"; } +.bi-sign-no-right-turn-fill::before { content: "\f863"; } +.bi-sign-no-right-turn::before { content: "\f864"; } +.bi-sign-railroad-fill::before { content: "\f865"; } +.bi-sign-railroad::before { content: "\f866"; } +.bi-building-add::before { content: "\f867"; } +.bi-building-check::before { content: "\f868"; } +.bi-building-dash::before { content: "\f869"; } +.bi-building-down::before { content: "\f86a"; } +.bi-building-exclamation::before { content: "\f86b"; } +.bi-building-fill-add::before { content: "\f86c"; } +.bi-building-fill-check::before { content: "\f86d"; } +.bi-building-fill-dash::before { content: "\f86e"; } +.bi-building-fill-down::before { content: "\f86f"; } +.bi-building-fill-exclamation::before { content: "\f870"; } +.bi-building-fill-gear::before { content: "\f871"; } +.bi-building-fill-lock::before { content: "\f872"; } +.bi-building-fill-slash::before { content: "\f873"; } +.bi-building-fill-up::before { content: "\f874"; } +.bi-building-fill-x::before { content: "\f875"; } +.bi-building-fill::before { content: "\f876"; } +.bi-building-gear::before { content: "\f877"; } +.bi-building-lock::before { content: "\f878"; } +.bi-building-slash::before { content: "\f879"; } +.bi-building-up::before { content: "\f87a"; } +.bi-building-x::before { content: "\f87b"; } +.bi-buildings-fill::before { content: "\f87c"; } +.bi-buildings::before { content: "\f87d"; } +.bi-bus-front-fill::before { content: "\f87e"; } +.bi-bus-front::before { content: "\f87f"; } +.bi-ev-front-fill::before { content: "\f880"; } +.bi-ev-front::before { content: "\f881"; } +.bi-globe-americas::before { content: "\f882"; } +.bi-globe-asia-australia::before { content: "\f883"; } +.bi-globe-central-south-asia::before { content: "\f884"; } +.bi-globe-europe-africa::before { content: "\f885"; } +.bi-house-add-fill::before { content: "\f886"; } +.bi-house-add::before { content: "\f887"; } +.bi-house-check-fill::before { content: "\f888"; } +.bi-house-check::before { content: "\f889"; } +.bi-house-dash-fill::before { content: "\f88a"; } +.bi-house-dash::before { content: "\f88b"; } +.bi-house-down-fill::before { content: "\f88c"; } +.bi-house-down::before { content: "\f88d"; } +.bi-house-exclamation-fill::before { content: "\f88e"; } +.bi-house-exclamation::before { content: "\f88f"; } +.bi-house-gear-fill::before { content: "\f890"; } +.bi-house-gear::before { content: "\f891"; } +.bi-house-lock-fill::before { content: "\f892"; } +.bi-house-lock::before { content: "\f893"; } +.bi-house-slash-fill::before { content: "\f894"; } +.bi-house-slash::before { content: "\f895"; } +.bi-house-up-fill::before { content: "\f896"; } +.bi-house-up::before { content: "\f897"; } +.bi-house-x-fill::before { content: "\f898"; } +.bi-house-x::before { content: "\f899"; } +.bi-person-add::before { content: "\f89a"; } +.bi-person-down::before { content: "\f89b"; } +.bi-person-exclamation::before { content: "\f89c"; } +.bi-person-fill-add::before { content: "\f89d"; } +.bi-person-fill-check::before { content: "\f89e"; } +.bi-person-fill-dash::before { content: "\f89f"; } +.bi-person-fill-down::before { content: "\f8a0"; } +.bi-person-fill-exclamation::before { content: "\f8a1"; } +.bi-person-fill-gear::before { content: "\f8a2"; } +.bi-person-fill-lock::before { content: "\f8a3"; } +.bi-person-fill-slash::before { content: "\f8a4"; } +.bi-person-fill-up::before { content: "\f8a5"; } +.bi-person-fill-x::before { content: "\f8a6"; } +.bi-person-gear::before { content: "\f8a7"; } +.bi-person-lock::before { content: "\f8a8"; } +.bi-person-slash::before { content: "\f8a9"; } +.bi-person-up::before { content: "\f8aa"; } +.bi-scooter::before { content: "\f8ab"; } +.bi-taxi-front-fill::before { content: "\f8ac"; } +.bi-taxi-front::before { content: "\f8ad"; } +.bi-amd::before { content: "\f8ae"; } +.bi-database-add::before { content: "\f8af"; } +.bi-database-check::before { content: "\f8b0"; } +.bi-database-dash::before { content: "\f8b1"; } +.bi-database-down::before { content: "\f8b2"; } +.bi-database-exclamation::before { content: "\f8b3"; } +.bi-database-fill-add::before { content: "\f8b4"; } +.bi-database-fill-check::before { content: "\f8b5"; } +.bi-database-fill-dash::before { content: "\f8b6"; } +.bi-database-fill-down::before { content: "\f8b7"; } +.bi-database-fill-exclamation::before { content: "\f8b8"; } +.bi-database-fill-gear::before { content: "\f8b9"; } +.bi-database-fill-lock::before { content: "\f8ba"; } +.bi-database-fill-slash::before { content: "\f8bb"; } +.bi-database-fill-up::before { content: "\f8bc"; } +.bi-database-fill-x::before { content: "\f8bd"; } +.bi-database-fill::before { content: "\f8be"; } +.bi-database-gear::before { content: "\f8bf"; } +.bi-database-lock::before { content: "\f8c0"; } +.bi-database-slash::before { content: "\f8c1"; } +.bi-database-up::before { content: "\f8c2"; } +.bi-database-x::before { content: "\f8c3"; } +.bi-database::before { content: "\f8c4"; } +.bi-houses-fill::before { content: "\f8c5"; } +.bi-houses::before { content: "\f8c6"; } +.bi-nvidia::before { content: "\f8c7"; } +.bi-person-vcard-fill::before { content: "\f8c8"; } +.bi-person-vcard::before { content: "\f8c9"; } +.bi-sina-weibo::before { content: "\f8ca"; } +.bi-tencent-qq::before { content: "\f8cb"; } +.bi-wikipedia::before { content: "\f8cc"; } +.bi-alphabet-uppercase::before { content: "\f2a5"; } +.bi-alphabet::before { content: "\f68a"; } +.bi-amazon::before { content: "\f68d"; } +.bi-arrows-collapse-vertical::before { content: "\f690"; } +.bi-arrows-expand-vertical::before { content: "\f695"; } +.bi-arrows-vertical::before { content: "\f698"; } +.bi-arrows::before { content: "\f6a2"; } +.bi-ban-fill::before { content: "\f6a3"; } +.bi-ban::before { content: "\f6b6"; } +.bi-bing::before { content: "\f6c2"; } +.bi-cake::before { content: "\f6e0"; } +.bi-cake2::before { content: "\f6ed"; } +.bi-cookie::before { content: "\f6ee"; } +.bi-copy::before { content: "\f759"; } +.bi-crosshair::before { content: "\f769"; } +.bi-crosshair2::before { content: "\f794"; } +.bi-emoji-astonished-fill::before { content: "\f795"; } +.bi-emoji-astonished::before { content: "\f79a"; } +.bi-emoji-grimace-fill::before { content: "\f79b"; } +.bi-emoji-grimace::before { content: "\f7a0"; } +.bi-emoji-grin-fill::before { content: "\f7a1"; } +.bi-emoji-grin::before { content: "\f7a6"; } +.bi-emoji-surprise-fill::before { content: "\f7a7"; } +.bi-emoji-surprise::before { content: "\f7ac"; } +.bi-emoji-tear-fill::before { content: "\f7ad"; } +.bi-emoji-tear::before { content: "\f7b2"; } +.bi-envelope-arrow-down-fill::before { content: "\f7b3"; } +.bi-envelope-arrow-down::before { content: "\f7b8"; } +.bi-envelope-arrow-up-fill::before { content: "\f7b9"; } +.bi-envelope-arrow-up::before { content: "\f7be"; } +.bi-feather::before { content: "\f7bf"; } +.bi-feather2::before { content: "\f7c4"; } +.bi-floppy-fill::before { content: "\f7c5"; } +.bi-floppy::before { content: "\f7d8"; } +.bi-floppy2-fill::before { content: "\f7d9"; } +.bi-floppy2::before { content: "\f7e4"; } +.bi-gitlab::before { content: "\f7e5"; } +.bi-highlighter::before { content: "\f7f8"; } +.bi-marker-tip::before { content: "\f802"; } +.bi-nvme-fill::before { content: "\f803"; } +.bi-nvme::before { content: "\f80c"; } +.bi-opencollective::before { content: "\f80d"; } +.bi-pci-card-network::before { content: "\f8cd"; } +.bi-pci-card-sound::before { content: "\f8ce"; } +.bi-radar::before { content: "\f8cf"; } +.bi-send-arrow-down-fill::before { content: "\f8d0"; } +.bi-send-arrow-down::before { content: "\f8d1"; } +.bi-send-arrow-up-fill::before { content: "\f8d2"; } +.bi-send-arrow-up::before { content: "\f8d3"; } +.bi-sim-slash-fill::before { content: "\f8d4"; } +.bi-sim-slash::before { content: "\f8d5"; } +.bi-sourceforge::before { content: "\f8d6"; } +.bi-substack::before { content: "\f8d7"; } +.bi-threads-fill::before { content: "\f8d8"; } +.bi-threads::before { content: "\f8d9"; } +.bi-transparency::before { content: "\f8da"; } +.bi-twitter-x::before { content: "\f8db"; } +.bi-type-h4::before { content: "\f8dc"; } +.bi-type-h5::before { content: "\f8dd"; } +.bi-type-h6::before { content: "\f8de"; } +.bi-backpack-fill::before { content: "\f8df"; } +.bi-backpack::before { content: "\f8e0"; } +.bi-backpack2-fill::before { content: "\f8e1"; } +.bi-backpack2::before { content: "\f8e2"; } +.bi-backpack3-fill::before { content: "\f8e3"; } +.bi-backpack3::before { content: "\f8e4"; } +.bi-backpack4-fill::before { content: "\f8e5"; } +.bi-backpack4::before { content: "\f8e6"; } +.bi-brilliance::before { content: "\f8e7"; } +.bi-cake-fill::before { content: "\f8e8"; } +.bi-cake2-fill::before { content: "\f8e9"; } +.bi-duffle-fill::before { content: "\f8ea"; } +.bi-duffle::before { content: "\f8eb"; } +.bi-exposure::before { content: "\f8ec"; } +.bi-gender-neuter::before { content: "\f8ed"; } +.bi-highlights::before { content: "\f8ee"; } +.bi-luggage-fill::before { content: "\f8ef"; } +.bi-luggage::before { content: "\f8f0"; } +.bi-mailbox-flag::before { content: "\f8f1"; } +.bi-mailbox2-flag::before { content: "\f8f2"; } +.bi-noise-reduction::before { content: "\f8f3"; } +.bi-passport-fill::before { content: "\f8f4"; } +.bi-passport::before { content: "\f8f5"; } +.bi-person-arms-up::before { content: "\f8f6"; } +.bi-person-raised-hand::before { content: "\f8f7"; } +.bi-person-standing-dress::before { content: "\f8f8"; } +.bi-person-standing::before { content: "\f8f9"; } +.bi-person-walking::before { content: "\f8fa"; } +.bi-person-wheelchair::before { content: "\f8fb"; } +.bi-shadows::before { content: "\f8fc"; } +.bi-suitcase-fill::before { content: "\f8fd"; } +.bi-suitcase-lg-fill::before { content: "\f8fe"; } +.bi-suitcase-lg::before { content: "\f8ff"; } +.bi-suitcase::before { content: "\f900"; } +.bi-suitcase2-fill::before { content: "\f901"; } +.bi-suitcase2::before { content: "\f902"; } +.bi-vignette::before { content: "\f903"; } +.bi-bluesky::before { content: "\f7f9"; } +.bi-tux::before { content: "\f904"; } +.bi-beaker-fill::before { content: "\f905"; } +.bi-beaker::before { content: "\f906"; } +.bi-flask-fill::before { content: "\f907"; } +.bi-flask-florence-fill::before { content: "\f908"; } +.bi-flask-florence::before { content: "\f909"; } +.bi-flask::before { content: "\f90a"; } +.bi-leaf-fill::before { content: "\f90b"; } +.bi-leaf::before { content: "\f90c"; } +.bi-measuring-cup-fill::before { content: "\f90d"; } +.bi-measuring-cup::before { content: "\f90e"; } +.bi-unlock2-fill::before { content: "\f90f"; } +.bi-unlock2::before { content: "\f910"; } +.bi-battery-low::before { content: "\f911"; } +.bi-anthropic::before { content: "\f912"; } +.bi-apple-music::before { content: "\f913"; } +.bi-claude::before { content: "\f914"; } +.bi-openai::before { content: "\f915"; } +.bi-perplexity::before { content: "\f916"; } +.bi-css::before { content: "\f917"; } +.bi-javascript::before { content: "\f918"; } +.bi-typescript::before { content: "\f919"; } +.bi-fork-knife::before { content: "\f91a"; } +.bi-globe-americas-fill::before { content: "\f91b"; } +.bi-globe-asia-australia-fill::before { content: "\f91c"; } +.bi-globe-central-south-asia-fill::before { content: "\f91d"; } +.bi-globe-europe-africa-fill::before { content: "\f91e"; } diff --git a/web/vendor/bootstrap-icons/bootstrap-icons.json b/web/vendor/bootstrap-icons/bootstrap-icons.json new file mode 100644 index 0000000..9d8873b --- /dev/null +++ b/web/vendor/bootstrap-icons/bootstrap-icons.json @@ -0,0 +1,2080 @@ +{ + "123": 63103, + "alarm-fill": 61697, + "alarm": 61698, + "align-bottom": 61699, + "align-center": 61700, + "align-end": 61701, + "align-middle": 61702, + "align-start": 61703, + "align-top": 61704, + "alt": 61705, + "app-indicator": 61706, + "app": 61707, + "archive-fill": 61708, + "archive": 61709, + "arrow-90deg-down": 61710, + "arrow-90deg-left": 61711, + "arrow-90deg-right": 61712, + "arrow-90deg-up": 61713, + "arrow-bar-down": 61714, + "arrow-bar-left": 61715, + "arrow-bar-right": 61716, + "arrow-bar-up": 61717, + "arrow-clockwise": 61718, + "arrow-counterclockwise": 61719, + "arrow-down-circle-fill": 61720, + "arrow-down-circle": 61721, + "arrow-down-left-circle-fill": 61722, + "arrow-down-left-circle": 61723, + "arrow-down-left-square-fill": 61724, + "arrow-down-left-square": 61725, + "arrow-down-left": 61726, + "arrow-down-right-circle-fill": 61727, + "arrow-down-right-circle": 61728, + "arrow-down-right-square-fill": 61729, + "arrow-down-right-square": 61730, + "arrow-down-right": 61731, + "arrow-down-short": 61732, + "arrow-down-square-fill": 61733, + "arrow-down-square": 61734, + "arrow-down-up": 61735, + "arrow-down": 61736, + "arrow-left-circle-fill": 61737, + "arrow-left-circle": 61738, + "arrow-left-right": 61739, + "arrow-left-short": 61740, + "arrow-left-square-fill": 61741, + "arrow-left-square": 61742, + "arrow-left": 61743, + "arrow-repeat": 61744, + "arrow-return-left": 61745, + "arrow-return-right": 61746, + "arrow-right-circle-fill": 61747, + "arrow-right-circle": 61748, + "arrow-right-short": 61749, + "arrow-right-square-fill": 61750, + "arrow-right-square": 61751, + "arrow-right": 61752, + "arrow-up-circle-fill": 61753, + "arrow-up-circle": 61754, + "arrow-up-left-circle-fill": 61755, + "arrow-up-left-circle": 61756, + "arrow-up-left-square-fill": 61757, + "arrow-up-left-square": 61758, + "arrow-up-left": 61759, + "arrow-up-right-circle-fill": 61760, + "arrow-up-right-circle": 61761, + "arrow-up-right-square-fill": 61762, + "arrow-up-right-square": 61763, + "arrow-up-right": 61764, + "arrow-up-short": 61765, + "arrow-up-square-fill": 61766, + "arrow-up-square": 61767, + "arrow-up": 61768, + "arrows-angle-contract": 61769, + "arrows-angle-expand": 61770, + "arrows-collapse": 61771, + "arrows-expand": 61772, + "arrows-fullscreen": 61773, + "arrows-move": 61774, + "aspect-ratio-fill": 61775, + "aspect-ratio": 61776, + "asterisk": 61777, + "at": 61778, + "award-fill": 61779, + "award": 61780, + "back": 61781, + "backspace-fill": 61782, + "backspace-reverse-fill": 61783, + "backspace-reverse": 61784, + "backspace": 61785, + "badge-3d-fill": 61786, + "badge-3d": 61787, + "badge-4k-fill": 61788, + "badge-4k": 61789, + "badge-8k-fill": 61790, + "badge-8k": 61791, + "badge-ad-fill": 61792, + "badge-ad": 61793, + "badge-ar-fill": 61794, + "badge-ar": 61795, + "badge-cc-fill": 61796, + "badge-cc": 61797, + "badge-hd-fill": 61798, + "badge-hd": 61799, + "badge-tm-fill": 61800, + "badge-tm": 61801, + "badge-vo-fill": 61802, + "badge-vo": 61803, + "badge-vr-fill": 61804, + "badge-vr": 61805, + "badge-wc-fill": 61806, + "badge-wc": 61807, + "bag-check-fill": 61808, + "bag-check": 61809, + "bag-dash-fill": 61810, + "bag-dash": 61811, + "bag-fill": 61812, + "bag-plus-fill": 61813, + "bag-plus": 61814, + "bag-x-fill": 61815, + "bag-x": 61816, + "bag": 61817, + "bar-chart-fill": 61818, + "bar-chart-line-fill": 61819, + "bar-chart-line": 61820, + "bar-chart-steps": 61821, + "bar-chart": 61822, + "basket-fill": 61823, + "basket": 61824, + "basket2-fill": 61825, + "basket2": 61826, + "basket3-fill": 61827, + "basket3": 61828, + "battery-charging": 61829, + "battery-full": 61830, + "battery-half": 61831, + "battery": 61832, + "bell-fill": 61833, + "bell": 61834, + "bezier": 61835, + "bezier2": 61836, + "bicycle": 61837, + "binoculars-fill": 61838, + "binoculars": 61839, + "blockquote-left": 61840, + "blockquote-right": 61841, + "book-fill": 61842, + "book-half": 61843, + "book": 61844, + "bookmark-check-fill": 61845, + "bookmark-check": 61846, + "bookmark-dash-fill": 61847, + "bookmark-dash": 61848, + "bookmark-fill": 61849, + "bookmark-heart-fill": 61850, + "bookmark-heart": 61851, + "bookmark-plus-fill": 61852, + "bookmark-plus": 61853, + "bookmark-star-fill": 61854, + "bookmark-star": 61855, + "bookmark-x-fill": 61856, + "bookmark-x": 61857, + "bookmark": 61858, + "bookmarks-fill": 61859, + "bookmarks": 61860, + "bookshelf": 61861, + "bootstrap-fill": 61862, + "bootstrap-reboot": 61863, + "bootstrap": 61864, + "border-all": 61865, + "border-bottom": 61866, + "border-center": 61867, + "border-inner": 61868, + "border-left": 61869, + "border-middle": 61870, + "border-outer": 61871, + "border-right": 61872, + "border-style": 61873, + "border-top": 61874, + "border-width": 61875, + "border": 61876, + "bounding-box-circles": 61877, + "bounding-box": 61878, + "box-arrow-down-left": 61879, + "box-arrow-down-right": 61880, + "box-arrow-down": 61881, + "box-arrow-in-down-left": 61882, + "box-arrow-in-down-right": 61883, + "box-arrow-in-down": 61884, + "box-arrow-in-left": 61885, + "box-arrow-in-right": 61886, + "box-arrow-in-up-left": 61887, + "box-arrow-in-up-right": 61888, + "box-arrow-in-up": 61889, + "box-arrow-left": 61890, + "box-arrow-right": 61891, + "box-arrow-up-left": 61892, + "box-arrow-up-right": 61893, + "box-arrow-up": 61894, + "box-seam": 61895, + "box": 61896, + "braces": 61897, + "bricks": 61898, + "briefcase-fill": 61899, + "briefcase": 61900, + "brightness-alt-high-fill": 61901, + "brightness-alt-high": 61902, + "brightness-alt-low-fill": 61903, + "brightness-alt-low": 61904, + "brightness-high-fill": 61905, + "brightness-high": 61906, + "brightness-low-fill": 61907, + "brightness-low": 61908, + "broadcast-pin": 61909, + "broadcast": 61910, + "brush-fill": 61911, + "brush": 61912, + "bucket-fill": 61913, + "bucket": 61914, + "bug-fill": 61915, + "bug": 61916, + "building": 61917, + "bullseye": 61918, + "calculator-fill": 61919, + "calculator": 61920, + "calendar-check-fill": 61921, + "calendar-check": 61922, + "calendar-date-fill": 61923, + "calendar-date": 61924, + "calendar-day-fill": 61925, + "calendar-day": 61926, + "calendar-event-fill": 61927, + "calendar-event": 61928, + "calendar-fill": 61929, + "calendar-minus-fill": 61930, + "calendar-minus": 61931, + "calendar-month-fill": 61932, + "calendar-month": 61933, + "calendar-plus-fill": 61934, + "calendar-plus": 61935, + "calendar-range-fill": 61936, + "calendar-range": 61937, + "calendar-week-fill": 61938, + "calendar-week": 61939, + "calendar-x-fill": 61940, + "calendar-x": 61941, + "calendar": 61942, + "calendar2-check-fill": 61943, + "calendar2-check": 61944, + "calendar2-date-fill": 61945, + "calendar2-date": 61946, + "calendar2-day-fill": 61947, + "calendar2-day": 61948, + "calendar2-event-fill": 61949, + "calendar2-event": 61950, + "calendar2-fill": 61951, + "calendar2-minus-fill": 61952, + "calendar2-minus": 61953, + "calendar2-month-fill": 61954, + "calendar2-month": 61955, + "calendar2-plus-fill": 61956, + "calendar2-plus": 61957, + "calendar2-range-fill": 61958, + "calendar2-range": 61959, + "calendar2-week-fill": 61960, + "calendar2-week": 61961, + "calendar2-x-fill": 61962, + "calendar2-x": 61963, + "calendar2": 61964, + "calendar3-event-fill": 61965, + "calendar3-event": 61966, + "calendar3-fill": 61967, + "calendar3-range-fill": 61968, + "calendar3-range": 61969, + "calendar3-week-fill": 61970, + "calendar3-week": 61971, + "calendar3": 61972, + "calendar4-event": 61973, + "calendar4-range": 61974, + "calendar4-week": 61975, + "calendar4": 61976, + "camera-fill": 61977, + "camera-reels-fill": 61978, + "camera-reels": 61979, + "camera-video-fill": 61980, + "camera-video-off-fill": 61981, + "camera-video-off": 61982, + "camera-video": 61983, + "camera": 61984, + "camera2": 61985, + "capslock-fill": 61986, + "capslock": 61987, + "card-checklist": 61988, + "card-heading": 61989, + "card-image": 61990, + "card-list": 61991, + "card-text": 61992, + "caret-down-fill": 61993, + "caret-down-square-fill": 61994, + "caret-down-square": 61995, + "caret-down": 61996, + "caret-left-fill": 61997, + "caret-left-square-fill": 61998, + "caret-left-square": 61999, + "caret-left": 62000, + "caret-right-fill": 62001, + "caret-right-square-fill": 62002, + "caret-right-square": 62003, + "caret-right": 62004, + "caret-up-fill": 62005, + "caret-up-square-fill": 62006, + "caret-up-square": 62007, + "caret-up": 62008, + "cart-check-fill": 62009, + "cart-check": 62010, + "cart-dash-fill": 62011, + "cart-dash": 62012, + "cart-fill": 62013, + "cart-plus-fill": 62014, + "cart-plus": 62015, + "cart-x-fill": 62016, + "cart-x": 62017, + "cart": 62018, + "cart2": 62019, + "cart3": 62020, + "cart4": 62021, + "cash-stack": 62022, + "cash": 62023, + "cast": 62024, + "chat-dots-fill": 62025, + "chat-dots": 62026, + "chat-fill": 62027, + "chat-left-dots-fill": 62028, + "chat-left-dots": 62029, + "chat-left-fill": 62030, + "chat-left-quote-fill": 62031, + "chat-left-quote": 62032, + "chat-left-text-fill": 62033, + "chat-left-text": 62034, + "chat-left": 62035, + "chat-quote-fill": 62036, + "chat-quote": 62037, + "chat-right-dots-fill": 62038, + "chat-right-dots": 62039, + "chat-right-fill": 62040, + "chat-right-quote-fill": 62041, + "chat-right-quote": 62042, + "chat-right-text-fill": 62043, + "chat-right-text": 62044, + "chat-right": 62045, + "chat-square-dots-fill": 62046, + "chat-square-dots": 62047, + "chat-square-fill": 62048, + "chat-square-quote-fill": 62049, + "chat-square-quote": 62050, + "chat-square-text-fill": 62051, + "chat-square-text": 62052, + "chat-square": 62053, + "chat-text-fill": 62054, + "chat-text": 62055, + "chat": 62056, + "check-all": 62057, + "check-circle-fill": 62058, + "check-circle": 62059, + "check-square-fill": 62060, + "check-square": 62061, + "check": 62062, + "check2-all": 62063, + "check2-circle": 62064, + "check2-square": 62065, + "check2": 62066, + "chevron-bar-contract": 62067, + "chevron-bar-down": 62068, + "chevron-bar-expand": 62069, + "chevron-bar-left": 62070, + "chevron-bar-right": 62071, + "chevron-bar-up": 62072, + "chevron-compact-down": 62073, + "chevron-compact-left": 62074, + "chevron-compact-right": 62075, + "chevron-compact-up": 62076, + "chevron-contract": 62077, + "chevron-double-down": 62078, + "chevron-double-left": 62079, + "chevron-double-right": 62080, + "chevron-double-up": 62081, + "chevron-down": 62082, + "chevron-expand": 62083, + "chevron-left": 62084, + "chevron-right": 62085, + "chevron-up": 62086, + "circle-fill": 62087, + "circle-half": 62088, + "circle-square": 62089, + "circle": 62090, + "clipboard-check": 62091, + "clipboard-data": 62092, + "clipboard-minus": 62093, + "clipboard-plus": 62094, + "clipboard-x": 62095, + "clipboard": 62096, + "clock-fill": 62097, + "clock-history": 62098, + "clock": 62099, + "cloud-arrow-down-fill": 62100, + "cloud-arrow-down": 62101, + "cloud-arrow-up-fill": 62102, + "cloud-arrow-up": 62103, + "cloud-check-fill": 62104, + "cloud-check": 62105, + "cloud-download-fill": 62106, + "cloud-download": 62107, + "cloud-drizzle-fill": 62108, + "cloud-drizzle": 62109, + "cloud-fill": 62110, + "cloud-fog-fill": 62111, + "cloud-fog": 62112, + "cloud-fog2-fill": 62113, + "cloud-fog2": 62114, + "cloud-hail-fill": 62115, + "cloud-hail": 62116, + "cloud-haze-fill": 62118, + "cloud-haze": 62119, + "cloud-haze2-fill": 62120, + "cloud-lightning-fill": 62121, + "cloud-lightning-rain-fill": 62122, + "cloud-lightning-rain": 62123, + "cloud-lightning": 62124, + "cloud-minus-fill": 62125, + "cloud-minus": 62126, + "cloud-moon-fill": 62127, + "cloud-moon": 62128, + "cloud-plus-fill": 62129, + "cloud-plus": 62130, + "cloud-rain-fill": 62131, + "cloud-rain-heavy-fill": 62132, + "cloud-rain-heavy": 62133, + "cloud-rain": 62134, + "cloud-slash-fill": 62135, + "cloud-slash": 62136, + "cloud-sleet-fill": 62137, + "cloud-sleet": 62138, + "cloud-snow-fill": 62139, + "cloud-snow": 62140, + "cloud-sun-fill": 62141, + "cloud-sun": 62142, + "cloud-upload-fill": 62143, + "cloud-upload": 62144, + "cloud": 62145, + "clouds-fill": 62146, + "clouds": 62147, + "cloudy-fill": 62148, + "cloudy": 62149, + "code-slash": 62150, + "code-square": 62151, + "code": 62152, + "collection-fill": 62153, + "collection-play-fill": 62154, + "collection-play": 62155, + "collection": 62156, + "columns-gap": 62157, + "columns": 62158, + "command": 62159, + "compass-fill": 62160, + "compass": 62161, + "cone-striped": 62162, + "cone": 62163, + "controller": 62164, + "cpu-fill": 62165, + "cpu": 62166, + "credit-card-2-back-fill": 62167, + "credit-card-2-back": 62168, + "credit-card-2-front-fill": 62169, + "credit-card-2-front": 62170, + "credit-card-fill": 62171, + "credit-card": 62172, + "crop": 62173, + "cup-fill": 62174, + "cup-straw": 62175, + "cup": 62176, + "cursor-fill": 62177, + "cursor-text": 62178, + "cursor": 62179, + "dash-circle-dotted": 62180, + "dash-circle-fill": 62181, + "dash-circle": 62182, + "dash-square-dotted": 62183, + "dash-square-fill": 62184, + "dash-square": 62185, + "dash": 62186, + "diagram-2-fill": 62187, + "diagram-2": 62188, + "diagram-3-fill": 62189, + "diagram-3": 62190, + "diamond-fill": 62191, + "diamond-half": 62192, + "diamond": 62193, + "dice-1-fill": 62194, + "dice-1": 62195, + "dice-2-fill": 62196, + "dice-2": 62197, + "dice-3-fill": 62198, + "dice-3": 62199, + "dice-4-fill": 62200, + "dice-4": 62201, + "dice-5-fill": 62202, + "dice-5": 62203, + "dice-6-fill": 62204, + "dice-6": 62205, + "disc-fill": 62206, + "disc": 62207, + "discord": 62208, + "display-fill": 62209, + "display": 62210, + "distribute-horizontal": 62211, + "distribute-vertical": 62212, + "door-closed-fill": 62213, + "door-closed": 62214, + "door-open-fill": 62215, + "door-open": 62216, + "dot": 62217, + "download": 62218, + "droplet-fill": 62219, + "droplet-half": 62220, + "droplet": 62221, + "earbuds": 62222, + "easel-fill": 62223, + "easel": 62224, + "egg-fill": 62225, + "egg-fried": 62226, + "egg": 62227, + "eject-fill": 62228, + "eject": 62229, + "emoji-angry-fill": 62230, + "emoji-angry": 62231, + "emoji-dizzy-fill": 62232, + "emoji-dizzy": 62233, + "emoji-expressionless-fill": 62234, + "emoji-expressionless": 62235, + "emoji-frown-fill": 62236, + "emoji-frown": 62237, + "emoji-heart-eyes-fill": 62238, + "emoji-heart-eyes": 62239, + "emoji-laughing-fill": 62240, + "emoji-laughing": 62241, + "emoji-neutral-fill": 62242, + "emoji-neutral": 62243, + "emoji-smile-fill": 62244, + "emoji-smile-upside-down-fill": 62245, + "emoji-smile-upside-down": 62246, + "emoji-smile": 62247, + "emoji-sunglasses-fill": 62248, + "emoji-sunglasses": 62249, + "emoji-wink-fill": 62250, + "emoji-wink": 62251, + "envelope-fill": 62252, + "envelope-open-fill": 62253, + "envelope-open": 62254, + "envelope": 62255, + "eraser-fill": 62256, + "eraser": 62257, + "exclamation-circle-fill": 62258, + "exclamation-circle": 62259, + "exclamation-diamond-fill": 62260, + "exclamation-diamond": 62261, + "exclamation-octagon-fill": 62262, + "exclamation-octagon": 62263, + "exclamation-square-fill": 62264, + "exclamation-square": 62265, + "exclamation-triangle-fill": 62266, + "exclamation-triangle": 62267, + "exclamation": 62268, + "exclude": 62269, + "eye-fill": 62270, + "eye-slash-fill": 62271, + "eye-slash": 62272, + "eye": 62273, + "eyedropper": 62274, + "eyeglasses": 62275, + "facebook": 62276, + "file-arrow-down-fill": 62277, + "file-arrow-down": 62278, + "file-arrow-up-fill": 62279, + "file-arrow-up": 62280, + "file-bar-graph-fill": 62281, + "file-bar-graph": 62282, + "file-binary-fill": 62283, + "file-binary": 62284, + "file-break-fill": 62285, + "file-break": 62286, + "file-check-fill": 62287, + "file-check": 62288, + "file-code-fill": 62289, + "file-code": 62290, + "file-diff-fill": 62291, + "file-diff": 62292, + "file-earmark-arrow-down-fill": 62293, + "file-earmark-arrow-down": 62294, + "file-earmark-arrow-up-fill": 62295, + "file-earmark-arrow-up": 62296, + "file-earmark-bar-graph-fill": 62297, + "file-earmark-bar-graph": 62298, + "file-earmark-binary-fill": 62299, + "file-earmark-binary": 62300, + "file-earmark-break-fill": 62301, + "file-earmark-break": 62302, + "file-earmark-check-fill": 62303, + "file-earmark-check": 62304, + "file-earmark-code-fill": 62305, + "file-earmark-code": 62306, + "file-earmark-diff-fill": 62307, + "file-earmark-diff": 62308, + "file-earmark-easel-fill": 62309, + "file-earmark-easel": 62310, + "file-earmark-excel-fill": 62311, + "file-earmark-excel": 62312, + "file-earmark-fill": 62313, + "file-earmark-font-fill": 62314, + "file-earmark-font": 62315, + "file-earmark-image-fill": 62316, + "file-earmark-image": 62317, + "file-earmark-lock-fill": 62318, + "file-earmark-lock": 62319, + "file-earmark-lock2-fill": 62320, + "file-earmark-lock2": 62321, + "file-earmark-medical-fill": 62322, + "file-earmark-medical": 62323, + "file-earmark-minus-fill": 62324, + "file-earmark-minus": 62325, + "file-earmark-music-fill": 62326, + "file-earmark-music": 62327, + "file-earmark-person-fill": 62328, + "file-earmark-person": 62329, + "file-earmark-play-fill": 62330, + "file-earmark-play": 62331, + "file-earmark-plus-fill": 62332, + "file-earmark-plus": 62333, + "file-earmark-post-fill": 62334, + "file-earmark-post": 62335, + "file-earmark-ppt-fill": 62336, + "file-earmark-ppt": 62337, + "file-earmark-richtext-fill": 62338, + "file-earmark-richtext": 62339, + "file-earmark-ruled-fill": 62340, + "file-earmark-ruled": 62341, + "file-earmark-slides-fill": 62342, + "file-earmark-slides": 62343, + "file-earmark-spreadsheet-fill": 62344, + "file-earmark-spreadsheet": 62345, + "file-earmark-text-fill": 62346, + "file-earmark-text": 62347, + "file-earmark-word-fill": 62348, + "file-earmark-word": 62349, + "file-earmark-x-fill": 62350, + "file-earmark-x": 62351, + "file-earmark-zip-fill": 62352, + "file-earmark-zip": 62353, + "file-earmark": 62354, + "file-easel-fill": 62355, + "file-easel": 62356, + "file-excel-fill": 62357, + "file-excel": 62358, + "file-fill": 62359, + "file-font-fill": 62360, + "file-font": 62361, + "file-image-fill": 62362, + "file-image": 62363, + "file-lock-fill": 62364, + "file-lock": 62365, + "file-lock2-fill": 62366, + "file-lock2": 62367, + "file-medical-fill": 62368, + "file-medical": 62369, + "file-minus-fill": 62370, + "file-minus": 62371, + "file-music-fill": 62372, + "file-music": 62373, + "file-person-fill": 62374, + "file-person": 62375, + "file-play-fill": 62376, + "file-play": 62377, + "file-plus-fill": 62378, + "file-plus": 62379, + "file-post-fill": 62380, + "file-post": 62381, + "file-ppt-fill": 62382, + "file-ppt": 62383, + "file-richtext-fill": 62384, + "file-richtext": 62385, + "file-ruled-fill": 62386, + "file-ruled": 62387, + "file-slides-fill": 62388, + "file-slides": 62389, + "file-spreadsheet-fill": 62390, + "file-spreadsheet": 62391, + "file-text-fill": 62392, + "file-text": 62393, + "file-word-fill": 62394, + "file-word": 62395, + "file-x-fill": 62396, + "file-x": 62397, + "file-zip-fill": 62398, + "file-zip": 62399, + "file": 62400, + "files-alt": 62401, + "files": 62402, + "film": 62403, + "filter-circle-fill": 62404, + "filter-circle": 62405, + "filter-left": 62406, + "filter-right": 62407, + "filter-square-fill": 62408, + "filter-square": 62409, + "filter": 62410, + "flag-fill": 62411, + "flag": 62412, + "flower1": 62413, + "flower2": 62414, + "flower3": 62415, + "folder-check": 62416, + "folder-fill": 62417, + "folder-minus": 62418, + "folder-plus": 62419, + "folder-symlink-fill": 62420, + "folder-symlink": 62421, + "folder-x": 62422, + "folder": 62423, + "folder2-open": 62424, + "folder2": 62425, + "fonts": 62426, + "forward-fill": 62427, + "forward": 62428, + "front": 62429, + "fullscreen-exit": 62430, + "fullscreen": 62431, + "funnel-fill": 62432, + "funnel": 62433, + "gear-fill": 62434, + "gear-wide-connected": 62435, + "gear-wide": 62436, + "gear": 62437, + "gem": 62438, + "geo-alt-fill": 62439, + "geo-alt": 62440, + "geo-fill": 62441, + "geo": 62442, + "gift-fill": 62443, + "gift": 62444, + "github": 62445, + "globe": 62446, + "globe2": 62447, + "google": 62448, + "graph-down": 62449, + "graph-up": 62450, + "grid-1x2-fill": 62451, + "grid-1x2": 62452, + "grid-3x2-gap-fill": 62453, + "grid-3x2-gap": 62454, + "grid-3x2": 62455, + "grid-3x3-gap-fill": 62456, + "grid-3x3-gap": 62457, + "grid-3x3": 62458, + "grid-fill": 62459, + "grid": 62460, + "grip-horizontal": 62461, + "grip-vertical": 62462, + "hammer": 62463, + "hand-index-fill": 62464, + "hand-index-thumb-fill": 62465, + "hand-index-thumb": 62466, + "hand-index": 62467, + "hand-thumbs-down-fill": 62468, + "hand-thumbs-down": 62469, + "hand-thumbs-up-fill": 62470, + "hand-thumbs-up": 62471, + "handbag-fill": 62472, + "handbag": 62473, + "hash": 62474, + "hdd-fill": 62475, + "hdd-network-fill": 62476, + "hdd-network": 62477, + "hdd-rack-fill": 62478, + "hdd-rack": 62479, + "hdd-stack-fill": 62480, + "hdd-stack": 62481, + "hdd": 62482, + "headphones": 62483, + "headset": 62484, + "heart-fill": 62485, + "heart-half": 62486, + "heart": 62487, + "heptagon-fill": 62488, + "heptagon-half": 62489, + "heptagon": 62490, + "hexagon-fill": 62491, + "hexagon-half": 62492, + "hexagon": 62493, + "hourglass-bottom": 62494, + "hourglass-split": 62495, + "hourglass-top": 62496, + "hourglass": 62497, + "house-door-fill": 62498, + "house-door": 62499, + "house-fill": 62500, + "house": 62501, + "hr": 62502, + "hurricane": 62503, + "image-alt": 62504, + "image-fill": 62505, + "image": 62506, + "images": 62507, + "inbox-fill": 62508, + "inbox": 62509, + "inboxes-fill": 62510, + "inboxes": 62511, + "info-circle-fill": 62512, + "info-circle": 62513, + "info-square-fill": 62514, + "info-square": 62515, + "info": 62516, + "input-cursor-text": 62517, + "input-cursor": 62518, + "instagram": 62519, + "intersect": 62520, + "journal-album": 62521, + "journal-arrow-down": 62522, + "journal-arrow-up": 62523, + "journal-bookmark-fill": 62524, + "journal-bookmark": 62525, + "journal-check": 62526, + "journal-code": 62527, + "journal-medical": 62528, + "journal-minus": 62529, + "journal-plus": 62530, + "journal-richtext": 62531, + "journal-text": 62532, + "journal-x": 62533, + "journal": 62534, + "journals": 62535, + "joystick": 62536, + "justify-left": 62537, + "justify-right": 62538, + "justify": 62539, + "kanban-fill": 62540, + "kanban": 62541, + "key-fill": 62542, + "key": 62543, + "keyboard-fill": 62544, + "keyboard": 62545, + "ladder": 62546, + "lamp-fill": 62547, + "lamp": 62548, + "laptop-fill": 62549, + "laptop": 62550, + "layer-backward": 62551, + "layer-forward": 62552, + "layers-fill": 62553, + "layers-half": 62554, + "layers": 62555, + "layout-sidebar-inset-reverse": 62556, + "layout-sidebar-inset": 62557, + "layout-sidebar-reverse": 62558, + "layout-sidebar": 62559, + "layout-split": 62560, + "layout-text-sidebar-reverse": 62561, + "layout-text-sidebar": 62562, + "layout-text-window-reverse": 62563, + "layout-text-window": 62564, + "layout-three-columns": 62565, + "layout-wtf": 62566, + "life-preserver": 62567, + "lightbulb-fill": 62568, + "lightbulb-off-fill": 62569, + "lightbulb-off": 62570, + "lightbulb": 62571, + "lightning-charge-fill": 62572, + "lightning-charge": 62573, + "lightning-fill": 62574, + "lightning": 62575, + "link-45deg": 62576, + "link": 62577, + "linkedin": 62578, + "list-check": 62579, + "list-nested": 62580, + "list-ol": 62581, + "list-stars": 62582, + "list-task": 62583, + "list-ul": 62584, + "list": 62585, + "lock-fill": 62586, + "lock": 62587, + "mailbox": 62588, + "mailbox2": 62589, + "map-fill": 62590, + "map": 62591, + "markdown-fill": 62592, + "markdown": 62593, + "mask": 62594, + "megaphone-fill": 62595, + "megaphone": 62596, + "menu-app-fill": 62597, + "menu-app": 62598, + "menu-button-fill": 62599, + "menu-button-wide-fill": 62600, + "menu-button-wide": 62601, + "menu-button": 62602, + "menu-down": 62603, + "menu-up": 62604, + "mic-fill": 62605, + "mic-mute-fill": 62606, + "mic-mute": 62607, + "mic": 62608, + "minecart-loaded": 62609, + "minecart": 62610, + "moisture": 62611, + "moon-fill": 62612, + "moon-stars-fill": 62613, + "moon-stars": 62614, + "moon": 62615, + "mouse-fill": 62616, + "mouse": 62617, + "mouse2-fill": 62618, + "mouse2": 62619, + "mouse3-fill": 62620, + "mouse3": 62621, + "music-note-beamed": 62622, + "music-note-list": 62623, + "music-note": 62624, + "music-player-fill": 62625, + "music-player": 62626, + "newspaper": 62627, + "node-minus-fill": 62628, + "node-minus": 62629, + "node-plus-fill": 62630, + "node-plus": 62631, + "nut-fill": 62632, + "nut": 62633, + "octagon-fill": 62634, + "octagon-half": 62635, + "octagon": 62636, + "option": 62637, + "outlet": 62638, + "paint-bucket": 62639, + "palette-fill": 62640, + "palette": 62641, + "palette2": 62642, + "paperclip": 62643, + "paragraph": 62644, + "patch-check-fill": 62645, + "patch-check": 62646, + "patch-exclamation-fill": 62647, + "patch-exclamation": 62648, + "patch-minus-fill": 62649, + "patch-minus": 62650, + "patch-plus-fill": 62651, + "patch-plus": 62652, + "patch-question-fill": 62653, + "patch-question": 62654, + "pause-btn-fill": 62655, + "pause-btn": 62656, + "pause-circle-fill": 62657, + "pause-circle": 62658, + "pause-fill": 62659, + "pause": 62660, + "peace-fill": 62661, + "peace": 62662, + "pen-fill": 62663, + "pen": 62664, + "pencil-fill": 62665, + "pencil-square": 62666, + "pencil": 62667, + "pentagon-fill": 62668, + "pentagon-half": 62669, + "pentagon": 62670, + "people-fill": 62671, + "people": 62672, + "percent": 62673, + "person-badge-fill": 62674, + "person-badge": 62675, + "person-bounding-box": 62676, + "person-check-fill": 62677, + "person-check": 62678, + "person-circle": 62679, + "person-dash-fill": 62680, + "person-dash": 62681, + "person-fill": 62682, + "person-lines-fill": 62683, + "person-plus-fill": 62684, + "person-plus": 62685, + "person-square": 62686, + "person-x-fill": 62687, + "person-x": 62688, + "person": 62689, + "phone-fill": 62690, + "phone-landscape-fill": 62691, + "phone-landscape": 62692, + "phone-vibrate-fill": 62693, + "phone-vibrate": 62694, + "phone": 62695, + "pie-chart-fill": 62696, + "pie-chart": 62697, + "pin-angle-fill": 62698, + "pin-angle": 62699, + "pin-fill": 62700, + "pin": 62701, + "pip-fill": 62702, + "pip": 62703, + "play-btn-fill": 62704, + "play-btn": 62705, + "play-circle-fill": 62706, + "play-circle": 62707, + "play-fill": 62708, + "play": 62709, + "plug-fill": 62710, + "plug": 62711, + "plus-circle-dotted": 62712, + "plus-circle-fill": 62713, + "plus-circle": 62714, + "plus-square-dotted": 62715, + "plus-square-fill": 62716, + "plus-square": 62717, + "plus": 62718, + "power": 62719, + "printer-fill": 62720, + "printer": 62721, + "puzzle-fill": 62722, + "puzzle": 62723, + "question-circle-fill": 62724, + "question-circle": 62725, + "question-diamond-fill": 62726, + "question-diamond": 62727, + "question-octagon-fill": 62728, + "question-octagon": 62729, + "question-square-fill": 62730, + "question-square": 62731, + "question": 62732, + "rainbow": 62733, + "receipt-cutoff": 62734, + "receipt": 62735, + "reception-0": 62736, + "reception-1": 62737, + "reception-2": 62738, + "reception-3": 62739, + "reception-4": 62740, + "record-btn-fill": 62741, + "record-btn": 62742, + "record-circle-fill": 62743, + "record-circle": 62744, + "record-fill": 62745, + "record": 62746, + "record2-fill": 62747, + "record2": 62748, + "reply-all-fill": 62749, + "reply-all": 62750, + "reply-fill": 62751, + "reply": 62752, + "rss-fill": 62753, + "rss": 62754, + "rulers": 62755, + "save-fill": 62756, + "save": 62757, + "save2-fill": 62758, + "save2": 62759, + "scissors": 62760, + "screwdriver": 62761, + "search": 62762, + "segmented-nav": 62763, + "server": 62764, + "share-fill": 62765, + "share": 62766, + "shield-check": 62767, + "shield-exclamation": 62768, + "shield-fill-check": 62769, + "shield-fill-exclamation": 62770, + "shield-fill-minus": 62771, + "shield-fill-plus": 62772, + "shield-fill-x": 62773, + "shield-fill": 62774, + "shield-lock-fill": 62775, + "shield-lock": 62776, + "shield-minus": 62777, + "shield-plus": 62778, + "shield-shaded": 62779, + "shield-slash-fill": 62780, + "shield-slash": 62781, + "shield-x": 62782, + "shield": 62783, + "shift-fill": 62784, + "shift": 62785, + "shop-window": 62786, + "shop": 62787, + "shuffle": 62788, + "signpost-2-fill": 62789, + "signpost-2": 62790, + "signpost-fill": 62791, + "signpost-split-fill": 62792, + "signpost-split": 62793, + "signpost": 62794, + "sim-fill": 62795, + "sim": 62796, + "skip-backward-btn-fill": 62797, + "skip-backward-btn": 62798, + "skip-backward-circle-fill": 62799, + "skip-backward-circle": 62800, + "skip-backward-fill": 62801, + "skip-backward": 62802, + "skip-end-btn-fill": 62803, + "skip-end-btn": 62804, + "skip-end-circle-fill": 62805, + "skip-end-circle": 62806, + "skip-end-fill": 62807, + "skip-end": 62808, + "skip-forward-btn-fill": 62809, + "skip-forward-btn": 62810, + "skip-forward-circle-fill": 62811, + "skip-forward-circle": 62812, + "skip-forward-fill": 62813, + "skip-forward": 62814, + "skip-start-btn-fill": 62815, + "skip-start-btn": 62816, + "skip-start-circle-fill": 62817, + "skip-start-circle": 62818, + "skip-start-fill": 62819, + "skip-start": 62820, + "slack": 62821, + "slash-circle-fill": 62822, + "slash-circle": 62823, + "slash-square-fill": 62824, + "slash-square": 62825, + "slash": 62826, + "sliders": 62827, + "smartwatch": 62828, + "snow": 62829, + "snow2": 62830, + "snow3": 62831, + "sort-alpha-down-alt": 62832, + "sort-alpha-down": 62833, + "sort-alpha-up-alt": 62834, + "sort-alpha-up": 62835, + "sort-down-alt": 62836, + "sort-down": 62837, + "sort-numeric-down-alt": 62838, + "sort-numeric-down": 62839, + "sort-numeric-up-alt": 62840, + "sort-numeric-up": 62841, + "sort-up-alt": 62842, + "sort-up": 62843, + "soundwave": 62844, + "speaker-fill": 62845, + "speaker": 62846, + "speedometer": 62847, + "speedometer2": 62848, + "spellcheck": 62849, + "square-fill": 62850, + "square-half": 62851, + "square": 62852, + "stack": 62853, + "star-fill": 62854, + "star-half": 62855, + "star": 62856, + "stars": 62857, + "stickies-fill": 62858, + "stickies": 62859, + "sticky-fill": 62860, + "sticky": 62861, + "stop-btn-fill": 62862, + "stop-btn": 62863, + "stop-circle-fill": 62864, + "stop-circle": 62865, + "stop-fill": 62866, + "stop": 62867, + "stoplights-fill": 62868, + "stoplights": 62869, + "stopwatch-fill": 62870, + "stopwatch": 62871, + "subtract": 62872, + "suit-club-fill": 62873, + "suit-club": 62874, + "suit-diamond-fill": 62875, + "suit-diamond": 62876, + "suit-heart-fill": 62877, + "suit-heart": 62878, + "suit-spade-fill": 62879, + "suit-spade": 62880, + "sun-fill": 62881, + "sun": 62882, + "sunglasses": 62883, + "sunrise-fill": 62884, + "sunrise": 62885, + "sunset-fill": 62886, + "sunset": 62887, + "symmetry-horizontal": 62888, + "symmetry-vertical": 62889, + "table": 62890, + "tablet-fill": 62891, + "tablet-landscape-fill": 62892, + "tablet-landscape": 62893, + "tablet": 62894, + "tag-fill": 62895, + "tag": 62896, + "tags-fill": 62897, + "tags": 62898, + "telegram": 62899, + "telephone-fill": 62900, + "telephone-forward-fill": 62901, + "telephone-forward": 62902, + "telephone-inbound-fill": 62903, + "telephone-inbound": 62904, + "telephone-minus-fill": 62905, + "telephone-minus": 62906, + "telephone-outbound-fill": 62907, + "telephone-outbound": 62908, + "telephone-plus-fill": 62909, + "telephone-plus": 62910, + "telephone-x-fill": 62911, + "telephone-x": 62912, + "telephone": 62913, + "terminal-fill": 62914, + "terminal": 62915, + "text-center": 62916, + "text-indent-left": 62917, + "text-indent-right": 62918, + "text-left": 62919, + "text-paragraph": 62920, + "text-right": 62921, + "textarea-resize": 62922, + "textarea-t": 62923, + "textarea": 62924, + "thermometer-half": 62925, + "thermometer-high": 62926, + "thermometer-low": 62927, + "thermometer-snow": 62928, + "thermometer-sun": 62929, + "thermometer": 62930, + "three-dots-vertical": 62931, + "three-dots": 62932, + "toggle-off": 62933, + "toggle-on": 62934, + "toggle2-off": 62935, + "toggle2-on": 62936, + "toggles": 62937, + "toggles2": 62938, + "tools": 62939, + "tornado": 62940, + "trash-fill": 62941, + "trash": 62942, + "trash2-fill": 62943, + "trash2": 62944, + "tree-fill": 62945, + "tree": 62946, + "triangle-fill": 62947, + "triangle-half": 62948, + "triangle": 62949, + "trophy-fill": 62950, + "trophy": 62951, + "tropical-storm": 62952, + "truck-flatbed": 62953, + "truck": 62954, + "tsunami": 62955, + "tv-fill": 62956, + "tv": 62957, + "twitch": 62958, + "twitter": 62959, + "type-bold": 62960, + "type-h1": 62961, + "type-h2": 62962, + "type-h3": 62963, + "type-italic": 62964, + "type-strikethrough": 62965, + "type-underline": 62966, + "type": 62967, + "ui-checks-grid": 62968, + "ui-checks": 62969, + "ui-radios-grid": 62970, + "ui-radios": 62971, + "umbrella-fill": 62972, + "umbrella": 62973, + "union": 62974, + "unlock-fill": 62975, + "unlock": 62976, + "upc-scan": 62977, + "upc": 62978, + "upload": 62979, + "vector-pen": 62980, + "view-list": 62981, + "view-stacked": 62982, + "vinyl-fill": 62983, + "vinyl": 62984, + "voicemail": 62985, + "volume-down-fill": 62986, + "volume-down": 62987, + "volume-mute-fill": 62988, + "volume-mute": 62989, + "volume-off-fill": 62990, + "volume-off": 62991, + "volume-up-fill": 62992, + "volume-up": 62993, + "vr": 62994, + "wallet-fill": 62995, + "wallet": 62996, + "wallet2": 62997, + "watch": 62998, + "water": 62999, + "whatsapp": 63000, + "wifi-1": 63001, + "wifi-2": 63002, + "wifi-off": 63003, + "wifi": 63004, + "wind": 63005, + "window-dock": 63006, + "window-sidebar": 63007, + "window": 63008, + "wrench": 63009, + "x-circle-fill": 63010, + "x-circle": 63011, + "x-diamond-fill": 63012, + "x-diamond": 63013, + "x-octagon-fill": 63014, + "x-octagon": 63015, + "x-square-fill": 63016, + "x-square": 63017, + "x": 63018, + "youtube": 63019, + "zoom-in": 63020, + "zoom-out": 63021, + "bank": 63022, + "bank2": 63023, + "bell-slash-fill": 63024, + "bell-slash": 63025, + "cash-coin": 63026, + "check-lg": 63027, + "coin": 63028, + "currency-bitcoin": 63029, + "currency-dollar": 63030, + "currency-euro": 63031, + "currency-exchange": 63032, + "currency-pound": 63033, + "currency-yen": 63034, + "dash-lg": 63035, + "exclamation-lg": 63036, + "file-earmark-pdf-fill": 63037, + "file-earmark-pdf": 63038, + "file-pdf-fill": 63039, + "file-pdf": 63040, + "gender-ambiguous": 63041, + "gender-female": 63042, + "gender-male": 63043, + "gender-trans": 63044, + "headset-vr": 63045, + "info-lg": 63046, + "mastodon": 63047, + "messenger": 63048, + "piggy-bank-fill": 63049, + "piggy-bank": 63050, + "pin-map-fill": 63051, + "pin-map": 63052, + "plus-lg": 63053, + "question-lg": 63054, + "recycle": 63055, + "reddit": 63056, + "safe-fill": 63057, + "safe2-fill": 63058, + "safe2": 63059, + "sd-card-fill": 63060, + "sd-card": 63061, + "skype": 63062, + "slash-lg": 63063, + "translate": 63064, + "x-lg": 63065, + "safe": 63066, + "apple": 63067, + "microsoft": 63069, + "windows": 63070, + "behance": 63068, + "dribbble": 63071, + "line": 63072, + "medium": 63073, + "paypal": 63074, + "pinterest": 63075, + "signal": 63076, + "snapchat": 63077, + "spotify": 63078, + "stack-overflow": 63079, + "strava": 63080, + "wordpress": 63081, + "vimeo": 63082, + "activity": 63083, + "easel2-fill": 63084, + "easel2": 63085, + "easel3-fill": 63086, + "easel3": 63087, + "fan": 63088, + "fingerprint": 63089, + "graph-down-arrow": 63090, + "graph-up-arrow": 63091, + "hypnotize": 63092, + "magic": 63093, + "person-rolodex": 63094, + "person-video": 63095, + "person-video2": 63096, + "person-video3": 63097, + "person-workspace": 63098, + "radioactive": 63099, + "webcam-fill": 63100, + "webcam": 63101, + "yin-yang": 63102, + "bandaid-fill": 63104, + "bandaid": 63105, + "bluetooth": 63106, + "body-text": 63107, + "boombox": 63108, + "boxes": 63109, + "dpad-fill": 63110, + "dpad": 63111, + "ear-fill": 63112, + "ear": 63113, + "envelope-check-fill": 63115, + "envelope-check": 63116, + "envelope-dash-fill": 63118, + "envelope-dash": 63119, + "envelope-exclamation-fill": 63121, + "envelope-exclamation": 63122, + "envelope-plus-fill": 63123, + "envelope-plus": 63124, + "envelope-slash-fill": 63126, + "envelope-slash": 63127, + "envelope-x-fill": 63129, + "envelope-x": 63130, + "explicit-fill": 63131, + "explicit": 63132, + "git": 63133, + "infinity": 63134, + "list-columns-reverse": 63135, + "list-columns": 63136, + "meta": 63137, + "nintendo-switch": 63140, + "pc-display-horizontal": 63141, + "pc-display": 63142, + "pc-horizontal": 63143, + "pc": 63144, + "playstation": 63145, + "plus-slash-minus": 63146, + "projector-fill": 63147, + "projector": 63148, + "qr-code-scan": 63149, + "qr-code": 63150, + "quora": 63151, + "quote": 63152, + "robot": 63153, + "send-check-fill": 63154, + "send-check": 63155, + "send-dash-fill": 63156, + "send-dash": 63157, + "send-exclamation-fill": 63159, + "send-exclamation": 63160, + "send-fill": 63161, + "send-plus-fill": 63162, + "send-plus": 63163, + "send-slash-fill": 63164, + "send-slash": 63165, + "send-x-fill": 63166, + "send-x": 63167, + "send": 63168, + "steam": 63169, + "terminal-dash": 63171, + "terminal-plus": 63172, + "terminal-split": 63173, + "ticket-detailed-fill": 63174, + "ticket-detailed": 63175, + "ticket-fill": 63176, + "ticket-perforated-fill": 63177, + "ticket-perforated": 63178, + "ticket": 63179, + "tiktok": 63180, + "window-dash": 63181, + "window-desktop": 63182, + "window-fullscreen": 63183, + "window-plus": 63184, + "window-split": 63185, + "window-stack": 63186, + "window-x": 63187, + "xbox": 63188, + "ethernet": 63189, + "hdmi-fill": 63190, + "hdmi": 63191, + "usb-c-fill": 63192, + "usb-c": 63193, + "usb-fill": 63194, + "usb-plug-fill": 63195, + "usb-plug": 63196, + "usb-symbol": 63197, + "usb": 63198, + "boombox-fill": 63199, + "displayport": 63201, + "gpu-card": 63202, + "memory": 63203, + "modem-fill": 63204, + "modem": 63205, + "motherboard-fill": 63206, + "motherboard": 63207, + "optical-audio-fill": 63208, + "optical-audio": 63209, + "pci-card": 63210, + "router-fill": 63211, + "router": 63212, + "thunderbolt-fill": 63215, + "thunderbolt": 63216, + "usb-drive-fill": 63217, + "usb-drive": 63218, + "usb-micro-fill": 63219, + "usb-micro": 63220, + "usb-mini-fill": 63221, + "usb-mini": 63222, + "cloud-haze2": 63223, + "device-hdd-fill": 63224, + "device-hdd": 63225, + "device-ssd-fill": 63226, + "device-ssd": 63227, + "displayport-fill": 63228, + "mortarboard-fill": 63229, + "mortarboard": 63230, + "terminal-x": 63231, + "arrow-through-heart-fill": 63232, + "arrow-through-heart": 63233, + "badge-sd-fill": 63234, + "badge-sd": 63235, + "bag-heart-fill": 63236, + "bag-heart": 63237, + "balloon-fill": 63238, + "balloon-heart-fill": 63239, + "balloon-heart": 63240, + "balloon": 63241, + "box2-fill": 63242, + "box2-heart-fill": 63243, + "box2-heart": 63244, + "box2": 63245, + "braces-asterisk": 63246, + "calendar-heart-fill": 63247, + "calendar-heart": 63248, + "calendar2-heart-fill": 63249, + "calendar2-heart": 63250, + "chat-heart-fill": 63251, + "chat-heart": 63252, + "chat-left-heart-fill": 63253, + "chat-left-heart": 63254, + "chat-right-heart-fill": 63255, + "chat-right-heart": 63256, + "chat-square-heart-fill": 63257, + "chat-square-heart": 63258, + "clipboard-check-fill": 63259, + "clipboard-data-fill": 63260, + "clipboard-fill": 63261, + "clipboard-heart-fill": 63262, + "clipboard-heart": 63263, + "clipboard-minus-fill": 63264, + "clipboard-plus-fill": 63265, + "clipboard-pulse": 63266, + "clipboard-x-fill": 63267, + "clipboard2-check-fill": 63268, + "clipboard2-check": 63269, + "clipboard2-data-fill": 63270, + "clipboard2-data": 63271, + "clipboard2-fill": 63272, + "clipboard2-heart-fill": 63273, + "clipboard2-heart": 63274, + "clipboard2-minus-fill": 63275, + "clipboard2-minus": 63276, + "clipboard2-plus-fill": 63277, + "clipboard2-plus": 63278, + "clipboard2-pulse-fill": 63279, + "clipboard2-pulse": 63280, + "clipboard2-x-fill": 63281, + "clipboard2-x": 63282, + "clipboard2": 63283, + "emoji-kiss-fill": 63284, + "emoji-kiss": 63285, + "envelope-heart-fill": 63286, + "envelope-heart": 63287, + "envelope-open-heart-fill": 63288, + "envelope-open-heart": 63289, + "envelope-paper-fill": 63290, + "envelope-paper-heart-fill": 63291, + "envelope-paper-heart": 63292, + "envelope-paper": 63293, + "filetype-aac": 63294, + "filetype-ai": 63295, + "filetype-bmp": 63296, + "filetype-cs": 63297, + "filetype-css": 63298, + "filetype-csv": 63299, + "filetype-doc": 63300, + "filetype-docx": 63301, + "filetype-exe": 63302, + "filetype-gif": 63303, + "filetype-heic": 63304, + "filetype-html": 63305, + "filetype-java": 63306, + "filetype-jpg": 63307, + "filetype-js": 63308, + "filetype-jsx": 63309, + "filetype-key": 63310, + "filetype-m4p": 63311, + "filetype-md": 63312, + "filetype-mdx": 63313, + "filetype-mov": 63314, + "filetype-mp3": 63315, + "filetype-mp4": 63316, + "filetype-otf": 63317, + "filetype-pdf": 63318, + "filetype-php": 63319, + "filetype-png": 63320, + "filetype-ppt": 63322, + "filetype-psd": 63323, + "filetype-py": 63324, + "filetype-raw": 63325, + "filetype-rb": 63326, + "filetype-sass": 63327, + "filetype-scss": 63328, + "filetype-sh": 63329, + "filetype-svg": 63330, + "filetype-tiff": 63331, + "filetype-tsx": 63332, + "filetype-ttf": 63333, + "filetype-txt": 63334, + "filetype-wav": 63335, + "filetype-woff": 63336, + "filetype-xls": 63338, + "filetype-xml": 63339, + "filetype-yml": 63340, + "heart-arrow": 63341, + "heart-pulse-fill": 63342, + "heart-pulse": 63343, + "heartbreak-fill": 63344, + "heartbreak": 63345, + "hearts": 63346, + "hospital-fill": 63347, + "hospital": 63348, + "house-heart-fill": 63349, + "house-heart": 63350, + "incognito": 63351, + "magnet-fill": 63352, + "magnet": 63353, + "person-heart": 63354, + "person-hearts": 63355, + "phone-flip": 63356, + "plugin": 63357, + "postage-fill": 63358, + "postage-heart-fill": 63359, + "postage-heart": 63360, + "postage": 63361, + "postcard-fill": 63362, + "postcard-heart-fill": 63363, + "postcard-heart": 63364, + "postcard": 63365, + "search-heart-fill": 63366, + "search-heart": 63367, + "sliders2-vertical": 63368, + "sliders2": 63369, + "trash3-fill": 63370, + "trash3": 63371, + "valentine": 63372, + "valentine2": 63373, + "wrench-adjustable-circle-fill": 63374, + "wrench-adjustable-circle": 63375, + "wrench-adjustable": 63376, + "filetype-json": 63377, + "filetype-pptx": 63378, + "filetype-xlsx": 63379, + "1-circle-fill": 63382, + "1-circle": 63383, + "1-square-fill": 63384, + "1-square": 63385, + "2-circle-fill": 63388, + "2-circle": 63389, + "2-square-fill": 63390, + "2-square": 63391, + "3-circle-fill": 63394, + "3-circle": 63395, + "3-square-fill": 63396, + "3-square": 63397, + "4-circle-fill": 63400, + "4-circle": 63401, + "4-square-fill": 63402, + "4-square": 63403, + "5-circle-fill": 63406, + "5-circle": 63407, + "5-square-fill": 63408, + "5-square": 63409, + "6-circle-fill": 63412, + "6-circle": 63413, + "6-square-fill": 63414, + "6-square": 63415, + "7-circle-fill": 63418, + "7-circle": 63419, + "7-square-fill": 63420, + "7-square": 63421, + "8-circle-fill": 63424, + "8-circle": 63425, + "8-square-fill": 63426, + "8-square": 63427, + "9-circle-fill": 63430, + "9-circle": 63431, + "9-square-fill": 63432, + "9-square": 63433, + "airplane-engines-fill": 63434, + "airplane-engines": 63435, + "airplane-fill": 63436, + "airplane": 63437, + "alexa": 63438, + "alipay": 63439, + "android": 63440, + "android2": 63441, + "box-fill": 63442, + "box-seam-fill": 63443, + "browser-chrome": 63444, + "browser-edge": 63445, + "browser-firefox": 63446, + "browser-safari": 63447, + "c-circle-fill": 63450, + "c-circle": 63451, + "c-square-fill": 63452, + "c-square": 63453, + "capsule-pill": 63454, + "capsule": 63455, + "car-front-fill": 63456, + "car-front": 63457, + "cassette-fill": 63458, + "cassette": 63459, + "cc-circle-fill": 63462, + "cc-circle": 63463, + "cc-square-fill": 63464, + "cc-square": 63465, + "cup-hot-fill": 63466, + "cup-hot": 63467, + "currency-rupee": 63468, + "dropbox": 63469, + "escape": 63470, + "fast-forward-btn-fill": 63471, + "fast-forward-btn": 63472, + "fast-forward-circle-fill": 63473, + "fast-forward-circle": 63474, + "fast-forward-fill": 63475, + "fast-forward": 63476, + "filetype-sql": 63477, + "fire": 63478, + "google-play": 63479, + "h-circle-fill": 63482, + "h-circle": 63483, + "h-square-fill": 63484, + "h-square": 63485, + "indent": 63486, + "lungs-fill": 63487, + "lungs": 63488, + "microsoft-teams": 63489, + "p-circle-fill": 63492, + "p-circle": 63493, + "p-square-fill": 63494, + "p-square": 63495, + "pass-fill": 63496, + "pass": 63497, + "prescription": 63498, + "prescription2": 63499, + "r-circle-fill": 63502, + "r-circle": 63503, + "r-square-fill": 63504, + "r-square": 63505, + "repeat-1": 63506, + "repeat": 63507, + "rewind-btn-fill": 63508, + "rewind-btn": 63509, + "rewind-circle-fill": 63510, + "rewind-circle": 63511, + "rewind-fill": 63512, + "rewind": 63513, + "train-freight-front-fill": 63514, + "train-freight-front": 63515, + "train-front-fill": 63516, + "train-front": 63517, + "train-lightrail-front-fill": 63518, + "train-lightrail-front": 63519, + "truck-front-fill": 63520, + "truck-front": 63521, + "ubuntu": 63522, + "unindent": 63523, + "unity": 63524, + "universal-access-circle": 63525, + "universal-access": 63526, + "virus": 63527, + "virus2": 63528, + "wechat": 63529, + "yelp": 63530, + "sign-stop-fill": 63531, + "sign-stop-lights-fill": 63532, + "sign-stop-lights": 63533, + "sign-stop": 63534, + "sign-turn-left-fill": 63535, + "sign-turn-left": 63536, + "sign-turn-right-fill": 63537, + "sign-turn-right": 63538, + "sign-turn-slight-left-fill": 63539, + "sign-turn-slight-left": 63540, + "sign-turn-slight-right-fill": 63541, + "sign-turn-slight-right": 63542, + "sign-yield-fill": 63543, + "sign-yield": 63544, + "ev-station-fill": 63545, + "ev-station": 63546, + "fuel-pump-diesel-fill": 63547, + "fuel-pump-diesel": 63548, + "fuel-pump-fill": 63549, + "fuel-pump": 63550, + "0-circle-fill": 63551, + "0-circle": 63552, + "0-square-fill": 63553, + "0-square": 63554, + "rocket-fill": 63555, + "rocket-takeoff-fill": 63556, + "rocket-takeoff": 63557, + "rocket": 63558, + "stripe": 63559, + "subscript": 63560, + "superscript": 63561, + "trello": 63562, + "envelope-at-fill": 63563, + "envelope-at": 63564, + "regex": 63565, + "text-wrap": 63566, + "sign-dead-end-fill": 63567, + "sign-dead-end": 63568, + "sign-do-not-enter-fill": 63569, + "sign-do-not-enter": 63570, + "sign-intersection-fill": 63571, + "sign-intersection-side-fill": 63572, + "sign-intersection-side": 63573, + "sign-intersection-t-fill": 63574, + "sign-intersection-t": 63575, + "sign-intersection-y-fill": 63576, + "sign-intersection-y": 63577, + "sign-intersection": 63578, + "sign-merge-left-fill": 63579, + "sign-merge-left": 63580, + "sign-merge-right-fill": 63581, + "sign-merge-right": 63582, + "sign-no-left-turn-fill": 63583, + "sign-no-left-turn": 63584, + "sign-no-parking-fill": 63585, + "sign-no-parking": 63586, + "sign-no-right-turn-fill": 63587, + "sign-no-right-turn": 63588, + "sign-railroad-fill": 63589, + "sign-railroad": 63590, + "building-add": 63591, + "building-check": 63592, + "building-dash": 63593, + "building-down": 63594, + "building-exclamation": 63595, + "building-fill-add": 63596, + "building-fill-check": 63597, + "building-fill-dash": 63598, + "building-fill-down": 63599, + "building-fill-exclamation": 63600, + "building-fill-gear": 63601, + "building-fill-lock": 63602, + "building-fill-slash": 63603, + "building-fill-up": 63604, + "building-fill-x": 63605, + "building-fill": 63606, + "building-gear": 63607, + "building-lock": 63608, + "building-slash": 63609, + "building-up": 63610, + "building-x": 63611, + "buildings-fill": 63612, + "buildings": 63613, + "bus-front-fill": 63614, + "bus-front": 63615, + "ev-front-fill": 63616, + "ev-front": 63617, + "globe-americas": 63618, + "globe-asia-australia": 63619, + "globe-central-south-asia": 63620, + "globe-europe-africa": 63621, + "house-add-fill": 63622, + "house-add": 63623, + "house-check-fill": 63624, + "house-check": 63625, + "house-dash-fill": 63626, + "house-dash": 63627, + "house-down-fill": 63628, + "house-down": 63629, + "house-exclamation-fill": 63630, + "house-exclamation": 63631, + "house-gear-fill": 63632, + "house-gear": 63633, + "house-lock-fill": 63634, + "house-lock": 63635, + "house-slash-fill": 63636, + "house-slash": 63637, + "house-up-fill": 63638, + "house-up": 63639, + "house-x-fill": 63640, + "house-x": 63641, + "person-add": 63642, + "person-down": 63643, + "person-exclamation": 63644, + "person-fill-add": 63645, + "person-fill-check": 63646, + "person-fill-dash": 63647, + "person-fill-down": 63648, + "person-fill-exclamation": 63649, + "person-fill-gear": 63650, + "person-fill-lock": 63651, + "person-fill-slash": 63652, + "person-fill-up": 63653, + "person-fill-x": 63654, + "person-gear": 63655, + "person-lock": 63656, + "person-slash": 63657, + "person-up": 63658, + "scooter": 63659, + "taxi-front-fill": 63660, + "taxi-front": 63661, + "amd": 63662, + "database-add": 63663, + "database-check": 63664, + "database-dash": 63665, + "database-down": 63666, + "database-exclamation": 63667, + "database-fill-add": 63668, + "database-fill-check": 63669, + "database-fill-dash": 63670, + "database-fill-down": 63671, + "database-fill-exclamation": 63672, + "database-fill-gear": 63673, + "database-fill-lock": 63674, + "database-fill-slash": 63675, + "database-fill-up": 63676, + "database-fill-x": 63677, + "database-fill": 63678, + "database-gear": 63679, + "database-lock": 63680, + "database-slash": 63681, + "database-up": 63682, + "database-x": 63683, + "database": 63684, + "houses-fill": 63685, + "houses": 63686, + "nvidia": 63687, + "person-vcard-fill": 63688, + "person-vcard": 63689, + "sina-weibo": 63690, + "tencent-qq": 63691, + "wikipedia": 63692, + "alphabet-uppercase": 62117, + "alphabet": 63114, + "amazon": 63117, + "arrows-collapse-vertical": 63120, + "arrows-expand-vertical": 63125, + "arrows-vertical": 63128, + "arrows": 63138, + "ban-fill": 63139, + "ban": 63158, + "bing": 63170, + "cake": 63200, + "cake2": 63213, + "cookie": 63214, + "copy": 63321, + "crosshair": 63337, + "crosshair2": 63380, + "emoji-astonished-fill": 63381, + "emoji-astonished": 63386, + "emoji-grimace-fill": 63387, + "emoji-grimace": 63392, + "emoji-grin-fill": 63393, + "emoji-grin": 63398, + "emoji-surprise-fill": 63399, + "emoji-surprise": 63404, + "emoji-tear-fill": 63405, + "emoji-tear": 63410, + "envelope-arrow-down-fill": 63411, + "envelope-arrow-down": 63416, + "envelope-arrow-up-fill": 63417, + "envelope-arrow-up": 63422, + "feather": 63423, + "feather2": 63428, + "floppy-fill": 63429, + "floppy": 63448, + "floppy2-fill": 63449, + "floppy2": 63460, + "gitlab": 63461, + "highlighter": 63480, + "marker-tip": 63490, + "nvme-fill": 63491, + "nvme": 63500, + "opencollective": 63501, + "pci-card-network": 63693, + "pci-card-sound": 63694, + "radar": 63695, + "send-arrow-down-fill": 63696, + "send-arrow-down": 63697, + "send-arrow-up-fill": 63698, + "send-arrow-up": 63699, + "sim-slash-fill": 63700, + "sim-slash": 63701, + "sourceforge": 63702, + "substack": 63703, + "threads-fill": 63704, + "threads": 63705, + "transparency": 63706, + "twitter-x": 63707, + "type-h4": 63708, + "type-h5": 63709, + "type-h6": 63710, + "backpack-fill": 63711, + "backpack": 63712, + "backpack2-fill": 63713, + "backpack2": 63714, + "backpack3-fill": 63715, + "backpack3": 63716, + "backpack4-fill": 63717, + "backpack4": 63718, + "brilliance": 63719, + "cake-fill": 63720, + "cake2-fill": 63721, + "duffle-fill": 63722, + "duffle": 63723, + "exposure": 63724, + "gender-neuter": 63725, + "highlights": 63726, + "luggage-fill": 63727, + "luggage": 63728, + "mailbox-flag": 63729, + "mailbox2-flag": 63730, + "noise-reduction": 63731, + "passport-fill": 63732, + "passport": 63733, + "person-arms-up": 63734, + "person-raised-hand": 63735, + "person-standing-dress": 63736, + "person-standing": 63737, + "person-walking": 63738, + "person-wheelchair": 63739, + "shadows": 63740, + "suitcase-fill": 63741, + "suitcase-lg-fill": 63742, + "suitcase-lg": 63743, + "suitcase": 63744, + "suitcase2-fill": 63745, + "suitcase2": 63746, + "vignette": 63747, + "bluesky": 63481, + "tux": 63748, + "beaker-fill": 63749, + "beaker": 63750, + "flask-fill": 63751, + "flask-florence-fill": 63752, + "flask-florence": 63753, + "flask": 63754, + "leaf-fill": 63755, + "leaf": 63756, + "measuring-cup-fill": 63757, + "measuring-cup": 63758, + "unlock2-fill": 63759, + "unlock2": 63760, + "battery-low": 63761, + "anthropic": 63762, + "apple-music": 63763, + "claude": 63764, + "openai": 63765, + "perplexity": 63766, + "css": 63767, + "javascript": 63768, + "typescript": 63769, + "fork-knife": 63770, + "globe-americas-fill": 63771, + "globe-asia-australia-fill": 63772, + "globe-central-south-asia-fill": 63773, + "globe-europe-africa-fill": 63774 +} \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bootstrap-icons.min.css b/web/vendor/bootstrap-icons/bootstrap-icons.min.css new file mode 100644 index 0000000..706a5c8 --- /dev/null +++ b/web/vendor/bootstrap-icons/bootstrap-icons.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap Icons v1.13.1 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */@font-face{font-display:block;font-family:bootstrap-icons;src:url("fonts/bootstrap-icons.woff2?e34853135f9e39acf64315236852cd5a") format("woff2"),url("fonts/bootstrap-icons.woff?e34853135f9e39acf64315236852cd5a") format("woff")}.bi::before,[class*=" bi-"]::before,[class^=bi-]::before{display:inline-block;font-family:bootstrap-icons!important;font-style:normal;font-weight:400!important;font-variant:normal;text-transform:none;line-height:1;vertical-align:-.125em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.bi-123::before{content:"\f67f"}.bi-alarm-fill::before{content:"\f101"}.bi-alarm::before{content:"\f102"}.bi-align-bottom::before{content:"\f103"}.bi-align-center::before{content:"\f104"}.bi-align-end::before{content:"\f105"}.bi-align-middle::before{content:"\f106"}.bi-align-start::before{content:"\f107"}.bi-align-top::before{content:"\f108"}.bi-alt::before{content:"\f109"}.bi-app-indicator::before{content:"\f10a"}.bi-app::before{content:"\f10b"}.bi-archive-fill::before{content:"\f10c"}.bi-archive::before{content:"\f10d"}.bi-arrow-90deg-down::before{content:"\f10e"}.bi-arrow-90deg-left::before{content:"\f10f"}.bi-arrow-90deg-right::before{content:"\f110"}.bi-arrow-90deg-up::before{content:"\f111"}.bi-arrow-bar-down::before{content:"\f112"}.bi-arrow-bar-left::before{content:"\f113"}.bi-arrow-bar-right::before{content:"\f114"}.bi-arrow-bar-up::before{content:"\f115"}.bi-arrow-clockwise::before{content:"\f116"}.bi-arrow-counterclockwise::before{content:"\f117"}.bi-arrow-down-circle-fill::before{content:"\f118"}.bi-arrow-down-circle::before{content:"\f119"}.bi-arrow-down-left-circle-fill::before{content:"\f11a"}.bi-arrow-down-left-circle::before{content:"\f11b"}.bi-arrow-down-left-square-fill::before{content:"\f11c"}.bi-arrow-down-left-square::before{content:"\f11d"}.bi-arrow-down-left::before{content:"\f11e"}.bi-arrow-down-right-circle-fill::before{content:"\f11f"}.bi-arrow-down-right-circle::before{content:"\f120"}.bi-arrow-down-right-square-fill::before{content:"\f121"}.bi-arrow-down-right-square::before{content:"\f122"}.bi-arrow-down-right::before{content:"\f123"}.bi-arrow-down-short::before{content:"\f124"}.bi-arrow-down-square-fill::before{content:"\f125"}.bi-arrow-down-square::before{content:"\f126"}.bi-arrow-down-up::before{content:"\f127"}.bi-arrow-down::before{content:"\f128"}.bi-arrow-left-circle-fill::before{content:"\f129"}.bi-arrow-left-circle::before{content:"\f12a"}.bi-arrow-left-right::before{content:"\f12b"}.bi-arrow-left-short::before{content:"\f12c"}.bi-arrow-left-square-fill::before{content:"\f12d"}.bi-arrow-left-square::before{content:"\f12e"}.bi-arrow-left::before{content:"\f12f"}.bi-arrow-repeat::before{content:"\f130"}.bi-arrow-return-left::before{content:"\f131"}.bi-arrow-return-right::before{content:"\f132"}.bi-arrow-right-circle-fill::before{content:"\f133"}.bi-arrow-right-circle::before{content:"\f134"}.bi-arrow-right-short::before{content:"\f135"}.bi-arrow-right-square-fill::before{content:"\f136"}.bi-arrow-right-square::before{content:"\f137"}.bi-arrow-right::before{content:"\f138"}.bi-arrow-up-circle-fill::before{content:"\f139"}.bi-arrow-up-circle::before{content:"\f13a"}.bi-arrow-up-left-circle-fill::before{content:"\f13b"}.bi-arrow-up-left-circle::before{content:"\f13c"}.bi-arrow-up-left-square-fill::before{content:"\f13d"}.bi-arrow-up-left-square::before{content:"\f13e"}.bi-arrow-up-left::before{content:"\f13f"}.bi-arrow-up-right-circle-fill::before{content:"\f140"}.bi-arrow-up-right-circle::before{content:"\f141"}.bi-arrow-up-right-square-fill::before{content:"\f142"}.bi-arrow-up-right-square::before{content:"\f143"}.bi-arrow-up-right::before{content:"\f144"}.bi-arrow-up-short::before{content:"\f145"}.bi-arrow-up-square-fill::before{content:"\f146"}.bi-arrow-up-square::before{content:"\f147"}.bi-arrow-up::before{content:"\f148"}.bi-arrows-angle-contract::before{content:"\f149"}.bi-arrows-angle-expand::before{content:"\f14a"}.bi-arrows-collapse::before{content:"\f14b"}.bi-arrows-expand::before{content:"\f14c"}.bi-arrows-fullscreen::before{content:"\f14d"}.bi-arrows-move::before{content:"\f14e"}.bi-aspect-ratio-fill::before{content:"\f14f"}.bi-aspect-ratio::before{content:"\f150"}.bi-asterisk::before{content:"\f151"}.bi-at::before{content:"\f152"}.bi-award-fill::before{content:"\f153"}.bi-award::before{content:"\f154"}.bi-back::before{content:"\f155"}.bi-backspace-fill::before{content:"\f156"}.bi-backspace-reverse-fill::before{content:"\f157"}.bi-backspace-reverse::before{content:"\f158"}.bi-backspace::before{content:"\f159"}.bi-badge-3d-fill::before{content:"\f15a"}.bi-badge-3d::before{content:"\f15b"}.bi-badge-4k-fill::before{content:"\f15c"}.bi-badge-4k::before{content:"\f15d"}.bi-badge-8k-fill::before{content:"\f15e"}.bi-badge-8k::before{content:"\f15f"}.bi-badge-ad-fill::before{content:"\f160"}.bi-badge-ad::before{content:"\f161"}.bi-badge-ar-fill::before{content:"\f162"}.bi-badge-ar::before{content:"\f163"}.bi-badge-cc-fill::before{content:"\f164"}.bi-badge-cc::before{content:"\f165"}.bi-badge-hd-fill::before{content:"\f166"}.bi-badge-hd::before{content:"\f167"}.bi-badge-tm-fill::before{content:"\f168"}.bi-badge-tm::before{content:"\f169"}.bi-badge-vo-fill::before{content:"\f16a"}.bi-badge-vo::before{content:"\f16b"}.bi-badge-vr-fill::before{content:"\f16c"}.bi-badge-vr::before{content:"\f16d"}.bi-badge-wc-fill::before{content:"\f16e"}.bi-badge-wc::before{content:"\f16f"}.bi-bag-check-fill::before{content:"\f170"}.bi-bag-check::before{content:"\f171"}.bi-bag-dash-fill::before{content:"\f172"}.bi-bag-dash::before{content:"\f173"}.bi-bag-fill::before{content:"\f174"}.bi-bag-plus-fill::before{content:"\f175"}.bi-bag-plus::before{content:"\f176"}.bi-bag-x-fill::before{content:"\f177"}.bi-bag-x::before{content:"\f178"}.bi-bag::before{content:"\f179"}.bi-bar-chart-fill::before{content:"\f17a"}.bi-bar-chart-line-fill::before{content:"\f17b"}.bi-bar-chart-line::before{content:"\f17c"}.bi-bar-chart-steps::before{content:"\f17d"}.bi-bar-chart::before{content:"\f17e"}.bi-basket-fill::before{content:"\f17f"}.bi-basket::before{content:"\f180"}.bi-basket2-fill::before{content:"\f181"}.bi-basket2::before{content:"\f182"}.bi-basket3-fill::before{content:"\f183"}.bi-basket3::before{content:"\f184"}.bi-battery-charging::before{content:"\f185"}.bi-battery-full::before{content:"\f186"}.bi-battery-half::before{content:"\f187"}.bi-battery::before{content:"\f188"}.bi-bell-fill::before{content:"\f189"}.bi-bell::before{content:"\f18a"}.bi-bezier::before{content:"\f18b"}.bi-bezier2::before{content:"\f18c"}.bi-bicycle::before{content:"\f18d"}.bi-binoculars-fill::before{content:"\f18e"}.bi-binoculars::before{content:"\f18f"}.bi-blockquote-left::before{content:"\f190"}.bi-blockquote-right::before{content:"\f191"}.bi-book-fill::before{content:"\f192"}.bi-book-half::before{content:"\f193"}.bi-book::before{content:"\f194"}.bi-bookmark-check-fill::before{content:"\f195"}.bi-bookmark-check::before{content:"\f196"}.bi-bookmark-dash-fill::before{content:"\f197"}.bi-bookmark-dash::before{content:"\f198"}.bi-bookmark-fill::before{content:"\f199"}.bi-bookmark-heart-fill::before{content:"\f19a"}.bi-bookmark-heart::before{content:"\f19b"}.bi-bookmark-plus-fill::before{content:"\f19c"}.bi-bookmark-plus::before{content:"\f19d"}.bi-bookmark-star-fill::before{content:"\f19e"}.bi-bookmark-star::before{content:"\f19f"}.bi-bookmark-x-fill::before{content:"\f1a0"}.bi-bookmark-x::before{content:"\f1a1"}.bi-bookmark::before{content:"\f1a2"}.bi-bookmarks-fill::before{content:"\f1a3"}.bi-bookmarks::before{content:"\f1a4"}.bi-bookshelf::before{content:"\f1a5"}.bi-bootstrap-fill::before{content:"\f1a6"}.bi-bootstrap-reboot::before{content:"\f1a7"}.bi-bootstrap::before{content:"\f1a8"}.bi-border-all::before{content:"\f1a9"}.bi-border-bottom::before{content:"\f1aa"}.bi-border-center::before{content:"\f1ab"}.bi-border-inner::before{content:"\f1ac"}.bi-border-left::before{content:"\f1ad"}.bi-border-middle::before{content:"\f1ae"}.bi-border-outer::before{content:"\f1af"}.bi-border-right::before{content:"\f1b0"}.bi-border-style::before{content:"\f1b1"}.bi-border-top::before{content:"\f1b2"}.bi-border-width::before{content:"\f1b3"}.bi-border::before{content:"\f1b4"}.bi-bounding-box-circles::before{content:"\f1b5"}.bi-bounding-box::before{content:"\f1b6"}.bi-box-arrow-down-left::before{content:"\f1b7"}.bi-box-arrow-down-right::before{content:"\f1b8"}.bi-box-arrow-down::before{content:"\f1b9"}.bi-box-arrow-in-down-left::before{content:"\f1ba"}.bi-box-arrow-in-down-right::before{content:"\f1bb"}.bi-box-arrow-in-down::before{content:"\f1bc"}.bi-box-arrow-in-left::before{content:"\f1bd"}.bi-box-arrow-in-right::before{content:"\f1be"}.bi-box-arrow-in-up-left::before{content:"\f1bf"}.bi-box-arrow-in-up-right::before{content:"\f1c0"}.bi-box-arrow-in-up::before{content:"\f1c1"}.bi-box-arrow-left::before{content:"\f1c2"}.bi-box-arrow-right::before{content:"\f1c3"}.bi-box-arrow-up-left::before{content:"\f1c4"}.bi-box-arrow-up-right::before{content:"\f1c5"}.bi-box-arrow-up::before{content:"\f1c6"}.bi-box-seam::before{content:"\f1c7"}.bi-box::before{content:"\f1c8"}.bi-braces::before{content:"\f1c9"}.bi-bricks::before{content:"\f1ca"}.bi-briefcase-fill::before{content:"\f1cb"}.bi-briefcase::before{content:"\f1cc"}.bi-brightness-alt-high-fill::before{content:"\f1cd"}.bi-brightness-alt-high::before{content:"\f1ce"}.bi-brightness-alt-low-fill::before{content:"\f1cf"}.bi-brightness-alt-low::before{content:"\f1d0"}.bi-brightness-high-fill::before{content:"\f1d1"}.bi-brightness-high::before{content:"\f1d2"}.bi-brightness-low-fill::before{content:"\f1d3"}.bi-brightness-low::before{content:"\f1d4"}.bi-broadcast-pin::before{content:"\f1d5"}.bi-broadcast::before{content:"\f1d6"}.bi-brush-fill::before{content:"\f1d7"}.bi-brush::before{content:"\f1d8"}.bi-bucket-fill::before{content:"\f1d9"}.bi-bucket::before{content:"\f1da"}.bi-bug-fill::before{content:"\f1db"}.bi-bug::before{content:"\f1dc"}.bi-building::before{content:"\f1dd"}.bi-bullseye::before{content:"\f1de"}.bi-calculator-fill::before{content:"\f1df"}.bi-calculator::before{content:"\f1e0"}.bi-calendar-check-fill::before{content:"\f1e1"}.bi-calendar-check::before{content:"\f1e2"}.bi-calendar-date-fill::before{content:"\f1e3"}.bi-calendar-date::before{content:"\f1e4"}.bi-calendar-day-fill::before{content:"\f1e5"}.bi-calendar-day::before{content:"\f1e6"}.bi-calendar-event-fill::before{content:"\f1e7"}.bi-calendar-event::before{content:"\f1e8"}.bi-calendar-fill::before{content:"\f1e9"}.bi-calendar-minus-fill::before{content:"\f1ea"}.bi-calendar-minus::before{content:"\f1eb"}.bi-calendar-month-fill::before{content:"\f1ec"}.bi-calendar-month::before{content:"\f1ed"}.bi-calendar-plus-fill::before{content:"\f1ee"}.bi-calendar-plus::before{content:"\f1ef"}.bi-calendar-range-fill::before{content:"\f1f0"}.bi-calendar-range::before{content:"\f1f1"}.bi-calendar-week-fill::before{content:"\f1f2"}.bi-calendar-week::before{content:"\f1f3"}.bi-calendar-x-fill::before{content:"\f1f4"}.bi-calendar-x::before{content:"\f1f5"}.bi-calendar::before{content:"\f1f6"}.bi-calendar2-check-fill::before{content:"\f1f7"}.bi-calendar2-check::before{content:"\f1f8"}.bi-calendar2-date-fill::before{content:"\f1f9"}.bi-calendar2-date::before{content:"\f1fa"}.bi-calendar2-day-fill::before{content:"\f1fb"}.bi-calendar2-day::before{content:"\f1fc"}.bi-calendar2-event-fill::before{content:"\f1fd"}.bi-calendar2-event::before{content:"\f1fe"}.bi-calendar2-fill::before{content:"\f1ff"}.bi-calendar2-minus-fill::before{content:"\f200"}.bi-calendar2-minus::before{content:"\f201"}.bi-calendar2-month-fill::before{content:"\f202"}.bi-calendar2-month::before{content:"\f203"}.bi-calendar2-plus-fill::before{content:"\f204"}.bi-calendar2-plus::before{content:"\f205"}.bi-calendar2-range-fill::before{content:"\f206"}.bi-calendar2-range::before{content:"\f207"}.bi-calendar2-week-fill::before{content:"\f208"}.bi-calendar2-week::before{content:"\f209"}.bi-calendar2-x-fill::before{content:"\f20a"}.bi-calendar2-x::before{content:"\f20b"}.bi-calendar2::before{content:"\f20c"}.bi-calendar3-event-fill::before{content:"\f20d"}.bi-calendar3-event::before{content:"\f20e"}.bi-calendar3-fill::before{content:"\f20f"}.bi-calendar3-range-fill::before{content:"\f210"}.bi-calendar3-range::before{content:"\f211"}.bi-calendar3-week-fill::before{content:"\f212"}.bi-calendar3-week::before{content:"\f213"}.bi-calendar3::before{content:"\f214"}.bi-calendar4-event::before{content:"\f215"}.bi-calendar4-range::before{content:"\f216"}.bi-calendar4-week::before{content:"\f217"}.bi-calendar4::before{content:"\f218"}.bi-camera-fill::before{content:"\f219"}.bi-camera-reels-fill::before{content:"\f21a"}.bi-camera-reels::before{content:"\f21b"}.bi-camera-video-fill::before{content:"\f21c"}.bi-camera-video-off-fill::before{content:"\f21d"}.bi-camera-video-off::before{content:"\f21e"}.bi-camera-video::before{content:"\f21f"}.bi-camera::before{content:"\f220"}.bi-camera2::before{content:"\f221"}.bi-capslock-fill::before{content:"\f222"}.bi-capslock::before{content:"\f223"}.bi-card-checklist::before{content:"\f224"}.bi-card-heading::before{content:"\f225"}.bi-card-image::before{content:"\f226"}.bi-card-list::before{content:"\f227"}.bi-card-text::before{content:"\f228"}.bi-caret-down-fill::before{content:"\f229"}.bi-caret-down-square-fill::before{content:"\f22a"}.bi-caret-down-square::before{content:"\f22b"}.bi-caret-down::before{content:"\f22c"}.bi-caret-left-fill::before{content:"\f22d"}.bi-caret-left-square-fill::before{content:"\f22e"}.bi-caret-left-square::before{content:"\f22f"}.bi-caret-left::before{content:"\f230"}.bi-caret-right-fill::before{content:"\f231"}.bi-caret-right-square-fill::before{content:"\f232"}.bi-caret-right-square::before{content:"\f233"}.bi-caret-right::before{content:"\f234"}.bi-caret-up-fill::before{content:"\f235"}.bi-caret-up-square-fill::before{content:"\f236"}.bi-caret-up-square::before{content:"\f237"}.bi-caret-up::before{content:"\f238"}.bi-cart-check-fill::before{content:"\f239"}.bi-cart-check::before{content:"\f23a"}.bi-cart-dash-fill::before{content:"\f23b"}.bi-cart-dash::before{content:"\f23c"}.bi-cart-fill::before{content:"\f23d"}.bi-cart-plus-fill::before{content:"\f23e"}.bi-cart-plus::before{content:"\f23f"}.bi-cart-x-fill::before{content:"\f240"}.bi-cart-x::before{content:"\f241"}.bi-cart::before{content:"\f242"}.bi-cart2::before{content:"\f243"}.bi-cart3::before{content:"\f244"}.bi-cart4::before{content:"\f245"}.bi-cash-stack::before{content:"\f246"}.bi-cash::before{content:"\f247"}.bi-cast::before{content:"\f248"}.bi-chat-dots-fill::before{content:"\f249"}.bi-chat-dots::before{content:"\f24a"}.bi-chat-fill::before{content:"\f24b"}.bi-chat-left-dots-fill::before{content:"\f24c"}.bi-chat-left-dots::before{content:"\f24d"}.bi-chat-left-fill::before{content:"\f24e"}.bi-chat-left-quote-fill::before{content:"\f24f"}.bi-chat-left-quote::before{content:"\f250"}.bi-chat-left-text-fill::before{content:"\f251"}.bi-chat-left-text::before{content:"\f252"}.bi-chat-left::before{content:"\f253"}.bi-chat-quote-fill::before{content:"\f254"}.bi-chat-quote::before{content:"\f255"}.bi-chat-right-dots-fill::before{content:"\f256"}.bi-chat-right-dots::before{content:"\f257"}.bi-chat-right-fill::before{content:"\f258"}.bi-chat-right-quote-fill::before{content:"\f259"}.bi-chat-right-quote::before{content:"\f25a"}.bi-chat-right-text-fill::before{content:"\f25b"}.bi-chat-right-text::before{content:"\f25c"}.bi-chat-right::before{content:"\f25d"}.bi-chat-square-dots-fill::before{content:"\f25e"}.bi-chat-square-dots::before{content:"\f25f"}.bi-chat-square-fill::before{content:"\f260"}.bi-chat-square-quote-fill::before{content:"\f261"}.bi-chat-square-quote::before{content:"\f262"}.bi-chat-square-text-fill::before{content:"\f263"}.bi-chat-square-text::before{content:"\f264"}.bi-chat-square::before{content:"\f265"}.bi-chat-text-fill::before{content:"\f266"}.bi-chat-text::before{content:"\f267"}.bi-chat::before{content:"\f268"}.bi-check-all::before{content:"\f269"}.bi-check-circle-fill::before{content:"\f26a"}.bi-check-circle::before{content:"\f26b"}.bi-check-square-fill::before{content:"\f26c"}.bi-check-square::before{content:"\f26d"}.bi-check::before{content:"\f26e"}.bi-check2-all::before{content:"\f26f"}.bi-check2-circle::before{content:"\f270"}.bi-check2-square::before{content:"\f271"}.bi-check2::before{content:"\f272"}.bi-chevron-bar-contract::before{content:"\f273"}.bi-chevron-bar-down::before{content:"\f274"}.bi-chevron-bar-expand::before{content:"\f275"}.bi-chevron-bar-left::before{content:"\f276"}.bi-chevron-bar-right::before{content:"\f277"}.bi-chevron-bar-up::before{content:"\f278"}.bi-chevron-compact-down::before{content:"\f279"}.bi-chevron-compact-left::before{content:"\f27a"}.bi-chevron-compact-right::before{content:"\f27b"}.bi-chevron-compact-up::before{content:"\f27c"}.bi-chevron-contract::before{content:"\f27d"}.bi-chevron-double-down::before{content:"\f27e"}.bi-chevron-double-left::before{content:"\f27f"}.bi-chevron-double-right::before{content:"\f280"}.bi-chevron-double-up::before{content:"\f281"}.bi-chevron-down::before{content:"\f282"}.bi-chevron-expand::before{content:"\f283"}.bi-chevron-left::before{content:"\f284"}.bi-chevron-right::before{content:"\f285"}.bi-chevron-up::before{content:"\f286"}.bi-circle-fill::before{content:"\f287"}.bi-circle-half::before{content:"\f288"}.bi-circle-square::before{content:"\f289"}.bi-circle::before{content:"\f28a"}.bi-clipboard-check::before{content:"\f28b"}.bi-clipboard-data::before{content:"\f28c"}.bi-clipboard-minus::before{content:"\f28d"}.bi-clipboard-plus::before{content:"\f28e"}.bi-clipboard-x::before{content:"\f28f"}.bi-clipboard::before{content:"\f290"}.bi-clock-fill::before{content:"\f291"}.bi-clock-history::before{content:"\f292"}.bi-clock::before{content:"\f293"}.bi-cloud-arrow-down-fill::before{content:"\f294"}.bi-cloud-arrow-down::before{content:"\f295"}.bi-cloud-arrow-up-fill::before{content:"\f296"}.bi-cloud-arrow-up::before{content:"\f297"}.bi-cloud-check-fill::before{content:"\f298"}.bi-cloud-check::before{content:"\f299"}.bi-cloud-download-fill::before{content:"\f29a"}.bi-cloud-download::before{content:"\f29b"}.bi-cloud-drizzle-fill::before{content:"\f29c"}.bi-cloud-drizzle::before{content:"\f29d"}.bi-cloud-fill::before{content:"\f29e"}.bi-cloud-fog-fill::before{content:"\f29f"}.bi-cloud-fog::before{content:"\f2a0"}.bi-cloud-fog2-fill::before{content:"\f2a1"}.bi-cloud-fog2::before{content:"\f2a2"}.bi-cloud-hail-fill::before{content:"\f2a3"}.bi-cloud-hail::before{content:"\f2a4"}.bi-cloud-haze-fill::before{content:"\f2a6"}.bi-cloud-haze::before{content:"\f2a7"}.bi-cloud-haze2-fill::before{content:"\f2a8"}.bi-cloud-lightning-fill::before{content:"\f2a9"}.bi-cloud-lightning-rain-fill::before{content:"\f2aa"}.bi-cloud-lightning-rain::before{content:"\f2ab"}.bi-cloud-lightning::before{content:"\f2ac"}.bi-cloud-minus-fill::before{content:"\f2ad"}.bi-cloud-minus::before{content:"\f2ae"}.bi-cloud-moon-fill::before{content:"\f2af"}.bi-cloud-moon::before{content:"\f2b0"}.bi-cloud-plus-fill::before{content:"\f2b1"}.bi-cloud-plus::before{content:"\f2b2"}.bi-cloud-rain-fill::before{content:"\f2b3"}.bi-cloud-rain-heavy-fill::before{content:"\f2b4"}.bi-cloud-rain-heavy::before{content:"\f2b5"}.bi-cloud-rain::before{content:"\f2b6"}.bi-cloud-slash-fill::before{content:"\f2b7"}.bi-cloud-slash::before{content:"\f2b8"}.bi-cloud-sleet-fill::before{content:"\f2b9"}.bi-cloud-sleet::before{content:"\f2ba"}.bi-cloud-snow-fill::before{content:"\f2bb"}.bi-cloud-snow::before{content:"\f2bc"}.bi-cloud-sun-fill::before{content:"\f2bd"}.bi-cloud-sun::before{content:"\f2be"}.bi-cloud-upload-fill::before{content:"\f2bf"}.bi-cloud-upload::before{content:"\f2c0"}.bi-cloud::before{content:"\f2c1"}.bi-clouds-fill::before{content:"\f2c2"}.bi-clouds::before{content:"\f2c3"}.bi-cloudy-fill::before{content:"\f2c4"}.bi-cloudy::before{content:"\f2c5"}.bi-code-slash::before{content:"\f2c6"}.bi-code-square::before{content:"\f2c7"}.bi-code::before{content:"\f2c8"}.bi-collection-fill::before{content:"\f2c9"}.bi-collection-play-fill::before{content:"\f2ca"}.bi-collection-play::before{content:"\f2cb"}.bi-collection::before{content:"\f2cc"}.bi-columns-gap::before{content:"\f2cd"}.bi-columns::before{content:"\f2ce"}.bi-command::before{content:"\f2cf"}.bi-compass-fill::before{content:"\f2d0"}.bi-compass::before{content:"\f2d1"}.bi-cone-striped::before{content:"\f2d2"}.bi-cone::before{content:"\f2d3"}.bi-controller::before{content:"\f2d4"}.bi-cpu-fill::before{content:"\f2d5"}.bi-cpu::before{content:"\f2d6"}.bi-credit-card-2-back-fill::before{content:"\f2d7"}.bi-credit-card-2-back::before{content:"\f2d8"}.bi-credit-card-2-front-fill::before{content:"\f2d9"}.bi-credit-card-2-front::before{content:"\f2da"}.bi-credit-card-fill::before{content:"\f2db"}.bi-credit-card::before{content:"\f2dc"}.bi-crop::before{content:"\f2dd"}.bi-cup-fill::before{content:"\f2de"}.bi-cup-straw::before{content:"\f2df"}.bi-cup::before{content:"\f2e0"}.bi-cursor-fill::before{content:"\f2e1"}.bi-cursor-text::before{content:"\f2e2"}.bi-cursor::before{content:"\f2e3"}.bi-dash-circle-dotted::before{content:"\f2e4"}.bi-dash-circle-fill::before{content:"\f2e5"}.bi-dash-circle::before{content:"\f2e6"}.bi-dash-square-dotted::before{content:"\f2e7"}.bi-dash-square-fill::before{content:"\f2e8"}.bi-dash-square::before{content:"\f2e9"}.bi-dash::before{content:"\f2ea"}.bi-diagram-2-fill::before{content:"\f2eb"}.bi-diagram-2::before{content:"\f2ec"}.bi-diagram-3-fill::before{content:"\f2ed"}.bi-diagram-3::before{content:"\f2ee"}.bi-diamond-fill::before{content:"\f2ef"}.bi-diamond-half::before{content:"\f2f0"}.bi-diamond::before{content:"\f2f1"}.bi-dice-1-fill::before{content:"\f2f2"}.bi-dice-1::before{content:"\f2f3"}.bi-dice-2-fill::before{content:"\f2f4"}.bi-dice-2::before{content:"\f2f5"}.bi-dice-3-fill::before{content:"\f2f6"}.bi-dice-3::before{content:"\f2f7"}.bi-dice-4-fill::before{content:"\f2f8"}.bi-dice-4::before{content:"\f2f9"}.bi-dice-5-fill::before{content:"\f2fa"}.bi-dice-5::before{content:"\f2fb"}.bi-dice-6-fill::before{content:"\f2fc"}.bi-dice-6::before{content:"\f2fd"}.bi-disc-fill::before{content:"\f2fe"}.bi-disc::before{content:"\f2ff"}.bi-discord::before{content:"\f300"}.bi-display-fill::before{content:"\f301"}.bi-display::before{content:"\f302"}.bi-distribute-horizontal::before{content:"\f303"}.bi-distribute-vertical::before{content:"\f304"}.bi-door-closed-fill::before{content:"\f305"}.bi-door-closed::before{content:"\f306"}.bi-door-open-fill::before{content:"\f307"}.bi-door-open::before{content:"\f308"}.bi-dot::before{content:"\f309"}.bi-download::before{content:"\f30a"}.bi-droplet-fill::before{content:"\f30b"}.bi-droplet-half::before{content:"\f30c"}.bi-droplet::before{content:"\f30d"}.bi-earbuds::before{content:"\f30e"}.bi-easel-fill::before{content:"\f30f"}.bi-easel::before{content:"\f310"}.bi-egg-fill::before{content:"\f311"}.bi-egg-fried::before{content:"\f312"}.bi-egg::before{content:"\f313"}.bi-eject-fill::before{content:"\f314"}.bi-eject::before{content:"\f315"}.bi-emoji-angry-fill::before{content:"\f316"}.bi-emoji-angry::before{content:"\f317"}.bi-emoji-dizzy-fill::before{content:"\f318"}.bi-emoji-dizzy::before{content:"\f319"}.bi-emoji-expressionless-fill::before{content:"\f31a"}.bi-emoji-expressionless::before{content:"\f31b"}.bi-emoji-frown-fill::before{content:"\f31c"}.bi-emoji-frown::before{content:"\f31d"}.bi-emoji-heart-eyes-fill::before{content:"\f31e"}.bi-emoji-heart-eyes::before{content:"\f31f"}.bi-emoji-laughing-fill::before{content:"\f320"}.bi-emoji-laughing::before{content:"\f321"}.bi-emoji-neutral-fill::before{content:"\f322"}.bi-emoji-neutral::before{content:"\f323"}.bi-emoji-smile-fill::before{content:"\f324"}.bi-emoji-smile-upside-down-fill::before{content:"\f325"}.bi-emoji-smile-upside-down::before{content:"\f326"}.bi-emoji-smile::before{content:"\f327"}.bi-emoji-sunglasses-fill::before{content:"\f328"}.bi-emoji-sunglasses::before{content:"\f329"}.bi-emoji-wink-fill::before{content:"\f32a"}.bi-emoji-wink::before{content:"\f32b"}.bi-envelope-fill::before{content:"\f32c"}.bi-envelope-open-fill::before{content:"\f32d"}.bi-envelope-open::before{content:"\f32e"}.bi-envelope::before{content:"\f32f"}.bi-eraser-fill::before{content:"\f330"}.bi-eraser::before{content:"\f331"}.bi-exclamation-circle-fill::before{content:"\f332"}.bi-exclamation-circle::before{content:"\f333"}.bi-exclamation-diamond-fill::before{content:"\f334"}.bi-exclamation-diamond::before{content:"\f335"}.bi-exclamation-octagon-fill::before{content:"\f336"}.bi-exclamation-octagon::before{content:"\f337"}.bi-exclamation-square-fill::before{content:"\f338"}.bi-exclamation-square::before{content:"\f339"}.bi-exclamation-triangle-fill::before{content:"\f33a"}.bi-exclamation-triangle::before{content:"\f33b"}.bi-exclamation::before{content:"\f33c"}.bi-exclude::before{content:"\f33d"}.bi-eye-fill::before{content:"\f33e"}.bi-eye-slash-fill::before{content:"\f33f"}.bi-eye-slash::before{content:"\f340"}.bi-eye::before{content:"\f341"}.bi-eyedropper::before{content:"\f342"}.bi-eyeglasses::before{content:"\f343"}.bi-facebook::before{content:"\f344"}.bi-file-arrow-down-fill::before{content:"\f345"}.bi-file-arrow-down::before{content:"\f346"}.bi-file-arrow-up-fill::before{content:"\f347"}.bi-file-arrow-up::before{content:"\f348"}.bi-file-bar-graph-fill::before{content:"\f349"}.bi-file-bar-graph::before{content:"\f34a"}.bi-file-binary-fill::before{content:"\f34b"}.bi-file-binary::before{content:"\f34c"}.bi-file-break-fill::before{content:"\f34d"}.bi-file-break::before{content:"\f34e"}.bi-file-check-fill::before{content:"\f34f"}.bi-file-check::before{content:"\f350"}.bi-file-code-fill::before{content:"\f351"}.bi-file-code::before{content:"\f352"}.bi-file-diff-fill::before{content:"\f353"}.bi-file-diff::before{content:"\f354"}.bi-file-earmark-arrow-down-fill::before{content:"\f355"}.bi-file-earmark-arrow-down::before{content:"\f356"}.bi-file-earmark-arrow-up-fill::before{content:"\f357"}.bi-file-earmark-arrow-up::before{content:"\f358"}.bi-file-earmark-bar-graph-fill::before{content:"\f359"}.bi-file-earmark-bar-graph::before{content:"\f35a"}.bi-file-earmark-binary-fill::before{content:"\f35b"}.bi-file-earmark-binary::before{content:"\f35c"}.bi-file-earmark-break-fill::before{content:"\f35d"}.bi-file-earmark-break::before{content:"\f35e"}.bi-file-earmark-check-fill::before{content:"\f35f"}.bi-file-earmark-check::before{content:"\f360"}.bi-file-earmark-code-fill::before{content:"\f361"}.bi-file-earmark-code::before{content:"\f362"}.bi-file-earmark-diff-fill::before{content:"\f363"}.bi-file-earmark-diff::before{content:"\f364"}.bi-file-earmark-easel-fill::before{content:"\f365"}.bi-file-earmark-easel::before{content:"\f366"}.bi-file-earmark-excel-fill::before{content:"\f367"}.bi-file-earmark-excel::before{content:"\f368"}.bi-file-earmark-fill::before{content:"\f369"}.bi-file-earmark-font-fill::before{content:"\f36a"}.bi-file-earmark-font::before{content:"\f36b"}.bi-file-earmark-image-fill::before{content:"\f36c"}.bi-file-earmark-image::before{content:"\f36d"}.bi-file-earmark-lock-fill::before{content:"\f36e"}.bi-file-earmark-lock::before{content:"\f36f"}.bi-file-earmark-lock2-fill::before{content:"\f370"}.bi-file-earmark-lock2::before{content:"\f371"}.bi-file-earmark-medical-fill::before{content:"\f372"}.bi-file-earmark-medical::before{content:"\f373"}.bi-file-earmark-minus-fill::before{content:"\f374"}.bi-file-earmark-minus::before{content:"\f375"}.bi-file-earmark-music-fill::before{content:"\f376"}.bi-file-earmark-music::before{content:"\f377"}.bi-file-earmark-person-fill::before{content:"\f378"}.bi-file-earmark-person::before{content:"\f379"}.bi-file-earmark-play-fill::before{content:"\f37a"}.bi-file-earmark-play::before{content:"\f37b"}.bi-file-earmark-plus-fill::before{content:"\f37c"}.bi-file-earmark-plus::before{content:"\f37d"}.bi-file-earmark-post-fill::before{content:"\f37e"}.bi-file-earmark-post::before{content:"\f37f"}.bi-file-earmark-ppt-fill::before{content:"\f380"}.bi-file-earmark-ppt::before{content:"\f381"}.bi-file-earmark-richtext-fill::before{content:"\f382"}.bi-file-earmark-richtext::before{content:"\f383"}.bi-file-earmark-ruled-fill::before{content:"\f384"}.bi-file-earmark-ruled::before{content:"\f385"}.bi-file-earmark-slides-fill::before{content:"\f386"}.bi-file-earmark-slides::before{content:"\f387"}.bi-file-earmark-spreadsheet-fill::before{content:"\f388"}.bi-file-earmark-spreadsheet::before{content:"\f389"}.bi-file-earmark-text-fill::before{content:"\f38a"}.bi-file-earmark-text::before{content:"\f38b"}.bi-file-earmark-word-fill::before{content:"\f38c"}.bi-file-earmark-word::before{content:"\f38d"}.bi-file-earmark-x-fill::before{content:"\f38e"}.bi-file-earmark-x::before{content:"\f38f"}.bi-file-earmark-zip-fill::before{content:"\f390"}.bi-file-earmark-zip::before{content:"\f391"}.bi-file-earmark::before{content:"\f392"}.bi-file-easel-fill::before{content:"\f393"}.bi-file-easel::before{content:"\f394"}.bi-file-excel-fill::before{content:"\f395"}.bi-file-excel::before{content:"\f396"}.bi-file-fill::before{content:"\f397"}.bi-file-font-fill::before{content:"\f398"}.bi-file-font::before{content:"\f399"}.bi-file-image-fill::before{content:"\f39a"}.bi-file-image::before{content:"\f39b"}.bi-file-lock-fill::before{content:"\f39c"}.bi-file-lock::before{content:"\f39d"}.bi-file-lock2-fill::before{content:"\f39e"}.bi-file-lock2::before{content:"\f39f"}.bi-file-medical-fill::before{content:"\f3a0"}.bi-file-medical::before{content:"\f3a1"}.bi-file-minus-fill::before{content:"\f3a2"}.bi-file-minus::before{content:"\f3a3"}.bi-file-music-fill::before{content:"\f3a4"}.bi-file-music::before{content:"\f3a5"}.bi-file-person-fill::before{content:"\f3a6"}.bi-file-person::before{content:"\f3a7"}.bi-file-play-fill::before{content:"\f3a8"}.bi-file-play::before{content:"\f3a9"}.bi-file-plus-fill::before{content:"\f3aa"}.bi-file-plus::before{content:"\f3ab"}.bi-file-post-fill::before{content:"\f3ac"}.bi-file-post::before{content:"\f3ad"}.bi-file-ppt-fill::before{content:"\f3ae"}.bi-file-ppt::before{content:"\f3af"}.bi-file-richtext-fill::before{content:"\f3b0"}.bi-file-richtext::before{content:"\f3b1"}.bi-file-ruled-fill::before{content:"\f3b2"}.bi-file-ruled::before{content:"\f3b3"}.bi-file-slides-fill::before{content:"\f3b4"}.bi-file-slides::before{content:"\f3b5"}.bi-file-spreadsheet-fill::before{content:"\f3b6"}.bi-file-spreadsheet::before{content:"\f3b7"}.bi-file-text-fill::before{content:"\f3b8"}.bi-file-text::before{content:"\f3b9"}.bi-file-word-fill::before{content:"\f3ba"}.bi-file-word::before{content:"\f3bb"}.bi-file-x-fill::before{content:"\f3bc"}.bi-file-x::before{content:"\f3bd"}.bi-file-zip-fill::before{content:"\f3be"}.bi-file-zip::before{content:"\f3bf"}.bi-file::before{content:"\f3c0"}.bi-files-alt::before{content:"\f3c1"}.bi-files::before{content:"\f3c2"}.bi-film::before{content:"\f3c3"}.bi-filter-circle-fill::before{content:"\f3c4"}.bi-filter-circle::before{content:"\f3c5"}.bi-filter-left::before{content:"\f3c6"}.bi-filter-right::before{content:"\f3c7"}.bi-filter-square-fill::before{content:"\f3c8"}.bi-filter-square::before{content:"\f3c9"}.bi-filter::before{content:"\f3ca"}.bi-flag-fill::before{content:"\f3cb"}.bi-flag::before{content:"\f3cc"}.bi-flower1::before{content:"\f3cd"}.bi-flower2::before{content:"\f3ce"}.bi-flower3::before{content:"\f3cf"}.bi-folder-check::before{content:"\f3d0"}.bi-folder-fill::before{content:"\f3d1"}.bi-folder-minus::before{content:"\f3d2"}.bi-folder-plus::before{content:"\f3d3"}.bi-folder-symlink-fill::before{content:"\f3d4"}.bi-folder-symlink::before{content:"\f3d5"}.bi-folder-x::before{content:"\f3d6"}.bi-folder::before{content:"\f3d7"}.bi-folder2-open::before{content:"\f3d8"}.bi-folder2::before{content:"\f3d9"}.bi-fonts::before{content:"\f3da"}.bi-forward-fill::before{content:"\f3db"}.bi-forward::before{content:"\f3dc"}.bi-front::before{content:"\f3dd"}.bi-fullscreen-exit::before{content:"\f3de"}.bi-fullscreen::before{content:"\f3df"}.bi-funnel-fill::before{content:"\f3e0"}.bi-funnel::before{content:"\f3e1"}.bi-gear-fill::before{content:"\f3e2"}.bi-gear-wide-connected::before{content:"\f3e3"}.bi-gear-wide::before{content:"\f3e4"}.bi-gear::before{content:"\f3e5"}.bi-gem::before{content:"\f3e6"}.bi-geo-alt-fill::before{content:"\f3e7"}.bi-geo-alt::before{content:"\f3e8"}.bi-geo-fill::before{content:"\f3e9"}.bi-geo::before{content:"\f3ea"}.bi-gift-fill::before{content:"\f3eb"}.bi-gift::before{content:"\f3ec"}.bi-github::before{content:"\f3ed"}.bi-globe::before{content:"\f3ee"}.bi-globe2::before{content:"\f3ef"}.bi-google::before{content:"\f3f0"}.bi-graph-down::before{content:"\f3f1"}.bi-graph-up::before{content:"\f3f2"}.bi-grid-1x2-fill::before{content:"\f3f3"}.bi-grid-1x2::before{content:"\f3f4"}.bi-grid-3x2-gap-fill::before{content:"\f3f5"}.bi-grid-3x2-gap::before{content:"\f3f6"}.bi-grid-3x2::before{content:"\f3f7"}.bi-grid-3x3-gap-fill::before{content:"\f3f8"}.bi-grid-3x3-gap::before{content:"\f3f9"}.bi-grid-3x3::before{content:"\f3fa"}.bi-grid-fill::before{content:"\f3fb"}.bi-grid::before{content:"\f3fc"}.bi-grip-horizontal::before{content:"\f3fd"}.bi-grip-vertical::before{content:"\f3fe"}.bi-hammer::before{content:"\f3ff"}.bi-hand-index-fill::before{content:"\f400"}.bi-hand-index-thumb-fill::before{content:"\f401"}.bi-hand-index-thumb::before{content:"\f402"}.bi-hand-index::before{content:"\f403"}.bi-hand-thumbs-down-fill::before{content:"\f404"}.bi-hand-thumbs-down::before{content:"\f405"}.bi-hand-thumbs-up-fill::before{content:"\f406"}.bi-hand-thumbs-up::before{content:"\f407"}.bi-handbag-fill::before{content:"\f408"}.bi-handbag::before{content:"\f409"}.bi-hash::before{content:"\f40a"}.bi-hdd-fill::before{content:"\f40b"}.bi-hdd-network-fill::before{content:"\f40c"}.bi-hdd-network::before{content:"\f40d"}.bi-hdd-rack-fill::before{content:"\f40e"}.bi-hdd-rack::before{content:"\f40f"}.bi-hdd-stack-fill::before{content:"\f410"}.bi-hdd-stack::before{content:"\f411"}.bi-hdd::before{content:"\f412"}.bi-headphones::before{content:"\f413"}.bi-headset::before{content:"\f414"}.bi-heart-fill::before{content:"\f415"}.bi-heart-half::before{content:"\f416"}.bi-heart::before{content:"\f417"}.bi-heptagon-fill::before{content:"\f418"}.bi-heptagon-half::before{content:"\f419"}.bi-heptagon::before{content:"\f41a"}.bi-hexagon-fill::before{content:"\f41b"}.bi-hexagon-half::before{content:"\f41c"}.bi-hexagon::before{content:"\f41d"}.bi-hourglass-bottom::before{content:"\f41e"}.bi-hourglass-split::before{content:"\f41f"}.bi-hourglass-top::before{content:"\f420"}.bi-hourglass::before{content:"\f421"}.bi-house-door-fill::before{content:"\f422"}.bi-house-door::before{content:"\f423"}.bi-house-fill::before{content:"\f424"}.bi-house::before{content:"\f425"}.bi-hr::before{content:"\f426"}.bi-hurricane::before{content:"\f427"}.bi-image-alt::before{content:"\f428"}.bi-image-fill::before{content:"\f429"}.bi-image::before{content:"\f42a"}.bi-images::before{content:"\f42b"}.bi-inbox-fill::before{content:"\f42c"}.bi-inbox::before{content:"\f42d"}.bi-inboxes-fill::before{content:"\f42e"}.bi-inboxes::before{content:"\f42f"}.bi-info-circle-fill::before{content:"\f430"}.bi-info-circle::before{content:"\f431"}.bi-info-square-fill::before{content:"\f432"}.bi-info-square::before{content:"\f433"}.bi-info::before{content:"\f434"}.bi-input-cursor-text::before{content:"\f435"}.bi-input-cursor::before{content:"\f436"}.bi-instagram::before{content:"\f437"}.bi-intersect::before{content:"\f438"}.bi-journal-album::before{content:"\f439"}.bi-journal-arrow-down::before{content:"\f43a"}.bi-journal-arrow-up::before{content:"\f43b"}.bi-journal-bookmark-fill::before{content:"\f43c"}.bi-journal-bookmark::before{content:"\f43d"}.bi-journal-check::before{content:"\f43e"}.bi-journal-code::before{content:"\f43f"}.bi-journal-medical::before{content:"\f440"}.bi-journal-minus::before{content:"\f441"}.bi-journal-plus::before{content:"\f442"}.bi-journal-richtext::before{content:"\f443"}.bi-journal-text::before{content:"\f444"}.bi-journal-x::before{content:"\f445"}.bi-journal::before{content:"\f446"}.bi-journals::before{content:"\f447"}.bi-joystick::before{content:"\f448"}.bi-justify-left::before{content:"\f449"}.bi-justify-right::before{content:"\f44a"}.bi-justify::before{content:"\f44b"}.bi-kanban-fill::before{content:"\f44c"}.bi-kanban::before{content:"\f44d"}.bi-key-fill::before{content:"\f44e"}.bi-key::before{content:"\f44f"}.bi-keyboard-fill::before{content:"\f450"}.bi-keyboard::before{content:"\f451"}.bi-ladder::before{content:"\f452"}.bi-lamp-fill::before{content:"\f453"}.bi-lamp::before{content:"\f454"}.bi-laptop-fill::before{content:"\f455"}.bi-laptop::before{content:"\f456"}.bi-layer-backward::before{content:"\f457"}.bi-layer-forward::before{content:"\f458"}.bi-layers-fill::before{content:"\f459"}.bi-layers-half::before{content:"\f45a"}.bi-layers::before{content:"\f45b"}.bi-layout-sidebar-inset-reverse::before{content:"\f45c"}.bi-layout-sidebar-inset::before{content:"\f45d"}.bi-layout-sidebar-reverse::before{content:"\f45e"}.bi-layout-sidebar::before{content:"\f45f"}.bi-layout-split::before{content:"\f460"}.bi-layout-text-sidebar-reverse::before{content:"\f461"}.bi-layout-text-sidebar::before{content:"\f462"}.bi-layout-text-window-reverse::before{content:"\f463"}.bi-layout-text-window::before{content:"\f464"}.bi-layout-three-columns::before{content:"\f465"}.bi-layout-wtf::before{content:"\f466"}.bi-life-preserver::before{content:"\f467"}.bi-lightbulb-fill::before{content:"\f468"}.bi-lightbulb-off-fill::before{content:"\f469"}.bi-lightbulb-off::before{content:"\f46a"}.bi-lightbulb::before{content:"\f46b"}.bi-lightning-charge-fill::before{content:"\f46c"}.bi-lightning-charge::before{content:"\f46d"}.bi-lightning-fill::before{content:"\f46e"}.bi-lightning::before{content:"\f46f"}.bi-link-45deg::before{content:"\f470"}.bi-link::before{content:"\f471"}.bi-linkedin::before{content:"\f472"}.bi-list-check::before{content:"\f473"}.bi-list-nested::before{content:"\f474"}.bi-list-ol::before{content:"\f475"}.bi-list-stars::before{content:"\f476"}.bi-list-task::before{content:"\f477"}.bi-list-ul::before{content:"\f478"}.bi-list::before{content:"\f479"}.bi-lock-fill::before{content:"\f47a"}.bi-lock::before{content:"\f47b"}.bi-mailbox::before{content:"\f47c"}.bi-mailbox2::before{content:"\f47d"}.bi-map-fill::before{content:"\f47e"}.bi-map::before{content:"\f47f"}.bi-markdown-fill::before{content:"\f480"}.bi-markdown::before{content:"\f481"}.bi-mask::before{content:"\f482"}.bi-megaphone-fill::before{content:"\f483"}.bi-megaphone::before{content:"\f484"}.bi-menu-app-fill::before{content:"\f485"}.bi-menu-app::before{content:"\f486"}.bi-menu-button-fill::before{content:"\f487"}.bi-menu-button-wide-fill::before{content:"\f488"}.bi-menu-button-wide::before{content:"\f489"}.bi-menu-button::before{content:"\f48a"}.bi-menu-down::before{content:"\f48b"}.bi-menu-up::before{content:"\f48c"}.bi-mic-fill::before{content:"\f48d"}.bi-mic-mute-fill::before{content:"\f48e"}.bi-mic-mute::before{content:"\f48f"}.bi-mic::before{content:"\f490"}.bi-minecart-loaded::before{content:"\f491"}.bi-minecart::before{content:"\f492"}.bi-moisture::before{content:"\f493"}.bi-moon-fill::before{content:"\f494"}.bi-moon-stars-fill::before{content:"\f495"}.bi-moon-stars::before{content:"\f496"}.bi-moon::before{content:"\f497"}.bi-mouse-fill::before{content:"\f498"}.bi-mouse::before{content:"\f499"}.bi-mouse2-fill::before{content:"\f49a"}.bi-mouse2::before{content:"\f49b"}.bi-mouse3-fill::before{content:"\f49c"}.bi-mouse3::before{content:"\f49d"}.bi-music-note-beamed::before{content:"\f49e"}.bi-music-note-list::before{content:"\f49f"}.bi-music-note::before{content:"\f4a0"}.bi-music-player-fill::before{content:"\f4a1"}.bi-music-player::before{content:"\f4a2"}.bi-newspaper::before{content:"\f4a3"}.bi-node-minus-fill::before{content:"\f4a4"}.bi-node-minus::before{content:"\f4a5"}.bi-node-plus-fill::before{content:"\f4a6"}.bi-node-plus::before{content:"\f4a7"}.bi-nut-fill::before{content:"\f4a8"}.bi-nut::before{content:"\f4a9"}.bi-octagon-fill::before{content:"\f4aa"}.bi-octagon-half::before{content:"\f4ab"}.bi-octagon::before{content:"\f4ac"}.bi-option::before{content:"\f4ad"}.bi-outlet::before{content:"\f4ae"}.bi-paint-bucket::before{content:"\f4af"}.bi-palette-fill::before{content:"\f4b0"}.bi-palette::before{content:"\f4b1"}.bi-palette2::before{content:"\f4b2"}.bi-paperclip::before{content:"\f4b3"}.bi-paragraph::before{content:"\f4b4"}.bi-patch-check-fill::before{content:"\f4b5"}.bi-patch-check::before{content:"\f4b6"}.bi-patch-exclamation-fill::before{content:"\f4b7"}.bi-patch-exclamation::before{content:"\f4b8"}.bi-patch-minus-fill::before{content:"\f4b9"}.bi-patch-minus::before{content:"\f4ba"}.bi-patch-plus-fill::before{content:"\f4bb"}.bi-patch-plus::before{content:"\f4bc"}.bi-patch-question-fill::before{content:"\f4bd"}.bi-patch-question::before{content:"\f4be"}.bi-pause-btn-fill::before{content:"\f4bf"}.bi-pause-btn::before{content:"\f4c0"}.bi-pause-circle-fill::before{content:"\f4c1"}.bi-pause-circle::before{content:"\f4c2"}.bi-pause-fill::before{content:"\f4c3"}.bi-pause::before{content:"\f4c4"}.bi-peace-fill::before{content:"\f4c5"}.bi-peace::before{content:"\f4c6"}.bi-pen-fill::before{content:"\f4c7"}.bi-pen::before{content:"\f4c8"}.bi-pencil-fill::before{content:"\f4c9"}.bi-pencil-square::before{content:"\f4ca"}.bi-pencil::before{content:"\f4cb"}.bi-pentagon-fill::before{content:"\f4cc"}.bi-pentagon-half::before{content:"\f4cd"}.bi-pentagon::before{content:"\f4ce"}.bi-people-fill::before{content:"\f4cf"}.bi-people::before{content:"\f4d0"}.bi-percent::before{content:"\f4d1"}.bi-person-badge-fill::before{content:"\f4d2"}.bi-person-badge::before{content:"\f4d3"}.bi-person-bounding-box::before{content:"\f4d4"}.bi-person-check-fill::before{content:"\f4d5"}.bi-person-check::before{content:"\f4d6"}.bi-person-circle::before{content:"\f4d7"}.bi-person-dash-fill::before{content:"\f4d8"}.bi-person-dash::before{content:"\f4d9"}.bi-person-fill::before{content:"\f4da"}.bi-person-lines-fill::before{content:"\f4db"}.bi-person-plus-fill::before{content:"\f4dc"}.bi-person-plus::before{content:"\f4dd"}.bi-person-square::before{content:"\f4de"}.bi-person-x-fill::before{content:"\f4df"}.bi-person-x::before{content:"\f4e0"}.bi-person::before{content:"\f4e1"}.bi-phone-fill::before{content:"\f4e2"}.bi-phone-landscape-fill::before{content:"\f4e3"}.bi-phone-landscape::before{content:"\f4e4"}.bi-phone-vibrate-fill::before{content:"\f4e5"}.bi-phone-vibrate::before{content:"\f4e6"}.bi-phone::before{content:"\f4e7"}.bi-pie-chart-fill::before{content:"\f4e8"}.bi-pie-chart::before{content:"\f4e9"}.bi-pin-angle-fill::before{content:"\f4ea"}.bi-pin-angle::before{content:"\f4eb"}.bi-pin-fill::before{content:"\f4ec"}.bi-pin::before{content:"\f4ed"}.bi-pip-fill::before{content:"\f4ee"}.bi-pip::before{content:"\f4ef"}.bi-play-btn-fill::before{content:"\f4f0"}.bi-play-btn::before{content:"\f4f1"}.bi-play-circle-fill::before{content:"\f4f2"}.bi-play-circle::before{content:"\f4f3"}.bi-play-fill::before{content:"\f4f4"}.bi-play::before{content:"\f4f5"}.bi-plug-fill::before{content:"\f4f6"}.bi-plug::before{content:"\f4f7"}.bi-plus-circle-dotted::before{content:"\f4f8"}.bi-plus-circle-fill::before{content:"\f4f9"}.bi-plus-circle::before{content:"\f4fa"}.bi-plus-square-dotted::before{content:"\f4fb"}.bi-plus-square-fill::before{content:"\f4fc"}.bi-plus-square::before{content:"\f4fd"}.bi-plus::before{content:"\f4fe"}.bi-power::before{content:"\f4ff"}.bi-printer-fill::before{content:"\f500"}.bi-printer::before{content:"\f501"}.bi-puzzle-fill::before{content:"\f502"}.bi-puzzle::before{content:"\f503"}.bi-question-circle-fill::before{content:"\f504"}.bi-question-circle::before{content:"\f505"}.bi-question-diamond-fill::before{content:"\f506"}.bi-question-diamond::before{content:"\f507"}.bi-question-octagon-fill::before{content:"\f508"}.bi-question-octagon::before{content:"\f509"}.bi-question-square-fill::before{content:"\f50a"}.bi-question-square::before{content:"\f50b"}.bi-question::before{content:"\f50c"}.bi-rainbow::before{content:"\f50d"}.bi-receipt-cutoff::before{content:"\f50e"}.bi-receipt::before{content:"\f50f"}.bi-reception-0::before{content:"\f510"}.bi-reception-1::before{content:"\f511"}.bi-reception-2::before{content:"\f512"}.bi-reception-3::before{content:"\f513"}.bi-reception-4::before{content:"\f514"}.bi-record-btn-fill::before{content:"\f515"}.bi-record-btn::before{content:"\f516"}.bi-record-circle-fill::before{content:"\f517"}.bi-record-circle::before{content:"\f518"}.bi-record-fill::before{content:"\f519"}.bi-record::before{content:"\f51a"}.bi-record2-fill::before{content:"\f51b"}.bi-record2::before{content:"\f51c"}.bi-reply-all-fill::before{content:"\f51d"}.bi-reply-all::before{content:"\f51e"}.bi-reply-fill::before{content:"\f51f"}.bi-reply::before{content:"\f520"}.bi-rss-fill::before{content:"\f521"}.bi-rss::before{content:"\f522"}.bi-rulers::before{content:"\f523"}.bi-save-fill::before{content:"\f524"}.bi-save::before{content:"\f525"}.bi-save2-fill::before{content:"\f526"}.bi-save2::before{content:"\f527"}.bi-scissors::before{content:"\f528"}.bi-screwdriver::before{content:"\f529"}.bi-search::before{content:"\f52a"}.bi-segmented-nav::before{content:"\f52b"}.bi-server::before{content:"\f52c"}.bi-share-fill::before{content:"\f52d"}.bi-share::before{content:"\f52e"}.bi-shield-check::before{content:"\f52f"}.bi-shield-exclamation::before{content:"\f530"}.bi-shield-fill-check::before{content:"\f531"}.bi-shield-fill-exclamation::before{content:"\f532"}.bi-shield-fill-minus::before{content:"\f533"}.bi-shield-fill-plus::before{content:"\f534"}.bi-shield-fill-x::before{content:"\f535"}.bi-shield-fill::before{content:"\f536"}.bi-shield-lock-fill::before{content:"\f537"}.bi-shield-lock::before{content:"\f538"}.bi-shield-minus::before{content:"\f539"}.bi-shield-plus::before{content:"\f53a"}.bi-shield-shaded::before{content:"\f53b"}.bi-shield-slash-fill::before{content:"\f53c"}.bi-shield-slash::before{content:"\f53d"}.bi-shield-x::before{content:"\f53e"}.bi-shield::before{content:"\f53f"}.bi-shift-fill::before{content:"\f540"}.bi-shift::before{content:"\f541"}.bi-shop-window::before{content:"\f542"}.bi-shop::before{content:"\f543"}.bi-shuffle::before{content:"\f544"}.bi-signpost-2-fill::before{content:"\f545"}.bi-signpost-2::before{content:"\f546"}.bi-signpost-fill::before{content:"\f547"}.bi-signpost-split-fill::before{content:"\f548"}.bi-signpost-split::before{content:"\f549"}.bi-signpost::before{content:"\f54a"}.bi-sim-fill::before{content:"\f54b"}.bi-sim::before{content:"\f54c"}.bi-skip-backward-btn-fill::before{content:"\f54d"}.bi-skip-backward-btn::before{content:"\f54e"}.bi-skip-backward-circle-fill::before{content:"\f54f"}.bi-skip-backward-circle::before{content:"\f550"}.bi-skip-backward-fill::before{content:"\f551"}.bi-skip-backward::before{content:"\f552"}.bi-skip-end-btn-fill::before{content:"\f553"}.bi-skip-end-btn::before{content:"\f554"}.bi-skip-end-circle-fill::before{content:"\f555"}.bi-skip-end-circle::before{content:"\f556"}.bi-skip-end-fill::before{content:"\f557"}.bi-skip-end::before{content:"\f558"}.bi-skip-forward-btn-fill::before{content:"\f559"}.bi-skip-forward-btn::before{content:"\f55a"}.bi-skip-forward-circle-fill::before{content:"\f55b"}.bi-skip-forward-circle::before{content:"\f55c"}.bi-skip-forward-fill::before{content:"\f55d"}.bi-skip-forward::before{content:"\f55e"}.bi-skip-start-btn-fill::before{content:"\f55f"}.bi-skip-start-btn::before{content:"\f560"}.bi-skip-start-circle-fill::before{content:"\f561"}.bi-skip-start-circle::before{content:"\f562"}.bi-skip-start-fill::before{content:"\f563"}.bi-skip-start::before{content:"\f564"}.bi-slack::before{content:"\f565"}.bi-slash-circle-fill::before{content:"\f566"}.bi-slash-circle::before{content:"\f567"}.bi-slash-square-fill::before{content:"\f568"}.bi-slash-square::before{content:"\f569"}.bi-slash::before{content:"\f56a"}.bi-sliders::before{content:"\f56b"}.bi-smartwatch::before{content:"\f56c"}.bi-snow::before{content:"\f56d"}.bi-snow2::before{content:"\f56e"}.bi-snow3::before{content:"\f56f"}.bi-sort-alpha-down-alt::before{content:"\f570"}.bi-sort-alpha-down::before{content:"\f571"}.bi-sort-alpha-up-alt::before{content:"\f572"}.bi-sort-alpha-up::before{content:"\f573"}.bi-sort-down-alt::before{content:"\f574"}.bi-sort-down::before{content:"\f575"}.bi-sort-numeric-down-alt::before{content:"\f576"}.bi-sort-numeric-down::before{content:"\f577"}.bi-sort-numeric-up-alt::before{content:"\f578"}.bi-sort-numeric-up::before{content:"\f579"}.bi-sort-up-alt::before{content:"\f57a"}.bi-sort-up::before{content:"\f57b"}.bi-soundwave::before{content:"\f57c"}.bi-speaker-fill::before{content:"\f57d"}.bi-speaker::before{content:"\f57e"}.bi-speedometer::before{content:"\f57f"}.bi-speedometer2::before{content:"\f580"}.bi-spellcheck::before{content:"\f581"}.bi-square-fill::before{content:"\f582"}.bi-square-half::before{content:"\f583"}.bi-square::before{content:"\f584"}.bi-stack::before{content:"\f585"}.bi-star-fill::before{content:"\f586"}.bi-star-half::before{content:"\f587"}.bi-star::before{content:"\f588"}.bi-stars::before{content:"\f589"}.bi-stickies-fill::before{content:"\f58a"}.bi-stickies::before{content:"\f58b"}.bi-sticky-fill::before{content:"\f58c"}.bi-sticky::before{content:"\f58d"}.bi-stop-btn-fill::before{content:"\f58e"}.bi-stop-btn::before{content:"\f58f"}.bi-stop-circle-fill::before{content:"\f590"}.bi-stop-circle::before{content:"\f591"}.bi-stop-fill::before{content:"\f592"}.bi-stop::before{content:"\f593"}.bi-stoplights-fill::before{content:"\f594"}.bi-stoplights::before{content:"\f595"}.bi-stopwatch-fill::before{content:"\f596"}.bi-stopwatch::before{content:"\f597"}.bi-subtract::before{content:"\f598"}.bi-suit-club-fill::before{content:"\f599"}.bi-suit-club::before{content:"\f59a"}.bi-suit-diamond-fill::before{content:"\f59b"}.bi-suit-diamond::before{content:"\f59c"}.bi-suit-heart-fill::before{content:"\f59d"}.bi-suit-heart::before{content:"\f59e"}.bi-suit-spade-fill::before{content:"\f59f"}.bi-suit-spade::before{content:"\f5a0"}.bi-sun-fill::before{content:"\f5a1"}.bi-sun::before{content:"\f5a2"}.bi-sunglasses::before{content:"\f5a3"}.bi-sunrise-fill::before{content:"\f5a4"}.bi-sunrise::before{content:"\f5a5"}.bi-sunset-fill::before{content:"\f5a6"}.bi-sunset::before{content:"\f5a7"}.bi-symmetry-horizontal::before{content:"\f5a8"}.bi-symmetry-vertical::before{content:"\f5a9"}.bi-table::before{content:"\f5aa"}.bi-tablet-fill::before{content:"\f5ab"}.bi-tablet-landscape-fill::before{content:"\f5ac"}.bi-tablet-landscape::before{content:"\f5ad"}.bi-tablet::before{content:"\f5ae"}.bi-tag-fill::before{content:"\f5af"}.bi-tag::before{content:"\f5b0"}.bi-tags-fill::before{content:"\f5b1"}.bi-tags::before{content:"\f5b2"}.bi-telegram::before{content:"\f5b3"}.bi-telephone-fill::before{content:"\f5b4"}.bi-telephone-forward-fill::before{content:"\f5b5"}.bi-telephone-forward::before{content:"\f5b6"}.bi-telephone-inbound-fill::before{content:"\f5b7"}.bi-telephone-inbound::before{content:"\f5b8"}.bi-telephone-minus-fill::before{content:"\f5b9"}.bi-telephone-minus::before{content:"\f5ba"}.bi-telephone-outbound-fill::before{content:"\f5bb"}.bi-telephone-outbound::before{content:"\f5bc"}.bi-telephone-plus-fill::before{content:"\f5bd"}.bi-telephone-plus::before{content:"\f5be"}.bi-telephone-x-fill::before{content:"\f5bf"}.bi-telephone-x::before{content:"\f5c0"}.bi-telephone::before{content:"\f5c1"}.bi-terminal-fill::before{content:"\f5c2"}.bi-terminal::before{content:"\f5c3"}.bi-text-center::before{content:"\f5c4"}.bi-text-indent-left::before{content:"\f5c5"}.bi-text-indent-right::before{content:"\f5c6"}.bi-text-left::before{content:"\f5c7"}.bi-text-paragraph::before{content:"\f5c8"}.bi-text-right::before{content:"\f5c9"}.bi-textarea-resize::before{content:"\f5ca"}.bi-textarea-t::before{content:"\f5cb"}.bi-textarea::before{content:"\f5cc"}.bi-thermometer-half::before{content:"\f5cd"}.bi-thermometer-high::before{content:"\f5ce"}.bi-thermometer-low::before{content:"\f5cf"}.bi-thermometer-snow::before{content:"\f5d0"}.bi-thermometer-sun::before{content:"\f5d1"}.bi-thermometer::before{content:"\f5d2"}.bi-three-dots-vertical::before{content:"\f5d3"}.bi-three-dots::before{content:"\f5d4"}.bi-toggle-off::before{content:"\f5d5"}.bi-toggle-on::before{content:"\f5d6"}.bi-toggle2-off::before{content:"\f5d7"}.bi-toggle2-on::before{content:"\f5d8"}.bi-toggles::before{content:"\f5d9"}.bi-toggles2::before{content:"\f5da"}.bi-tools::before{content:"\f5db"}.bi-tornado::before{content:"\f5dc"}.bi-trash-fill::before{content:"\f5dd"}.bi-trash::before{content:"\f5de"}.bi-trash2-fill::before{content:"\f5df"}.bi-trash2::before{content:"\f5e0"}.bi-tree-fill::before{content:"\f5e1"}.bi-tree::before{content:"\f5e2"}.bi-triangle-fill::before{content:"\f5e3"}.bi-triangle-half::before{content:"\f5e4"}.bi-triangle::before{content:"\f5e5"}.bi-trophy-fill::before{content:"\f5e6"}.bi-trophy::before{content:"\f5e7"}.bi-tropical-storm::before{content:"\f5e8"}.bi-truck-flatbed::before{content:"\f5e9"}.bi-truck::before{content:"\f5ea"}.bi-tsunami::before{content:"\f5eb"}.bi-tv-fill::before{content:"\f5ec"}.bi-tv::before{content:"\f5ed"}.bi-twitch::before{content:"\f5ee"}.bi-twitter::before{content:"\f5ef"}.bi-type-bold::before{content:"\f5f0"}.bi-type-h1::before{content:"\f5f1"}.bi-type-h2::before{content:"\f5f2"}.bi-type-h3::before{content:"\f5f3"}.bi-type-italic::before{content:"\f5f4"}.bi-type-strikethrough::before{content:"\f5f5"}.bi-type-underline::before{content:"\f5f6"}.bi-type::before{content:"\f5f7"}.bi-ui-checks-grid::before{content:"\f5f8"}.bi-ui-checks::before{content:"\f5f9"}.bi-ui-radios-grid::before{content:"\f5fa"}.bi-ui-radios::before{content:"\f5fb"}.bi-umbrella-fill::before{content:"\f5fc"}.bi-umbrella::before{content:"\f5fd"}.bi-union::before{content:"\f5fe"}.bi-unlock-fill::before{content:"\f5ff"}.bi-unlock::before{content:"\f600"}.bi-upc-scan::before{content:"\f601"}.bi-upc::before{content:"\f602"}.bi-upload::before{content:"\f603"}.bi-vector-pen::before{content:"\f604"}.bi-view-list::before{content:"\f605"}.bi-view-stacked::before{content:"\f606"}.bi-vinyl-fill::before{content:"\f607"}.bi-vinyl::before{content:"\f608"}.bi-voicemail::before{content:"\f609"}.bi-volume-down-fill::before{content:"\f60a"}.bi-volume-down::before{content:"\f60b"}.bi-volume-mute-fill::before{content:"\f60c"}.bi-volume-mute::before{content:"\f60d"}.bi-volume-off-fill::before{content:"\f60e"}.bi-volume-off::before{content:"\f60f"}.bi-volume-up-fill::before{content:"\f610"}.bi-volume-up::before{content:"\f611"}.bi-vr::before{content:"\f612"}.bi-wallet-fill::before{content:"\f613"}.bi-wallet::before{content:"\f614"}.bi-wallet2::before{content:"\f615"}.bi-watch::before{content:"\f616"}.bi-water::before{content:"\f617"}.bi-whatsapp::before{content:"\f618"}.bi-wifi-1::before{content:"\f619"}.bi-wifi-2::before{content:"\f61a"}.bi-wifi-off::before{content:"\f61b"}.bi-wifi::before{content:"\f61c"}.bi-wind::before{content:"\f61d"}.bi-window-dock::before{content:"\f61e"}.bi-window-sidebar::before{content:"\f61f"}.bi-window::before{content:"\f620"}.bi-wrench::before{content:"\f621"}.bi-x-circle-fill::before{content:"\f622"}.bi-x-circle::before{content:"\f623"}.bi-x-diamond-fill::before{content:"\f624"}.bi-x-diamond::before{content:"\f625"}.bi-x-octagon-fill::before{content:"\f626"}.bi-x-octagon::before{content:"\f627"}.bi-x-square-fill::before{content:"\f628"}.bi-x-square::before{content:"\f629"}.bi-x::before{content:"\f62a"}.bi-youtube::before{content:"\f62b"}.bi-zoom-in::before{content:"\f62c"}.bi-zoom-out::before{content:"\f62d"}.bi-bank::before{content:"\f62e"}.bi-bank2::before{content:"\f62f"}.bi-bell-slash-fill::before{content:"\f630"}.bi-bell-slash::before{content:"\f631"}.bi-cash-coin::before{content:"\f632"}.bi-check-lg::before{content:"\f633"}.bi-coin::before{content:"\f634"}.bi-currency-bitcoin::before{content:"\f635"}.bi-currency-dollar::before{content:"\f636"}.bi-currency-euro::before{content:"\f637"}.bi-currency-exchange::before{content:"\f638"}.bi-currency-pound::before{content:"\f639"}.bi-currency-yen::before{content:"\f63a"}.bi-dash-lg::before{content:"\f63b"}.bi-exclamation-lg::before{content:"\f63c"}.bi-file-earmark-pdf-fill::before{content:"\f63d"}.bi-file-earmark-pdf::before{content:"\f63e"}.bi-file-pdf-fill::before{content:"\f63f"}.bi-file-pdf::before{content:"\f640"}.bi-gender-ambiguous::before{content:"\f641"}.bi-gender-female::before{content:"\f642"}.bi-gender-male::before{content:"\f643"}.bi-gender-trans::before{content:"\f644"}.bi-headset-vr::before{content:"\f645"}.bi-info-lg::before{content:"\f646"}.bi-mastodon::before{content:"\f647"}.bi-messenger::before{content:"\f648"}.bi-piggy-bank-fill::before{content:"\f649"}.bi-piggy-bank::before{content:"\f64a"}.bi-pin-map-fill::before{content:"\f64b"}.bi-pin-map::before{content:"\f64c"}.bi-plus-lg::before{content:"\f64d"}.bi-question-lg::before{content:"\f64e"}.bi-recycle::before{content:"\f64f"}.bi-reddit::before{content:"\f650"}.bi-safe-fill::before{content:"\f651"}.bi-safe2-fill::before{content:"\f652"}.bi-safe2::before{content:"\f653"}.bi-sd-card-fill::before{content:"\f654"}.bi-sd-card::before{content:"\f655"}.bi-skype::before{content:"\f656"}.bi-slash-lg::before{content:"\f657"}.bi-translate::before{content:"\f658"}.bi-x-lg::before{content:"\f659"}.bi-safe::before{content:"\f65a"}.bi-apple::before{content:"\f65b"}.bi-microsoft::before{content:"\f65d"}.bi-windows::before{content:"\f65e"}.bi-behance::before{content:"\f65c"}.bi-dribbble::before{content:"\f65f"}.bi-line::before{content:"\f660"}.bi-medium::before{content:"\f661"}.bi-paypal::before{content:"\f662"}.bi-pinterest::before{content:"\f663"}.bi-signal::before{content:"\f664"}.bi-snapchat::before{content:"\f665"}.bi-spotify::before{content:"\f666"}.bi-stack-overflow::before{content:"\f667"}.bi-strava::before{content:"\f668"}.bi-wordpress::before{content:"\f669"}.bi-vimeo::before{content:"\f66a"}.bi-activity::before{content:"\f66b"}.bi-easel2-fill::before{content:"\f66c"}.bi-easel2::before{content:"\f66d"}.bi-easel3-fill::before{content:"\f66e"}.bi-easel3::before{content:"\f66f"}.bi-fan::before{content:"\f670"}.bi-fingerprint::before{content:"\f671"}.bi-graph-down-arrow::before{content:"\f672"}.bi-graph-up-arrow::before{content:"\f673"}.bi-hypnotize::before{content:"\f674"}.bi-magic::before{content:"\f675"}.bi-person-rolodex::before{content:"\f676"}.bi-person-video::before{content:"\f677"}.bi-person-video2::before{content:"\f678"}.bi-person-video3::before{content:"\f679"}.bi-person-workspace::before{content:"\f67a"}.bi-radioactive::before{content:"\f67b"}.bi-webcam-fill::before{content:"\f67c"}.bi-webcam::before{content:"\f67d"}.bi-yin-yang::before{content:"\f67e"}.bi-bandaid-fill::before{content:"\f680"}.bi-bandaid::before{content:"\f681"}.bi-bluetooth::before{content:"\f682"}.bi-body-text::before{content:"\f683"}.bi-boombox::before{content:"\f684"}.bi-boxes::before{content:"\f685"}.bi-dpad-fill::before{content:"\f686"}.bi-dpad::before{content:"\f687"}.bi-ear-fill::before{content:"\f688"}.bi-ear::before{content:"\f689"}.bi-envelope-check-fill::before{content:"\f68b"}.bi-envelope-check::before{content:"\f68c"}.bi-envelope-dash-fill::before{content:"\f68e"}.bi-envelope-dash::before{content:"\f68f"}.bi-envelope-exclamation-fill::before{content:"\f691"}.bi-envelope-exclamation::before{content:"\f692"}.bi-envelope-plus-fill::before{content:"\f693"}.bi-envelope-plus::before{content:"\f694"}.bi-envelope-slash-fill::before{content:"\f696"}.bi-envelope-slash::before{content:"\f697"}.bi-envelope-x-fill::before{content:"\f699"}.bi-envelope-x::before{content:"\f69a"}.bi-explicit-fill::before{content:"\f69b"}.bi-explicit::before{content:"\f69c"}.bi-git::before{content:"\f69d"}.bi-infinity::before{content:"\f69e"}.bi-list-columns-reverse::before{content:"\f69f"}.bi-list-columns::before{content:"\f6a0"}.bi-meta::before{content:"\f6a1"}.bi-nintendo-switch::before{content:"\f6a4"}.bi-pc-display-horizontal::before{content:"\f6a5"}.bi-pc-display::before{content:"\f6a6"}.bi-pc-horizontal::before{content:"\f6a7"}.bi-pc::before{content:"\f6a8"}.bi-playstation::before{content:"\f6a9"}.bi-plus-slash-minus::before{content:"\f6aa"}.bi-projector-fill::before{content:"\f6ab"}.bi-projector::before{content:"\f6ac"}.bi-qr-code-scan::before{content:"\f6ad"}.bi-qr-code::before{content:"\f6ae"}.bi-quora::before{content:"\f6af"}.bi-quote::before{content:"\f6b0"}.bi-robot::before{content:"\f6b1"}.bi-send-check-fill::before{content:"\f6b2"}.bi-send-check::before{content:"\f6b3"}.bi-send-dash-fill::before{content:"\f6b4"}.bi-send-dash::before{content:"\f6b5"}.bi-send-exclamation-fill::before{content:"\f6b7"}.bi-send-exclamation::before{content:"\f6b8"}.bi-send-fill::before{content:"\f6b9"}.bi-send-plus-fill::before{content:"\f6ba"}.bi-send-plus::before{content:"\f6bb"}.bi-send-slash-fill::before{content:"\f6bc"}.bi-send-slash::before{content:"\f6bd"}.bi-send-x-fill::before{content:"\f6be"}.bi-send-x::before{content:"\f6bf"}.bi-send::before{content:"\f6c0"}.bi-steam::before{content:"\f6c1"}.bi-terminal-dash::before{content:"\f6c3"}.bi-terminal-plus::before{content:"\f6c4"}.bi-terminal-split::before{content:"\f6c5"}.bi-ticket-detailed-fill::before{content:"\f6c6"}.bi-ticket-detailed::before{content:"\f6c7"}.bi-ticket-fill::before{content:"\f6c8"}.bi-ticket-perforated-fill::before{content:"\f6c9"}.bi-ticket-perforated::before{content:"\f6ca"}.bi-ticket::before{content:"\f6cb"}.bi-tiktok::before{content:"\f6cc"}.bi-window-dash::before{content:"\f6cd"}.bi-window-desktop::before{content:"\f6ce"}.bi-window-fullscreen::before{content:"\f6cf"}.bi-window-plus::before{content:"\f6d0"}.bi-window-split::before{content:"\f6d1"}.bi-window-stack::before{content:"\f6d2"}.bi-window-x::before{content:"\f6d3"}.bi-xbox::before{content:"\f6d4"}.bi-ethernet::before{content:"\f6d5"}.bi-hdmi-fill::before{content:"\f6d6"}.bi-hdmi::before{content:"\f6d7"}.bi-usb-c-fill::before{content:"\f6d8"}.bi-usb-c::before{content:"\f6d9"}.bi-usb-fill::before{content:"\f6da"}.bi-usb-plug-fill::before{content:"\f6db"}.bi-usb-plug::before{content:"\f6dc"}.bi-usb-symbol::before{content:"\f6dd"}.bi-usb::before{content:"\f6de"}.bi-boombox-fill::before{content:"\f6df"}.bi-displayport::before{content:"\f6e1"}.bi-gpu-card::before{content:"\f6e2"}.bi-memory::before{content:"\f6e3"}.bi-modem-fill::before{content:"\f6e4"}.bi-modem::before{content:"\f6e5"}.bi-motherboard-fill::before{content:"\f6e6"}.bi-motherboard::before{content:"\f6e7"}.bi-optical-audio-fill::before{content:"\f6e8"}.bi-optical-audio::before{content:"\f6e9"}.bi-pci-card::before{content:"\f6ea"}.bi-router-fill::before{content:"\f6eb"}.bi-router::before{content:"\f6ec"}.bi-thunderbolt-fill::before{content:"\f6ef"}.bi-thunderbolt::before{content:"\f6f0"}.bi-usb-drive-fill::before{content:"\f6f1"}.bi-usb-drive::before{content:"\f6f2"}.bi-usb-micro-fill::before{content:"\f6f3"}.bi-usb-micro::before{content:"\f6f4"}.bi-usb-mini-fill::before{content:"\f6f5"}.bi-usb-mini::before{content:"\f6f6"}.bi-cloud-haze2::before{content:"\f6f7"}.bi-device-hdd-fill::before{content:"\f6f8"}.bi-device-hdd::before{content:"\f6f9"}.bi-device-ssd-fill::before{content:"\f6fa"}.bi-device-ssd::before{content:"\f6fb"}.bi-displayport-fill::before{content:"\f6fc"}.bi-mortarboard-fill::before{content:"\f6fd"}.bi-mortarboard::before{content:"\f6fe"}.bi-terminal-x::before{content:"\f6ff"}.bi-arrow-through-heart-fill::before{content:"\f700"}.bi-arrow-through-heart::before{content:"\f701"}.bi-badge-sd-fill::before{content:"\f702"}.bi-badge-sd::before{content:"\f703"}.bi-bag-heart-fill::before{content:"\f704"}.bi-bag-heart::before{content:"\f705"}.bi-balloon-fill::before{content:"\f706"}.bi-balloon-heart-fill::before{content:"\f707"}.bi-balloon-heart::before{content:"\f708"}.bi-balloon::before{content:"\f709"}.bi-box2-fill::before{content:"\f70a"}.bi-box2-heart-fill::before{content:"\f70b"}.bi-box2-heart::before{content:"\f70c"}.bi-box2::before{content:"\f70d"}.bi-braces-asterisk::before{content:"\f70e"}.bi-calendar-heart-fill::before{content:"\f70f"}.bi-calendar-heart::before{content:"\f710"}.bi-calendar2-heart-fill::before{content:"\f711"}.bi-calendar2-heart::before{content:"\f712"}.bi-chat-heart-fill::before{content:"\f713"}.bi-chat-heart::before{content:"\f714"}.bi-chat-left-heart-fill::before{content:"\f715"}.bi-chat-left-heart::before{content:"\f716"}.bi-chat-right-heart-fill::before{content:"\f717"}.bi-chat-right-heart::before{content:"\f718"}.bi-chat-square-heart-fill::before{content:"\f719"}.bi-chat-square-heart::before{content:"\f71a"}.bi-clipboard-check-fill::before{content:"\f71b"}.bi-clipboard-data-fill::before{content:"\f71c"}.bi-clipboard-fill::before{content:"\f71d"}.bi-clipboard-heart-fill::before{content:"\f71e"}.bi-clipboard-heart::before{content:"\f71f"}.bi-clipboard-minus-fill::before{content:"\f720"}.bi-clipboard-plus-fill::before{content:"\f721"}.bi-clipboard-pulse::before{content:"\f722"}.bi-clipboard-x-fill::before{content:"\f723"}.bi-clipboard2-check-fill::before{content:"\f724"}.bi-clipboard2-check::before{content:"\f725"}.bi-clipboard2-data-fill::before{content:"\f726"}.bi-clipboard2-data::before{content:"\f727"}.bi-clipboard2-fill::before{content:"\f728"}.bi-clipboard2-heart-fill::before{content:"\f729"}.bi-clipboard2-heart::before{content:"\f72a"}.bi-clipboard2-minus-fill::before{content:"\f72b"}.bi-clipboard2-minus::before{content:"\f72c"}.bi-clipboard2-plus-fill::before{content:"\f72d"}.bi-clipboard2-plus::before{content:"\f72e"}.bi-clipboard2-pulse-fill::before{content:"\f72f"}.bi-clipboard2-pulse::before{content:"\f730"}.bi-clipboard2-x-fill::before{content:"\f731"}.bi-clipboard2-x::before{content:"\f732"}.bi-clipboard2::before{content:"\f733"}.bi-emoji-kiss-fill::before{content:"\f734"}.bi-emoji-kiss::before{content:"\f735"}.bi-envelope-heart-fill::before{content:"\f736"}.bi-envelope-heart::before{content:"\f737"}.bi-envelope-open-heart-fill::before{content:"\f738"}.bi-envelope-open-heart::before{content:"\f739"}.bi-envelope-paper-fill::before{content:"\f73a"}.bi-envelope-paper-heart-fill::before{content:"\f73b"}.bi-envelope-paper-heart::before{content:"\f73c"}.bi-envelope-paper::before{content:"\f73d"}.bi-filetype-aac::before{content:"\f73e"}.bi-filetype-ai::before{content:"\f73f"}.bi-filetype-bmp::before{content:"\f740"}.bi-filetype-cs::before{content:"\f741"}.bi-filetype-css::before{content:"\f742"}.bi-filetype-csv::before{content:"\f743"}.bi-filetype-doc::before{content:"\f744"}.bi-filetype-docx::before{content:"\f745"}.bi-filetype-exe::before{content:"\f746"}.bi-filetype-gif::before{content:"\f747"}.bi-filetype-heic::before{content:"\f748"}.bi-filetype-html::before{content:"\f749"}.bi-filetype-java::before{content:"\f74a"}.bi-filetype-jpg::before{content:"\f74b"}.bi-filetype-js::before{content:"\f74c"}.bi-filetype-jsx::before{content:"\f74d"}.bi-filetype-key::before{content:"\f74e"}.bi-filetype-m4p::before{content:"\f74f"}.bi-filetype-md::before{content:"\f750"}.bi-filetype-mdx::before{content:"\f751"}.bi-filetype-mov::before{content:"\f752"}.bi-filetype-mp3::before{content:"\f753"}.bi-filetype-mp4::before{content:"\f754"}.bi-filetype-otf::before{content:"\f755"}.bi-filetype-pdf::before{content:"\f756"}.bi-filetype-php::before{content:"\f757"}.bi-filetype-png::before{content:"\f758"}.bi-filetype-ppt::before{content:"\f75a"}.bi-filetype-psd::before{content:"\f75b"}.bi-filetype-py::before{content:"\f75c"}.bi-filetype-raw::before{content:"\f75d"}.bi-filetype-rb::before{content:"\f75e"}.bi-filetype-sass::before{content:"\f75f"}.bi-filetype-scss::before{content:"\f760"}.bi-filetype-sh::before{content:"\f761"}.bi-filetype-svg::before{content:"\f762"}.bi-filetype-tiff::before{content:"\f763"}.bi-filetype-tsx::before{content:"\f764"}.bi-filetype-ttf::before{content:"\f765"}.bi-filetype-txt::before{content:"\f766"}.bi-filetype-wav::before{content:"\f767"}.bi-filetype-woff::before{content:"\f768"}.bi-filetype-xls::before{content:"\f76a"}.bi-filetype-xml::before{content:"\f76b"}.bi-filetype-yml::before{content:"\f76c"}.bi-heart-arrow::before{content:"\f76d"}.bi-heart-pulse-fill::before{content:"\f76e"}.bi-heart-pulse::before{content:"\f76f"}.bi-heartbreak-fill::before{content:"\f770"}.bi-heartbreak::before{content:"\f771"}.bi-hearts::before{content:"\f772"}.bi-hospital-fill::before{content:"\f773"}.bi-hospital::before{content:"\f774"}.bi-house-heart-fill::before{content:"\f775"}.bi-house-heart::before{content:"\f776"}.bi-incognito::before{content:"\f777"}.bi-magnet-fill::before{content:"\f778"}.bi-magnet::before{content:"\f779"}.bi-person-heart::before{content:"\f77a"}.bi-person-hearts::before{content:"\f77b"}.bi-phone-flip::before{content:"\f77c"}.bi-plugin::before{content:"\f77d"}.bi-postage-fill::before{content:"\f77e"}.bi-postage-heart-fill::before{content:"\f77f"}.bi-postage-heart::before{content:"\f780"}.bi-postage::before{content:"\f781"}.bi-postcard-fill::before{content:"\f782"}.bi-postcard-heart-fill::before{content:"\f783"}.bi-postcard-heart::before{content:"\f784"}.bi-postcard::before{content:"\f785"}.bi-search-heart-fill::before{content:"\f786"}.bi-search-heart::before{content:"\f787"}.bi-sliders2-vertical::before{content:"\f788"}.bi-sliders2::before{content:"\f789"}.bi-trash3-fill::before{content:"\f78a"}.bi-trash3::before{content:"\f78b"}.bi-valentine::before{content:"\f78c"}.bi-valentine2::before{content:"\f78d"}.bi-wrench-adjustable-circle-fill::before{content:"\f78e"}.bi-wrench-adjustable-circle::before{content:"\f78f"}.bi-wrench-adjustable::before{content:"\f790"}.bi-filetype-json::before{content:"\f791"}.bi-filetype-pptx::before{content:"\f792"}.bi-filetype-xlsx::before{content:"\f793"}.bi-1-circle-fill::before{content:"\f796"}.bi-1-circle::before{content:"\f797"}.bi-1-square-fill::before{content:"\f798"}.bi-1-square::before{content:"\f799"}.bi-2-circle-fill::before{content:"\f79c"}.bi-2-circle::before{content:"\f79d"}.bi-2-square-fill::before{content:"\f79e"}.bi-2-square::before{content:"\f79f"}.bi-3-circle-fill::before{content:"\f7a2"}.bi-3-circle::before{content:"\f7a3"}.bi-3-square-fill::before{content:"\f7a4"}.bi-3-square::before{content:"\f7a5"}.bi-4-circle-fill::before{content:"\f7a8"}.bi-4-circle::before{content:"\f7a9"}.bi-4-square-fill::before{content:"\f7aa"}.bi-4-square::before{content:"\f7ab"}.bi-5-circle-fill::before{content:"\f7ae"}.bi-5-circle::before{content:"\f7af"}.bi-5-square-fill::before{content:"\f7b0"}.bi-5-square::before{content:"\f7b1"}.bi-6-circle-fill::before{content:"\f7b4"}.bi-6-circle::before{content:"\f7b5"}.bi-6-square-fill::before{content:"\f7b6"}.bi-6-square::before{content:"\f7b7"}.bi-7-circle-fill::before{content:"\f7ba"}.bi-7-circle::before{content:"\f7bb"}.bi-7-square-fill::before{content:"\f7bc"}.bi-7-square::before{content:"\f7bd"}.bi-8-circle-fill::before{content:"\f7c0"}.bi-8-circle::before{content:"\f7c1"}.bi-8-square-fill::before{content:"\f7c2"}.bi-8-square::before{content:"\f7c3"}.bi-9-circle-fill::before{content:"\f7c6"}.bi-9-circle::before{content:"\f7c7"}.bi-9-square-fill::before{content:"\f7c8"}.bi-9-square::before{content:"\f7c9"}.bi-airplane-engines-fill::before{content:"\f7ca"}.bi-airplane-engines::before{content:"\f7cb"}.bi-airplane-fill::before{content:"\f7cc"}.bi-airplane::before{content:"\f7cd"}.bi-alexa::before{content:"\f7ce"}.bi-alipay::before{content:"\f7cf"}.bi-android::before{content:"\f7d0"}.bi-android2::before{content:"\f7d1"}.bi-box-fill::before{content:"\f7d2"}.bi-box-seam-fill::before{content:"\f7d3"}.bi-browser-chrome::before{content:"\f7d4"}.bi-browser-edge::before{content:"\f7d5"}.bi-browser-firefox::before{content:"\f7d6"}.bi-browser-safari::before{content:"\f7d7"}.bi-c-circle-fill::before{content:"\f7da"}.bi-c-circle::before{content:"\f7db"}.bi-c-square-fill::before{content:"\f7dc"}.bi-c-square::before{content:"\f7dd"}.bi-capsule-pill::before{content:"\f7de"}.bi-capsule::before{content:"\f7df"}.bi-car-front-fill::before{content:"\f7e0"}.bi-car-front::before{content:"\f7e1"}.bi-cassette-fill::before{content:"\f7e2"}.bi-cassette::before{content:"\f7e3"}.bi-cc-circle-fill::before{content:"\f7e6"}.bi-cc-circle::before{content:"\f7e7"}.bi-cc-square-fill::before{content:"\f7e8"}.bi-cc-square::before{content:"\f7e9"}.bi-cup-hot-fill::before{content:"\f7ea"}.bi-cup-hot::before{content:"\f7eb"}.bi-currency-rupee::before{content:"\f7ec"}.bi-dropbox::before{content:"\f7ed"}.bi-escape::before{content:"\f7ee"}.bi-fast-forward-btn-fill::before{content:"\f7ef"}.bi-fast-forward-btn::before{content:"\f7f0"}.bi-fast-forward-circle-fill::before{content:"\f7f1"}.bi-fast-forward-circle::before{content:"\f7f2"}.bi-fast-forward-fill::before{content:"\f7f3"}.bi-fast-forward::before{content:"\f7f4"}.bi-filetype-sql::before{content:"\f7f5"}.bi-fire::before{content:"\f7f6"}.bi-google-play::before{content:"\f7f7"}.bi-h-circle-fill::before{content:"\f7fa"}.bi-h-circle::before{content:"\f7fb"}.bi-h-square-fill::before{content:"\f7fc"}.bi-h-square::before{content:"\f7fd"}.bi-indent::before{content:"\f7fe"}.bi-lungs-fill::before{content:"\f7ff"}.bi-lungs::before{content:"\f800"}.bi-microsoft-teams::before{content:"\f801"}.bi-p-circle-fill::before{content:"\f804"}.bi-p-circle::before{content:"\f805"}.bi-p-square-fill::before{content:"\f806"}.bi-p-square::before{content:"\f807"}.bi-pass-fill::before{content:"\f808"}.bi-pass::before{content:"\f809"}.bi-prescription::before{content:"\f80a"}.bi-prescription2::before{content:"\f80b"}.bi-r-circle-fill::before{content:"\f80e"}.bi-r-circle::before{content:"\f80f"}.bi-r-square-fill::before{content:"\f810"}.bi-r-square::before{content:"\f811"}.bi-repeat-1::before{content:"\f812"}.bi-repeat::before{content:"\f813"}.bi-rewind-btn-fill::before{content:"\f814"}.bi-rewind-btn::before{content:"\f815"}.bi-rewind-circle-fill::before{content:"\f816"}.bi-rewind-circle::before{content:"\f817"}.bi-rewind-fill::before{content:"\f818"}.bi-rewind::before{content:"\f819"}.bi-train-freight-front-fill::before{content:"\f81a"}.bi-train-freight-front::before{content:"\f81b"}.bi-train-front-fill::before{content:"\f81c"}.bi-train-front::before{content:"\f81d"}.bi-train-lightrail-front-fill::before{content:"\f81e"}.bi-train-lightrail-front::before{content:"\f81f"}.bi-truck-front-fill::before{content:"\f820"}.bi-truck-front::before{content:"\f821"}.bi-ubuntu::before{content:"\f822"}.bi-unindent::before{content:"\f823"}.bi-unity::before{content:"\f824"}.bi-universal-access-circle::before{content:"\f825"}.bi-universal-access::before{content:"\f826"}.bi-virus::before{content:"\f827"}.bi-virus2::before{content:"\f828"}.bi-wechat::before{content:"\f829"}.bi-yelp::before{content:"\f82a"}.bi-sign-stop-fill::before{content:"\f82b"}.bi-sign-stop-lights-fill::before{content:"\f82c"}.bi-sign-stop-lights::before{content:"\f82d"}.bi-sign-stop::before{content:"\f82e"}.bi-sign-turn-left-fill::before{content:"\f82f"}.bi-sign-turn-left::before{content:"\f830"}.bi-sign-turn-right-fill::before{content:"\f831"}.bi-sign-turn-right::before{content:"\f832"}.bi-sign-turn-slight-left-fill::before{content:"\f833"}.bi-sign-turn-slight-left::before{content:"\f834"}.bi-sign-turn-slight-right-fill::before{content:"\f835"}.bi-sign-turn-slight-right::before{content:"\f836"}.bi-sign-yield-fill::before{content:"\f837"}.bi-sign-yield::before{content:"\f838"}.bi-ev-station-fill::before{content:"\f839"}.bi-ev-station::before{content:"\f83a"}.bi-fuel-pump-diesel-fill::before{content:"\f83b"}.bi-fuel-pump-diesel::before{content:"\f83c"}.bi-fuel-pump-fill::before{content:"\f83d"}.bi-fuel-pump::before{content:"\f83e"}.bi-0-circle-fill::before{content:"\f83f"}.bi-0-circle::before{content:"\f840"}.bi-0-square-fill::before{content:"\f841"}.bi-0-square::before{content:"\f842"}.bi-rocket-fill::before{content:"\f843"}.bi-rocket-takeoff-fill::before{content:"\f844"}.bi-rocket-takeoff::before{content:"\f845"}.bi-rocket::before{content:"\f846"}.bi-stripe::before{content:"\f847"}.bi-subscript::before{content:"\f848"}.bi-superscript::before{content:"\f849"}.bi-trello::before{content:"\f84a"}.bi-envelope-at-fill::before{content:"\f84b"}.bi-envelope-at::before{content:"\f84c"}.bi-regex::before{content:"\f84d"}.bi-text-wrap::before{content:"\f84e"}.bi-sign-dead-end-fill::before{content:"\f84f"}.bi-sign-dead-end::before{content:"\f850"}.bi-sign-do-not-enter-fill::before{content:"\f851"}.bi-sign-do-not-enter::before{content:"\f852"}.bi-sign-intersection-fill::before{content:"\f853"}.bi-sign-intersection-side-fill::before{content:"\f854"}.bi-sign-intersection-side::before{content:"\f855"}.bi-sign-intersection-t-fill::before{content:"\f856"}.bi-sign-intersection-t::before{content:"\f857"}.bi-sign-intersection-y-fill::before{content:"\f858"}.bi-sign-intersection-y::before{content:"\f859"}.bi-sign-intersection::before{content:"\f85a"}.bi-sign-merge-left-fill::before{content:"\f85b"}.bi-sign-merge-left::before{content:"\f85c"}.bi-sign-merge-right-fill::before{content:"\f85d"}.bi-sign-merge-right::before{content:"\f85e"}.bi-sign-no-left-turn-fill::before{content:"\f85f"}.bi-sign-no-left-turn::before{content:"\f860"}.bi-sign-no-parking-fill::before{content:"\f861"}.bi-sign-no-parking::before{content:"\f862"}.bi-sign-no-right-turn-fill::before{content:"\f863"}.bi-sign-no-right-turn::before{content:"\f864"}.bi-sign-railroad-fill::before{content:"\f865"}.bi-sign-railroad::before{content:"\f866"}.bi-building-add::before{content:"\f867"}.bi-building-check::before{content:"\f868"}.bi-building-dash::before{content:"\f869"}.bi-building-down::before{content:"\f86a"}.bi-building-exclamation::before{content:"\f86b"}.bi-building-fill-add::before{content:"\f86c"}.bi-building-fill-check::before{content:"\f86d"}.bi-building-fill-dash::before{content:"\f86e"}.bi-building-fill-down::before{content:"\f86f"}.bi-building-fill-exclamation::before{content:"\f870"}.bi-building-fill-gear::before{content:"\f871"}.bi-building-fill-lock::before{content:"\f872"}.bi-building-fill-slash::before{content:"\f873"}.bi-building-fill-up::before{content:"\f874"}.bi-building-fill-x::before{content:"\f875"}.bi-building-fill::before{content:"\f876"}.bi-building-gear::before{content:"\f877"}.bi-building-lock::before{content:"\f878"}.bi-building-slash::before{content:"\f879"}.bi-building-up::before{content:"\f87a"}.bi-building-x::before{content:"\f87b"}.bi-buildings-fill::before{content:"\f87c"}.bi-buildings::before{content:"\f87d"}.bi-bus-front-fill::before{content:"\f87e"}.bi-bus-front::before{content:"\f87f"}.bi-ev-front-fill::before{content:"\f880"}.bi-ev-front::before{content:"\f881"}.bi-globe-americas::before{content:"\f882"}.bi-globe-asia-australia::before{content:"\f883"}.bi-globe-central-south-asia::before{content:"\f884"}.bi-globe-europe-africa::before{content:"\f885"}.bi-house-add-fill::before{content:"\f886"}.bi-house-add::before{content:"\f887"}.bi-house-check-fill::before{content:"\f888"}.bi-house-check::before{content:"\f889"}.bi-house-dash-fill::before{content:"\f88a"}.bi-house-dash::before{content:"\f88b"}.bi-house-down-fill::before{content:"\f88c"}.bi-house-down::before{content:"\f88d"}.bi-house-exclamation-fill::before{content:"\f88e"}.bi-house-exclamation::before{content:"\f88f"}.bi-house-gear-fill::before{content:"\f890"}.bi-house-gear::before{content:"\f891"}.bi-house-lock-fill::before{content:"\f892"}.bi-house-lock::before{content:"\f893"}.bi-house-slash-fill::before{content:"\f894"}.bi-house-slash::before{content:"\f895"}.bi-house-up-fill::before{content:"\f896"}.bi-house-up::before{content:"\f897"}.bi-house-x-fill::before{content:"\f898"}.bi-house-x::before{content:"\f899"}.bi-person-add::before{content:"\f89a"}.bi-person-down::before{content:"\f89b"}.bi-person-exclamation::before{content:"\f89c"}.bi-person-fill-add::before{content:"\f89d"}.bi-person-fill-check::before{content:"\f89e"}.bi-person-fill-dash::before{content:"\f89f"}.bi-person-fill-down::before{content:"\f8a0"}.bi-person-fill-exclamation::before{content:"\f8a1"}.bi-person-fill-gear::before{content:"\f8a2"}.bi-person-fill-lock::before{content:"\f8a3"}.bi-person-fill-slash::before{content:"\f8a4"}.bi-person-fill-up::before{content:"\f8a5"}.bi-person-fill-x::before{content:"\f8a6"}.bi-person-gear::before{content:"\f8a7"}.bi-person-lock::before{content:"\f8a8"}.bi-person-slash::before{content:"\f8a9"}.bi-person-up::before{content:"\f8aa"}.bi-scooter::before{content:"\f8ab"}.bi-taxi-front-fill::before{content:"\f8ac"}.bi-taxi-front::before{content:"\f8ad"}.bi-amd::before{content:"\f8ae"}.bi-database-add::before{content:"\f8af"}.bi-database-check::before{content:"\f8b0"}.bi-database-dash::before{content:"\f8b1"}.bi-database-down::before{content:"\f8b2"}.bi-database-exclamation::before{content:"\f8b3"}.bi-database-fill-add::before{content:"\f8b4"}.bi-database-fill-check::before{content:"\f8b5"}.bi-database-fill-dash::before{content:"\f8b6"}.bi-database-fill-down::before{content:"\f8b7"}.bi-database-fill-exclamation::before{content:"\f8b8"}.bi-database-fill-gear::before{content:"\f8b9"}.bi-database-fill-lock::before{content:"\f8ba"}.bi-database-fill-slash::before{content:"\f8bb"}.bi-database-fill-up::before{content:"\f8bc"}.bi-database-fill-x::before{content:"\f8bd"}.bi-database-fill::before{content:"\f8be"}.bi-database-gear::before{content:"\f8bf"}.bi-database-lock::before{content:"\f8c0"}.bi-database-slash::before{content:"\f8c1"}.bi-database-up::before{content:"\f8c2"}.bi-database-x::before{content:"\f8c3"}.bi-database::before{content:"\f8c4"}.bi-houses-fill::before{content:"\f8c5"}.bi-houses::before{content:"\f8c6"}.bi-nvidia::before{content:"\f8c7"}.bi-person-vcard-fill::before{content:"\f8c8"}.bi-person-vcard::before{content:"\f8c9"}.bi-sina-weibo::before{content:"\f8ca"}.bi-tencent-qq::before{content:"\f8cb"}.bi-wikipedia::before{content:"\f8cc"}.bi-alphabet-uppercase::before{content:"\f2a5"}.bi-alphabet::before{content:"\f68a"}.bi-amazon::before{content:"\f68d"}.bi-arrows-collapse-vertical::before{content:"\f690"}.bi-arrows-expand-vertical::before{content:"\f695"}.bi-arrows-vertical::before{content:"\f698"}.bi-arrows::before{content:"\f6a2"}.bi-ban-fill::before{content:"\f6a3"}.bi-ban::before{content:"\f6b6"}.bi-bing::before{content:"\f6c2"}.bi-cake::before{content:"\f6e0"}.bi-cake2::before{content:"\f6ed"}.bi-cookie::before{content:"\f6ee"}.bi-copy::before{content:"\f759"}.bi-crosshair::before{content:"\f769"}.bi-crosshair2::before{content:"\f794"}.bi-emoji-astonished-fill::before{content:"\f795"}.bi-emoji-astonished::before{content:"\f79a"}.bi-emoji-grimace-fill::before{content:"\f79b"}.bi-emoji-grimace::before{content:"\f7a0"}.bi-emoji-grin-fill::before{content:"\f7a1"}.bi-emoji-grin::before{content:"\f7a6"}.bi-emoji-surprise-fill::before{content:"\f7a7"}.bi-emoji-surprise::before{content:"\f7ac"}.bi-emoji-tear-fill::before{content:"\f7ad"}.bi-emoji-tear::before{content:"\f7b2"}.bi-envelope-arrow-down-fill::before{content:"\f7b3"}.bi-envelope-arrow-down::before{content:"\f7b8"}.bi-envelope-arrow-up-fill::before{content:"\f7b9"}.bi-envelope-arrow-up::before{content:"\f7be"}.bi-feather::before{content:"\f7bf"}.bi-feather2::before{content:"\f7c4"}.bi-floppy-fill::before{content:"\f7c5"}.bi-floppy::before{content:"\f7d8"}.bi-floppy2-fill::before{content:"\f7d9"}.bi-floppy2::before{content:"\f7e4"}.bi-gitlab::before{content:"\f7e5"}.bi-highlighter::before{content:"\f7f8"}.bi-marker-tip::before{content:"\f802"}.bi-nvme-fill::before{content:"\f803"}.bi-nvme::before{content:"\f80c"}.bi-opencollective::before{content:"\f80d"}.bi-pci-card-network::before{content:"\f8cd"}.bi-pci-card-sound::before{content:"\f8ce"}.bi-radar::before{content:"\f8cf"}.bi-send-arrow-down-fill::before{content:"\f8d0"}.bi-send-arrow-down::before{content:"\f8d1"}.bi-send-arrow-up-fill::before{content:"\f8d2"}.bi-send-arrow-up::before{content:"\f8d3"}.bi-sim-slash-fill::before{content:"\f8d4"}.bi-sim-slash::before{content:"\f8d5"}.bi-sourceforge::before{content:"\f8d6"}.bi-substack::before{content:"\f8d7"}.bi-threads-fill::before{content:"\f8d8"}.bi-threads::before{content:"\f8d9"}.bi-transparency::before{content:"\f8da"}.bi-twitter-x::before{content:"\f8db"}.bi-type-h4::before{content:"\f8dc"}.bi-type-h5::before{content:"\f8dd"}.bi-type-h6::before{content:"\f8de"}.bi-backpack-fill::before{content:"\f8df"}.bi-backpack::before{content:"\f8e0"}.bi-backpack2-fill::before{content:"\f8e1"}.bi-backpack2::before{content:"\f8e2"}.bi-backpack3-fill::before{content:"\f8e3"}.bi-backpack3::before{content:"\f8e4"}.bi-backpack4-fill::before{content:"\f8e5"}.bi-backpack4::before{content:"\f8e6"}.bi-brilliance::before{content:"\f8e7"}.bi-cake-fill::before{content:"\f8e8"}.bi-cake2-fill::before{content:"\f8e9"}.bi-duffle-fill::before{content:"\f8ea"}.bi-duffle::before{content:"\f8eb"}.bi-exposure::before{content:"\f8ec"}.bi-gender-neuter::before{content:"\f8ed"}.bi-highlights::before{content:"\f8ee"}.bi-luggage-fill::before{content:"\f8ef"}.bi-luggage::before{content:"\f8f0"}.bi-mailbox-flag::before{content:"\f8f1"}.bi-mailbox2-flag::before{content:"\f8f2"}.bi-noise-reduction::before{content:"\f8f3"}.bi-passport-fill::before{content:"\f8f4"}.bi-passport::before{content:"\f8f5"}.bi-person-arms-up::before{content:"\f8f6"}.bi-person-raised-hand::before{content:"\f8f7"}.bi-person-standing-dress::before{content:"\f8f8"}.bi-person-standing::before{content:"\f8f9"}.bi-person-walking::before{content:"\f8fa"}.bi-person-wheelchair::before{content:"\f8fb"}.bi-shadows::before{content:"\f8fc"}.bi-suitcase-fill::before{content:"\f8fd"}.bi-suitcase-lg-fill::before{content:"\f8fe"}.bi-suitcase-lg::before{content:"\f8ff"}.bi-suitcase::before{content:"\f900"}.bi-suitcase2-fill::before{content:"\f901"}.bi-suitcase2::before{content:"\f902"}.bi-vignette::before{content:"\f903"}.bi-bluesky::before{content:"\f7f9"}.bi-tux::before{content:"\f904"}.bi-beaker-fill::before{content:"\f905"}.bi-beaker::before{content:"\f906"}.bi-flask-fill::before{content:"\f907"}.bi-flask-florence-fill::before{content:"\f908"}.bi-flask-florence::before{content:"\f909"}.bi-flask::before{content:"\f90a"}.bi-leaf-fill::before{content:"\f90b"}.bi-leaf::before{content:"\f90c"}.bi-measuring-cup-fill::before{content:"\f90d"}.bi-measuring-cup::before{content:"\f90e"}.bi-unlock2-fill::before{content:"\f90f"}.bi-unlock2::before{content:"\f910"}.bi-battery-low::before{content:"\f911"}.bi-anthropic::before{content:"\f912"}.bi-apple-music::before{content:"\f913"}.bi-claude::before{content:"\f914"}.bi-openai::before{content:"\f915"}.bi-perplexity::before{content:"\f916"}.bi-css::before{content:"\f917"}.bi-javascript::before{content:"\f918"}.bi-typescript::before{content:"\f919"}.bi-fork-knife::before{content:"\f91a"}.bi-globe-americas-fill::before{content:"\f91b"}.bi-globe-asia-australia-fill::before{content:"\f91c"}.bi-globe-central-south-asia-fill::before{content:"\f91d"}.bi-globe-europe-africa-fill::before{content:"\f91e"} \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bootstrap-icons.scss b/web/vendor/bootstrap-icons/bootstrap-icons.scss new file mode 100644 index 0000000..19735c4 --- /dev/null +++ b/web/vendor/bootstrap-icons/bootstrap-icons.scss @@ -0,0 +1,2118 @@ +/*! + * Bootstrap Icons v1.13.1 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ + +$bootstrap-icons-font: "bootstrap-icons" !default; +$bootstrap-icons-font-dir: "./fonts" !default; +$bootstrap-icons-font-file: "#{$bootstrap-icons-font-dir}/#{$bootstrap-icons-font}" !default; +$bootstrap-icons-font-hash: "24e3eb84d0bcaf83d77f904c78ac1f47" !default; +$bootstrap-icons-font-src: url("#{$bootstrap-icons-font-file}.woff2?#{$bootstrap-icons-font-hash}") format("woff2"), + url("#{$bootstrap-icons-font-file}.woff?#{$bootstrap-icons-font-hash}") format("woff") !default; + +@font-face { + font-display: block; + font-family: $bootstrap-icons-font; + src: $bootstrap-icons-font-src; +} + +.bi::before, +[class^="bi-"]::before, +[class*=" bi-"]::before { + display: inline-block; + font-family: $bootstrap-icons-font !important; + font-style: normal; + font-weight: normal !important; + font-variant: normal; + text-transform: none; + line-height: 1; + vertical-align: -.125em; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +$bootstrap-icons-map: ( + "123": "\f67f", + "alarm-fill": "\f101", + "alarm": "\f102", + "align-bottom": "\f103", + "align-center": "\f104", + "align-end": "\f105", + "align-middle": "\f106", + "align-start": "\f107", + "align-top": "\f108", + "alt": "\f109", + "app-indicator": "\f10a", + "app": "\f10b", + "archive-fill": "\f10c", + "archive": "\f10d", + "arrow-90deg-down": "\f10e", + "arrow-90deg-left": "\f10f", + "arrow-90deg-right": "\f110", + "arrow-90deg-up": "\f111", + "arrow-bar-down": "\f112", + "arrow-bar-left": "\f113", + "arrow-bar-right": "\f114", + "arrow-bar-up": "\f115", + "arrow-clockwise": "\f116", + "arrow-counterclockwise": "\f117", + "arrow-down-circle-fill": "\f118", + "arrow-down-circle": "\f119", + "arrow-down-left-circle-fill": "\f11a", + "arrow-down-left-circle": "\f11b", + "arrow-down-left-square-fill": "\f11c", + "arrow-down-left-square": "\f11d", + "arrow-down-left": "\f11e", + "arrow-down-right-circle-fill": "\f11f", + "arrow-down-right-circle": "\f120", + "arrow-down-right-square-fill": "\f121", + "arrow-down-right-square": "\f122", + "arrow-down-right": "\f123", + "arrow-down-short": "\f124", + "arrow-down-square-fill": "\f125", + "arrow-down-square": "\f126", + "arrow-down-up": "\f127", + "arrow-down": "\f128", + "arrow-left-circle-fill": "\f129", + "arrow-left-circle": "\f12a", + "arrow-left-right": "\f12b", + "arrow-left-short": "\f12c", + "arrow-left-square-fill": "\f12d", + "arrow-left-square": "\f12e", + "arrow-left": "\f12f", + "arrow-repeat": "\f130", + "arrow-return-left": "\f131", + "arrow-return-right": "\f132", + "arrow-right-circle-fill": "\f133", + "arrow-right-circle": "\f134", + "arrow-right-short": "\f135", + "arrow-right-square-fill": "\f136", + "arrow-right-square": "\f137", + "arrow-right": "\f138", + "arrow-up-circle-fill": "\f139", + "arrow-up-circle": "\f13a", + "arrow-up-left-circle-fill": "\f13b", + "arrow-up-left-circle": "\f13c", + "arrow-up-left-square-fill": "\f13d", + "arrow-up-left-square": "\f13e", + "arrow-up-left": "\f13f", + "arrow-up-right-circle-fill": "\f140", + "arrow-up-right-circle": "\f141", + "arrow-up-right-square-fill": "\f142", + "arrow-up-right-square": "\f143", + "arrow-up-right": "\f144", + "arrow-up-short": "\f145", + "arrow-up-square-fill": "\f146", + "arrow-up-square": "\f147", + "arrow-up": "\f148", + "arrows-angle-contract": "\f149", + "arrows-angle-expand": "\f14a", + "arrows-collapse": "\f14b", + "arrows-expand": "\f14c", + "arrows-fullscreen": "\f14d", + "arrows-move": "\f14e", + "aspect-ratio-fill": "\f14f", + "aspect-ratio": "\f150", + "asterisk": "\f151", + "at": "\f152", + "award-fill": "\f153", + "award": "\f154", + "back": "\f155", + "backspace-fill": "\f156", + "backspace-reverse-fill": "\f157", + "backspace-reverse": "\f158", + "backspace": "\f159", + "badge-3d-fill": "\f15a", + "badge-3d": "\f15b", + "badge-4k-fill": "\f15c", + "badge-4k": "\f15d", + "badge-8k-fill": "\f15e", + "badge-8k": "\f15f", + "badge-ad-fill": "\f160", + "badge-ad": "\f161", + "badge-ar-fill": "\f162", + "badge-ar": "\f163", + "badge-cc-fill": "\f164", + "badge-cc": "\f165", + "badge-hd-fill": "\f166", + "badge-hd": "\f167", + "badge-tm-fill": "\f168", + "badge-tm": "\f169", + "badge-vo-fill": "\f16a", + "badge-vo": "\f16b", + "badge-vr-fill": "\f16c", + "badge-vr": "\f16d", + "badge-wc-fill": "\f16e", + "badge-wc": "\f16f", + "bag-check-fill": "\f170", + "bag-check": "\f171", + "bag-dash-fill": "\f172", + "bag-dash": "\f173", + "bag-fill": "\f174", + "bag-plus-fill": "\f175", + "bag-plus": "\f176", + "bag-x-fill": "\f177", + "bag-x": "\f178", + "bag": "\f179", + "bar-chart-fill": "\f17a", + "bar-chart-line-fill": "\f17b", + "bar-chart-line": "\f17c", + "bar-chart-steps": "\f17d", + "bar-chart": "\f17e", + "basket-fill": "\f17f", + "basket": "\f180", + "basket2-fill": "\f181", + "basket2": "\f182", + "basket3-fill": "\f183", + "basket3": "\f184", + "battery-charging": "\f185", + "battery-full": "\f186", + "battery-half": "\f187", + "battery": "\f188", + "bell-fill": "\f189", + "bell": "\f18a", + "bezier": "\f18b", + "bezier2": "\f18c", + "bicycle": "\f18d", + "binoculars-fill": "\f18e", + "binoculars": "\f18f", + "blockquote-left": "\f190", + "blockquote-right": "\f191", + "book-fill": "\f192", + "book-half": "\f193", + "book": "\f194", + "bookmark-check-fill": "\f195", + "bookmark-check": "\f196", + "bookmark-dash-fill": "\f197", + "bookmark-dash": "\f198", + "bookmark-fill": "\f199", + "bookmark-heart-fill": "\f19a", + "bookmark-heart": "\f19b", + "bookmark-plus-fill": "\f19c", + "bookmark-plus": "\f19d", + "bookmark-star-fill": "\f19e", + "bookmark-star": "\f19f", + "bookmark-x-fill": "\f1a0", + "bookmark-x": "\f1a1", + "bookmark": "\f1a2", + "bookmarks-fill": "\f1a3", + "bookmarks": "\f1a4", + "bookshelf": "\f1a5", + "bootstrap-fill": "\f1a6", + "bootstrap-reboot": "\f1a7", + "bootstrap": "\f1a8", + "border-all": "\f1a9", + "border-bottom": "\f1aa", + "border-center": "\f1ab", + "border-inner": "\f1ac", + "border-left": "\f1ad", + "border-middle": "\f1ae", + "border-outer": "\f1af", + "border-right": "\f1b0", + "border-style": "\f1b1", + "border-top": "\f1b2", + "border-width": "\f1b3", + "border": "\f1b4", + "bounding-box-circles": "\f1b5", + "bounding-box": "\f1b6", + "box-arrow-down-left": "\f1b7", + "box-arrow-down-right": "\f1b8", + "box-arrow-down": "\f1b9", + "box-arrow-in-down-left": "\f1ba", + "box-arrow-in-down-right": "\f1bb", + "box-arrow-in-down": "\f1bc", + "box-arrow-in-left": "\f1bd", + "box-arrow-in-right": "\f1be", + "box-arrow-in-up-left": "\f1bf", + "box-arrow-in-up-right": "\f1c0", + "box-arrow-in-up": "\f1c1", + "box-arrow-left": "\f1c2", + "box-arrow-right": "\f1c3", + "box-arrow-up-left": "\f1c4", + "box-arrow-up-right": "\f1c5", + "box-arrow-up": "\f1c6", + "box-seam": "\f1c7", + "box": "\f1c8", + "braces": "\f1c9", + "bricks": "\f1ca", + "briefcase-fill": "\f1cb", + "briefcase": "\f1cc", + "brightness-alt-high-fill": "\f1cd", + "brightness-alt-high": "\f1ce", + "brightness-alt-low-fill": "\f1cf", + "brightness-alt-low": "\f1d0", + "brightness-high-fill": "\f1d1", + "brightness-high": "\f1d2", + "brightness-low-fill": "\f1d3", + "brightness-low": "\f1d4", + "broadcast-pin": "\f1d5", + "broadcast": "\f1d6", + "brush-fill": "\f1d7", + "brush": "\f1d8", + "bucket-fill": "\f1d9", + "bucket": "\f1da", + "bug-fill": "\f1db", + "bug": "\f1dc", + "building": "\f1dd", + "bullseye": "\f1de", + "calculator-fill": "\f1df", + "calculator": "\f1e0", + "calendar-check-fill": "\f1e1", + "calendar-check": "\f1e2", + "calendar-date-fill": "\f1e3", + "calendar-date": "\f1e4", + "calendar-day-fill": "\f1e5", + "calendar-day": "\f1e6", + "calendar-event-fill": "\f1e7", + "calendar-event": "\f1e8", + "calendar-fill": "\f1e9", + "calendar-minus-fill": "\f1ea", + "calendar-minus": "\f1eb", + "calendar-month-fill": "\f1ec", + "calendar-month": "\f1ed", + "calendar-plus-fill": "\f1ee", + "calendar-plus": "\f1ef", + "calendar-range-fill": "\f1f0", + "calendar-range": "\f1f1", + "calendar-week-fill": "\f1f2", + "calendar-week": "\f1f3", + "calendar-x-fill": "\f1f4", + "calendar-x": "\f1f5", + "calendar": "\f1f6", + "calendar2-check-fill": "\f1f7", + "calendar2-check": "\f1f8", + "calendar2-date-fill": "\f1f9", + "calendar2-date": "\f1fa", + "calendar2-day-fill": "\f1fb", + "calendar2-day": "\f1fc", + "calendar2-event-fill": "\f1fd", + "calendar2-event": "\f1fe", + "calendar2-fill": "\f1ff", + "calendar2-minus-fill": "\f200", + "calendar2-minus": "\f201", + "calendar2-month-fill": "\f202", + "calendar2-month": "\f203", + "calendar2-plus-fill": "\f204", + "calendar2-plus": "\f205", + "calendar2-range-fill": "\f206", + "calendar2-range": "\f207", + "calendar2-week-fill": "\f208", + "calendar2-week": "\f209", + "calendar2-x-fill": "\f20a", + "calendar2-x": "\f20b", + "calendar2": "\f20c", + "calendar3-event-fill": "\f20d", + "calendar3-event": "\f20e", + "calendar3-fill": "\f20f", + "calendar3-range-fill": "\f210", + "calendar3-range": "\f211", + "calendar3-week-fill": "\f212", + "calendar3-week": "\f213", + "calendar3": "\f214", + "calendar4-event": "\f215", + "calendar4-range": "\f216", + "calendar4-week": "\f217", + "calendar4": "\f218", + "camera-fill": "\f219", + "camera-reels-fill": "\f21a", + "camera-reels": "\f21b", + "camera-video-fill": "\f21c", + "camera-video-off-fill": "\f21d", + "camera-video-off": "\f21e", + "camera-video": "\f21f", + "camera": "\f220", + "camera2": "\f221", + "capslock-fill": "\f222", + "capslock": "\f223", + "card-checklist": "\f224", + "card-heading": "\f225", + "card-image": "\f226", + "card-list": "\f227", + "card-text": "\f228", + "caret-down-fill": "\f229", + "caret-down-square-fill": "\f22a", + "caret-down-square": "\f22b", + "caret-down": "\f22c", + "caret-left-fill": "\f22d", + "caret-left-square-fill": "\f22e", + "caret-left-square": "\f22f", + "caret-left": "\f230", + "caret-right-fill": "\f231", + "caret-right-square-fill": "\f232", + "caret-right-square": "\f233", + "caret-right": "\f234", + "caret-up-fill": "\f235", + "caret-up-square-fill": "\f236", + "caret-up-square": "\f237", + "caret-up": "\f238", + "cart-check-fill": "\f239", + "cart-check": "\f23a", + "cart-dash-fill": "\f23b", + "cart-dash": "\f23c", + "cart-fill": "\f23d", + "cart-plus-fill": "\f23e", + "cart-plus": "\f23f", + "cart-x-fill": "\f240", + "cart-x": "\f241", + "cart": "\f242", + "cart2": "\f243", + "cart3": "\f244", + "cart4": "\f245", + "cash-stack": "\f246", + "cash": "\f247", + "cast": "\f248", + "chat-dots-fill": "\f249", + "chat-dots": "\f24a", + "chat-fill": "\f24b", + "chat-left-dots-fill": "\f24c", + "chat-left-dots": "\f24d", + "chat-left-fill": "\f24e", + "chat-left-quote-fill": "\f24f", + "chat-left-quote": "\f250", + "chat-left-text-fill": "\f251", + "chat-left-text": "\f252", + "chat-left": "\f253", + "chat-quote-fill": "\f254", + "chat-quote": "\f255", + "chat-right-dots-fill": "\f256", + "chat-right-dots": "\f257", + "chat-right-fill": "\f258", + "chat-right-quote-fill": "\f259", + "chat-right-quote": "\f25a", + "chat-right-text-fill": "\f25b", + "chat-right-text": "\f25c", + "chat-right": "\f25d", + "chat-square-dots-fill": "\f25e", + "chat-square-dots": "\f25f", + "chat-square-fill": "\f260", + "chat-square-quote-fill": "\f261", + "chat-square-quote": "\f262", + "chat-square-text-fill": "\f263", + "chat-square-text": "\f264", + "chat-square": "\f265", + "chat-text-fill": "\f266", + "chat-text": "\f267", + "chat": "\f268", + "check-all": "\f269", + "check-circle-fill": "\f26a", + "check-circle": "\f26b", + "check-square-fill": "\f26c", + "check-square": "\f26d", + "check": "\f26e", + "check2-all": "\f26f", + "check2-circle": "\f270", + "check2-square": "\f271", + "check2": "\f272", + "chevron-bar-contract": "\f273", + "chevron-bar-down": "\f274", + "chevron-bar-expand": "\f275", + "chevron-bar-left": "\f276", + "chevron-bar-right": "\f277", + "chevron-bar-up": "\f278", + "chevron-compact-down": "\f279", + "chevron-compact-left": "\f27a", + "chevron-compact-right": "\f27b", + "chevron-compact-up": "\f27c", + "chevron-contract": "\f27d", + "chevron-double-down": "\f27e", + "chevron-double-left": "\f27f", + "chevron-double-right": "\f280", + "chevron-double-up": "\f281", + "chevron-down": "\f282", + "chevron-expand": "\f283", + "chevron-left": "\f284", + "chevron-right": "\f285", + "chevron-up": "\f286", + "circle-fill": "\f287", + "circle-half": "\f288", + "circle-square": "\f289", + "circle": "\f28a", + "clipboard-check": "\f28b", + "clipboard-data": "\f28c", + "clipboard-minus": "\f28d", + "clipboard-plus": "\f28e", + "clipboard-x": "\f28f", + "clipboard": "\f290", + "clock-fill": "\f291", + "clock-history": "\f292", + "clock": "\f293", + "cloud-arrow-down-fill": "\f294", + "cloud-arrow-down": "\f295", + "cloud-arrow-up-fill": "\f296", + "cloud-arrow-up": "\f297", + "cloud-check-fill": "\f298", + "cloud-check": "\f299", + "cloud-download-fill": "\f29a", + "cloud-download": "\f29b", + "cloud-drizzle-fill": "\f29c", + "cloud-drizzle": "\f29d", + "cloud-fill": "\f29e", + "cloud-fog-fill": "\f29f", + "cloud-fog": "\f2a0", + "cloud-fog2-fill": "\f2a1", + "cloud-fog2": "\f2a2", + "cloud-hail-fill": "\f2a3", + "cloud-hail": "\f2a4", + "cloud-haze-fill": "\f2a6", + "cloud-haze": "\f2a7", + "cloud-haze2-fill": "\f2a8", + "cloud-lightning-fill": "\f2a9", + "cloud-lightning-rain-fill": "\f2aa", + "cloud-lightning-rain": "\f2ab", + "cloud-lightning": "\f2ac", + "cloud-minus-fill": "\f2ad", + "cloud-minus": "\f2ae", + "cloud-moon-fill": "\f2af", + "cloud-moon": "\f2b0", + "cloud-plus-fill": "\f2b1", + "cloud-plus": "\f2b2", + "cloud-rain-fill": "\f2b3", + "cloud-rain-heavy-fill": "\f2b4", + "cloud-rain-heavy": "\f2b5", + "cloud-rain": "\f2b6", + "cloud-slash-fill": "\f2b7", + "cloud-slash": "\f2b8", + "cloud-sleet-fill": "\f2b9", + "cloud-sleet": "\f2ba", + "cloud-snow-fill": "\f2bb", + "cloud-snow": "\f2bc", + "cloud-sun-fill": "\f2bd", + "cloud-sun": "\f2be", + "cloud-upload-fill": "\f2bf", + "cloud-upload": "\f2c0", + "cloud": "\f2c1", + "clouds-fill": "\f2c2", + "clouds": "\f2c3", + "cloudy-fill": "\f2c4", + "cloudy": "\f2c5", + "code-slash": "\f2c6", + "code-square": "\f2c7", + "code": "\f2c8", + "collection-fill": "\f2c9", + "collection-play-fill": "\f2ca", + "collection-play": "\f2cb", + "collection": "\f2cc", + "columns-gap": "\f2cd", + "columns": "\f2ce", + "command": "\f2cf", + "compass-fill": "\f2d0", + "compass": "\f2d1", + "cone-striped": "\f2d2", + "cone": "\f2d3", + "controller": "\f2d4", + "cpu-fill": "\f2d5", + "cpu": "\f2d6", + "credit-card-2-back-fill": "\f2d7", + "credit-card-2-back": "\f2d8", + "credit-card-2-front-fill": "\f2d9", + "credit-card-2-front": "\f2da", + "credit-card-fill": "\f2db", + "credit-card": "\f2dc", + "crop": "\f2dd", + "cup-fill": "\f2de", + "cup-straw": "\f2df", + "cup": "\f2e0", + "cursor-fill": "\f2e1", + "cursor-text": "\f2e2", + "cursor": "\f2e3", + "dash-circle-dotted": "\f2e4", + "dash-circle-fill": "\f2e5", + "dash-circle": "\f2e6", + "dash-square-dotted": "\f2e7", + "dash-square-fill": "\f2e8", + "dash-square": "\f2e9", + "dash": "\f2ea", + "diagram-2-fill": "\f2eb", + "diagram-2": "\f2ec", + "diagram-3-fill": "\f2ed", + "diagram-3": "\f2ee", + "diamond-fill": "\f2ef", + "diamond-half": "\f2f0", + "diamond": "\f2f1", + "dice-1-fill": "\f2f2", + "dice-1": "\f2f3", + "dice-2-fill": "\f2f4", + "dice-2": "\f2f5", + "dice-3-fill": "\f2f6", + "dice-3": "\f2f7", + "dice-4-fill": "\f2f8", + "dice-4": "\f2f9", + "dice-5-fill": "\f2fa", + "dice-5": "\f2fb", + "dice-6-fill": "\f2fc", + "dice-6": "\f2fd", + "disc-fill": "\f2fe", + "disc": "\f2ff", + "discord": "\f300", + "display-fill": "\f301", + "display": "\f302", + "distribute-horizontal": "\f303", + "distribute-vertical": "\f304", + "door-closed-fill": "\f305", + "door-closed": "\f306", + "door-open-fill": "\f307", + "door-open": "\f308", + "dot": "\f309", + "download": "\f30a", + "droplet-fill": "\f30b", + "droplet-half": "\f30c", + "droplet": "\f30d", + "earbuds": "\f30e", + "easel-fill": "\f30f", + "easel": "\f310", + "egg-fill": "\f311", + "egg-fried": "\f312", + "egg": "\f313", + "eject-fill": "\f314", + "eject": "\f315", + "emoji-angry-fill": "\f316", + "emoji-angry": "\f317", + "emoji-dizzy-fill": "\f318", + "emoji-dizzy": "\f319", + "emoji-expressionless-fill": "\f31a", + "emoji-expressionless": "\f31b", + "emoji-frown-fill": "\f31c", + "emoji-frown": "\f31d", + "emoji-heart-eyes-fill": "\f31e", + "emoji-heart-eyes": "\f31f", + "emoji-laughing-fill": "\f320", + "emoji-laughing": "\f321", + "emoji-neutral-fill": "\f322", + "emoji-neutral": "\f323", + "emoji-smile-fill": "\f324", + "emoji-smile-upside-down-fill": "\f325", + "emoji-smile-upside-down": "\f326", + "emoji-smile": "\f327", + "emoji-sunglasses-fill": "\f328", + "emoji-sunglasses": "\f329", + "emoji-wink-fill": "\f32a", + "emoji-wink": "\f32b", + "envelope-fill": "\f32c", + "envelope-open-fill": "\f32d", + "envelope-open": "\f32e", + "envelope": "\f32f", + "eraser-fill": "\f330", + "eraser": "\f331", + "exclamation-circle-fill": "\f332", + "exclamation-circle": "\f333", + "exclamation-diamond-fill": "\f334", + "exclamation-diamond": "\f335", + "exclamation-octagon-fill": "\f336", + "exclamation-octagon": "\f337", + "exclamation-square-fill": "\f338", + "exclamation-square": "\f339", + "exclamation-triangle-fill": "\f33a", + "exclamation-triangle": "\f33b", + "exclamation": "\f33c", + "exclude": "\f33d", + "eye-fill": "\f33e", + "eye-slash-fill": "\f33f", + "eye-slash": "\f340", + "eye": "\f341", + "eyedropper": "\f342", + "eyeglasses": "\f343", + "facebook": "\f344", + "file-arrow-down-fill": "\f345", + "file-arrow-down": "\f346", + "file-arrow-up-fill": "\f347", + "file-arrow-up": "\f348", + "file-bar-graph-fill": "\f349", + "file-bar-graph": "\f34a", + "file-binary-fill": "\f34b", + "file-binary": "\f34c", + "file-break-fill": "\f34d", + "file-break": "\f34e", + "file-check-fill": "\f34f", + "file-check": "\f350", + "file-code-fill": "\f351", + "file-code": "\f352", + "file-diff-fill": "\f353", + "file-diff": "\f354", + "file-earmark-arrow-down-fill": "\f355", + "file-earmark-arrow-down": "\f356", + "file-earmark-arrow-up-fill": "\f357", + "file-earmark-arrow-up": "\f358", + "file-earmark-bar-graph-fill": "\f359", + "file-earmark-bar-graph": "\f35a", + "file-earmark-binary-fill": "\f35b", + "file-earmark-binary": "\f35c", + "file-earmark-break-fill": "\f35d", + "file-earmark-break": "\f35e", + "file-earmark-check-fill": "\f35f", + "file-earmark-check": "\f360", + "file-earmark-code-fill": "\f361", + "file-earmark-code": "\f362", + "file-earmark-diff-fill": "\f363", + "file-earmark-diff": "\f364", + "file-earmark-easel-fill": "\f365", + "file-earmark-easel": "\f366", + "file-earmark-excel-fill": "\f367", + "file-earmark-excel": "\f368", + "file-earmark-fill": "\f369", + "file-earmark-font-fill": "\f36a", + "file-earmark-font": "\f36b", + "file-earmark-image-fill": "\f36c", + "file-earmark-image": "\f36d", + "file-earmark-lock-fill": "\f36e", + "file-earmark-lock": "\f36f", + "file-earmark-lock2-fill": "\f370", + "file-earmark-lock2": "\f371", + "file-earmark-medical-fill": "\f372", + "file-earmark-medical": "\f373", + "file-earmark-minus-fill": "\f374", + "file-earmark-minus": "\f375", + "file-earmark-music-fill": "\f376", + "file-earmark-music": "\f377", + "file-earmark-person-fill": "\f378", + "file-earmark-person": "\f379", + "file-earmark-play-fill": "\f37a", + "file-earmark-play": "\f37b", + "file-earmark-plus-fill": "\f37c", + "file-earmark-plus": "\f37d", + "file-earmark-post-fill": "\f37e", + "file-earmark-post": "\f37f", + "file-earmark-ppt-fill": "\f380", + "file-earmark-ppt": "\f381", + "file-earmark-richtext-fill": "\f382", + "file-earmark-richtext": "\f383", + "file-earmark-ruled-fill": "\f384", + "file-earmark-ruled": "\f385", + "file-earmark-slides-fill": "\f386", + "file-earmark-slides": "\f387", + "file-earmark-spreadsheet-fill": "\f388", + "file-earmark-spreadsheet": "\f389", + "file-earmark-text-fill": "\f38a", + "file-earmark-text": "\f38b", + "file-earmark-word-fill": "\f38c", + "file-earmark-word": "\f38d", + "file-earmark-x-fill": "\f38e", + "file-earmark-x": "\f38f", + "file-earmark-zip-fill": "\f390", + "file-earmark-zip": "\f391", + "file-earmark": "\f392", + "file-easel-fill": "\f393", + "file-easel": "\f394", + "file-excel-fill": "\f395", + "file-excel": "\f396", + "file-fill": "\f397", + "file-font-fill": "\f398", + "file-font": "\f399", + "file-image-fill": "\f39a", + "file-image": "\f39b", + "file-lock-fill": "\f39c", + "file-lock": "\f39d", + "file-lock2-fill": "\f39e", + "file-lock2": "\f39f", + "file-medical-fill": "\f3a0", + "file-medical": "\f3a1", + "file-minus-fill": "\f3a2", + "file-minus": "\f3a3", + "file-music-fill": "\f3a4", + "file-music": "\f3a5", + "file-person-fill": "\f3a6", + "file-person": "\f3a7", + "file-play-fill": "\f3a8", + "file-play": "\f3a9", + "file-plus-fill": "\f3aa", + "file-plus": "\f3ab", + "file-post-fill": "\f3ac", + "file-post": "\f3ad", + "file-ppt-fill": "\f3ae", + "file-ppt": "\f3af", + "file-richtext-fill": "\f3b0", + "file-richtext": "\f3b1", + "file-ruled-fill": "\f3b2", + "file-ruled": "\f3b3", + "file-slides-fill": "\f3b4", + "file-slides": "\f3b5", + "file-spreadsheet-fill": "\f3b6", + "file-spreadsheet": "\f3b7", + "file-text-fill": "\f3b8", + "file-text": "\f3b9", + "file-word-fill": "\f3ba", + "file-word": "\f3bb", + "file-x-fill": "\f3bc", + "file-x": "\f3bd", + "file-zip-fill": "\f3be", + "file-zip": "\f3bf", + "file": "\f3c0", + "files-alt": "\f3c1", + "files": "\f3c2", + "film": "\f3c3", + "filter-circle-fill": "\f3c4", + "filter-circle": "\f3c5", + "filter-left": "\f3c6", + "filter-right": "\f3c7", + "filter-square-fill": "\f3c8", + "filter-square": "\f3c9", + "filter": "\f3ca", + "flag-fill": "\f3cb", + "flag": "\f3cc", + "flower1": "\f3cd", + "flower2": "\f3ce", + "flower3": "\f3cf", + "folder-check": "\f3d0", + "folder-fill": "\f3d1", + "folder-minus": "\f3d2", + "folder-plus": "\f3d3", + "folder-symlink-fill": "\f3d4", + "folder-symlink": "\f3d5", + "folder-x": "\f3d6", + "folder": "\f3d7", + "folder2-open": "\f3d8", + "folder2": "\f3d9", + "fonts": "\f3da", + "forward-fill": "\f3db", + "forward": "\f3dc", + "front": "\f3dd", + "fullscreen-exit": "\f3de", + "fullscreen": "\f3df", + "funnel-fill": "\f3e0", + "funnel": "\f3e1", + "gear-fill": "\f3e2", + "gear-wide-connected": "\f3e3", + "gear-wide": "\f3e4", + "gear": "\f3e5", + "gem": "\f3e6", + "geo-alt-fill": "\f3e7", + "geo-alt": "\f3e8", + "geo-fill": "\f3e9", + "geo": "\f3ea", + "gift-fill": "\f3eb", + "gift": "\f3ec", + "github": "\f3ed", + "globe": "\f3ee", + "globe2": "\f3ef", + "google": "\f3f0", + "graph-down": "\f3f1", + "graph-up": "\f3f2", + "grid-1x2-fill": "\f3f3", + "grid-1x2": "\f3f4", + "grid-3x2-gap-fill": "\f3f5", + "grid-3x2-gap": "\f3f6", + "grid-3x2": "\f3f7", + "grid-3x3-gap-fill": "\f3f8", + "grid-3x3-gap": "\f3f9", + "grid-3x3": "\f3fa", + "grid-fill": "\f3fb", + "grid": "\f3fc", + "grip-horizontal": "\f3fd", + "grip-vertical": "\f3fe", + "hammer": "\f3ff", + "hand-index-fill": "\f400", + "hand-index-thumb-fill": "\f401", + "hand-index-thumb": "\f402", + "hand-index": "\f403", + "hand-thumbs-down-fill": "\f404", + "hand-thumbs-down": "\f405", + "hand-thumbs-up-fill": "\f406", + "hand-thumbs-up": "\f407", + "handbag-fill": "\f408", + "handbag": "\f409", + "hash": "\f40a", + "hdd-fill": "\f40b", + "hdd-network-fill": "\f40c", + "hdd-network": "\f40d", + "hdd-rack-fill": "\f40e", + "hdd-rack": "\f40f", + "hdd-stack-fill": "\f410", + "hdd-stack": "\f411", + "hdd": "\f412", + "headphones": "\f413", + "headset": "\f414", + "heart-fill": "\f415", + "heart-half": "\f416", + "heart": "\f417", + "heptagon-fill": "\f418", + "heptagon-half": "\f419", + "heptagon": "\f41a", + "hexagon-fill": "\f41b", + "hexagon-half": "\f41c", + "hexagon": "\f41d", + "hourglass-bottom": "\f41e", + "hourglass-split": "\f41f", + "hourglass-top": "\f420", + "hourglass": "\f421", + "house-door-fill": "\f422", + "house-door": "\f423", + "house-fill": "\f424", + "house": "\f425", + "hr": "\f426", + "hurricane": "\f427", + "image-alt": "\f428", + "image-fill": "\f429", + "image": "\f42a", + "images": "\f42b", + "inbox-fill": "\f42c", + "inbox": "\f42d", + "inboxes-fill": "\f42e", + "inboxes": "\f42f", + "info-circle-fill": "\f430", + "info-circle": "\f431", + "info-square-fill": "\f432", + "info-square": "\f433", + "info": "\f434", + "input-cursor-text": "\f435", + "input-cursor": "\f436", + "instagram": "\f437", + "intersect": "\f438", + "journal-album": "\f439", + "journal-arrow-down": "\f43a", + "journal-arrow-up": "\f43b", + "journal-bookmark-fill": "\f43c", + "journal-bookmark": "\f43d", + "journal-check": "\f43e", + "journal-code": "\f43f", + "journal-medical": "\f440", + "journal-minus": "\f441", + "journal-plus": "\f442", + "journal-richtext": "\f443", + "journal-text": "\f444", + "journal-x": "\f445", + "journal": "\f446", + "journals": "\f447", + "joystick": "\f448", + "justify-left": "\f449", + "justify-right": "\f44a", + "justify": "\f44b", + "kanban-fill": "\f44c", + "kanban": "\f44d", + "key-fill": "\f44e", + "key": "\f44f", + "keyboard-fill": "\f450", + "keyboard": "\f451", + "ladder": "\f452", + "lamp-fill": "\f453", + "lamp": "\f454", + "laptop-fill": "\f455", + "laptop": "\f456", + "layer-backward": "\f457", + "layer-forward": "\f458", + "layers-fill": "\f459", + "layers-half": "\f45a", + "layers": "\f45b", + "layout-sidebar-inset-reverse": "\f45c", + "layout-sidebar-inset": "\f45d", + "layout-sidebar-reverse": "\f45e", + "layout-sidebar": "\f45f", + "layout-split": "\f460", + "layout-text-sidebar-reverse": "\f461", + "layout-text-sidebar": "\f462", + "layout-text-window-reverse": "\f463", + "layout-text-window": "\f464", + "layout-three-columns": "\f465", + "layout-wtf": "\f466", + "life-preserver": "\f467", + "lightbulb-fill": "\f468", + "lightbulb-off-fill": "\f469", + "lightbulb-off": "\f46a", + "lightbulb": "\f46b", + "lightning-charge-fill": "\f46c", + "lightning-charge": "\f46d", + "lightning-fill": "\f46e", + "lightning": "\f46f", + "link-45deg": "\f470", + "link": "\f471", + "linkedin": "\f472", + "list-check": "\f473", + "list-nested": "\f474", + "list-ol": "\f475", + "list-stars": "\f476", + "list-task": "\f477", + "list-ul": "\f478", + "list": "\f479", + "lock-fill": "\f47a", + "lock": "\f47b", + "mailbox": "\f47c", + "mailbox2": "\f47d", + "map-fill": "\f47e", + "map": "\f47f", + "markdown-fill": "\f480", + "markdown": "\f481", + "mask": "\f482", + "megaphone-fill": "\f483", + "megaphone": "\f484", + "menu-app-fill": "\f485", + "menu-app": "\f486", + "menu-button-fill": "\f487", + "menu-button-wide-fill": "\f488", + "menu-button-wide": "\f489", + "menu-button": "\f48a", + "menu-down": "\f48b", + "menu-up": "\f48c", + "mic-fill": "\f48d", + "mic-mute-fill": "\f48e", + "mic-mute": "\f48f", + "mic": "\f490", + "minecart-loaded": "\f491", + "minecart": "\f492", + "moisture": "\f493", + "moon-fill": "\f494", + "moon-stars-fill": "\f495", + "moon-stars": "\f496", + "moon": "\f497", + "mouse-fill": "\f498", + "mouse": "\f499", + "mouse2-fill": "\f49a", + "mouse2": "\f49b", + "mouse3-fill": "\f49c", + "mouse3": "\f49d", + "music-note-beamed": "\f49e", + "music-note-list": "\f49f", + "music-note": "\f4a0", + "music-player-fill": "\f4a1", + "music-player": "\f4a2", + "newspaper": "\f4a3", + "node-minus-fill": "\f4a4", + "node-minus": "\f4a5", + "node-plus-fill": "\f4a6", + "node-plus": "\f4a7", + "nut-fill": "\f4a8", + "nut": "\f4a9", + "octagon-fill": "\f4aa", + "octagon-half": "\f4ab", + "octagon": "\f4ac", + "option": "\f4ad", + "outlet": "\f4ae", + "paint-bucket": "\f4af", + "palette-fill": "\f4b0", + "palette": "\f4b1", + "palette2": "\f4b2", + "paperclip": "\f4b3", + "paragraph": "\f4b4", + "patch-check-fill": "\f4b5", + "patch-check": "\f4b6", + "patch-exclamation-fill": "\f4b7", + "patch-exclamation": "\f4b8", + "patch-minus-fill": "\f4b9", + "patch-minus": "\f4ba", + "patch-plus-fill": "\f4bb", + "patch-plus": "\f4bc", + "patch-question-fill": "\f4bd", + "patch-question": "\f4be", + "pause-btn-fill": "\f4bf", + "pause-btn": "\f4c0", + "pause-circle-fill": "\f4c1", + "pause-circle": "\f4c2", + "pause-fill": "\f4c3", + "pause": "\f4c4", + "peace-fill": "\f4c5", + "peace": "\f4c6", + "pen-fill": "\f4c7", + "pen": "\f4c8", + "pencil-fill": "\f4c9", + "pencil-square": "\f4ca", + "pencil": "\f4cb", + "pentagon-fill": "\f4cc", + "pentagon-half": "\f4cd", + "pentagon": "\f4ce", + "people-fill": "\f4cf", + "people": "\f4d0", + "percent": "\f4d1", + "person-badge-fill": "\f4d2", + "person-badge": "\f4d3", + "person-bounding-box": "\f4d4", + "person-check-fill": "\f4d5", + "person-check": "\f4d6", + "person-circle": "\f4d7", + "person-dash-fill": "\f4d8", + "person-dash": "\f4d9", + "person-fill": "\f4da", + "person-lines-fill": "\f4db", + "person-plus-fill": "\f4dc", + "person-plus": "\f4dd", + "person-square": "\f4de", + "person-x-fill": "\f4df", + "person-x": "\f4e0", + "person": "\f4e1", + "phone-fill": "\f4e2", + "phone-landscape-fill": "\f4e3", + "phone-landscape": "\f4e4", + "phone-vibrate-fill": "\f4e5", + "phone-vibrate": "\f4e6", + "phone": "\f4e7", + "pie-chart-fill": "\f4e8", + "pie-chart": "\f4e9", + "pin-angle-fill": "\f4ea", + "pin-angle": "\f4eb", + "pin-fill": "\f4ec", + "pin": "\f4ed", + "pip-fill": "\f4ee", + "pip": "\f4ef", + "play-btn-fill": "\f4f0", + "play-btn": "\f4f1", + "play-circle-fill": "\f4f2", + "play-circle": "\f4f3", + "play-fill": "\f4f4", + "play": "\f4f5", + "plug-fill": "\f4f6", + "plug": "\f4f7", + "plus-circle-dotted": "\f4f8", + "plus-circle-fill": "\f4f9", + "plus-circle": "\f4fa", + "plus-square-dotted": "\f4fb", + "plus-square-fill": "\f4fc", + "plus-square": "\f4fd", + "plus": "\f4fe", + "power": "\f4ff", + "printer-fill": "\f500", + "printer": "\f501", + "puzzle-fill": "\f502", + "puzzle": "\f503", + "question-circle-fill": "\f504", + "question-circle": "\f505", + "question-diamond-fill": "\f506", + "question-diamond": "\f507", + "question-octagon-fill": "\f508", + "question-octagon": "\f509", + "question-square-fill": "\f50a", + "question-square": "\f50b", + "question": "\f50c", + "rainbow": "\f50d", + "receipt-cutoff": "\f50e", + "receipt": "\f50f", + "reception-0": "\f510", + "reception-1": "\f511", + "reception-2": "\f512", + "reception-3": "\f513", + "reception-4": "\f514", + "record-btn-fill": "\f515", + "record-btn": "\f516", + "record-circle-fill": "\f517", + "record-circle": "\f518", + "record-fill": "\f519", + "record": "\f51a", + "record2-fill": "\f51b", + "record2": "\f51c", + "reply-all-fill": "\f51d", + "reply-all": "\f51e", + "reply-fill": "\f51f", + "reply": "\f520", + "rss-fill": "\f521", + "rss": "\f522", + "rulers": "\f523", + "save-fill": "\f524", + "save": "\f525", + "save2-fill": "\f526", + "save2": "\f527", + "scissors": "\f528", + "screwdriver": "\f529", + "search": "\f52a", + "segmented-nav": "\f52b", + "server": "\f52c", + "share-fill": "\f52d", + "share": "\f52e", + "shield-check": "\f52f", + "shield-exclamation": "\f530", + "shield-fill-check": "\f531", + "shield-fill-exclamation": "\f532", + "shield-fill-minus": "\f533", + "shield-fill-plus": "\f534", + "shield-fill-x": "\f535", + "shield-fill": "\f536", + "shield-lock-fill": "\f537", + "shield-lock": "\f538", + "shield-minus": "\f539", + "shield-plus": "\f53a", + "shield-shaded": "\f53b", + "shield-slash-fill": "\f53c", + "shield-slash": "\f53d", + "shield-x": "\f53e", + "shield": "\f53f", + "shift-fill": "\f540", + "shift": "\f541", + "shop-window": "\f542", + "shop": "\f543", + "shuffle": "\f544", + "signpost-2-fill": "\f545", + "signpost-2": "\f546", + "signpost-fill": "\f547", + "signpost-split-fill": "\f548", + "signpost-split": "\f549", + "signpost": "\f54a", + "sim-fill": "\f54b", + "sim": "\f54c", + "skip-backward-btn-fill": "\f54d", + "skip-backward-btn": "\f54e", + "skip-backward-circle-fill": "\f54f", + "skip-backward-circle": "\f550", + "skip-backward-fill": "\f551", + "skip-backward": "\f552", + "skip-end-btn-fill": "\f553", + "skip-end-btn": "\f554", + "skip-end-circle-fill": "\f555", + "skip-end-circle": "\f556", + "skip-end-fill": "\f557", + "skip-end": "\f558", + "skip-forward-btn-fill": "\f559", + "skip-forward-btn": "\f55a", + "skip-forward-circle-fill": "\f55b", + "skip-forward-circle": "\f55c", + "skip-forward-fill": "\f55d", + "skip-forward": "\f55e", + "skip-start-btn-fill": "\f55f", + "skip-start-btn": "\f560", + "skip-start-circle-fill": "\f561", + "skip-start-circle": "\f562", + "skip-start-fill": "\f563", + "skip-start": "\f564", + "slack": "\f565", + "slash-circle-fill": "\f566", + "slash-circle": "\f567", + "slash-square-fill": "\f568", + "slash-square": "\f569", + "slash": "\f56a", + "sliders": "\f56b", + "smartwatch": "\f56c", + "snow": "\f56d", + "snow2": "\f56e", + "snow3": "\f56f", + "sort-alpha-down-alt": "\f570", + "sort-alpha-down": "\f571", + "sort-alpha-up-alt": "\f572", + "sort-alpha-up": "\f573", + "sort-down-alt": "\f574", + "sort-down": "\f575", + "sort-numeric-down-alt": "\f576", + "sort-numeric-down": "\f577", + "sort-numeric-up-alt": "\f578", + "sort-numeric-up": "\f579", + "sort-up-alt": "\f57a", + "sort-up": "\f57b", + "soundwave": "\f57c", + "speaker-fill": "\f57d", + "speaker": "\f57e", + "speedometer": "\f57f", + "speedometer2": "\f580", + "spellcheck": "\f581", + "square-fill": "\f582", + "square-half": "\f583", + "square": "\f584", + "stack": "\f585", + "star-fill": "\f586", + "star-half": "\f587", + "star": "\f588", + "stars": "\f589", + "stickies-fill": "\f58a", + "stickies": "\f58b", + "sticky-fill": "\f58c", + "sticky": "\f58d", + "stop-btn-fill": "\f58e", + "stop-btn": "\f58f", + "stop-circle-fill": "\f590", + "stop-circle": "\f591", + "stop-fill": "\f592", + "stop": "\f593", + "stoplights-fill": "\f594", + "stoplights": "\f595", + "stopwatch-fill": "\f596", + "stopwatch": "\f597", + "subtract": "\f598", + "suit-club-fill": "\f599", + "suit-club": "\f59a", + "suit-diamond-fill": "\f59b", + "suit-diamond": "\f59c", + "suit-heart-fill": "\f59d", + "suit-heart": "\f59e", + "suit-spade-fill": "\f59f", + "suit-spade": "\f5a0", + "sun-fill": "\f5a1", + "sun": "\f5a2", + "sunglasses": "\f5a3", + "sunrise-fill": "\f5a4", + "sunrise": "\f5a5", + "sunset-fill": "\f5a6", + "sunset": "\f5a7", + "symmetry-horizontal": "\f5a8", + "symmetry-vertical": "\f5a9", + "table": "\f5aa", + "tablet-fill": "\f5ab", + "tablet-landscape-fill": "\f5ac", + "tablet-landscape": "\f5ad", + "tablet": "\f5ae", + "tag-fill": "\f5af", + "tag": "\f5b0", + "tags-fill": "\f5b1", + "tags": "\f5b2", + "telegram": "\f5b3", + "telephone-fill": "\f5b4", + "telephone-forward-fill": "\f5b5", + "telephone-forward": "\f5b6", + "telephone-inbound-fill": "\f5b7", + "telephone-inbound": "\f5b8", + "telephone-minus-fill": "\f5b9", + "telephone-minus": "\f5ba", + "telephone-outbound-fill": "\f5bb", + "telephone-outbound": "\f5bc", + "telephone-plus-fill": "\f5bd", + "telephone-plus": "\f5be", + "telephone-x-fill": "\f5bf", + "telephone-x": "\f5c0", + "telephone": "\f5c1", + "terminal-fill": "\f5c2", + "terminal": "\f5c3", + "text-center": "\f5c4", + "text-indent-left": "\f5c5", + "text-indent-right": "\f5c6", + "text-left": "\f5c7", + "text-paragraph": "\f5c8", + "text-right": "\f5c9", + "textarea-resize": "\f5ca", + "textarea-t": "\f5cb", + "textarea": "\f5cc", + "thermometer-half": "\f5cd", + "thermometer-high": "\f5ce", + "thermometer-low": "\f5cf", + "thermometer-snow": "\f5d0", + "thermometer-sun": "\f5d1", + "thermometer": "\f5d2", + "three-dots-vertical": "\f5d3", + "three-dots": "\f5d4", + "toggle-off": "\f5d5", + "toggle-on": "\f5d6", + "toggle2-off": "\f5d7", + "toggle2-on": "\f5d8", + "toggles": "\f5d9", + "toggles2": "\f5da", + "tools": "\f5db", + "tornado": "\f5dc", + "trash-fill": "\f5dd", + "trash": "\f5de", + "trash2-fill": "\f5df", + "trash2": "\f5e0", + "tree-fill": "\f5e1", + "tree": "\f5e2", + "triangle-fill": "\f5e3", + "triangle-half": "\f5e4", + "triangle": "\f5e5", + "trophy-fill": "\f5e6", + "trophy": "\f5e7", + "tropical-storm": "\f5e8", + "truck-flatbed": "\f5e9", + "truck": "\f5ea", + "tsunami": "\f5eb", + "tv-fill": "\f5ec", + "tv": "\f5ed", + "twitch": "\f5ee", + "twitter": "\f5ef", + "type-bold": "\f5f0", + "type-h1": "\f5f1", + "type-h2": "\f5f2", + "type-h3": "\f5f3", + "type-italic": "\f5f4", + "type-strikethrough": "\f5f5", + "type-underline": "\f5f6", + "type": "\f5f7", + "ui-checks-grid": "\f5f8", + "ui-checks": "\f5f9", + "ui-radios-grid": "\f5fa", + "ui-radios": "\f5fb", + "umbrella-fill": "\f5fc", + "umbrella": "\f5fd", + "union": "\f5fe", + "unlock-fill": "\f5ff", + "unlock": "\f600", + "upc-scan": "\f601", + "upc": "\f602", + "upload": "\f603", + "vector-pen": "\f604", + "view-list": "\f605", + "view-stacked": "\f606", + "vinyl-fill": "\f607", + "vinyl": "\f608", + "voicemail": "\f609", + "volume-down-fill": "\f60a", + "volume-down": "\f60b", + "volume-mute-fill": "\f60c", + "volume-mute": "\f60d", + "volume-off-fill": "\f60e", + "volume-off": "\f60f", + "volume-up-fill": "\f610", + "volume-up": "\f611", + "vr": "\f612", + "wallet-fill": "\f613", + "wallet": "\f614", + "wallet2": "\f615", + "watch": "\f616", + "water": "\f617", + "whatsapp": "\f618", + "wifi-1": "\f619", + "wifi-2": "\f61a", + "wifi-off": "\f61b", + "wifi": "\f61c", + "wind": "\f61d", + "window-dock": "\f61e", + "window-sidebar": "\f61f", + "window": "\f620", + "wrench": "\f621", + "x-circle-fill": "\f622", + "x-circle": "\f623", + "x-diamond-fill": "\f624", + "x-diamond": "\f625", + "x-octagon-fill": "\f626", + "x-octagon": "\f627", + "x-square-fill": "\f628", + "x-square": "\f629", + "x": "\f62a", + "youtube": "\f62b", + "zoom-in": "\f62c", + "zoom-out": "\f62d", + "bank": "\f62e", + "bank2": "\f62f", + "bell-slash-fill": "\f630", + "bell-slash": "\f631", + "cash-coin": "\f632", + "check-lg": "\f633", + "coin": "\f634", + "currency-bitcoin": "\f635", + "currency-dollar": "\f636", + "currency-euro": "\f637", + "currency-exchange": "\f638", + "currency-pound": "\f639", + "currency-yen": "\f63a", + "dash-lg": "\f63b", + "exclamation-lg": "\f63c", + "file-earmark-pdf-fill": "\f63d", + "file-earmark-pdf": "\f63e", + "file-pdf-fill": "\f63f", + "file-pdf": "\f640", + "gender-ambiguous": "\f641", + "gender-female": "\f642", + "gender-male": "\f643", + "gender-trans": "\f644", + "headset-vr": "\f645", + "info-lg": "\f646", + "mastodon": "\f647", + "messenger": "\f648", + "piggy-bank-fill": "\f649", + "piggy-bank": "\f64a", + "pin-map-fill": "\f64b", + "pin-map": "\f64c", + "plus-lg": "\f64d", + "question-lg": "\f64e", + "recycle": "\f64f", + "reddit": "\f650", + "safe-fill": "\f651", + "safe2-fill": "\f652", + "safe2": "\f653", + "sd-card-fill": "\f654", + "sd-card": "\f655", + "skype": "\f656", + "slash-lg": "\f657", + "translate": "\f658", + "x-lg": "\f659", + "safe": "\f65a", + "apple": "\f65b", + "microsoft": "\f65d", + "windows": "\f65e", + "behance": "\f65c", + "dribbble": "\f65f", + "line": "\f660", + "medium": "\f661", + "paypal": "\f662", + "pinterest": "\f663", + "signal": "\f664", + "snapchat": "\f665", + "spotify": "\f666", + "stack-overflow": "\f667", + "strava": "\f668", + "wordpress": "\f669", + "vimeo": "\f66a", + "activity": "\f66b", + "easel2-fill": "\f66c", + "easel2": "\f66d", + "easel3-fill": "\f66e", + "easel3": "\f66f", + "fan": "\f670", + "fingerprint": "\f671", + "graph-down-arrow": "\f672", + "graph-up-arrow": "\f673", + "hypnotize": "\f674", + "magic": "\f675", + "person-rolodex": "\f676", + "person-video": "\f677", + "person-video2": "\f678", + "person-video3": "\f679", + "person-workspace": "\f67a", + "radioactive": "\f67b", + "webcam-fill": "\f67c", + "webcam": "\f67d", + "yin-yang": "\f67e", + "bandaid-fill": "\f680", + "bandaid": "\f681", + "bluetooth": "\f682", + "body-text": "\f683", + "boombox": "\f684", + "boxes": "\f685", + "dpad-fill": "\f686", + "dpad": "\f687", + "ear-fill": "\f688", + "ear": "\f689", + "envelope-check-fill": "\f68b", + "envelope-check": "\f68c", + "envelope-dash-fill": "\f68e", + "envelope-dash": "\f68f", + "envelope-exclamation-fill": "\f691", + "envelope-exclamation": "\f692", + "envelope-plus-fill": "\f693", + "envelope-plus": "\f694", + "envelope-slash-fill": "\f696", + "envelope-slash": "\f697", + "envelope-x-fill": "\f699", + "envelope-x": "\f69a", + "explicit-fill": "\f69b", + "explicit": "\f69c", + "git": "\f69d", + "infinity": "\f69e", + "list-columns-reverse": "\f69f", + "list-columns": "\f6a0", + "meta": "\f6a1", + "nintendo-switch": "\f6a4", + "pc-display-horizontal": "\f6a5", + "pc-display": "\f6a6", + "pc-horizontal": "\f6a7", + "pc": "\f6a8", + "playstation": "\f6a9", + "plus-slash-minus": "\f6aa", + "projector-fill": "\f6ab", + "projector": "\f6ac", + "qr-code-scan": "\f6ad", + "qr-code": "\f6ae", + "quora": "\f6af", + "quote": "\f6b0", + "robot": "\f6b1", + "send-check-fill": "\f6b2", + "send-check": "\f6b3", + "send-dash-fill": "\f6b4", + "send-dash": "\f6b5", + "send-exclamation-fill": "\f6b7", + "send-exclamation": "\f6b8", + "send-fill": "\f6b9", + "send-plus-fill": "\f6ba", + "send-plus": "\f6bb", + "send-slash-fill": "\f6bc", + "send-slash": "\f6bd", + "send-x-fill": "\f6be", + "send-x": "\f6bf", + "send": "\f6c0", + "steam": "\f6c1", + "terminal-dash": "\f6c3", + "terminal-plus": "\f6c4", + "terminal-split": "\f6c5", + "ticket-detailed-fill": "\f6c6", + "ticket-detailed": "\f6c7", + "ticket-fill": "\f6c8", + "ticket-perforated-fill": "\f6c9", + "ticket-perforated": "\f6ca", + "ticket": "\f6cb", + "tiktok": "\f6cc", + "window-dash": "\f6cd", + "window-desktop": "\f6ce", + "window-fullscreen": "\f6cf", + "window-plus": "\f6d0", + "window-split": "\f6d1", + "window-stack": "\f6d2", + "window-x": "\f6d3", + "xbox": "\f6d4", + "ethernet": "\f6d5", + "hdmi-fill": "\f6d6", + "hdmi": "\f6d7", + "usb-c-fill": "\f6d8", + "usb-c": "\f6d9", + "usb-fill": "\f6da", + "usb-plug-fill": "\f6db", + "usb-plug": "\f6dc", + "usb-symbol": "\f6dd", + "usb": "\f6de", + "boombox-fill": "\f6df", + "displayport": "\f6e1", + "gpu-card": "\f6e2", + "memory": "\f6e3", + "modem-fill": "\f6e4", + "modem": "\f6e5", + "motherboard-fill": "\f6e6", + "motherboard": "\f6e7", + "optical-audio-fill": "\f6e8", + "optical-audio": "\f6e9", + "pci-card": "\f6ea", + "router-fill": "\f6eb", + "router": "\f6ec", + "thunderbolt-fill": "\f6ef", + "thunderbolt": "\f6f0", + "usb-drive-fill": "\f6f1", + "usb-drive": "\f6f2", + "usb-micro-fill": "\f6f3", + "usb-micro": "\f6f4", + "usb-mini-fill": "\f6f5", + "usb-mini": "\f6f6", + "cloud-haze2": "\f6f7", + "device-hdd-fill": "\f6f8", + "device-hdd": "\f6f9", + "device-ssd-fill": "\f6fa", + "device-ssd": "\f6fb", + "displayport-fill": "\f6fc", + "mortarboard-fill": "\f6fd", + "mortarboard": "\f6fe", + "terminal-x": "\f6ff", + "arrow-through-heart-fill": "\f700", + "arrow-through-heart": "\f701", + "badge-sd-fill": "\f702", + "badge-sd": "\f703", + "bag-heart-fill": "\f704", + "bag-heart": "\f705", + "balloon-fill": "\f706", + "balloon-heart-fill": "\f707", + "balloon-heart": "\f708", + "balloon": "\f709", + "box2-fill": "\f70a", + "box2-heart-fill": "\f70b", + "box2-heart": "\f70c", + "box2": "\f70d", + "braces-asterisk": "\f70e", + "calendar-heart-fill": "\f70f", + "calendar-heart": "\f710", + "calendar2-heart-fill": "\f711", + "calendar2-heart": "\f712", + "chat-heart-fill": "\f713", + "chat-heart": "\f714", + "chat-left-heart-fill": "\f715", + "chat-left-heart": "\f716", + "chat-right-heart-fill": "\f717", + "chat-right-heart": "\f718", + "chat-square-heart-fill": "\f719", + "chat-square-heart": "\f71a", + "clipboard-check-fill": "\f71b", + "clipboard-data-fill": "\f71c", + "clipboard-fill": "\f71d", + "clipboard-heart-fill": "\f71e", + "clipboard-heart": "\f71f", + "clipboard-minus-fill": "\f720", + "clipboard-plus-fill": "\f721", + "clipboard-pulse": "\f722", + "clipboard-x-fill": "\f723", + "clipboard2-check-fill": "\f724", + "clipboard2-check": "\f725", + "clipboard2-data-fill": "\f726", + "clipboard2-data": "\f727", + "clipboard2-fill": "\f728", + "clipboard2-heart-fill": "\f729", + "clipboard2-heart": "\f72a", + "clipboard2-minus-fill": "\f72b", + "clipboard2-minus": "\f72c", + "clipboard2-plus-fill": "\f72d", + "clipboard2-plus": "\f72e", + "clipboard2-pulse-fill": "\f72f", + "clipboard2-pulse": "\f730", + "clipboard2-x-fill": "\f731", + "clipboard2-x": "\f732", + "clipboard2": "\f733", + "emoji-kiss-fill": "\f734", + "emoji-kiss": "\f735", + "envelope-heart-fill": "\f736", + "envelope-heart": "\f737", + "envelope-open-heart-fill": "\f738", + "envelope-open-heart": "\f739", + "envelope-paper-fill": "\f73a", + "envelope-paper-heart-fill": "\f73b", + "envelope-paper-heart": "\f73c", + "envelope-paper": "\f73d", + "filetype-aac": "\f73e", + "filetype-ai": "\f73f", + "filetype-bmp": "\f740", + "filetype-cs": "\f741", + "filetype-css": "\f742", + "filetype-csv": "\f743", + "filetype-doc": "\f744", + "filetype-docx": "\f745", + "filetype-exe": "\f746", + "filetype-gif": "\f747", + "filetype-heic": "\f748", + "filetype-html": "\f749", + "filetype-java": "\f74a", + "filetype-jpg": "\f74b", + "filetype-js": "\f74c", + "filetype-jsx": "\f74d", + "filetype-key": "\f74e", + "filetype-m4p": "\f74f", + "filetype-md": "\f750", + "filetype-mdx": "\f751", + "filetype-mov": "\f752", + "filetype-mp3": "\f753", + "filetype-mp4": "\f754", + "filetype-otf": "\f755", + "filetype-pdf": "\f756", + "filetype-php": "\f757", + "filetype-png": "\f758", + "filetype-ppt": "\f75a", + "filetype-psd": "\f75b", + "filetype-py": "\f75c", + "filetype-raw": "\f75d", + "filetype-rb": "\f75e", + "filetype-sass": "\f75f", + "filetype-scss": "\f760", + "filetype-sh": "\f761", + "filetype-svg": "\f762", + "filetype-tiff": "\f763", + "filetype-tsx": "\f764", + "filetype-ttf": "\f765", + "filetype-txt": "\f766", + "filetype-wav": "\f767", + "filetype-woff": "\f768", + "filetype-xls": "\f76a", + "filetype-xml": "\f76b", + "filetype-yml": "\f76c", + "heart-arrow": "\f76d", + "heart-pulse-fill": "\f76e", + "heart-pulse": "\f76f", + "heartbreak-fill": "\f770", + "heartbreak": "\f771", + "hearts": "\f772", + "hospital-fill": "\f773", + "hospital": "\f774", + "house-heart-fill": "\f775", + "house-heart": "\f776", + "incognito": "\f777", + "magnet-fill": "\f778", + "magnet": "\f779", + "person-heart": "\f77a", + "person-hearts": "\f77b", + "phone-flip": "\f77c", + "plugin": "\f77d", + "postage-fill": "\f77e", + "postage-heart-fill": "\f77f", + "postage-heart": "\f780", + "postage": "\f781", + "postcard-fill": "\f782", + "postcard-heart-fill": "\f783", + "postcard-heart": "\f784", + "postcard": "\f785", + "search-heart-fill": "\f786", + "search-heart": "\f787", + "sliders2-vertical": "\f788", + "sliders2": "\f789", + "trash3-fill": "\f78a", + "trash3": "\f78b", + "valentine": "\f78c", + "valentine2": "\f78d", + "wrench-adjustable-circle-fill": "\f78e", + "wrench-adjustable-circle": "\f78f", + "wrench-adjustable": "\f790", + "filetype-json": "\f791", + "filetype-pptx": "\f792", + "filetype-xlsx": "\f793", + "1-circle-fill": "\f796", + "1-circle": "\f797", + "1-square-fill": "\f798", + "1-square": "\f799", + "2-circle-fill": "\f79c", + "2-circle": "\f79d", + "2-square-fill": "\f79e", + "2-square": "\f79f", + "3-circle-fill": "\f7a2", + "3-circle": "\f7a3", + "3-square-fill": "\f7a4", + "3-square": "\f7a5", + "4-circle-fill": "\f7a8", + "4-circle": "\f7a9", + "4-square-fill": "\f7aa", + "4-square": "\f7ab", + "5-circle-fill": "\f7ae", + "5-circle": "\f7af", + "5-square-fill": "\f7b0", + "5-square": "\f7b1", + "6-circle-fill": "\f7b4", + "6-circle": "\f7b5", + "6-square-fill": "\f7b6", + "6-square": "\f7b7", + "7-circle-fill": "\f7ba", + "7-circle": "\f7bb", + "7-square-fill": "\f7bc", + "7-square": "\f7bd", + "8-circle-fill": "\f7c0", + "8-circle": "\f7c1", + "8-square-fill": "\f7c2", + "8-square": "\f7c3", + "9-circle-fill": "\f7c6", + "9-circle": "\f7c7", + "9-square-fill": "\f7c8", + "9-square": "\f7c9", + "airplane-engines-fill": "\f7ca", + "airplane-engines": "\f7cb", + "airplane-fill": "\f7cc", + "airplane": "\f7cd", + "alexa": "\f7ce", + "alipay": "\f7cf", + "android": "\f7d0", + "android2": "\f7d1", + "box-fill": "\f7d2", + "box-seam-fill": "\f7d3", + "browser-chrome": "\f7d4", + "browser-edge": "\f7d5", + "browser-firefox": "\f7d6", + "browser-safari": "\f7d7", + "c-circle-fill": "\f7da", + "c-circle": "\f7db", + "c-square-fill": "\f7dc", + "c-square": "\f7dd", + "capsule-pill": "\f7de", + "capsule": "\f7df", + "car-front-fill": "\f7e0", + "car-front": "\f7e1", + "cassette-fill": "\f7e2", + "cassette": "\f7e3", + "cc-circle-fill": "\f7e6", + "cc-circle": "\f7e7", + "cc-square-fill": "\f7e8", + "cc-square": "\f7e9", + "cup-hot-fill": "\f7ea", + "cup-hot": "\f7eb", + "currency-rupee": "\f7ec", + "dropbox": "\f7ed", + "escape": "\f7ee", + "fast-forward-btn-fill": "\f7ef", + "fast-forward-btn": "\f7f0", + "fast-forward-circle-fill": "\f7f1", + "fast-forward-circle": "\f7f2", + "fast-forward-fill": "\f7f3", + "fast-forward": "\f7f4", + "filetype-sql": "\f7f5", + "fire": "\f7f6", + "google-play": "\f7f7", + "h-circle-fill": "\f7fa", + "h-circle": "\f7fb", + "h-square-fill": "\f7fc", + "h-square": "\f7fd", + "indent": "\f7fe", + "lungs-fill": "\f7ff", + "lungs": "\f800", + "microsoft-teams": "\f801", + "p-circle-fill": "\f804", + "p-circle": "\f805", + "p-square-fill": "\f806", + "p-square": "\f807", + "pass-fill": "\f808", + "pass": "\f809", + "prescription": "\f80a", + "prescription2": "\f80b", + "r-circle-fill": "\f80e", + "r-circle": "\f80f", + "r-square-fill": "\f810", + "r-square": "\f811", + "repeat-1": "\f812", + "repeat": "\f813", + "rewind-btn-fill": "\f814", + "rewind-btn": "\f815", + "rewind-circle-fill": "\f816", + "rewind-circle": "\f817", + "rewind-fill": "\f818", + "rewind": "\f819", + "train-freight-front-fill": "\f81a", + "train-freight-front": "\f81b", + "train-front-fill": "\f81c", + "train-front": "\f81d", + "train-lightrail-front-fill": "\f81e", + "train-lightrail-front": "\f81f", + "truck-front-fill": "\f820", + "truck-front": "\f821", + "ubuntu": "\f822", + "unindent": "\f823", + "unity": "\f824", + "universal-access-circle": "\f825", + "universal-access": "\f826", + "virus": "\f827", + "virus2": "\f828", + "wechat": "\f829", + "yelp": "\f82a", + "sign-stop-fill": "\f82b", + "sign-stop-lights-fill": "\f82c", + "sign-stop-lights": "\f82d", + "sign-stop": "\f82e", + "sign-turn-left-fill": "\f82f", + "sign-turn-left": "\f830", + "sign-turn-right-fill": "\f831", + "sign-turn-right": "\f832", + "sign-turn-slight-left-fill": "\f833", + "sign-turn-slight-left": "\f834", + "sign-turn-slight-right-fill": "\f835", + "sign-turn-slight-right": "\f836", + "sign-yield-fill": "\f837", + "sign-yield": "\f838", + "ev-station-fill": "\f839", + "ev-station": "\f83a", + "fuel-pump-diesel-fill": "\f83b", + "fuel-pump-diesel": "\f83c", + "fuel-pump-fill": "\f83d", + "fuel-pump": "\f83e", + "0-circle-fill": "\f83f", + "0-circle": "\f840", + "0-square-fill": "\f841", + "0-square": "\f842", + "rocket-fill": "\f843", + "rocket-takeoff-fill": "\f844", + "rocket-takeoff": "\f845", + "rocket": "\f846", + "stripe": "\f847", + "subscript": "\f848", + "superscript": "\f849", + "trello": "\f84a", + "envelope-at-fill": "\f84b", + "envelope-at": "\f84c", + "regex": "\f84d", + "text-wrap": "\f84e", + "sign-dead-end-fill": "\f84f", + "sign-dead-end": "\f850", + "sign-do-not-enter-fill": "\f851", + "sign-do-not-enter": "\f852", + "sign-intersection-fill": "\f853", + "sign-intersection-side-fill": "\f854", + "sign-intersection-side": "\f855", + "sign-intersection-t-fill": "\f856", + "sign-intersection-t": "\f857", + "sign-intersection-y-fill": "\f858", + "sign-intersection-y": "\f859", + "sign-intersection": "\f85a", + "sign-merge-left-fill": "\f85b", + "sign-merge-left": "\f85c", + "sign-merge-right-fill": "\f85d", + "sign-merge-right": "\f85e", + "sign-no-left-turn-fill": "\f85f", + "sign-no-left-turn": "\f860", + "sign-no-parking-fill": "\f861", + "sign-no-parking": "\f862", + "sign-no-right-turn-fill": "\f863", + "sign-no-right-turn": "\f864", + "sign-railroad-fill": "\f865", + "sign-railroad": "\f866", + "building-add": "\f867", + "building-check": "\f868", + "building-dash": "\f869", + "building-down": "\f86a", + "building-exclamation": "\f86b", + "building-fill-add": "\f86c", + "building-fill-check": "\f86d", + "building-fill-dash": "\f86e", + "building-fill-down": "\f86f", + "building-fill-exclamation": "\f870", + "building-fill-gear": "\f871", + "building-fill-lock": "\f872", + "building-fill-slash": "\f873", + "building-fill-up": "\f874", + "building-fill-x": "\f875", + "building-fill": "\f876", + "building-gear": "\f877", + "building-lock": "\f878", + "building-slash": "\f879", + "building-up": "\f87a", + "building-x": "\f87b", + "buildings-fill": "\f87c", + "buildings": "\f87d", + "bus-front-fill": "\f87e", + "bus-front": "\f87f", + "ev-front-fill": "\f880", + "ev-front": "\f881", + "globe-americas": "\f882", + "globe-asia-australia": "\f883", + "globe-central-south-asia": "\f884", + "globe-europe-africa": "\f885", + "house-add-fill": "\f886", + "house-add": "\f887", + "house-check-fill": "\f888", + "house-check": "\f889", + "house-dash-fill": "\f88a", + "house-dash": "\f88b", + "house-down-fill": "\f88c", + "house-down": "\f88d", + "house-exclamation-fill": "\f88e", + "house-exclamation": "\f88f", + "house-gear-fill": "\f890", + "house-gear": "\f891", + "house-lock-fill": "\f892", + "house-lock": "\f893", + "house-slash-fill": "\f894", + "house-slash": "\f895", + "house-up-fill": "\f896", + "house-up": "\f897", + "house-x-fill": "\f898", + "house-x": "\f899", + "person-add": "\f89a", + "person-down": "\f89b", + "person-exclamation": "\f89c", + "person-fill-add": "\f89d", + "person-fill-check": "\f89e", + "person-fill-dash": "\f89f", + "person-fill-down": "\f8a0", + "person-fill-exclamation": "\f8a1", + "person-fill-gear": "\f8a2", + "person-fill-lock": "\f8a3", + "person-fill-slash": "\f8a4", + "person-fill-up": "\f8a5", + "person-fill-x": "\f8a6", + "person-gear": "\f8a7", + "person-lock": "\f8a8", + "person-slash": "\f8a9", + "person-up": "\f8aa", + "scooter": "\f8ab", + "taxi-front-fill": "\f8ac", + "taxi-front": "\f8ad", + "amd": "\f8ae", + "database-add": "\f8af", + "database-check": "\f8b0", + "database-dash": "\f8b1", + "database-down": "\f8b2", + "database-exclamation": "\f8b3", + "database-fill-add": "\f8b4", + "database-fill-check": "\f8b5", + "database-fill-dash": "\f8b6", + "database-fill-down": "\f8b7", + "database-fill-exclamation": "\f8b8", + "database-fill-gear": "\f8b9", + "database-fill-lock": "\f8ba", + "database-fill-slash": "\f8bb", + "database-fill-up": "\f8bc", + "database-fill-x": "\f8bd", + "database-fill": "\f8be", + "database-gear": "\f8bf", + "database-lock": "\f8c0", + "database-slash": "\f8c1", + "database-up": "\f8c2", + "database-x": "\f8c3", + "database": "\f8c4", + "houses-fill": "\f8c5", + "houses": "\f8c6", + "nvidia": "\f8c7", + "person-vcard-fill": "\f8c8", + "person-vcard": "\f8c9", + "sina-weibo": "\f8ca", + "tencent-qq": "\f8cb", + "wikipedia": "\f8cc", + "alphabet-uppercase": "\f2a5", + "alphabet": "\f68a", + "amazon": "\f68d", + "arrows-collapse-vertical": "\f690", + "arrows-expand-vertical": "\f695", + "arrows-vertical": "\f698", + "arrows": "\f6a2", + "ban-fill": "\f6a3", + "ban": "\f6b6", + "bing": "\f6c2", + "cake": "\f6e0", + "cake2": "\f6ed", + "cookie": "\f6ee", + "copy": "\f759", + "crosshair": "\f769", + "crosshair2": "\f794", + "emoji-astonished-fill": "\f795", + "emoji-astonished": "\f79a", + "emoji-grimace-fill": "\f79b", + "emoji-grimace": "\f7a0", + "emoji-grin-fill": "\f7a1", + "emoji-grin": "\f7a6", + "emoji-surprise-fill": "\f7a7", + "emoji-surprise": "\f7ac", + "emoji-tear-fill": "\f7ad", + "emoji-tear": "\f7b2", + "envelope-arrow-down-fill": "\f7b3", + "envelope-arrow-down": "\f7b8", + "envelope-arrow-up-fill": "\f7b9", + "envelope-arrow-up": "\f7be", + "feather": "\f7bf", + "feather2": "\f7c4", + "floppy-fill": "\f7c5", + "floppy": "\f7d8", + "floppy2-fill": "\f7d9", + "floppy2": "\f7e4", + "gitlab": "\f7e5", + "highlighter": "\f7f8", + "marker-tip": "\f802", + "nvme-fill": "\f803", + "nvme": "\f80c", + "opencollective": "\f80d", + "pci-card-network": "\f8cd", + "pci-card-sound": "\f8ce", + "radar": "\f8cf", + "send-arrow-down-fill": "\f8d0", + "send-arrow-down": "\f8d1", + "send-arrow-up-fill": "\f8d2", + "send-arrow-up": "\f8d3", + "sim-slash-fill": "\f8d4", + "sim-slash": "\f8d5", + "sourceforge": "\f8d6", + "substack": "\f8d7", + "threads-fill": "\f8d8", + "threads": "\f8d9", + "transparency": "\f8da", + "twitter-x": "\f8db", + "type-h4": "\f8dc", + "type-h5": "\f8dd", + "type-h6": "\f8de", + "backpack-fill": "\f8df", + "backpack": "\f8e0", + "backpack2-fill": "\f8e1", + "backpack2": "\f8e2", + "backpack3-fill": "\f8e3", + "backpack3": "\f8e4", + "backpack4-fill": "\f8e5", + "backpack4": "\f8e6", + "brilliance": "\f8e7", + "cake-fill": "\f8e8", + "cake2-fill": "\f8e9", + "duffle-fill": "\f8ea", + "duffle": "\f8eb", + "exposure": "\f8ec", + "gender-neuter": "\f8ed", + "highlights": "\f8ee", + "luggage-fill": "\f8ef", + "luggage": "\f8f0", + "mailbox-flag": "\f8f1", + "mailbox2-flag": "\f8f2", + "noise-reduction": "\f8f3", + "passport-fill": "\f8f4", + "passport": "\f8f5", + "person-arms-up": "\f8f6", + "person-raised-hand": "\f8f7", + "person-standing-dress": "\f8f8", + "person-standing": "\f8f9", + "person-walking": "\f8fa", + "person-wheelchair": "\f8fb", + "shadows": "\f8fc", + "suitcase-fill": "\f8fd", + "suitcase-lg-fill": "\f8fe", + "suitcase-lg": "\f8ff", + "suitcase": "\f900", + "suitcase2-fill": "\f901", + "suitcase2": "\f902", + "vignette": "\f903", + "bluesky": "\f7f9", + "tux": "\f904", + "beaker-fill": "\f905", + "beaker": "\f906", + "flask-fill": "\f907", + "flask-florence-fill": "\f908", + "flask-florence": "\f909", + "flask": "\f90a", + "leaf-fill": "\f90b", + "leaf": "\f90c", + "measuring-cup-fill": "\f90d", + "measuring-cup": "\f90e", + "unlock2-fill": "\f90f", + "unlock2": "\f910", + "battery-low": "\f911", + "anthropic": "\f912", + "apple-music": "\f913", + "claude": "\f914", + "openai": "\f915", + "perplexity": "\f916", + "css": "\f917", + "javascript": "\f918", + "typescript": "\f919", + "fork-knife": "\f91a", + "globe-americas-fill": "\f91b", + "globe-asia-australia-fill": "\f91c", + "globe-central-south-asia-fill": "\f91d", + "globe-europe-africa-fill": "\f91e", +); + +@each $icon, $codepoint in $bootstrap-icons-map { + .bi-#{$icon}::before { content: $codepoint; } +} diff --git a/web/vendor/bootstrap-icons/bootstrap-icons.svg b/web/vendor/bootstrap-icons/bootstrap-icons.svg new file mode 100644 index 0000000..fa7e620 --- /dev/null +++ b/web/vendor/bootstrap-icons/bootstrap-icons.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bootstrap-reboot.svg b/web/vendor/bootstrap-icons/bootstrap-reboot.svg new file mode 100644 index 0000000..8d21030 --- /dev/null +++ b/web/vendor/bootstrap-icons/bootstrap-reboot.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bootstrap.svg b/web/vendor/bootstrap-icons/bootstrap.svg new file mode 100644 index 0000000..089e31f --- /dev/null +++ b/web/vendor/bootstrap-icons/bootstrap.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-all.svg b/web/vendor/bootstrap-icons/border-all.svg new file mode 100644 index 0000000..19128f2 --- /dev/null +++ b/web/vendor/bootstrap-icons/border-all.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-bottom.svg b/web/vendor/bootstrap-icons/border-bottom.svg new file mode 100644 index 0000000..84edcca --- /dev/null +++ b/web/vendor/bootstrap-icons/border-bottom.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-center.svg b/web/vendor/bootstrap-icons/border-center.svg new file mode 100644 index 0000000..a9cf9c4 --- /dev/null +++ b/web/vendor/bootstrap-icons/border-center.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-inner.svg b/web/vendor/bootstrap-icons/border-inner.svg new file mode 100644 index 0000000..6369007 --- /dev/null +++ b/web/vendor/bootstrap-icons/border-inner.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-left.svg b/web/vendor/bootstrap-icons/border-left.svg new file mode 100644 index 0000000..483c804 --- /dev/null +++ b/web/vendor/bootstrap-icons/border-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-middle.svg b/web/vendor/bootstrap-icons/border-middle.svg new file mode 100644 index 0000000..c9de407 --- /dev/null +++ b/web/vendor/bootstrap-icons/border-middle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-outer.svg b/web/vendor/bootstrap-icons/border-outer.svg new file mode 100644 index 0000000..4791bcb --- /dev/null +++ b/web/vendor/bootstrap-icons/border-outer.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-right.svg b/web/vendor/bootstrap-icons/border-right.svg new file mode 100644 index 0000000..23e09dc --- /dev/null +++ b/web/vendor/bootstrap-icons/border-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-style.svg b/web/vendor/bootstrap-icons/border-style.svg new file mode 100644 index 0000000..cec3fef --- /dev/null +++ b/web/vendor/bootstrap-icons/border-style.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-top.svg b/web/vendor/bootstrap-icons/border-top.svg new file mode 100644 index 0000000..77189df --- /dev/null +++ b/web/vendor/bootstrap-icons/border-top.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border-width.svg b/web/vendor/bootstrap-icons/border-width.svg new file mode 100644 index 0000000..6175326 --- /dev/null +++ b/web/vendor/bootstrap-icons/border-width.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/border.svg b/web/vendor/bootstrap-icons/border.svg new file mode 100644 index 0000000..a6390f9 --- /dev/null +++ b/web/vendor/bootstrap-icons/border.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bounding-box-circles.svg b/web/vendor/bootstrap-icons/bounding-box-circles.svg new file mode 100644 index 0000000..02113ba --- /dev/null +++ b/web/vendor/bootstrap-icons/bounding-box-circles.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bounding-box.svg b/web/vendor/bootstrap-icons/bounding-box.svg new file mode 100644 index 0000000..e8be147 --- /dev/null +++ b/web/vendor/bootstrap-icons/bounding-box.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-down-left.svg b/web/vendor/bootstrap-icons/box-arrow-down-left.svg new file mode 100644 index 0000000..20ffed9 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-down-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-down-right.svg b/web/vendor/bootstrap-icons/box-arrow-down-right.svg new file mode 100644 index 0000000..33780ef --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-down-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-down.svg b/web/vendor/bootstrap-icons/box-arrow-down.svg new file mode 100644 index 0000000..bf33d51 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-in-down-left.svg b/web/vendor/bootstrap-icons/box-arrow-in-down-left.svg new file mode 100644 index 0000000..fe3c579 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-in-down-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-in-down-right.svg b/web/vendor/bootstrap-icons/box-arrow-in-down-right.svg new file mode 100644 index 0000000..07082eb --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-in-down-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-in-down.svg b/web/vendor/bootstrap-icons/box-arrow-in-down.svg new file mode 100644 index 0000000..3b185d6 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-in-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-in-left.svg b/web/vendor/bootstrap-icons/box-arrow-in-left.svg new file mode 100644 index 0000000..1e1bc9a --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-in-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-in-right.svg b/web/vendor/bootstrap-icons/box-arrow-in-right.svg new file mode 100644 index 0000000..5d78def --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-in-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-in-up-left.svg b/web/vendor/bootstrap-icons/box-arrow-in-up-left.svg new file mode 100644 index 0000000..8401c43 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-in-up-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-in-up-right.svg b/web/vendor/bootstrap-icons/box-arrow-in-up-right.svg new file mode 100644 index 0000000..8a95e00 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-in-up-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-in-up.svg b/web/vendor/bootstrap-icons/box-arrow-in-up.svg new file mode 100644 index 0000000..6197bc3 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-in-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-left.svg b/web/vendor/bootstrap-icons/box-arrow-left.svg new file mode 100644 index 0000000..5d142b4 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-right.svg b/web/vendor/bootstrap-icons/box-arrow-right.svg new file mode 100644 index 0000000..682e033 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-up-left.svg b/web/vendor/bootstrap-icons/box-arrow-up-left.svg new file mode 100644 index 0000000..7dec12d --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-up-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-up-right.svg b/web/vendor/bootstrap-icons/box-arrow-up-right.svg new file mode 100644 index 0000000..03f68d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-up-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-arrow-up.svg b/web/vendor/bootstrap-icons/box-arrow-up.svg new file mode 100644 index 0000000..8f76892 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-arrow-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-fill.svg b/web/vendor/bootstrap-icons/box-fill.svg new file mode 100644 index 0000000..b1fe407 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-seam-fill.svg b/web/vendor/bootstrap-icons/box-seam-fill.svg new file mode 100644 index 0000000..b9283c7 --- /dev/null +++ b/web/vendor/bootstrap-icons/box-seam-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box-seam.svg b/web/vendor/bootstrap-icons/box-seam.svg new file mode 100644 index 0000000..ec2cb8a --- /dev/null +++ b/web/vendor/bootstrap-icons/box-seam.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box.svg b/web/vendor/bootstrap-icons/box.svg new file mode 100644 index 0000000..01b34c7 --- /dev/null +++ b/web/vendor/bootstrap-icons/box.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box2-fill.svg b/web/vendor/bootstrap-icons/box2-fill.svg new file mode 100644 index 0000000..78e7583 --- /dev/null +++ b/web/vendor/bootstrap-icons/box2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box2-heart-fill.svg b/web/vendor/bootstrap-icons/box2-heart-fill.svg new file mode 100644 index 0000000..49da486 --- /dev/null +++ b/web/vendor/bootstrap-icons/box2-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box2-heart.svg b/web/vendor/bootstrap-icons/box2-heart.svg new file mode 100644 index 0000000..a5e413a --- /dev/null +++ b/web/vendor/bootstrap-icons/box2-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/box2.svg b/web/vendor/bootstrap-icons/box2.svg new file mode 100644 index 0000000..bfeb554 --- /dev/null +++ b/web/vendor/bootstrap-icons/box2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/boxes.svg b/web/vendor/bootstrap-icons/boxes.svg new file mode 100644 index 0000000..af0d1d0 --- /dev/null +++ b/web/vendor/bootstrap-icons/boxes.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/braces-asterisk.svg b/web/vendor/bootstrap-icons/braces-asterisk.svg new file mode 100644 index 0000000..e159e9c --- /dev/null +++ b/web/vendor/bootstrap-icons/braces-asterisk.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/braces.svg b/web/vendor/bootstrap-icons/braces.svg new file mode 100644 index 0000000..d345d3b --- /dev/null +++ b/web/vendor/bootstrap-icons/braces.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bricks.svg b/web/vendor/bootstrap-icons/bricks.svg new file mode 100644 index 0000000..23c2c36 --- /dev/null +++ b/web/vendor/bootstrap-icons/bricks.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/briefcase-fill.svg b/web/vendor/bootstrap-icons/briefcase-fill.svg new file mode 100644 index 0000000..b37f2be --- /dev/null +++ b/web/vendor/bootstrap-icons/briefcase-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/briefcase.svg b/web/vendor/bootstrap-icons/briefcase.svg new file mode 100644 index 0000000..712998d --- /dev/null +++ b/web/vendor/bootstrap-icons/briefcase.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brightness-alt-high-fill.svg b/web/vendor/bootstrap-icons/brightness-alt-high-fill.svg new file mode 100644 index 0000000..06f7d0c --- /dev/null +++ b/web/vendor/bootstrap-icons/brightness-alt-high-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brightness-alt-high.svg b/web/vendor/bootstrap-icons/brightness-alt-high.svg new file mode 100644 index 0000000..e519ca7 --- /dev/null +++ b/web/vendor/bootstrap-icons/brightness-alt-high.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brightness-alt-low-fill.svg b/web/vendor/bootstrap-icons/brightness-alt-low-fill.svg new file mode 100644 index 0000000..ab30837 --- /dev/null +++ b/web/vendor/bootstrap-icons/brightness-alt-low-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brightness-alt-low.svg b/web/vendor/bootstrap-icons/brightness-alt-low.svg new file mode 100644 index 0000000..58bf6ed --- /dev/null +++ b/web/vendor/bootstrap-icons/brightness-alt-low.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brightness-high-fill.svg b/web/vendor/bootstrap-icons/brightness-high-fill.svg new file mode 100644 index 0000000..b759b04 --- /dev/null +++ b/web/vendor/bootstrap-icons/brightness-high-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brightness-high.svg b/web/vendor/bootstrap-icons/brightness-high.svg new file mode 100644 index 0000000..f00d050 --- /dev/null +++ b/web/vendor/bootstrap-icons/brightness-high.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brightness-low-fill.svg b/web/vendor/bootstrap-icons/brightness-low-fill.svg new file mode 100644 index 0000000..fc55680 --- /dev/null +++ b/web/vendor/bootstrap-icons/brightness-low-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brightness-low.svg b/web/vendor/bootstrap-icons/brightness-low.svg new file mode 100644 index 0000000..317918e --- /dev/null +++ b/web/vendor/bootstrap-icons/brightness-low.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brilliance.svg b/web/vendor/bootstrap-icons/brilliance.svg new file mode 100644 index 0000000..f6b5da6 --- /dev/null +++ b/web/vendor/bootstrap-icons/brilliance.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/broadcast-pin.svg b/web/vendor/bootstrap-icons/broadcast-pin.svg new file mode 100644 index 0000000..9c5f4a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/broadcast-pin.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/broadcast.svg b/web/vendor/bootstrap-icons/broadcast.svg new file mode 100644 index 0000000..b420a0b --- /dev/null +++ b/web/vendor/bootstrap-icons/broadcast.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/browser-chrome.svg b/web/vendor/bootstrap-icons/browser-chrome.svg new file mode 100644 index 0000000..63c344b --- /dev/null +++ b/web/vendor/bootstrap-icons/browser-chrome.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/browser-edge.svg b/web/vendor/bootstrap-icons/browser-edge.svg new file mode 100644 index 0000000..ed1dc7c --- /dev/null +++ b/web/vendor/bootstrap-icons/browser-edge.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/browser-firefox.svg b/web/vendor/bootstrap-icons/browser-firefox.svg new file mode 100644 index 0000000..ce0eabb --- /dev/null +++ b/web/vendor/bootstrap-icons/browser-firefox.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/browser-safari.svg b/web/vendor/bootstrap-icons/browser-safari.svg new file mode 100644 index 0000000..8c01296 --- /dev/null +++ b/web/vendor/bootstrap-icons/browser-safari.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brush-fill.svg b/web/vendor/bootstrap-icons/brush-fill.svg new file mode 100644 index 0000000..db77615 --- /dev/null +++ b/web/vendor/bootstrap-icons/brush-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/brush.svg b/web/vendor/bootstrap-icons/brush.svg new file mode 100644 index 0000000..86d88ef --- /dev/null +++ b/web/vendor/bootstrap-icons/brush.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bucket-fill.svg b/web/vendor/bootstrap-icons/bucket-fill.svg new file mode 100644 index 0000000..c0c95ab --- /dev/null +++ b/web/vendor/bootstrap-icons/bucket-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bucket.svg b/web/vendor/bootstrap-icons/bucket.svg new file mode 100644 index 0000000..252e75b --- /dev/null +++ b/web/vendor/bootstrap-icons/bucket.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bug-fill.svg b/web/vendor/bootstrap-icons/bug-fill.svg new file mode 100644 index 0000000..a36ff37 --- /dev/null +++ b/web/vendor/bootstrap-icons/bug-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bug.svg b/web/vendor/bootstrap-icons/bug.svg new file mode 100644 index 0000000..296ef32 --- /dev/null +++ b/web/vendor/bootstrap-icons/bug.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-add.svg b/web/vendor/bootstrap-icons/building-add.svg new file mode 100644 index 0000000..c2c3670 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-add.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-check.svg b/web/vendor/bootstrap-icons/building-check.svg new file mode 100644 index 0000000..95c3c54 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-check.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-dash.svg b/web/vendor/bootstrap-icons/building-dash.svg new file mode 100644 index 0000000..1e1634b --- /dev/null +++ b/web/vendor/bootstrap-icons/building-dash.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-down.svg b/web/vendor/bootstrap-icons/building-down.svg new file mode 100644 index 0000000..8538cf6 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-down.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-exclamation.svg b/web/vendor/bootstrap-icons/building-exclamation.svg new file mode 100644 index 0000000..ebfc709 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-add.svg b/web/vendor/bootstrap-icons/building-fill-add.svg new file mode 100644 index 0000000..6bbe567 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-check.svg b/web/vendor/bootstrap-icons/building-fill-check.svg new file mode 100644 index 0000000..c4f1881 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-dash.svg b/web/vendor/bootstrap-icons/building-fill-dash.svg new file mode 100644 index 0000000..1ce28a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-down.svg b/web/vendor/bootstrap-icons/building-fill-down.svg new file mode 100644 index 0000000..b1c55cd --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-exclamation.svg b/web/vendor/bootstrap-icons/building-fill-exclamation.svg new file mode 100644 index 0000000..3491f24 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-gear.svg b/web/vendor/bootstrap-icons/building-fill-gear.svg new file mode 100644 index 0000000..747a1bc --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-lock.svg b/web/vendor/bootstrap-icons/building-fill-lock.svg new file mode 100644 index 0000000..be73a41 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-slash.svg b/web/vendor/bootstrap-icons/building-fill-slash.svg new file mode 100644 index 0000000..d867309 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-up.svg b/web/vendor/bootstrap-icons/building-fill-up.svg new file mode 100644 index 0000000..d8cc4bc --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill-x.svg b/web/vendor/bootstrap-icons/building-fill-x.svg new file mode 100644 index 0000000..236aae3 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-fill.svg b/web/vendor/bootstrap-icons/building-fill.svg new file mode 100644 index 0000000..6924b41 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-gear.svg b/web/vendor/bootstrap-icons/building-gear.svg new file mode 100644 index 0000000..eabe790 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-lock.svg b/web/vendor/bootstrap-icons/building-lock.svg new file mode 100644 index 0000000..591a2e9 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-slash.svg b/web/vendor/bootstrap-icons/building-slash.svg new file mode 100644 index 0000000..c3f7787 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-slash.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-up.svg b/web/vendor/bootstrap-icons/building-up.svg new file mode 100644 index 0000000..ff2d5d9 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-up.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building-x.svg b/web/vendor/bootstrap-icons/building-x.svg new file mode 100644 index 0000000..70e67a3 --- /dev/null +++ b/web/vendor/bootstrap-icons/building-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/building.svg b/web/vendor/bootstrap-icons/building.svg new file mode 100644 index 0000000..916b049 --- /dev/null +++ b/web/vendor/bootstrap-icons/building.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/buildings-fill.svg b/web/vendor/bootstrap-icons/buildings-fill.svg new file mode 100644 index 0000000..6aea68d --- /dev/null +++ b/web/vendor/bootstrap-icons/buildings-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/buildings.svg b/web/vendor/bootstrap-icons/buildings.svg new file mode 100644 index 0000000..3028498 --- /dev/null +++ b/web/vendor/bootstrap-icons/buildings.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bullseye.svg b/web/vendor/bootstrap-icons/bullseye.svg new file mode 100644 index 0000000..16c2207 --- /dev/null +++ b/web/vendor/bootstrap-icons/bullseye.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bus-front-fill.svg b/web/vendor/bootstrap-icons/bus-front-fill.svg new file mode 100644 index 0000000..de21228 --- /dev/null +++ b/web/vendor/bootstrap-icons/bus-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/bus-front.svg b/web/vendor/bootstrap-icons/bus-front.svg new file mode 100644 index 0000000..95c5df5 --- /dev/null +++ b/web/vendor/bootstrap-icons/bus-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/c-circle-fill.svg b/web/vendor/bootstrap-icons/c-circle-fill.svg new file mode 100644 index 0000000..c0adc18 --- /dev/null +++ b/web/vendor/bootstrap-icons/c-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/c-circle.svg b/web/vendor/bootstrap-icons/c-circle.svg new file mode 100644 index 0000000..ac3dfac --- /dev/null +++ b/web/vendor/bootstrap-icons/c-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/c-square-fill.svg b/web/vendor/bootstrap-icons/c-square-fill.svg new file mode 100644 index 0000000..b26a27e --- /dev/null +++ b/web/vendor/bootstrap-icons/c-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/c-square.svg b/web/vendor/bootstrap-icons/c-square.svg new file mode 100644 index 0000000..cdd74a3 --- /dev/null +++ b/web/vendor/bootstrap-icons/c-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cake-fill.svg b/web/vendor/bootstrap-icons/cake-fill.svg new file mode 100644 index 0000000..4370e02 --- /dev/null +++ b/web/vendor/bootstrap-icons/cake-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cake.svg b/web/vendor/bootstrap-icons/cake.svg new file mode 100644 index 0000000..500747b --- /dev/null +++ b/web/vendor/bootstrap-icons/cake.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cake2-fill.svg b/web/vendor/bootstrap-icons/cake2-fill.svg new file mode 100644 index 0000000..1ed25f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/cake2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cake2.svg b/web/vendor/bootstrap-icons/cake2.svg new file mode 100644 index 0000000..a10dc80 --- /dev/null +++ b/web/vendor/bootstrap-icons/cake2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calculator-fill.svg b/web/vendor/bootstrap-icons/calculator-fill.svg new file mode 100644 index 0000000..2933419 --- /dev/null +++ b/web/vendor/bootstrap-icons/calculator-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calculator.svg b/web/vendor/bootstrap-icons/calculator.svg new file mode 100644 index 0000000..cc9761e --- /dev/null +++ b/web/vendor/bootstrap-icons/calculator.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-check-fill.svg b/web/vendor/bootstrap-icons/calendar-check-fill.svg new file mode 100644 index 0000000..967d182 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-check.svg b/web/vendor/bootstrap-icons/calendar-check.svg new file mode 100644 index 0000000..f778cd2 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-date-fill.svg b/web/vendor/bootstrap-icons/calendar-date-fill.svg new file mode 100644 index 0000000..59b31f2 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-date-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-date.svg b/web/vendor/bootstrap-icons/calendar-date.svg new file mode 100644 index 0000000..b73c8f5 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-date.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-day-fill.svg b/web/vendor/bootstrap-icons/calendar-day-fill.svg new file mode 100644 index 0000000..b9bcbf8 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-day-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-day.svg b/web/vendor/bootstrap-icons/calendar-day.svg new file mode 100644 index 0000000..6f8d871 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-day.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-event-fill.svg b/web/vendor/bootstrap-icons/calendar-event-fill.svg new file mode 100644 index 0000000..5b09eea --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-event-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-event.svg b/web/vendor/bootstrap-icons/calendar-event.svg new file mode 100644 index 0000000..57c734b --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-event.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-fill.svg b/web/vendor/bootstrap-icons/calendar-fill.svg new file mode 100644 index 0000000..789eb8b --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-heart-fill.svg b/web/vendor/bootstrap-icons/calendar-heart-fill.svg new file mode 100644 index 0000000..63d9e4c --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-heart.svg b/web/vendor/bootstrap-icons/calendar-heart.svg new file mode 100644 index 0000000..8ed9c38 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-minus-fill.svg b/web/vendor/bootstrap-icons/calendar-minus-fill.svg new file mode 100644 index 0000000..8dad6e1 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-minus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-minus.svg b/web/vendor/bootstrap-icons/calendar-minus.svg new file mode 100644 index 0000000..ecd4e97 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-month-fill.svg b/web/vendor/bootstrap-icons/calendar-month-fill.svg new file mode 100644 index 0000000..d8d56fe --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-month-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-month.svg b/web/vendor/bootstrap-icons/calendar-month.svg new file mode 100644 index 0000000..95b6419 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-month.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-plus-fill.svg b/web/vendor/bootstrap-icons/calendar-plus-fill.svg new file mode 100644 index 0000000..0ed0c83 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-plus.svg b/web/vendor/bootstrap-icons/calendar-plus.svg new file mode 100644 index 0000000..189b152 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-range-fill.svg b/web/vendor/bootstrap-icons/calendar-range-fill.svg new file mode 100644 index 0000000..324def0 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-range-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-range.svg b/web/vendor/bootstrap-icons/calendar-range.svg new file mode 100644 index 0000000..7db0947 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-range.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-week-fill.svg b/web/vendor/bootstrap-icons/calendar-week-fill.svg new file mode 100644 index 0000000..ab2128d --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-week-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-week.svg b/web/vendor/bootstrap-icons/calendar-week.svg new file mode 100644 index 0000000..b5dbcc9 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-week.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-x-fill.svg b/web/vendor/bootstrap-icons/calendar-x-fill.svg new file mode 100644 index 0000000..450e114 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar-x.svg b/web/vendor/bootstrap-icons/calendar-x.svg new file mode 100644 index 0000000..dc85a91 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar.svg b/web/vendor/bootstrap-icons/calendar.svg new file mode 100644 index 0000000..d32ebe7 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-check-fill.svg b/web/vendor/bootstrap-icons/calendar2-check-fill.svg new file mode 100644 index 0000000..a0c36c5 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-check.svg b/web/vendor/bootstrap-icons/calendar2-check.svg new file mode 100644 index 0000000..5429842 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-check.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-date-fill.svg b/web/vendor/bootstrap-icons/calendar2-date-fill.svg new file mode 100644 index 0000000..93b3941 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-date-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-date.svg b/web/vendor/bootstrap-icons/calendar2-date.svg new file mode 100644 index 0000000..61193cd --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-date.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-day-fill.svg b/web/vendor/bootstrap-icons/calendar2-day-fill.svg new file mode 100644 index 0000000..b605453 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-day-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-day.svg b/web/vendor/bootstrap-icons/calendar2-day.svg new file mode 100644 index 0000000..ce59878 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-day.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-event-fill.svg b/web/vendor/bootstrap-icons/calendar2-event-fill.svg new file mode 100644 index 0000000..2b245d1 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-event-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-event.svg b/web/vendor/bootstrap-icons/calendar2-event.svg new file mode 100644 index 0000000..36910dd --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-event.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-fill.svg b/web/vendor/bootstrap-icons/calendar2-fill.svg new file mode 100644 index 0000000..b28e594 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-heart-fill.svg b/web/vendor/bootstrap-icons/calendar2-heart-fill.svg new file mode 100644 index 0000000..f3723cd --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-heart.svg b/web/vendor/bootstrap-icons/calendar2-heart.svg new file mode 100644 index 0000000..995d9b8 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-minus-fill.svg b/web/vendor/bootstrap-icons/calendar2-minus-fill.svg new file mode 100644 index 0000000..bf52a36 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-minus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-minus.svg b/web/vendor/bootstrap-icons/calendar2-minus.svg new file mode 100644 index 0000000..62e6bbc --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-minus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-month-fill.svg b/web/vendor/bootstrap-icons/calendar2-month-fill.svg new file mode 100644 index 0000000..24b9c69 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-month-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-month.svg b/web/vendor/bootstrap-icons/calendar2-month.svg new file mode 100644 index 0000000..65d8295 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-month.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-plus-fill.svg b/web/vendor/bootstrap-icons/calendar2-plus-fill.svg new file mode 100644 index 0000000..26a2047 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-plus.svg b/web/vendor/bootstrap-icons/calendar2-plus.svg new file mode 100644 index 0000000..728148f --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-range-fill.svg b/web/vendor/bootstrap-icons/calendar2-range-fill.svg new file mode 100644 index 0000000..1ba46b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-range-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-range.svg b/web/vendor/bootstrap-icons/calendar2-range.svg new file mode 100644 index 0000000..9a657f4 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-range.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-week-fill.svg b/web/vendor/bootstrap-icons/calendar2-week-fill.svg new file mode 100644 index 0000000..f358667 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-week-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-week.svg b/web/vendor/bootstrap-icons/calendar2-week.svg new file mode 100644 index 0000000..07906a9 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-week.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-x-fill.svg b/web/vendor/bootstrap-icons/calendar2-x-fill.svg new file mode 100644 index 0000000..def799d --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2-x.svg b/web/vendor/bootstrap-icons/calendar2-x.svg new file mode 100644 index 0000000..d6f9e6f --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2-x.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar2.svg b/web/vendor/bootstrap-icons/calendar2.svg new file mode 100644 index 0000000..957f993 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar3-event-fill.svg b/web/vendor/bootstrap-icons/calendar3-event-fill.svg new file mode 100644 index 0000000..0901710 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar3-event-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar3-event.svg b/web/vendor/bootstrap-icons/calendar3-event.svg new file mode 100644 index 0000000..f702c36 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar3-event.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar3-fill.svg b/web/vendor/bootstrap-icons/calendar3-fill.svg new file mode 100644 index 0000000..f3bc116 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar3-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar3-range-fill.svg b/web/vendor/bootstrap-icons/calendar3-range-fill.svg new file mode 100644 index 0000000..7d16dc5 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar3-range-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar3-range.svg b/web/vendor/bootstrap-icons/calendar3-range.svg new file mode 100644 index 0000000..c19d9ca --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar3-range.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar3-week-fill.svg b/web/vendor/bootstrap-icons/calendar3-week-fill.svg new file mode 100644 index 0000000..1b66709 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar3-week-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar3-week.svg b/web/vendor/bootstrap-icons/calendar3-week.svg new file mode 100644 index 0000000..6d577b7 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar3-week.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar3.svg b/web/vendor/bootstrap-icons/calendar3.svg new file mode 100644 index 0000000..8e2aedf --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar3.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar4-event.svg b/web/vendor/bootstrap-icons/calendar4-event.svg new file mode 100644 index 0000000..0d29c0c --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar4-event.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar4-range.svg b/web/vendor/bootstrap-icons/calendar4-range.svg new file mode 100644 index 0000000..b260479 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar4-range.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar4-week.svg b/web/vendor/bootstrap-icons/calendar4-week.svg new file mode 100644 index 0000000..d934881 --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar4-week.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/calendar4.svg b/web/vendor/bootstrap-icons/calendar4.svg new file mode 100644 index 0000000..69c474c --- /dev/null +++ b/web/vendor/bootstrap-icons/calendar4.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera-fill.svg b/web/vendor/bootstrap-icons/camera-fill.svg new file mode 100644 index 0000000..6aa0240 --- /dev/null +++ b/web/vendor/bootstrap-icons/camera-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera-reels-fill.svg b/web/vendor/bootstrap-icons/camera-reels-fill.svg new file mode 100644 index 0000000..6bd48de --- /dev/null +++ b/web/vendor/bootstrap-icons/camera-reels-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera-reels.svg b/web/vendor/bootstrap-icons/camera-reels.svg new file mode 100644 index 0000000..0c6eca8 --- /dev/null +++ b/web/vendor/bootstrap-icons/camera-reels.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera-video-fill.svg b/web/vendor/bootstrap-icons/camera-video-fill.svg new file mode 100644 index 0000000..72dee37 --- /dev/null +++ b/web/vendor/bootstrap-icons/camera-video-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera-video-off-fill.svg b/web/vendor/bootstrap-icons/camera-video-off-fill.svg new file mode 100644 index 0000000..290dc19 --- /dev/null +++ b/web/vendor/bootstrap-icons/camera-video-off-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera-video-off.svg b/web/vendor/bootstrap-icons/camera-video-off.svg new file mode 100644 index 0000000..c9eb587 --- /dev/null +++ b/web/vendor/bootstrap-icons/camera-video-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera-video.svg b/web/vendor/bootstrap-icons/camera-video.svg new file mode 100644 index 0000000..a042d1a --- /dev/null +++ b/web/vendor/bootstrap-icons/camera-video.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera.svg b/web/vendor/bootstrap-icons/camera.svg new file mode 100644 index 0000000..3a926d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/camera.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/camera2.svg b/web/vendor/bootstrap-icons/camera2.svg new file mode 100644 index 0000000..ba9521e --- /dev/null +++ b/web/vendor/bootstrap-icons/camera2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/capslock-fill.svg b/web/vendor/bootstrap-icons/capslock-fill.svg new file mode 100644 index 0000000..f1c0bab --- /dev/null +++ b/web/vendor/bootstrap-icons/capslock-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/capslock.svg b/web/vendor/bootstrap-icons/capslock.svg new file mode 100644 index 0000000..b0b894a --- /dev/null +++ b/web/vendor/bootstrap-icons/capslock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/capsule-pill.svg b/web/vendor/bootstrap-icons/capsule-pill.svg new file mode 100644 index 0000000..b9f3b54 --- /dev/null +++ b/web/vendor/bootstrap-icons/capsule-pill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/capsule.svg b/web/vendor/bootstrap-icons/capsule.svg new file mode 100644 index 0000000..53d1a66 --- /dev/null +++ b/web/vendor/bootstrap-icons/capsule.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/car-front-fill.svg b/web/vendor/bootstrap-icons/car-front-fill.svg new file mode 100644 index 0000000..a47a870 --- /dev/null +++ b/web/vendor/bootstrap-icons/car-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/car-front.svg b/web/vendor/bootstrap-icons/car-front.svg new file mode 100644 index 0000000..890e4cf --- /dev/null +++ b/web/vendor/bootstrap-icons/car-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/card-checklist.svg b/web/vendor/bootstrap-icons/card-checklist.svg new file mode 100644 index 0000000..3044e68 --- /dev/null +++ b/web/vendor/bootstrap-icons/card-checklist.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/card-heading.svg b/web/vendor/bootstrap-icons/card-heading.svg new file mode 100644 index 0000000..a6be873 --- /dev/null +++ b/web/vendor/bootstrap-icons/card-heading.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/card-image.svg b/web/vendor/bootstrap-icons/card-image.svg new file mode 100644 index 0000000..7343f77 --- /dev/null +++ b/web/vendor/bootstrap-icons/card-image.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/card-list.svg b/web/vendor/bootstrap-icons/card-list.svg new file mode 100644 index 0000000..6270802 --- /dev/null +++ b/web/vendor/bootstrap-icons/card-list.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/card-text.svg b/web/vendor/bootstrap-icons/card-text.svg new file mode 100644 index 0000000..8f7470c --- /dev/null +++ b/web/vendor/bootstrap-icons/card-text.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-down-fill.svg b/web/vendor/bootstrap-icons/caret-down-fill.svg new file mode 100644 index 0000000..d7c3990 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-down-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-down-square-fill.svg b/web/vendor/bootstrap-icons/caret-down-square-fill.svg new file mode 100644 index 0000000..63199bb --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-down-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-down-square.svg b/web/vendor/bootstrap-icons/caret-down-square.svg new file mode 100644 index 0000000..0372625 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-down-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-down.svg b/web/vendor/bootstrap-icons/caret-down.svg new file mode 100644 index 0000000..627258a --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-left-fill.svg b/web/vendor/bootstrap-icons/caret-left-fill.svg new file mode 100644 index 0000000..d989dff --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-left-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-left-square-fill.svg b/web/vendor/bootstrap-icons/caret-left-square-fill.svg new file mode 100644 index 0000000..cc7e3a8 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-left-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-left-square.svg b/web/vendor/bootstrap-icons/caret-left-square.svg new file mode 100644 index 0000000..5e8cb20 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-left-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-left.svg b/web/vendor/bootstrap-icons/caret-left.svg new file mode 100644 index 0000000..4415336 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-right-fill.svg b/web/vendor/bootstrap-icons/caret-right-fill.svg new file mode 100644 index 0000000..b445551 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-right-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-right-square-fill.svg b/web/vendor/bootstrap-icons/caret-right-square-fill.svg new file mode 100644 index 0000000..2aded36 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-right-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-right-square.svg b/web/vendor/bootstrap-icons/caret-right-square.svg new file mode 100644 index 0000000..a3a44e2 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-right-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-right.svg b/web/vendor/bootstrap-icons/caret-right.svg new file mode 100644 index 0000000..7bcd8bb --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-up-fill.svg b/web/vendor/bootstrap-icons/caret-up-fill.svg new file mode 100644 index 0000000..a87820e --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-up-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-up-square-fill.svg b/web/vendor/bootstrap-icons/caret-up-square-fill.svg new file mode 100644 index 0000000..348fcf2 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-up-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-up-square.svg b/web/vendor/bootstrap-icons/caret-up-square.svg new file mode 100644 index 0000000..8ac2af4 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-up-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/caret-up.svg b/web/vendor/bootstrap-icons/caret-up.svg new file mode 100644 index 0000000..8e33519 --- /dev/null +++ b/web/vendor/bootstrap-icons/caret-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-check-fill.svg b/web/vendor/bootstrap-icons/cart-check-fill.svg new file mode 100644 index 0000000..612358c --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-check.svg b/web/vendor/bootstrap-icons/cart-check.svg new file mode 100644 index 0000000..68301db --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-dash-fill.svg b/web/vendor/bootstrap-icons/cart-dash-fill.svg new file mode 100644 index 0000000..a335b07 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-dash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-dash.svg b/web/vendor/bootstrap-icons/cart-dash.svg new file mode 100644 index 0000000..9c97c3b --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-fill.svg b/web/vendor/bootstrap-icons/cart-fill.svg new file mode 100644 index 0000000..974fc29 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-plus-fill.svg b/web/vendor/bootstrap-icons/cart-plus-fill.svg new file mode 100644 index 0000000..59e46e4 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-plus.svg b/web/vendor/bootstrap-icons/cart-plus.svg new file mode 100644 index 0000000..2baaae4 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-x-fill.svg b/web/vendor/bootstrap-icons/cart-x-fill.svg new file mode 100644 index 0000000..7ca0688 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart-x.svg b/web/vendor/bootstrap-icons/cart-x.svg new file mode 100644 index 0000000..2d8f213 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart.svg b/web/vendor/bootstrap-icons/cart.svg new file mode 100644 index 0000000..0e0f96c --- /dev/null +++ b/web/vendor/bootstrap-icons/cart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart2.svg b/web/vendor/bootstrap-icons/cart2.svg new file mode 100644 index 0000000..ea7f696 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart3.svg b/web/vendor/bootstrap-icons/cart3.svg new file mode 100644 index 0000000..af1b3c5 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cart4.svg b/web/vendor/bootstrap-icons/cart4.svg new file mode 100644 index 0000000..4631ac9 --- /dev/null +++ b/web/vendor/bootstrap-icons/cart4.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cash-coin.svg b/web/vendor/bootstrap-icons/cash-coin.svg new file mode 100644 index 0000000..2904f15 --- /dev/null +++ b/web/vendor/bootstrap-icons/cash-coin.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cash-stack.svg b/web/vendor/bootstrap-icons/cash-stack.svg new file mode 100644 index 0000000..fc8c282 --- /dev/null +++ b/web/vendor/bootstrap-icons/cash-stack.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cash.svg b/web/vendor/bootstrap-icons/cash.svg new file mode 100644 index 0000000..18cbff3 --- /dev/null +++ b/web/vendor/bootstrap-icons/cash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cassette-fill.svg b/web/vendor/bootstrap-icons/cassette-fill.svg new file mode 100644 index 0000000..18fd5e4 --- /dev/null +++ b/web/vendor/bootstrap-icons/cassette-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cassette.svg b/web/vendor/bootstrap-icons/cassette.svg new file mode 100644 index 0000000..2effe71 --- /dev/null +++ b/web/vendor/bootstrap-icons/cassette.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cast.svg b/web/vendor/bootstrap-icons/cast.svg new file mode 100644 index 0000000..01a9795 --- /dev/null +++ b/web/vendor/bootstrap-icons/cast.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cc-circle-fill.svg b/web/vendor/bootstrap-icons/cc-circle-fill.svg new file mode 100644 index 0000000..483d90c --- /dev/null +++ b/web/vendor/bootstrap-icons/cc-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cc-circle.svg b/web/vendor/bootstrap-icons/cc-circle.svg new file mode 100644 index 0000000..2dc2b4b --- /dev/null +++ b/web/vendor/bootstrap-icons/cc-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cc-square-fill.svg b/web/vendor/bootstrap-icons/cc-square-fill.svg new file mode 100644 index 0000000..b0a8350 --- /dev/null +++ b/web/vendor/bootstrap-icons/cc-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cc-square.svg b/web/vendor/bootstrap-icons/cc-square.svg new file mode 100644 index 0000000..b0f05b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/cc-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-dots-fill.svg b/web/vendor/bootstrap-icons/chat-dots-fill.svg new file mode 100644 index 0000000..5f74345 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-dots-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-dots.svg b/web/vendor/bootstrap-icons/chat-dots.svg new file mode 100644 index 0000000..20137cc --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-dots.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-fill.svg b/web/vendor/bootstrap-icons/chat-fill.svg new file mode 100644 index 0000000..c896939 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-heart-fill.svg b/web/vendor/bootstrap-icons/chat-heart-fill.svg new file mode 100644 index 0000000..ea01c34 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-heart.svg b/web/vendor/bootstrap-icons/chat-heart.svg new file mode 100644 index 0000000..8ea104e --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-dots-fill.svg b/web/vendor/bootstrap-icons/chat-left-dots-fill.svg new file mode 100644 index 0000000..a6614c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-dots-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-dots.svg b/web/vendor/bootstrap-icons/chat-left-dots.svg new file mode 100644 index 0000000..35f466a --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-dots.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-fill.svg b/web/vendor/bootstrap-icons/chat-left-fill.svg new file mode 100644 index 0000000..0de6e13 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-heart-fill.svg b/web/vendor/bootstrap-icons/chat-left-heart-fill.svg new file mode 100644 index 0000000..cc9502c --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-heart.svg b/web/vendor/bootstrap-icons/chat-left-heart.svg new file mode 100644 index 0000000..ec11692 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-quote-fill.svg b/web/vendor/bootstrap-icons/chat-left-quote-fill.svg new file mode 100644 index 0000000..d634a12 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-quote-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-quote.svg b/web/vendor/bootstrap-icons/chat-left-quote.svg new file mode 100644 index 0000000..376ceb1 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-quote.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-text-fill.svg b/web/vendor/bootstrap-icons/chat-left-text-fill.svg new file mode 100644 index 0000000..a78e343 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-text-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left-text.svg b/web/vendor/bootstrap-icons/chat-left-text.svg new file mode 100644 index 0000000..88d1906 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left-text.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-left.svg b/web/vendor/bootstrap-icons/chat-left.svg new file mode 100644 index 0000000..d93f0af --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-quote-fill.svg b/web/vendor/bootstrap-icons/chat-quote-fill.svg new file mode 100644 index 0000000..7150c44 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-quote-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-quote.svg b/web/vendor/bootstrap-icons/chat-quote.svg new file mode 100644 index 0000000..aa0edc4 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-quote.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-dots-fill.svg b/web/vendor/bootstrap-icons/chat-right-dots-fill.svg new file mode 100644 index 0000000..1020581 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-dots-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-dots.svg b/web/vendor/bootstrap-icons/chat-right-dots.svg new file mode 100644 index 0000000..d9b8bd2 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-dots.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-fill.svg b/web/vendor/bootstrap-icons/chat-right-fill.svg new file mode 100644 index 0000000..6381ddc --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-heart-fill.svg b/web/vendor/bootstrap-icons/chat-right-heart-fill.svg new file mode 100644 index 0000000..cf4abfe --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-heart.svg b/web/vendor/bootstrap-icons/chat-right-heart.svg new file mode 100644 index 0000000..e6b3880 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-quote-fill.svg b/web/vendor/bootstrap-icons/chat-right-quote-fill.svg new file mode 100644 index 0000000..1c4e536 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-quote-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-quote.svg b/web/vendor/bootstrap-icons/chat-right-quote.svg new file mode 100644 index 0000000..e9091bc --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-quote.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-text-fill.svg b/web/vendor/bootstrap-icons/chat-right-text-fill.svg new file mode 100644 index 0000000..3455983 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-text-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right-text.svg b/web/vendor/bootstrap-icons/chat-right-text.svg new file mode 100644 index 0000000..88341af --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right-text.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-right.svg b/web/vendor/bootstrap-icons/chat-right.svg new file mode 100644 index 0000000..a930c9a --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-dots-fill.svg b/web/vendor/bootstrap-icons/chat-square-dots-fill.svg new file mode 100644 index 0000000..09c97d1 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-dots-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-dots.svg b/web/vendor/bootstrap-icons/chat-square-dots.svg new file mode 100644 index 0000000..b06c02f --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-dots.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-fill.svg b/web/vendor/bootstrap-icons/chat-square-fill.svg new file mode 100644 index 0000000..4688831 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-heart-fill.svg b/web/vendor/bootstrap-icons/chat-square-heart-fill.svg new file mode 100644 index 0000000..902e0b5 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-heart.svg b/web/vendor/bootstrap-icons/chat-square-heart.svg new file mode 100644 index 0000000..6ba687e --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-quote-fill.svg b/web/vendor/bootstrap-icons/chat-square-quote-fill.svg new file mode 100644 index 0000000..2496b70 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-quote-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-quote.svg b/web/vendor/bootstrap-icons/chat-square-quote.svg new file mode 100644 index 0000000..a8f6b09 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-quote.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-text-fill.svg b/web/vendor/bootstrap-icons/chat-square-text-fill.svg new file mode 100644 index 0000000..6ebf567 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-text-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square-text.svg b/web/vendor/bootstrap-icons/chat-square-text.svg new file mode 100644 index 0000000..1296f92 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square-text.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-square.svg b/web/vendor/bootstrap-icons/chat-square.svg new file mode 100644 index 0000000..4e13ae8 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-text-fill.svg b/web/vendor/bootstrap-icons/chat-text-fill.svg new file mode 100644 index 0000000..93639f1 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-text-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat-text.svg b/web/vendor/bootstrap-icons/chat-text.svg new file mode 100644 index 0000000..f64f43f --- /dev/null +++ b/web/vendor/bootstrap-icons/chat-text.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chat.svg b/web/vendor/bootstrap-icons/chat.svg new file mode 100644 index 0000000..487d142 --- /dev/null +++ b/web/vendor/bootstrap-icons/chat.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check-all.svg b/web/vendor/bootstrap-icons/check-all.svg new file mode 100644 index 0000000..f91fd77 --- /dev/null +++ b/web/vendor/bootstrap-icons/check-all.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check-circle-fill.svg b/web/vendor/bootstrap-icons/check-circle-fill.svg new file mode 100644 index 0000000..0b7f412 --- /dev/null +++ b/web/vendor/bootstrap-icons/check-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check-circle.svg b/web/vendor/bootstrap-icons/check-circle.svg new file mode 100644 index 0000000..016f607 --- /dev/null +++ b/web/vendor/bootstrap-icons/check-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check-lg.svg b/web/vendor/bootstrap-icons/check-lg.svg new file mode 100644 index 0000000..63a8a3d --- /dev/null +++ b/web/vendor/bootstrap-icons/check-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check-square-fill.svg b/web/vendor/bootstrap-icons/check-square-fill.svg new file mode 100644 index 0000000..cbb56ed --- /dev/null +++ b/web/vendor/bootstrap-icons/check-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check-square.svg b/web/vendor/bootstrap-icons/check-square.svg new file mode 100644 index 0000000..b7d1116 --- /dev/null +++ b/web/vendor/bootstrap-icons/check-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check.svg b/web/vendor/bootstrap-icons/check.svg new file mode 100644 index 0000000..11ab547 --- /dev/null +++ b/web/vendor/bootstrap-icons/check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check2-all.svg b/web/vendor/bootstrap-icons/check2-all.svg new file mode 100644 index 0000000..cccc0b7 --- /dev/null +++ b/web/vendor/bootstrap-icons/check2-all.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check2-circle.svg b/web/vendor/bootstrap-icons/check2-circle.svg new file mode 100644 index 0000000..166e661 --- /dev/null +++ b/web/vendor/bootstrap-icons/check2-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check2-square.svg b/web/vendor/bootstrap-icons/check2-square.svg new file mode 100644 index 0000000..64c3669 --- /dev/null +++ b/web/vendor/bootstrap-icons/check2-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/check2.svg b/web/vendor/bootstrap-icons/check2.svg new file mode 100644 index 0000000..87168de --- /dev/null +++ b/web/vendor/bootstrap-icons/check2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-bar-contract.svg b/web/vendor/bootstrap-icons/chevron-bar-contract.svg new file mode 100644 index 0000000..52ec3f6 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-bar-contract.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-bar-down.svg b/web/vendor/bootstrap-icons/chevron-bar-down.svg new file mode 100644 index 0000000..8c10216 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-bar-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-bar-expand.svg b/web/vendor/bootstrap-icons/chevron-bar-expand.svg new file mode 100644 index 0000000..1260a20 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-bar-expand.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-bar-left.svg b/web/vendor/bootstrap-icons/chevron-bar-left.svg new file mode 100644 index 0000000..36afeb7 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-bar-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-bar-right.svg b/web/vendor/bootstrap-icons/chevron-bar-right.svg new file mode 100644 index 0000000..b71e040 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-bar-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-bar-up.svg b/web/vendor/bootstrap-icons/chevron-bar-up.svg new file mode 100644 index 0000000..c5da175 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-bar-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-compact-down.svg b/web/vendor/bootstrap-icons/chevron-compact-down.svg new file mode 100644 index 0000000..53d9d9a --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-compact-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-compact-left.svg b/web/vendor/bootstrap-icons/chevron-compact-left.svg new file mode 100644 index 0000000..277ddd8 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-compact-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-compact-right.svg b/web/vendor/bootstrap-icons/chevron-compact-right.svg new file mode 100644 index 0000000..24b5309 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-compact-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-compact-up.svg b/web/vendor/bootstrap-icons/chevron-compact-up.svg new file mode 100644 index 0000000..2a4f354 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-compact-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-contract.svg b/web/vendor/bootstrap-icons/chevron-contract.svg new file mode 100644 index 0000000..354ee86 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-contract.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-double-down.svg b/web/vendor/bootstrap-icons/chevron-double-down.svg new file mode 100644 index 0000000..bc99e59 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-double-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-double-left.svg b/web/vendor/bootstrap-icons/chevron-double-left.svg new file mode 100644 index 0000000..c4cd7f2 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-double-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-double-right.svg b/web/vendor/bootstrap-icons/chevron-double-right.svg new file mode 100644 index 0000000..dccd6c5 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-double-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-double-up.svg b/web/vendor/bootstrap-icons/chevron-double-up.svg new file mode 100644 index 0000000..ad7ba12 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-double-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-down.svg b/web/vendor/bootstrap-icons/chevron-down.svg new file mode 100644 index 0000000..a281907 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-expand.svg b/web/vendor/bootstrap-icons/chevron-expand.svg new file mode 100644 index 0000000..33e4ad8 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-expand.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-left.svg b/web/vendor/bootstrap-icons/chevron-left.svg new file mode 100644 index 0000000..5bcc1bb --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-right.svg b/web/vendor/bootstrap-icons/chevron-right.svg new file mode 100644 index 0000000..ab39af8 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/chevron-up.svg b/web/vendor/bootstrap-icons/chevron-up.svg new file mode 100644 index 0000000..4f3c7a0 --- /dev/null +++ b/web/vendor/bootstrap-icons/chevron-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/circle-fill.svg b/web/vendor/bootstrap-icons/circle-fill.svg new file mode 100644 index 0000000..e0d1b51 --- /dev/null +++ b/web/vendor/bootstrap-icons/circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/circle-half.svg b/web/vendor/bootstrap-icons/circle-half.svg new file mode 100644 index 0000000..497f6b7 --- /dev/null +++ b/web/vendor/bootstrap-icons/circle-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/circle-square.svg b/web/vendor/bootstrap-icons/circle-square.svg new file mode 100644 index 0000000..c0f62b7 --- /dev/null +++ b/web/vendor/bootstrap-icons/circle-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/circle.svg b/web/vendor/bootstrap-icons/circle.svg new file mode 100644 index 0000000..9bbabca --- /dev/null +++ b/web/vendor/bootstrap-icons/circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/claude.svg b/web/vendor/bootstrap-icons/claude.svg new file mode 100644 index 0000000..1403b27 --- /dev/null +++ b/web/vendor/bootstrap-icons/claude.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-check-fill.svg b/web/vendor/bootstrap-icons/clipboard-check-fill.svg new file mode 100644 index 0000000..598e850 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-check-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-check.svg b/web/vendor/bootstrap-icons/clipboard-check.svg new file mode 100644 index 0000000..cb9d8a2 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-check.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-data-fill.svg b/web/vendor/bootstrap-icons/clipboard-data-fill.svg new file mode 100644 index 0000000..e4a9425 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-data-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-data.svg b/web/vendor/bootstrap-icons/clipboard-data.svg new file mode 100644 index 0000000..622acbf --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-data.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-fill.svg b/web/vendor/bootstrap-icons/clipboard-fill.svg new file mode 100644 index 0000000..176c5e4 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-heart-fill.svg b/web/vendor/bootstrap-icons/clipboard-heart-fill.svg new file mode 100644 index 0000000..92de0a8 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-heart-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-heart.svg b/web/vendor/bootstrap-icons/clipboard-heart.svg new file mode 100644 index 0000000..c430ed2 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-heart.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-minus-fill.svg b/web/vendor/bootstrap-icons/clipboard-minus-fill.svg new file mode 100644 index 0000000..e47f43f --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-minus-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-minus.svg b/web/vendor/bootstrap-icons/clipboard-minus.svg new file mode 100644 index 0000000..d3675c4 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-minus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-plus-fill.svg b/web/vendor/bootstrap-icons/clipboard-plus-fill.svg new file mode 100644 index 0000000..8140aa8 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-plus-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-plus.svg b/web/vendor/bootstrap-icons/clipboard-plus.svg new file mode 100644 index 0000000..1d095d7 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-plus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-pulse.svg b/web/vendor/bootstrap-icons/clipboard-pulse.svg new file mode 100644 index 0000000..a357209 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-pulse.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-x-fill.svg b/web/vendor/bootstrap-icons/clipboard-x-fill.svg new file mode 100644 index 0000000..10fba82 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-x-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard-x.svg b/web/vendor/bootstrap-icons/clipboard-x.svg new file mode 100644 index 0000000..46df235 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard-x.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard.svg b/web/vendor/bootstrap-icons/clipboard.svg new file mode 100644 index 0000000..b92f42a --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-check-fill.svg b/web/vendor/bootstrap-icons/clipboard2-check-fill.svg new file mode 100644 index 0000000..b640432 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-check-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-check.svg b/web/vendor/bootstrap-icons/clipboard2-check.svg new file mode 100644 index 0000000..aba15bf --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-check.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-data-fill.svg b/web/vendor/bootstrap-icons/clipboard2-data-fill.svg new file mode 100644 index 0000000..56c127e --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-data-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-data.svg b/web/vendor/bootstrap-icons/clipboard2-data.svg new file mode 100644 index 0000000..75ac6c6 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-data.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-fill.svg b/web/vendor/bootstrap-icons/clipboard2-fill.svg new file mode 100644 index 0000000..6898571 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-heart-fill.svg b/web/vendor/bootstrap-icons/clipboard2-heart-fill.svg new file mode 100644 index 0000000..ce98945 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-heart-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-heart.svg b/web/vendor/bootstrap-icons/clipboard2-heart.svg new file mode 100644 index 0000000..879fef2 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-heart.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-minus-fill.svg b/web/vendor/bootstrap-icons/clipboard2-minus-fill.svg new file mode 100644 index 0000000..fcd4b56 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-minus-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-minus.svg b/web/vendor/bootstrap-icons/clipboard2-minus.svg new file mode 100644 index 0000000..f8c10e3 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-minus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-plus-fill.svg b/web/vendor/bootstrap-icons/clipboard2-plus-fill.svg new file mode 100644 index 0000000..be310e5 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-plus-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-plus.svg b/web/vendor/bootstrap-icons/clipboard2-plus.svg new file mode 100644 index 0000000..33eaa28 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-plus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-pulse-fill.svg b/web/vendor/bootstrap-icons/clipboard2-pulse-fill.svg new file mode 100644 index 0000000..bc7d6b3 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-pulse-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-pulse.svg b/web/vendor/bootstrap-icons/clipboard2-pulse.svg new file mode 100644 index 0000000..c641c61 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-pulse.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-x-fill.svg b/web/vendor/bootstrap-icons/clipboard2-x-fill.svg new file mode 100644 index 0000000..08828c6 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-x-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2-x.svg b/web/vendor/bootstrap-icons/clipboard2-x.svg new file mode 100644 index 0000000..06832cc --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2-x.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clipboard2.svg b/web/vendor/bootstrap-icons/clipboard2.svg new file mode 100644 index 0000000..d0f4529 --- /dev/null +++ b/web/vendor/bootstrap-icons/clipboard2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clock-fill.svg b/web/vendor/bootstrap-icons/clock-fill.svg new file mode 100644 index 0000000..148abcf --- /dev/null +++ b/web/vendor/bootstrap-icons/clock-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clock-history.svg b/web/vendor/bootstrap-icons/clock-history.svg new file mode 100644 index 0000000..f685e10 --- /dev/null +++ b/web/vendor/bootstrap-icons/clock-history.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clock.svg b/web/vendor/bootstrap-icons/clock.svg new file mode 100644 index 0000000..31c3c64 --- /dev/null +++ b/web/vendor/bootstrap-icons/clock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-arrow-down-fill.svg b/web/vendor/bootstrap-icons/cloud-arrow-down-fill.svg new file mode 100644 index 0000000..1b23dc9 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-arrow-down-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-arrow-down.svg b/web/vendor/bootstrap-icons/cloud-arrow-down.svg new file mode 100644 index 0000000..bb79bbe --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-arrow-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-arrow-up-fill.svg b/web/vendor/bootstrap-icons/cloud-arrow-up-fill.svg new file mode 100644 index 0000000..8366f05 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-arrow-up-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-arrow-up.svg b/web/vendor/bootstrap-icons/cloud-arrow-up.svg new file mode 100644 index 0000000..704756b --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-arrow-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-check-fill.svg b/web/vendor/bootstrap-icons/cloud-check-fill.svg new file mode 100644 index 0000000..a71feee --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-check.svg b/web/vendor/bootstrap-icons/cloud-check.svg new file mode 100644 index 0000000..d7599e9 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-download-fill.svg b/web/vendor/bootstrap-icons/cloud-download-fill.svg new file mode 100644 index 0000000..c8a44ba --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-download-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-download.svg b/web/vendor/bootstrap-icons/cloud-download.svg new file mode 100644 index 0000000..b71d7d7 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-download.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-drizzle-fill.svg b/web/vendor/bootstrap-icons/cloud-drizzle-fill.svg new file mode 100644 index 0000000..0d381ae --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-drizzle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-drizzle.svg b/web/vendor/bootstrap-icons/cloud-drizzle.svg new file mode 100644 index 0000000..f3c8599 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-drizzle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-fill.svg b/web/vendor/bootstrap-icons/cloud-fill.svg new file mode 100644 index 0000000..8849faa --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-fog-fill.svg b/web/vendor/bootstrap-icons/cloud-fog-fill.svg new file mode 100644 index 0000000..214caba --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-fog-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-fog.svg b/web/vendor/bootstrap-icons/cloud-fog.svg new file mode 100644 index 0000000..26a574a --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-fog.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-fog2-fill.svg b/web/vendor/bootstrap-icons/cloud-fog2-fill.svg new file mode 100644 index 0000000..8f67dea --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-fog2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-fog2.svg b/web/vendor/bootstrap-icons/cloud-fog2.svg new file mode 100644 index 0000000..9b0664f --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-fog2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-hail-fill.svg b/web/vendor/bootstrap-icons/cloud-hail-fill.svg new file mode 100644 index 0000000..0fa737f --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-hail-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-hail.svg b/web/vendor/bootstrap-icons/cloud-hail.svg new file mode 100644 index 0000000..3206a02 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-hail.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-haze-fill.svg b/web/vendor/bootstrap-icons/cloud-haze-fill.svg new file mode 100644 index 0000000..aa16c2c --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-haze-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-haze.svg b/web/vendor/bootstrap-icons/cloud-haze.svg new file mode 100644 index 0000000..578a565 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-haze.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-haze2-fill.svg b/web/vendor/bootstrap-icons/cloud-haze2-fill.svg new file mode 100644 index 0000000..3e22656 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-haze2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-haze2.svg b/web/vendor/bootstrap-icons/cloud-haze2.svg new file mode 100644 index 0000000..c43d91c --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-haze2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-lightning-fill.svg b/web/vendor/bootstrap-icons/cloud-lightning-fill.svg new file mode 100644 index 0000000..88fd930 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-lightning-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-lightning-rain-fill.svg b/web/vendor/bootstrap-icons/cloud-lightning-rain-fill.svg new file mode 100644 index 0000000..f5cd845 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-lightning-rain-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-lightning-rain.svg b/web/vendor/bootstrap-icons/cloud-lightning-rain.svg new file mode 100644 index 0000000..588b274 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-lightning-rain.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-lightning.svg b/web/vendor/bootstrap-icons/cloud-lightning.svg new file mode 100644 index 0000000..20c2680 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-lightning.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-minus-fill.svg b/web/vendor/bootstrap-icons/cloud-minus-fill.svg new file mode 100644 index 0000000..2fcc2bb --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-minus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-minus.svg b/web/vendor/bootstrap-icons/cloud-minus.svg new file mode 100644 index 0000000..54f47b2 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-moon-fill.svg b/web/vendor/bootstrap-icons/cloud-moon-fill.svg new file mode 100644 index 0000000..232dd4e --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-moon-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-moon.svg b/web/vendor/bootstrap-icons/cloud-moon.svg new file mode 100644 index 0000000..cc91617 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-moon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-plus-fill.svg b/web/vendor/bootstrap-icons/cloud-plus-fill.svg new file mode 100644 index 0000000..5337dc4 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-plus.svg b/web/vendor/bootstrap-icons/cloud-plus.svg new file mode 100644 index 0000000..9448796 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-rain-fill.svg b/web/vendor/bootstrap-icons/cloud-rain-fill.svg new file mode 100644 index 0000000..3ffee56 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-rain-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-rain-heavy-fill.svg b/web/vendor/bootstrap-icons/cloud-rain-heavy-fill.svg new file mode 100644 index 0000000..d92411b --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-rain-heavy-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-rain-heavy.svg b/web/vendor/bootstrap-icons/cloud-rain-heavy.svg new file mode 100644 index 0000000..ee9ef85 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-rain-heavy.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-rain.svg b/web/vendor/bootstrap-icons/cloud-rain.svg new file mode 100644 index 0000000..e22f16c --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-rain.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-slash-fill.svg b/web/vendor/bootstrap-icons/cloud-slash-fill.svg new file mode 100644 index 0000000..08a709b --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-slash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-slash.svg b/web/vendor/bootstrap-icons/cloud-slash.svg new file mode 100644 index 0000000..d7b680c --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-sleet-fill.svg b/web/vendor/bootstrap-icons/cloud-sleet-fill.svg new file mode 100644 index 0000000..1df3f33 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-sleet-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-sleet.svg b/web/vendor/bootstrap-icons/cloud-sleet.svg new file mode 100644 index 0000000..edc48c6 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-sleet.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-snow-fill.svg b/web/vendor/bootstrap-icons/cloud-snow-fill.svg new file mode 100644 index 0000000..32cda8d --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-snow-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-snow.svg b/web/vendor/bootstrap-icons/cloud-snow.svg new file mode 100644 index 0000000..26150c4 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-snow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-sun-fill.svg b/web/vendor/bootstrap-icons/cloud-sun-fill.svg new file mode 100644 index 0000000..da5ecac --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-sun-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-sun.svg b/web/vendor/bootstrap-icons/cloud-sun.svg new file mode 100644 index 0000000..caa95e9 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-sun.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-upload-fill.svg b/web/vendor/bootstrap-icons/cloud-upload-fill.svg new file mode 100644 index 0000000..2d0df59 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-upload-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud-upload.svg b/web/vendor/bootstrap-icons/cloud-upload.svg new file mode 100644 index 0000000..e5ca56e --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud-upload.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloud.svg b/web/vendor/bootstrap-icons/cloud.svg new file mode 100644 index 0000000..de877ab --- /dev/null +++ b/web/vendor/bootstrap-icons/cloud.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clouds-fill.svg b/web/vendor/bootstrap-icons/clouds-fill.svg new file mode 100644 index 0000000..d70e817 --- /dev/null +++ b/web/vendor/bootstrap-icons/clouds-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/clouds.svg b/web/vendor/bootstrap-icons/clouds.svg new file mode 100644 index 0000000..7e253e7 --- /dev/null +++ b/web/vendor/bootstrap-icons/clouds.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloudy-fill.svg b/web/vendor/bootstrap-icons/cloudy-fill.svg new file mode 100644 index 0000000..7bf27b7 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloudy-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cloudy.svg b/web/vendor/bootstrap-icons/cloudy.svg new file mode 100644 index 0000000..87c2017 --- /dev/null +++ b/web/vendor/bootstrap-icons/cloudy.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/code-slash.svg b/web/vendor/bootstrap-icons/code-slash.svg new file mode 100644 index 0000000..51a5c57 --- /dev/null +++ b/web/vendor/bootstrap-icons/code-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/code-square.svg b/web/vendor/bootstrap-icons/code-square.svg new file mode 100644 index 0000000..30fdef3 --- /dev/null +++ b/web/vendor/bootstrap-icons/code-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/code.svg b/web/vendor/bootstrap-icons/code.svg new file mode 100644 index 0000000..c0760e9 --- /dev/null +++ b/web/vendor/bootstrap-icons/code.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/coin.svg b/web/vendor/bootstrap-icons/coin.svg new file mode 100644 index 0000000..fb94cc5 --- /dev/null +++ b/web/vendor/bootstrap-icons/coin.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/collection-fill.svg b/web/vendor/bootstrap-icons/collection-fill.svg new file mode 100644 index 0000000..4e5fbce --- /dev/null +++ b/web/vendor/bootstrap-icons/collection-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/collection-play-fill.svg b/web/vendor/bootstrap-icons/collection-play-fill.svg new file mode 100644 index 0000000..b6820d0 --- /dev/null +++ b/web/vendor/bootstrap-icons/collection-play-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/collection-play.svg b/web/vendor/bootstrap-icons/collection-play.svg new file mode 100644 index 0000000..0c59f5d --- /dev/null +++ b/web/vendor/bootstrap-icons/collection-play.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/collection.svg b/web/vendor/bootstrap-icons/collection.svg new file mode 100644 index 0000000..8b5d5fd --- /dev/null +++ b/web/vendor/bootstrap-icons/collection.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/columns-gap.svg b/web/vendor/bootstrap-icons/columns-gap.svg new file mode 100644 index 0000000..8b4bb4e --- /dev/null +++ b/web/vendor/bootstrap-icons/columns-gap.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/columns.svg b/web/vendor/bootstrap-icons/columns.svg new file mode 100644 index 0000000..17632df --- /dev/null +++ b/web/vendor/bootstrap-icons/columns.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/command.svg b/web/vendor/bootstrap-icons/command.svg new file mode 100644 index 0000000..64fa00b --- /dev/null +++ b/web/vendor/bootstrap-icons/command.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/compass-fill.svg b/web/vendor/bootstrap-icons/compass-fill.svg new file mode 100644 index 0000000..1396c1f --- /dev/null +++ b/web/vendor/bootstrap-icons/compass-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/compass.svg b/web/vendor/bootstrap-icons/compass.svg new file mode 100644 index 0000000..9b402f3 --- /dev/null +++ b/web/vendor/bootstrap-icons/compass.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cone-striped.svg b/web/vendor/bootstrap-icons/cone-striped.svg new file mode 100644 index 0000000..28a9529 --- /dev/null +++ b/web/vendor/bootstrap-icons/cone-striped.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cone.svg b/web/vendor/bootstrap-icons/cone.svg new file mode 100644 index 0000000..b1a7d97 --- /dev/null +++ b/web/vendor/bootstrap-icons/cone.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/controller.svg b/web/vendor/bootstrap-icons/controller.svg new file mode 100644 index 0000000..b7ceedb --- /dev/null +++ b/web/vendor/bootstrap-icons/controller.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cookie.svg b/web/vendor/bootstrap-icons/cookie.svg new file mode 100644 index 0000000..7a4b2fa --- /dev/null +++ b/web/vendor/bootstrap-icons/cookie.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/copy.svg b/web/vendor/bootstrap-icons/copy.svg new file mode 100644 index 0000000..b590680 --- /dev/null +++ b/web/vendor/bootstrap-icons/copy.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cpu-fill.svg b/web/vendor/bootstrap-icons/cpu-fill.svg new file mode 100644 index 0000000..ce6e294 --- /dev/null +++ b/web/vendor/bootstrap-icons/cpu-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cpu.svg b/web/vendor/bootstrap-icons/cpu.svg new file mode 100644 index 0000000..88c0d56 --- /dev/null +++ b/web/vendor/bootstrap-icons/cpu.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/credit-card-2-back-fill.svg b/web/vendor/bootstrap-icons/credit-card-2-back-fill.svg new file mode 100644 index 0000000..032fb4a --- /dev/null +++ b/web/vendor/bootstrap-icons/credit-card-2-back-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/credit-card-2-back.svg b/web/vendor/bootstrap-icons/credit-card-2-back.svg new file mode 100644 index 0000000..b29419c --- /dev/null +++ b/web/vendor/bootstrap-icons/credit-card-2-back.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/credit-card-2-front-fill.svg b/web/vendor/bootstrap-icons/credit-card-2-front-fill.svg new file mode 100644 index 0000000..06684d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/credit-card-2-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/credit-card-2-front.svg b/web/vendor/bootstrap-icons/credit-card-2-front.svg new file mode 100644 index 0000000..0bbc290 --- /dev/null +++ b/web/vendor/bootstrap-icons/credit-card-2-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/credit-card-fill.svg b/web/vendor/bootstrap-icons/credit-card-fill.svg new file mode 100644 index 0000000..a4f899a --- /dev/null +++ b/web/vendor/bootstrap-icons/credit-card-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/credit-card.svg b/web/vendor/bootstrap-icons/credit-card.svg new file mode 100644 index 0000000..406233d --- /dev/null +++ b/web/vendor/bootstrap-icons/credit-card.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/crop.svg b/web/vendor/bootstrap-icons/crop.svg new file mode 100644 index 0000000..3b4bb60 --- /dev/null +++ b/web/vendor/bootstrap-icons/crop.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/crosshair.svg b/web/vendor/bootstrap-icons/crosshair.svg new file mode 100644 index 0000000..13bed74 --- /dev/null +++ b/web/vendor/bootstrap-icons/crosshair.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/crosshair2.svg b/web/vendor/bootstrap-icons/crosshair2.svg new file mode 100644 index 0000000..3c28586 --- /dev/null +++ b/web/vendor/bootstrap-icons/crosshair2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/css.svg b/web/vendor/bootstrap-icons/css.svg new file mode 100644 index 0000000..e336af1 --- /dev/null +++ b/web/vendor/bootstrap-icons/css.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cup-fill.svg b/web/vendor/bootstrap-icons/cup-fill.svg new file mode 100644 index 0000000..7173787 --- /dev/null +++ b/web/vendor/bootstrap-icons/cup-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cup-hot-fill.svg b/web/vendor/bootstrap-icons/cup-hot-fill.svg new file mode 100644 index 0000000..9d7c465 --- /dev/null +++ b/web/vendor/bootstrap-icons/cup-hot-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cup-hot.svg b/web/vendor/bootstrap-icons/cup-hot.svg new file mode 100644 index 0000000..a6f7e89 --- /dev/null +++ b/web/vendor/bootstrap-icons/cup-hot.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cup-straw.svg b/web/vendor/bootstrap-icons/cup-straw.svg new file mode 100644 index 0000000..9388da9 --- /dev/null +++ b/web/vendor/bootstrap-icons/cup-straw.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cup.svg b/web/vendor/bootstrap-icons/cup.svg new file mode 100644 index 0000000..2694ac8 --- /dev/null +++ b/web/vendor/bootstrap-icons/cup.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/currency-bitcoin.svg b/web/vendor/bootstrap-icons/currency-bitcoin.svg new file mode 100644 index 0000000..0477ff8 --- /dev/null +++ b/web/vendor/bootstrap-icons/currency-bitcoin.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/currency-dollar.svg b/web/vendor/bootstrap-icons/currency-dollar.svg new file mode 100644 index 0000000..7ead9a7 --- /dev/null +++ b/web/vendor/bootstrap-icons/currency-dollar.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/currency-euro.svg b/web/vendor/bootstrap-icons/currency-euro.svg new file mode 100644 index 0000000..90c83d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/currency-euro.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/currency-exchange.svg b/web/vendor/bootstrap-icons/currency-exchange.svg new file mode 100644 index 0000000..e332aa7 --- /dev/null +++ b/web/vendor/bootstrap-icons/currency-exchange.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/currency-pound.svg b/web/vendor/bootstrap-icons/currency-pound.svg new file mode 100644 index 0000000..4650876 --- /dev/null +++ b/web/vendor/bootstrap-icons/currency-pound.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/currency-rupee.svg b/web/vendor/bootstrap-icons/currency-rupee.svg new file mode 100644 index 0000000..4fdf9a2 --- /dev/null +++ b/web/vendor/bootstrap-icons/currency-rupee.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/currency-yen.svg b/web/vendor/bootstrap-icons/currency-yen.svg new file mode 100644 index 0000000..5bbf1a2 --- /dev/null +++ b/web/vendor/bootstrap-icons/currency-yen.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cursor-fill.svg b/web/vendor/bootstrap-icons/cursor-fill.svg new file mode 100644 index 0000000..093372b --- /dev/null +++ b/web/vendor/bootstrap-icons/cursor-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cursor-text.svg b/web/vendor/bootstrap-icons/cursor-text.svg new file mode 100644 index 0000000..27c057b --- /dev/null +++ b/web/vendor/bootstrap-icons/cursor-text.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/cursor.svg b/web/vendor/bootstrap-icons/cursor.svg new file mode 100644 index 0000000..e23e3fd --- /dev/null +++ b/web/vendor/bootstrap-icons/cursor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dash-circle-dotted.svg b/web/vendor/bootstrap-icons/dash-circle-dotted.svg new file mode 100644 index 0000000..1c011e2 --- /dev/null +++ b/web/vendor/bootstrap-icons/dash-circle-dotted.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dash-circle-fill.svg b/web/vendor/bootstrap-icons/dash-circle-fill.svg new file mode 100644 index 0000000..ac4eae0 --- /dev/null +++ b/web/vendor/bootstrap-icons/dash-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dash-circle.svg b/web/vendor/bootstrap-icons/dash-circle.svg new file mode 100644 index 0000000..c4abdd2 --- /dev/null +++ b/web/vendor/bootstrap-icons/dash-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dash-lg.svg b/web/vendor/bootstrap-icons/dash-lg.svg new file mode 100644 index 0000000..454aa7d --- /dev/null +++ b/web/vendor/bootstrap-icons/dash-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dash-square-dotted.svg b/web/vendor/bootstrap-icons/dash-square-dotted.svg new file mode 100644 index 0000000..90886c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/dash-square-dotted.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dash-square-fill.svg b/web/vendor/bootstrap-icons/dash-square-fill.svg new file mode 100644 index 0000000..dbe0db2 --- /dev/null +++ b/web/vendor/bootstrap-icons/dash-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dash-square.svg b/web/vendor/bootstrap-icons/dash-square.svg new file mode 100644 index 0000000..9381872 --- /dev/null +++ b/web/vendor/bootstrap-icons/dash-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dash.svg b/web/vendor/bootstrap-icons/dash.svg new file mode 100644 index 0000000..c3834b4 --- /dev/null +++ b/web/vendor/bootstrap-icons/dash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-add.svg b/web/vendor/bootstrap-icons/database-add.svg new file mode 100644 index 0000000..5f76340 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-check.svg b/web/vendor/bootstrap-icons/database-check.svg new file mode 100644 index 0000000..29c02b8 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-dash.svg b/web/vendor/bootstrap-icons/database-dash.svg new file mode 100644 index 0000000..184db0a --- /dev/null +++ b/web/vendor/bootstrap-icons/database-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-down.svg b/web/vendor/bootstrap-icons/database-down.svg new file mode 100644 index 0000000..e077452 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-exclamation.svg b/web/vendor/bootstrap-icons/database-exclamation.svg new file mode 100644 index 0000000..dbde50f --- /dev/null +++ b/web/vendor/bootstrap-icons/database-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-add.svg b/web/vendor/bootstrap-icons/database-fill-add.svg new file mode 100644 index 0000000..4273d5d --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-check.svg b/web/vendor/bootstrap-icons/database-fill-check.svg new file mode 100644 index 0000000..7690eed --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-dash.svg b/web/vendor/bootstrap-icons/database-fill-dash.svg new file mode 100644 index 0000000..48c468d --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-down.svg b/web/vendor/bootstrap-icons/database-fill-down.svg new file mode 100644 index 0000000..c3560d8 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-exclamation.svg b/web/vendor/bootstrap-icons/database-fill-exclamation.svg new file mode 100644 index 0000000..00073d0 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-exclamation.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-gear.svg b/web/vendor/bootstrap-icons/database-fill-gear.svg new file mode 100644 index 0000000..94fc2e6 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-lock.svg b/web/vendor/bootstrap-icons/database-fill-lock.svg new file mode 100644 index 0000000..a948205 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-lock.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-slash.svg b/web/vendor/bootstrap-icons/database-fill-slash.svg new file mode 100644 index 0000000..467e6bb --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-up.svg b/web/vendor/bootstrap-icons/database-fill-up.svg new file mode 100644 index 0000000..07f2d39 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill-x.svg b/web/vendor/bootstrap-icons/database-fill-x.svg new file mode 100644 index 0000000..73ff269 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill-x.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-fill.svg b/web/vendor/bootstrap-icons/database-fill.svg new file mode 100644 index 0000000..8603874 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-fill.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-gear.svg b/web/vendor/bootstrap-icons/database-gear.svg new file mode 100644 index 0000000..451763c --- /dev/null +++ b/web/vendor/bootstrap-icons/database-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-lock.svg b/web/vendor/bootstrap-icons/database-lock.svg new file mode 100644 index 0000000..e150cd2 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-slash.svg b/web/vendor/bootstrap-icons/database-slash.svg new file mode 100644 index 0000000..e0cc9f2 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-up.svg b/web/vendor/bootstrap-icons/database-up.svg new file mode 100644 index 0000000..63f7a10 --- /dev/null +++ b/web/vendor/bootstrap-icons/database-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database-x.svg b/web/vendor/bootstrap-icons/database-x.svg new file mode 100644 index 0000000..f97779b --- /dev/null +++ b/web/vendor/bootstrap-icons/database-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/database.svg b/web/vendor/bootstrap-icons/database.svg new file mode 100644 index 0000000..231c50c --- /dev/null +++ b/web/vendor/bootstrap-icons/database.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/device-hdd-fill.svg b/web/vendor/bootstrap-icons/device-hdd-fill.svg new file mode 100644 index 0000000..d5380c0 --- /dev/null +++ b/web/vendor/bootstrap-icons/device-hdd-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/device-hdd.svg b/web/vendor/bootstrap-icons/device-hdd.svg new file mode 100644 index 0000000..5163a58 --- /dev/null +++ b/web/vendor/bootstrap-icons/device-hdd.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/device-ssd-fill.svg b/web/vendor/bootstrap-icons/device-ssd-fill.svg new file mode 100644 index 0000000..0d1f9c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/device-ssd-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/device-ssd.svg b/web/vendor/bootstrap-icons/device-ssd.svg new file mode 100644 index 0000000..8405f21 --- /dev/null +++ b/web/vendor/bootstrap-icons/device-ssd.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/diagram-2-fill.svg b/web/vendor/bootstrap-icons/diagram-2-fill.svg new file mode 100644 index 0000000..397ae15 --- /dev/null +++ b/web/vendor/bootstrap-icons/diagram-2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/diagram-2.svg b/web/vendor/bootstrap-icons/diagram-2.svg new file mode 100644 index 0000000..a6e5439 --- /dev/null +++ b/web/vendor/bootstrap-icons/diagram-2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/diagram-3-fill.svg b/web/vendor/bootstrap-icons/diagram-3-fill.svg new file mode 100644 index 0000000..7e47423 --- /dev/null +++ b/web/vendor/bootstrap-icons/diagram-3-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/diagram-3.svg b/web/vendor/bootstrap-icons/diagram-3.svg new file mode 100644 index 0000000..ee3fd6f --- /dev/null +++ b/web/vendor/bootstrap-icons/diagram-3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/diamond-fill.svg b/web/vendor/bootstrap-icons/diamond-fill.svg new file mode 100644 index 0000000..1f86d1f --- /dev/null +++ b/web/vendor/bootstrap-icons/diamond-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/diamond-half.svg b/web/vendor/bootstrap-icons/diamond-half.svg new file mode 100644 index 0000000..68254b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/diamond-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/diamond.svg b/web/vendor/bootstrap-icons/diamond.svg new file mode 100644 index 0000000..44e2855 --- /dev/null +++ b/web/vendor/bootstrap-icons/diamond.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-1-fill.svg b/web/vendor/bootstrap-icons/dice-1-fill.svg new file mode 100644 index 0000000..a32e2cf --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-1-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-1.svg b/web/vendor/bootstrap-icons/dice-1.svg new file mode 100644 index 0000000..afc64b0 --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-1.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-2-fill.svg b/web/vendor/bootstrap-icons/dice-2-fill.svg new file mode 100644 index 0000000..131013e --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-2.svg b/web/vendor/bootstrap-icons/dice-2.svg new file mode 100644 index 0000000..ba1a79c --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-3-fill.svg b/web/vendor/bootstrap-icons/dice-3-fill.svg new file mode 100644 index 0000000..158065d --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-3-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-3.svg b/web/vendor/bootstrap-icons/dice-3.svg new file mode 100644 index 0000000..b64c675 --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-3.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-4-fill.svg b/web/vendor/bootstrap-icons/dice-4-fill.svg new file mode 100644 index 0000000..7cf6e2c --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-4-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-4.svg b/web/vendor/bootstrap-icons/dice-4.svg new file mode 100644 index 0000000..bd8bbf7 --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-4.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-5-fill.svg b/web/vendor/bootstrap-icons/dice-5-fill.svg new file mode 100644 index 0000000..289cb45 --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-5-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-5.svg b/web/vendor/bootstrap-icons/dice-5.svg new file mode 100644 index 0000000..cc96a35 --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-5.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-6-fill.svg b/web/vendor/bootstrap-icons/dice-6-fill.svg new file mode 100644 index 0000000..9b3d652 --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-6-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dice-6.svg b/web/vendor/bootstrap-icons/dice-6.svg new file mode 100644 index 0000000..47ba0aa --- /dev/null +++ b/web/vendor/bootstrap-icons/dice-6.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/disc-fill.svg b/web/vendor/bootstrap-icons/disc-fill.svg new file mode 100644 index 0000000..0d2d7f1 --- /dev/null +++ b/web/vendor/bootstrap-icons/disc-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/disc.svg b/web/vendor/bootstrap-icons/disc.svg new file mode 100644 index 0000000..360034c --- /dev/null +++ b/web/vendor/bootstrap-icons/disc.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/discord.svg b/web/vendor/bootstrap-icons/discord.svg new file mode 100644 index 0000000..9905364 --- /dev/null +++ b/web/vendor/bootstrap-icons/discord.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/display-fill.svg b/web/vendor/bootstrap-icons/display-fill.svg new file mode 100644 index 0000000..ed8e17e --- /dev/null +++ b/web/vendor/bootstrap-icons/display-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/display.svg b/web/vendor/bootstrap-icons/display.svg new file mode 100644 index 0000000..40a7d4d --- /dev/null +++ b/web/vendor/bootstrap-icons/display.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/displayport-fill.svg b/web/vendor/bootstrap-icons/displayport-fill.svg new file mode 100644 index 0000000..503a960 --- /dev/null +++ b/web/vendor/bootstrap-icons/displayport-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/displayport.svg b/web/vendor/bootstrap-icons/displayport.svg new file mode 100644 index 0000000..6b9e0bf --- /dev/null +++ b/web/vendor/bootstrap-icons/displayport.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/distribute-horizontal.svg b/web/vendor/bootstrap-icons/distribute-horizontal.svg new file mode 100644 index 0000000..3f7044f --- /dev/null +++ b/web/vendor/bootstrap-icons/distribute-horizontal.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/distribute-vertical.svg b/web/vendor/bootstrap-icons/distribute-vertical.svg new file mode 100644 index 0000000..cb77d1e --- /dev/null +++ b/web/vendor/bootstrap-icons/distribute-vertical.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/door-closed-fill.svg b/web/vendor/bootstrap-icons/door-closed-fill.svg new file mode 100644 index 0000000..1cad66b --- /dev/null +++ b/web/vendor/bootstrap-icons/door-closed-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/door-closed.svg b/web/vendor/bootstrap-icons/door-closed.svg new file mode 100644 index 0000000..e20b918 --- /dev/null +++ b/web/vendor/bootstrap-icons/door-closed.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/door-open-fill.svg b/web/vendor/bootstrap-icons/door-open-fill.svg new file mode 100644 index 0000000..38eaff0 --- /dev/null +++ b/web/vendor/bootstrap-icons/door-open-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/door-open.svg b/web/vendor/bootstrap-icons/door-open.svg new file mode 100644 index 0000000..328f353 --- /dev/null +++ b/web/vendor/bootstrap-icons/door-open.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dot.svg b/web/vendor/bootstrap-icons/dot.svg new file mode 100644 index 0000000..edc674e --- /dev/null +++ b/web/vendor/bootstrap-icons/dot.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/download.svg b/web/vendor/bootstrap-icons/download.svg new file mode 100644 index 0000000..90a34a3 --- /dev/null +++ b/web/vendor/bootstrap-icons/download.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dpad-fill.svg b/web/vendor/bootstrap-icons/dpad-fill.svg new file mode 100644 index 0000000..1c1153a --- /dev/null +++ b/web/vendor/bootstrap-icons/dpad-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dpad.svg b/web/vendor/bootstrap-icons/dpad.svg new file mode 100644 index 0000000..71ddb24 --- /dev/null +++ b/web/vendor/bootstrap-icons/dpad.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dribbble.svg b/web/vendor/bootstrap-icons/dribbble.svg new file mode 100644 index 0000000..725ff7f --- /dev/null +++ b/web/vendor/bootstrap-icons/dribbble.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/dropbox.svg b/web/vendor/bootstrap-icons/dropbox.svg new file mode 100644 index 0000000..d052f25 --- /dev/null +++ b/web/vendor/bootstrap-icons/dropbox.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/droplet-fill.svg b/web/vendor/bootstrap-icons/droplet-fill.svg new file mode 100644 index 0000000..85feddf --- /dev/null +++ b/web/vendor/bootstrap-icons/droplet-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/droplet-half.svg b/web/vendor/bootstrap-icons/droplet-half.svg new file mode 100644 index 0000000..bcd1c76 --- /dev/null +++ b/web/vendor/bootstrap-icons/droplet-half.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/droplet.svg b/web/vendor/bootstrap-icons/droplet.svg new file mode 100644 index 0000000..204ec67 --- /dev/null +++ b/web/vendor/bootstrap-icons/droplet.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/duffle-fill.svg b/web/vendor/bootstrap-icons/duffle-fill.svg new file mode 100644 index 0000000..885ee80 --- /dev/null +++ b/web/vendor/bootstrap-icons/duffle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/duffle.svg b/web/vendor/bootstrap-icons/duffle.svg new file mode 100644 index 0000000..1180de8 --- /dev/null +++ b/web/vendor/bootstrap-icons/duffle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ear-fill.svg b/web/vendor/bootstrap-icons/ear-fill.svg new file mode 100644 index 0000000..2d135d6 --- /dev/null +++ b/web/vendor/bootstrap-icons/ear-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ear.svg b/web/vendor/bootstrap-icons/ear.svg new file mode 100644 index 0000000..061fe2f --- /dev/null +++ b/web/vendor/bootstrap-icons/ear.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/earbuds.svg b/web/vendor/bootstrap-icons/earbuds.svg new file mode 100644 index 0000000..923bfca --- /dev/null +++ b/web/vendor/bootstrap-icons/earbuds.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/easel-fill.svg b/web/vendor/bootstrap-icons/easel-fill.svg new file mode 100644 index 0000000..8086507 --- /dev/null +++ b/web/vendor/bootstrap-icons/easel-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/easel.svg b/web/vendor/bootstrap-icons/easel.svg new file mode 100644 index 0000000..5c0f5a2 --- /dev/null +++ b/web/vendor/bootstrap-icons/easel.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/easel2-fill.svg b/web/vendor/bootstrap-icons/easel2-fill.svg new file mode 100644 index 0000000..309b438 --- /dev/null +++ b/web/vendor/bootstrap-icons/easel2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/easel2.svg b/web/vendor/bootstrap-icons/easel2.svg new file mode 100644 index 0000000..74372fb --- /dev/null +++ b/web/vendor/bootstrap-icons/easel2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/easel3-fill.svg b/web/vendor/bootstrap-icons/easel3-fill.svg new file mode 100644 index 0000000..fc547ea --- /dev/null +++ b/web/vendor/bootstrap-icons/easel3-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/easel3.svg b/web/vendor/bootstrap-icons/easel3.svg new file mode 100644 index 0000000..f06a868 --- /dev/null +++ b/web/vendor/bootstrap-icons/easel3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/egg-fill.svg b/web/vendor/bootstrap-icons/egg-fill.svg new file mode 100644 index 0000000..b70cf16 --- /dev/null +++ b/web/vendor/bootstrap-icons/egg-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/egg-fried.svg b/web/vendor/bootstrap-icons/egg-fried.svg new file mode 100644 index 0000000..b0cefbc --- /dev/null +++ b/web/vendor/bootstrap-icons/egg-fried.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/egg.svg b/web/vendor/bootstrap-icons/egg.svg new file mode 100644 index 0000000..f23d8d4 --- /dev/null +++ b/web/vendor/bootstrap-icons/egg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eject-fill.svg b/web/vendor/bootstrap-icons/eject-fill.svg new file mode 100644 index 0000000..9604988 --- /dev/null +++ b/web/vendor/bootstrap-icons/eject-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eject.svg b/web/vendor/bootstrap-icons/eject.svg new file mode 100644 index 0000000..71a3ab6 --- /dev/null +++ b/web/vendor/bootstrap-icons/eject.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-angry-fill.svg b/web/vendor/bootstrap-icons/emoji-angry-fill.svg new file mode 100644 index 0000000..d14d92d --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-angry-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-angry.svg b/web/vendor/bootstrap-icons/emoji-angry.svg new file mode 100644 index 0000000..ee925fe --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-angry.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-astonished-fill.svg b/web/vendor/bootstrap-icons/emoji-astonished-fill.svg new file mode 100644 index 0000000..22a566f --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-astonished-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-astonished.svg b/web/vendor/bootstrap-icons/emoji-astonished.svg new file mode 100644 index 0000000..13f7cdd --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-astonished.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-dizzy-fill.svg b/web/vendor/bootstrap-icons/emoji-dizzy-fill.svg new file mode 100644 index 0000000..98ab490 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-dizzy-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-dizzy.svg b/web/vendor/bootstrap-icons/emoji-dizzy.svg new file mode 100644 index 0000000..fcef602 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-dizzy.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-expressionless-fill.svg b/web/vendor/bootstrap-icons/emoji-expressionless-fill.svg new file mode 100644 index 0000000..17ac348 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-expressionless-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-expressionless.svg b/web/vendor/bootstrap-icons/emoji-expressionless.svg new file mode 100644 index 0000000..8d7f68f --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-expressionless.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-frown-fill.svg b/web/vendor/bootstrap-icons/emoji-frown-fill.svg new file mode 100644 index 0000000..7a16dfb --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-frown-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-frown.svg b/web/vendor/bootstrap-icons/emoji-frown.svg new file mode 100644 index 0000000..696031b --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-frown.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-grimace-fill.svg b/web/vendor/bootstrap-icons/emoji-grimace-fill.svg new file mode 100644 index 0000000..6cabf80 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-grimace-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-grimace.svg b/web/vendor/bootstrap-icons/emoji-grimace.svg new file mode 100644 index 0000000..75a2a09 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-grimace.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-grin-fill.svg b/web/vendor/bootstrap-icons/emoji-grin-fill.svg new file mode 100644 index 0000000..08c675e --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-grin-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-grin.svg b/web/vendor/bootstrap-icons/emoji-grin.svg new file mode 100644 index 0000000..32bbf51 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-grin.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-heart-eyes-fill.svg b/web/vendor/bootstrap-icons/emoji-heart-eyes-fill.svg new file mode 100644 index 0000000..d34f6e8 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-heart-eyes-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-heart-eyes.svg b/web/vendor/bootstrap-icons/emoji-heart-eyes.svg new file mode 100644 index 0000000..5832822 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-heart-eyes.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-kiss-fill.svg b/web/vendor/bootstrap-icons/emoji-kiss-fill.svg new file mode 100644 index 0000000..15a9cdd --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-kiss-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-kiss.svg b/web/vendor/bootstrap-icons/emoji-kiss.svg new file mode 100644 index 0000000..2348d97 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-kiss.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-laughing-fill.svg b/web/vendor/bootstrap-icons/emoji-laughing-fill.svg new file mode 100644 index 0000000..0130bf4 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-laughing-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-laughing.svg b/web/vendor/bootstrap-icons/emoji-laughing.svg new file mode 100644 index 0000000..76b87f5 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-laughing.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-neutral-fill.svg b/web/vendor/bootstrap-icons/emoji-neutral-fill.svg new file mode 100644 index 0000000..662603a --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-neutral-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-neutral.svg b/web/vendor/bootstrap-icons/emoji-neutral.svg new file mode 100644 index 0000000..d6b60f8 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-neutral.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-smile-fill.svg b/web/vendor/bootstrap-icons/emoji-smile-fill.svg new file mode 100644 index 0000000..439dff0 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-smile-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-smile-upside-down-fill.svg b/web/vendor/bootstrap-icons/emoji-smile-upside-down-fill.svg new file mode 100644 index 0000000..2d6acca --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-smile-upside-down-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-smile-upside-down.svg b/web/vendor/bootstrap-icons/emoji-smile-upside-down.svg new file mode 100644 index 0000000..d2d93ed --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-smile-upside-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-smile.svg b/web/vendor/bootstrap-icons/emoji-smile.svg new file mode 100644 index 0000000..bba78da --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-smile.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-sunglasses-fill.svg b/web/vendor/bootstrap-icons/emoji-sunglasses-fill.svg new file mode 100644 index 0000000..a1318c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-sunglasses-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-sunglasses.svg b/web/vendor/bootstrap-icons/emoji-sunglasses.svg new file mode 100644 index 0000000..188b56c --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-sunglasses.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-surprise-fill.svg b/web/vendor/bootstrap-icons/emoji-surprise-fill.svg new file mode 100644 index 0000000..9f6f620 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-surprise-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-surprise.svg b/web/vendor/bootstrap-icons/emoji-surprise.svg new file mode 100644 index 0000000..af246bf --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-surprise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-tear-fill.svg b/web/vendor/bootstrap-icons/emoji-tear-fill.svg new file mode 100644 index 0000000..3ccf87d --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-tear-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-tear.svg b/web/vendor/bootstrap-icons/emoji-tear.svg new file mode 100644 index 0000000..31b6597 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-tear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-wink-fill.svg b/web/vendor/bootstrap-icons/emoji-wink-fill.svg new file mode 100644 index 0000000..2f3e480 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-wink-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/emoji-wink.svg b/web/vendor/bootstrap-icons/emoji-wink.svg new file mode 100644 index 0000000..7fe9116 --- /dev/null +++ b/web/vendor/bootstrap-icons/emoji-wink.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-arrow-down-fill.svg b/web/vendor/bootstrap-icons/envelope-arrow-down-fill.svg new file mode 100644 index 0000000..a5160e7 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-arrow-down-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-arrow-down.svg b/web/vendor/bootstrap-icons/envelope-arrow-down.svg new file mode 100644 index 0000000..36b6f54 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-arrow-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-arrow-up-fill.svg b/web/vendor/bootstrap-icons/envelope-arrow-up-fill.svg new file mode 100644 index 0000000..2757974 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-arrow-up-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-arrow-up.svg b/web/vendor/bootstrap-icons/envelope-arrow-up.svg new file mode 100644 index 0000000..ff2fae0 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-arrow-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-at-fill.svg b/web/vendor/bootstrap-icons/envelope-at-fill.svg new file mode 100644 index 0000000..e39ff38 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-at-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-at.svg b/web/vendor/bootstrap-icons/envelope-at.svg new file mode 100644 index 0000000..163c3ba --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-at.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-check-fill.svg b/web/vendor/bootstrap-icons/envelope-check-fill.svg new file mode 100644 index 0000000..26d753a --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-check-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-check.svg b/web/vendor/bootstrap-icons/envelope-check.svg new file mode 100644 index 0000000..fbc765b --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-dash-fill.svg b/web/vendor/bootstrap-icons/envelope-dash-fill.svg new file mode 100644 index 0000000..6e9e745 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-dash-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-dash.svg b/web/vendor/bootstrap-icons/envelope-dash.svg new file mode 100644 index 0000000..d6457cf --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-exclamation-fill.svg b/web/vendor/bootstrap-icons/envelope-exclamation-fill.svg new file mode 100644 index 0000000..e14f047 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-exclamation-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-exclamation.svg b/web/vendor/bootstrap-icons/envelope-exclamation.svg new file mode 100644 index 0000000..4aca0a9 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-fill.svg b/web/vendor/bootstrap-icons/envelope-fill.svg new file mode 100644 index 0000000..966ef94 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-heart-fill.svg b/web/vendor/bootstrap-icons/envelope-heart-fill.svg new file mode 100644 index 0000000..4158841 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-heart-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-heart.svg b/web/vendor/bootstrap-icons/envelope-heart.svg new file mode 100644 index 0000000..c886df2 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-open-fill.svg b/web/vendor/bootstrap-icons/envelope-open-fill.svg new file mode 100644 index 0000000..972d0e3 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-open-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-open-heart-fill.svg b/web/vendor/bootstrap-icons/envelope-open-heart-fill.svg new file mode 100644 index 0000000..13263fa --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-open-heart-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-open-heart.svg b/web/vendor/bootstrap-icons/envelope-open-heart.svg new file mode 100644 index 0000000..7018116 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-open-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-open.svg b/web/vendor/bootstrap-icons/envelope-open.svg new file mode 100644 index 0000000..3daa2eb --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-open.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-paper-fill.svg b/web/vendor/bootstrap-icons/envelope-paper-fill.svg new file mode 100644 index 0000000..c8a187c --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-paper-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-paper-heart-fill.svg b/web/vendor/bootstrap-icons/envelope-paper-heart-fill.svg new file mode 100644 index 0000000..7f58d57 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-paper-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-paper-heart.svg b/web/vendor/bootstrap-icons/envelope-paper-heart.svg new file mode 100644 index 0000000..73b91d9 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-paper-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-paper.svg b/web/vendor/bootstrap-icons/envelope-paper.svg new file mode 100644 index 0000000..20fcc2a --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-paper.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-plus-fill.svg b/web/vendor/bootstrap-icons/envelope-plus-fill.svg new file mode 100644 index 0000000..453a9fd --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-plus-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-plus.svg b/web/vendor/bootstrap-icons/envelope-plus.svg new file mode 100644 index 0000000..7e960a0 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-slash-fill.svg b/web/vendor/bootstrap-icons/envelope-slash-fill.svg new file mode 100644 index 0000000..90eb7ef --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-slash-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-slash.svg b/web/vendor/bootstrap-icons/envelope-slash.svg new file mode 100644 index 0000000..65cb167 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-x-fill.svg b/web/vendor/bootstrap-icons/envelope-x-fill.svg new file mode 100644 index 0000000..8f6a79c --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-x-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope-x.svg b/web/vendor/bootstrap-icons/envelope-x.svg new file mode 100644 index 0000000..ea74027 --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/envelope.svg b/web/vendor/bootstrap-icons/envelope.svg new file mode 100644 index 0000000..78bf1de --- /dev/null +++ b/web/vendor/bootstrap-icons/envelope.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eraser-fill.svg b/web/vendor/bootstrap-icons/eraser-fill.svg new file mode 100644 index 0000000..c3866e1 --- /dev/null +++ b/web/vendor/bootstrap-icons/eraser-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eraser.svg b/web/vendor/bootstrap-icons/eraser.svg new file mode 100644 index 0000000..fe62336 --- /dev/null +++ b/web/vendor/bootstrap-icons/eraser.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/escape.svg b/web/vendor/bootstrap-icons/escape.svg new file mode 100644 index 0000000..66176b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/escape.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ethernet.svg b/web/vendor/bootstrap-icons/ethernet.svg new file mode 100644 index 0000000..739a2de --- /dev/null +++ b/web/vendor/bootstrap-icons/ethernet.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ev-front-fill.svg b/web/vendor/bootstrap-icons/ev-front-fill.svg new file mode 100644 index 0000000..53b947d --- /dev/null +++ b/web/vendor/bootstrap-icons/ev-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ev-front.svg b/web/vendor/bootstrap-icons/ev-front.svg new file mode 100644 index 0000000..7f053a5 --- /dev/null +++ b/web/vendor/bootstrap-icons/ev-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ev-station-fill.svg b/web/vendor/bootstrap-icons/ev-station-fill.svg new file mode 100644 index 0000000..a1ad007 --- /dev/null +++ b/web/vendor/bootstrap-icons/ev-station-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ev-station.svg b/web/vendor/bootstrap-icons/ev-station.svg new file mode 100644 index 0000000..90470f6 --- /dev/null +++ b/web/vendor/bootstrap-icons/ev-station.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-circle-fill.svg b/web/vendor/bootstrap-icons/exclamation-circle-fill.svg new file mode 100644 index 0000000..13ce7ab --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-circle.svg b/web/vendor/bootstrap-icons/exclamation-circle.svg new file mode 100644 index 0000000..f3befe0 --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-diamond-fill.svg b/web/vendor/bootstrap-icons/exclamation-diamond-fill.svg new file mode 100644 index 0000000..cb14aee --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-diamond-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-diamond.svg b/web/vendor/bootstrap-icons/exclamation-diamond.svg new file mode 100644 index 0000000..4881e6d --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-diamond.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-lg.svg b/web/vendor/bootstrap-icons/exclamation-lg.svg new file mode 100644 index 0000000..18f6a87 --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-octagon-fill.svg b/web/vendor/bootstrap-icons/exclamation-octagon-fill.svg new file mode 100644 index 0000000..494010b --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-octagon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-octagon.svg b/web/vendor/bootstrap-icons/exclamation-octagon.svg new file mode 100644 index 0000000..7f25938 --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-octagon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-square-fill.svg b/web/vendor/bootstrap-icons/exclamation-square-fill.svg new file mode 100644 index 0000000..d80a4e9 --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-square.svg b/web/vendor/bootstrap-icons/exclamation-square.svg new file mode 100644 index 0000000..2a0f2ae --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-triangle-fill.svg b/web/vendor/bootstrap-icons/exclamation-triangle-fill.svg new file mode 100644 index 0000000..52fd508 --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-triangle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation-triangle.svg b/web/vendor/bootstrap-icons/exclamation-triangle.svg new file mode 100644 index 0000000..506b777 --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation-triangle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclamation.svg b/web/vendor/bootstrap-icons/exclamation.svg new file mode 100644 index 0000000..d39cb95 --- /dev/null +++ b/web/vendor/bootstrap-icons/exclamation.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exclude.svg b/web/vendor/bootstrap-icons/exclude.svg new file mode 100644 index 0000000..ef18355 --- /dev/null +++ b/web/vendor/bootstrap-icons/exclude.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/explicit-fill.svg b/web/vendor/bootstrap-icons/explicit-fill.svg new file mode 100644 index 0000000..c0cb6f0 --- /dev/null +++ b/web/vendor/bootstrap-icons/explicit-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/explicit.svg b/web/vendor/bootstrap-icons/explicit.svg new file mode 100644 index 0000000..a7ffae7 --- /dev/null +++ b/web/vendor/bootstrap-icons/explicit.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/exposure.svg b/web/vendor/bootstrap-icons/exposure.svg new file mode 100644 index 0000000..9de0e64 --- /dev/null +++ b/web/vendor/bootstrap-icons/exposure.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eye-fill.svg b/web/vendor/bootstrap-icons/eye-fill.svg new file mode 100644 index 0000000..288d2eb --- /dev/null +++ b/web/vendor/bootstrap-icons/eye-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eye-slash-fill.svg b/web/vendor/bootstrap-icons/eye-slash-fill.svg new file mode 100644 index 0000000..10cca74 --- /dev/null +++ b/web/vendor/bootstrap-icons/eye-slash-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eye-slash.svg b/web/vendor/bootstrap-icons/eye-slash.svg new file mode 100644 index 0000000..359c270 --- /dev/null +++ b/web/vendor/bootstrap-icons/eye-slash.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eye.svg b/web/vendor/bootstrap-icons/eye.svg new file mode 100644 index 0000000..393b485 --- /dev/null +++ b/web/vendor/bootstrap-icons/eye.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eyedropper.svg b/web/vendor/bootstrap-icons/eyedropper.svg new file mode 100644 index 0000000..4579888 --- /dev/null +++ b/web/vendor/bootstrap-icons/eyedropper.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/eyeglasses.svg b/web/vendor/bootstrap-icons/eyeglasses.svg new file mode 100644 index 0000000..6b2eb97 --- /dev/null +++ b/web/vendor/bootstrap-icons/eyeglasses.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/facebook.svg b/web/vendor/bootstrap-icons/facebook.svg new file mode 100644 index 0000000..5fc7cec --- /dev/null +++ b/web/vendor/bootstrap-icons/facebook.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fan.svg b/web/vendor/bootstrap-icons/fan.svg new file mode 100644 index 0000000..ec8fe20 --- /dev/null +++ b/web/vendor/bootstrap-icons/fan.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fast-forward-btn-fill.svg b/web/vendor/bootstrap-icons/fast-forward-btn-fill.svg new file mode 100644 index 0000000..fd750fe --- /dev/null +++ b/web/vendor/bootstrap-icons/fast-forward-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fast-forward-btn.svg b/web/vendor/bootstrap-icons/fast-forward-btn.svg new file mode 100644 index 0000000..5e68554 --- /dev/null +++ b/web/vendor/bootstrap-icons/fast-forward-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fast-forward-circle-fill.svg b/web/vendor/bootstrap-icons/fast-forward-circle-fill.svg new file mode 100644 index 0000000..3946fa3 --- /dev/null +++ b/web/vendor/bootstrap-icons/fast-forward-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fast-forward-circle.svg b/web/vendor/bootstrap-icons/fast-forward-circle.svg new file mode 100644 index 0000000..e7f7158 --- /dev/null +++ b/web/vendor/bootstrap-icons/fast-forward-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fast-forward-fill.svg b/web/vendor/bootstrap-icons/fast-forward-fill.svg new file mode 100644 index 0000000..ae17a7e --- /dev/null +++ b/web/vendor/bootstrap-icons/fast-forward-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fast-forward.svg b/web/vendor/bootstrap-icons/fast-forward.svg new file mode 100644 index 0000000..2142979 --- /dev/null +++ b/web/vendor/bootstrap-icons/fast-forward.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/feather.svg b/web/vendor/bootstrap-icons/feather.svg new file mode 100644 index 0000000..f0462c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/feather.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/feather2.svg b/web/vendor/bootstrap-icons/feather2.svg new file mode 100644 index 0000000..badc17a --- /dev/null +++ b/web/vendor/bootstrap-icons/feather2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-arrow-down-fill.svg b/web/vendor/bootstrap-icons/file-arrow-down-fill.svg new file mode 100644 index 0000000..5b5c552 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-arrow-down-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-arrow-down.svg b/web/vendor/bootstrap-icons/file-arrow-down.svg new file mode 100644 index 0000000..f504b98 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-arrow-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-arrow-up-fill.svg b/web/vendor/bootstrap-icons/file-arrow-up-fill.svg new file mode 100644 index 0000000..ef56199 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-arrow-up-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-arrow-up.svg b/web/vendor/bootstrap-icons/file-arrow-up.svg new file mode 100644 index 0000000..9c88467 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-arrow-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-bar-graph-fill.svg b/web/vendor/bootstrap-icons/file-bar-graph-fill.svg new file mode 100644 index 0000000..686b60b --- /dev/null +++ b/web/vendor/bootstrap-icons/file-bar-graph-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-bar-graph.svg b/web/vendor/bootstrap-icons/file-bar-graph.svg new file mode 100644 index 0000000..f953927 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-bar-graph.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-binary-fill.svg b/web/vendor/bootstrap-icons/file-binary-fill.svg new file mode 100644 index 0000000..920c2a7 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-binary-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-binary.svg b/web/vendor/bootstrap-icons/file-binary.svg new file mode 100644 index 0000000..0ecece7 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-binary.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-break-fill.svg b/web/vendor/bootstrap-icons/file-break-fill.svg new file mode 100644 index 0000000..bdbe1b9 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-break-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-break.svg b/web/vendor/bootstrap-icons/file-break.svg new file mode 100644 index 0000000..e94b3a3 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-break.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-check-fill.svg b/web/vendor/bootstrap-icons/file-check-fill.svg new file mode 100644 index 0000000..cf165d3 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-check.svg b/web/vendor/bootstrap-icons/file-check.svg new file mode 100644 index 0000000..10863e1 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-code-fill.svg b/web/vendor/bootstrap-icons/file-code-fill.svg new file mode 100644 index 0000000..912ed0c --- /dev/null +++ b/web/vendor/bootstrap-icons/file-code-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-code.svg b/web/vendor/bootstrap-icons/file-code.svg new file mode 100644 index 0000000..58b0014 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-code.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-diff-fill.svg b/web/vendor/bootstrap-icons/file-diff-fill.svg new file mode 100644 index 0000000..c108b87 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-diff-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-diff.svg b/web/vendor/bootstrap-icons/file-diff.svg new file mode 100644 index 0000000..088c6de --- /dev/null +++ b/web/vendor/bootstrap-icons/file-diff.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-arrow-down-fill.svg b/web/vendor/bootstrap-icons/file-earmark-arrow-down-fill.svg new file mode 100644 index 0000000..3941f1f --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-arrow-down-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-arrow-down.svg b/web/vendor/bootstrap-icons/file-earmark-arrow-down.svg new file mode 100644 index 0000000..37c4cd5 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-arrow-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-arrow-up-fill.svg b/web/vendor/bootstrap-icons/file-earmark-arrow-up-fill.svg new file mode 100644 index 0000000..97a339d --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-arrow-up-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-arrow-up.svg b/web/vendor/bootstrap-icons/file-earmark-arrow-up.svg new file mode 100644 index 0000000..1c827e9 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-arrow-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-bar-graph-fill.svg b/web/vendor/bootstrap-icons/file-earmark-bar-graph-fill.svg new file mode 100644 index 0000000..a4c69b5 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-bar-graph-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-bar-graph.svg b/web/vendor/bootstrap-icons/file-earmark-bar-graph.svg new file mode 100644 index 0000000..d367eca --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-bar-graph.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-binary-fill.svg b/web/vendor/bootstrap-icons/file-earmark-binary-fill.svg new file mode 100644 index 0000000..a1f9933 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-binary-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-binary.svg b/web/vendor/bootstrap-icons/file-earmark-binary.svg new file mode 100644 index 0000000..1528578 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-binary.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-break-fill.svg b/web/vendor/bootstrap-icons/file-earmark-break-fill.svg new file mode 100644 index 0000000..e9aadce --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-break-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-break.svg b/web/vendor/bootstrap-icons/file-earmark-break.svg new file mode 100644 index 0000000..4874715 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-break.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-check-fill.svg b/web/vendor/bootstrap-icons/file-earmark-check-fill.svg new file mode 100644 index 0000000..f3e9eb3 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-check.svg b/web/vendor/bootstrap-icons/file-earmark-check.svg new file mode 100644 index 0000000..dc36963 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-code-fill.svg b/web/vendor/bootstrap-icons/file-earmark-code-fill.svg new file mode 100644 index 0000000..2c154fa --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-code-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-code.svg b/web/vendor/bootstrap-icons/file-earmark-code.svg new file mode 100644 index 0000000..ccd3528 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-code.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-diff-fill.svg b/web/vendor/bootstrap-icons/file-earmark-diff-fill.svg new file mode 100644 index 0000000..3b71e66 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-diff-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-diff.svg b/web/vendor/bootstrap-icons/file-earmark-diff.svg new file mode 100644 index 0000000..97dfc0b --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-diff.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-easel-fill.svg b/web/vendor/bootstrap-icons/file-earmark-easel-fill.svg new file mode 100644 index 0000000..e74c974 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-easel-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-easel.svg b/web/vendor/bootstrap-icons/file-earmark-easel.svg new file mode 100644 index 0000000..2feeabc --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-easel.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-excel-fill.svg b/web/vendor/bootstrap-icons/file-earmark-excel-fill.svg new file mode 100644 index 0000000..405a572 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-excel-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-excel.svg b/web/vendor/bootstrap-icons/file-earmark-excel.svg new file mode 100644 index 0000000..5432bcf --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-excel.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-fill.svg b/web/vendor/bootstrap-icons/file-earmark-fill.svg new file mode 100644 index 0000000..2f3ef6c --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-font-fill.svg b/web/vendor/bootstrap-icons/file-earmark-font-fill.svg new file mode 100644 index 0000000..d3014bf --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-font-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-font.svg b/web/vendor/bootstrap-icons/file-earmark-font.svg new file mode 100644 index 0000000..c9864cd --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-font.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-image-fill.svg b/web/vendor/bootstrap-icons/file-earmark-image-fill.svg new file mode 100644 index 0000000..3ec76dc --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-image-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-image.svg b/web/vendor/bootstrap-icons/file-earmark-image.svg new file mode 100644 index 0000000..1c3815d --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-image.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-lock-fill.svg b/web/vendor/bootstrap-icons/file-earmark-lock-fill.svg new file mode 100644 index 0000000..4c45a61 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-lock-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-lock.svg b/web/vendor/bootstrap-icons/file-earmark-lock.svg new file mode 100644 index 0000000..c8319a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-lock2-fill.svg b/web/vendor/bootstrap-icons/file-earmark-lock2-fill.svg new file mode 100644 index 0000000..76e8bd8 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-lock2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-lock2.svg b/web/vendor/bootstrap-icons/file-earmark-lock2.svg new file mode 100644 index 0000000..bd8f592 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-lock2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-medical-fill.svg b/web/vendor/bootstrap-icons/file-earmark-medical-fill.svg new file mode 100644 index 0000000..42a0581 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-medical-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-medical.svg b/web/vendor/bootstrap-icons/file-earmark-medical.svg new file mode 100644 index 0000000..e24c90c --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-medical.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-minus-fill.svg b/web/vendor/bootstrap-icons/file-earmark-minus-fill.svg new file mode 100644 index 0000000..a5dca2d --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-minus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-minus.svg b/web/vendor/bootstrap-icons/file-earmark-minus.svg new file mode 100644 index 0000000..cdc0026 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-music-fill.svg b/web/vendor/bootstrap-icons/file-earmark-music-fill.svg new file mode 100644 index 0000000..b865e4e --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-music-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-music.svg b/web/vendor/bootstrap-icons/file-earmark-music.svg new file mode 100644 index 0000000..e18ec85 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-music.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-pdf-fill.svg b/web/vendor/bootstrap-icons/file-earmark-pdf-fill.svg new file mode 100644 index 0000000..219c5a3 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-pdf-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-pdf.svg b/web/vendor/bootstrap-icons/file-earmark-pdf.svg new file mode 100644 index 0000000..51b9975 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-pdf.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-person-fill.svg b/web/vendor/bootstrap-icons/file-earmark-person-fill.svg new file mode 100644 index 0000000..49ceda6 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-person-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-person.svg b/web/vendor/bootstrap-icons/file-earmark-person.svg new file mode 100644 index 0000000..08a78cc --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-person.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-play-fill.svg b/web/vendor/bootstrap-icons/file-earmark-play-fill.svg new file mode 100644 index 0000000..341eb37 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-play-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-play.svg b/web/vendor/bootstrap-icons/file-earmark-play.svg new file mode 100644 index 0000000..abe215b --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-play.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-plus-fill.svg b/web/vendor/bootstrap-icons/file-earmark-plus-fill.svg new file mode 100644 index 0000000..ef99c8a --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-plus.svg b/web/vendor/bootstrap-icons/file-earmark-plus.svg new file mode 100644 index 0000000..964e855 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-post-fill.svg b/web/vendor/bootstrap-icons/file-earmark-post-fill.svg new file mode 100644 index 0000000..548773f --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-post-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-post.svg b/web/vendor/bootstrap-icons/file-earmark-post.svg new file mode 100644 index 0000000..d9f44a1 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-post.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-ppt-fill.svg b/web/vendor/bootstrap-icons/file-earmark-ppt-fill.svg new file mode 100644 index 0000000..e96a461 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-ppt-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-ppt.svg b/web/vendor/bootstrap-icons/file-earmark-ppt.svg new file mode 100644 index 0000000..cab71bc --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-ppt.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-richtext-fill.svg b/web/vendor/bootstrap-icons/file-earmark-richtext-fill.svg new file mode 100644 index 0000000..55da25c --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-richtext-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-richtext.svg b/web/vendor/bootstrap-icons/file-earmark-richtext.svg new file mode 100644 index 0000000..3f3a6ad --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-richtext.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-ruled-fill.svg b/web/vendor/bootstrap-icons/file-earmark-ruled-fill.svg new file mode 100644 index 0000000..ee90c80 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-ruled-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-ruled.svg b/web/vendor/bootstrap-icons/file-earmark-ruled.svg new file mode 100644 index 0000000..ebd617e --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-ruled.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-slides-fill.svg b/web/vendor/bootstrap-icons/file-earmark-slides-fill.svg new file mode 100644 index 0000000..e7f76f4 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-slides-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-slides.svg b/web/vendor/bootstrap-icons/file-earmark-slides.svg new file mode 100644 index 0000000..e643cd3 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-slides.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-spreadsheet-fill.svg b/web/vendor/bootstrap-icons/file-earmark-spreadsheet-fill.svg new file mode 100644 index 0000000..02ac9ec --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-spreadsheet-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-spreadsheet.svg b/web/vendor/bootstrap-icons/file-earmark-spreadsheet.svg new file mode 100644 index 0000000..a6bb6e4 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-spreadsheet.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-text-fill.svg b/web/vendor/bootstrap-icons/file-earmark-text-fill.svg new file mode 100644 index 0000000..bfedd6b --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-text-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-text.svg b/web/vendor/bootstrap-icons/file-earmark-text.svg new file mode 100644 index 0000000..7ae53fc --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-text.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-word-fill.svg b/web/vendor/bootstrap-icons/file-earmark-word-fill.svg new file mode 100644 index 0000000..259e767 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-word-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-word.svg b/web/vendor/bootstrap-icons/file-earmark-word.svg new file mode 100644 index 0000000..ef4727c --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-word.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-x-fill.svg b/web/vendor/bootstrap-icons/file-earmark-x-fill.svg new file mode 100644 index 0000000..f683966 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-x.svg b/web/vendor/bootstrap-icons/file-earmark-x.svg new file mode 100644 index 0000000..4dd9da9 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-zip-fill.svg b/web/vendor/bootstrap-icons/file-earmark-zip-fill.svg new file mode 100644 index 0000000..a17fa9e --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-zip-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark-zip.svg b/web/vendor/bootstrap-icons/file-earmark-zip.svg new file mode 100644 index 0000000..f5f8ccc --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark-zip.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-earmark.svg b/web/vendor/bootstrap-icons/file-earmark.svg new file mode 100644 index 0000000..d8d8774 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-earmark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-easel-fill.svg b/web/vendor/bootstrap-icons/file-easel-fill.svg new file mode 100644 index 0000000..fa9f915 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-easel-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-easel.svg b/web/vendor/bootstrap-icons/file-easel.svg new file mode 100644 index 0000000..6366bc0 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-easel.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-excel-fill.svg b/web/vendor/bootstrap-icons/file-excel-fill.svg new file mode 100644 index 0000000..bddcea2 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-excel-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-excel.svg b/web/vendor/bootstrap-icons/file-excel.svg new file mode 100644 index 0000000..8bf2f5e --- /dev/null +++ b/web/vendor/bootstrap-icons/file-excel.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-fill.svg b/web/vendor/bootstrap-icons/file-fill.svg new file mode 100644 index 0000000..e5f8c4a --- /dev/null +++ b/web/vendor/bootstrap-icons/file-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-font-fill.svg b/web/vendor/bootstrap-icons/file-font-fill.svg new file mode 100644 index 0000000..6bda7b1 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-font-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-font.svg b/web/vendor/bootstrap-icons/file-font.svg new file mode 100644 index 0000000..b75f9a4 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-font.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-image-fill.svg b/web/vendor/bootstrap-icons/file-image-fill.svg new file mode 100644 index 0000000..7c73ece --- /dev/null +++ b/web/vendor/bootstrap-icons/file-image-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-image.svg b/web/vendor/bootstrap-icons/file-image.svg new file mode 100644 index 0000000..b063628 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-image.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-lock-fill.svg b/web/vendor/bootstrap-icons/file-lock-fill.svg new file mode 100644 index 0000000..1f7c3f2 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-lock-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-lock.svg b/web/vendor/bootstrap-icons/file-lock.svg new file mode 100644 index 0000000..195deef --- /dev/null +++ b/web/vendor/bootstrap-icons/file-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-lock2-fill.svg b/web/vendor/bootstrap-icons/file-lock2-fill.svg new file mode 100644 index 0000000..82426e8 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-lock2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-lock2.svg b/web/vendor/bootstrap-icons/file-lock2.svg new file mode 100644 index 0000000..125a747 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-lock2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-medical-fill.svg b/web/vendor/bootstrap-icons/file-medical-fill.svg new file mode 100644 index 0000000..2f4d3b2 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-medical-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-medical.svg b/web/vendor/bootstrap-icons/file-medical.svg new file mode 100644 index 0000000..07e8495 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-medical.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-minus-fill.svg b/web/vendor/bootstrap-icons/file-minus-fill.svg new file mode 100644 index 0000000..99f7d53 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-minus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-minus.svg b/web/vendor/bootstrap-icons/file-minus.svg new file mode 100644 index 0000000..880409b --- /dev/null +++ b/web/vendor/bootstrap-icons/file-minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-music-fill.svg b/web/vendor/bootstrap-icons/file-music-fill.svg new file mode 100644 index 0000000..38099f3 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-music-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-music.svg b/web/vendor/bootstrap-icons/file-music.svg new file mode 100644 index 0000000..a24d41e --- /dev/null +++ b/web/vendor/bootstrap-icons/file-music.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-pdf-fill.svg b/web/vendor/bootstrap-icons/file-pdf-fill.svg new file mode 100644 index 0000000..c88d1b8 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-pdf-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-pdf.svg b/web/vendor/bootstrap-icons/file-pdf.svg new file mode 100644 index 0000000..e7bdcba --- /dev/null +++ b/web/vendor/bootstrap-icons/file-pdf.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-person-fill.svg b/web/vendor/bootstrap-icons/file-person-fill.svg new file mode 100644 index 0000000..c212b00 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-person-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-person.svg b/web/vendor/bootstrap-icons/file-person.svg new file mode 100644 index 0000000..e102abf --- /dev/null +++ b/web/vendor/bootstrap-icons/file-person.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-play-fill.svg b/web/vendor/bootstrap-icons/file-play-fill.svg new file mode 100644 index 0000000..a6dce8a --- /dev/null +++ b/web/vendor/bootstrap-icons/file-play-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-play.svg b/web/vendor/bootstrap-icons/file-play.svg new file mode 100644 index 0000000..d5505e3 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-play.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-plus-fill.svg b/web/vendor/bootstrap-icons/file-plus-fill.svg new file mode 100644 index 0000000..bdb7ee0 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-plus.svg b/web/vendor/bootstrap-icons/file-plus.svg new file mode 100644 index 0000000..af41ae1 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-post-fill.svg b/web/vendor/bootstrap-icons/file-post-fill.svg new file mode 100644 index 0000000..336f219 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-post-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-post.svg b/web/vendor/bootstrap-icons/file-post.svg new file mode 100644 index 0000000..6807724 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-post.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-ppt-fill.svg b/web/vendor/bootstrap-icons/file-ppt-fill.svg new file mode 100644 index 0000000..b8c3973 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-ppt-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-ppt.svg b/web/vendor/bootstrap-icons/file-ppt.svg new file mode 100644 index 0000000..bee27d4 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-ppt.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-richtext-fill.svg b/web/vendor/bootstrap-icons/file-richtext-fill.svg new file mode 100644 index 0000000..a98e5d7 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-richtext-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-richtext.svg b/web/vendor/bootstrap-icons/file-richtext.svg new file mode 100644 index 0000000..3ceb420 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-richtext.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-ruled-fill.svg b/web/vendor/bootstrap-icons/file-ruled-fill.svg new file mode 100644 index 0000000..8bfe726 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-ruled-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-ruled.svg b/web/vendor/bootstrap-icons/file-ruled.svg new file mode 100644 index 0000000..7236cfc --- /dev/null +++ b/web/vendor/bootstrap-icons/file-ruled.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-slides-fill.svg b/web/vendor/bootstrap-icons/file-slides-fill.svg new file mode 100644 index 0000000..b75d3f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-slides-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-slides.svg b/web/vendor/bootstrap-icons/file-slides.svg new file mode 100644 index 0000000..0e63548 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-slides.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-spreadsheet-fill.svg b/web/vendor/bootstrap-icons/file-spreadsheet-fill.svg new file mode 100644 index 0000000..6db7eb6 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-spreadsheet-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-spreadsheet.svg b/web/vendor/bootstrap-icons/file-spreadsheet.svg new file mode 100644 index 0000000..55b53ec --- /dev/null +++ b/web/vendor/bootstrap-icons/file-spreadsheet.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-text-fill.svg b/web/vendor/bootstrap-icons/file-text-fill.svg new file mode 100644 index 0000000..6da36b2 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-text-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-text.svg b/web/vendor/bootstrap-icons/file-text.svg new file mode 100644 index 0000000..95dc704 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-text.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-word-fill.svg b/web/vendor/bootstrap-icons/file-word-fill.svg new file mode 100644 index 0000000..6f578f0 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-word-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-word.svg b/web/vendor/bootstrap-icons/file-word.svg new file mode 100644 index 0000000..732b59a --- /dev/null +++ b/web/vendor/bootstrap-icons/file-word.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-x-fill.svg b/web/vendor/bootstrap-icons/file-x-fill.svg new file mode 100644 index 0000000..0455659 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-x.svg b/web/vendor/bootstrap-icons/file-x.svg new file mode 100644 index 0000000..5ab0b48 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-zip-fill.svg b/web/vendor/bootstrap-icons/file-zip-fill.svg new file mode 100644 index 0000000..1d80366 --- /dev/null +++ b/web/vendor/bootstrap-icons/file-zip-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file-zip.svg b/web/vendor/bootstrap-icons/file-zip.svg new file mode 100644 index 0000000..e3b633e --- /dev/null +++ b/web/vendor/bootstrap-icons/file-zip.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/file.svg b/web/vendor/bootstrap-icons/file.svg new file mode 100644 index 0000000..4a5dd73 --- /dev/null +++ b/web/vendor/bootstrap-icons/file.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/files-alt.svg b/web/vendor/bootstrap-icons/files-alt.svg new file mode 100644 index 0000000..b42d764 --- /dev/null +++ b/web/vendor/bootstrap-icons/files-alt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/files.svg b/web/vendor/bootstrap-icons/files.svg new file mode 100644 index 0000000..6170ab5 --- /dev/null +++ b/web/vendor/bootstrap-icons/files.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-aac.svg b/web/vendor/bootstrap-icons/filetype-aac.svg new file mode 100644 index 0000000..b6a5c47 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-aac.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-ai.svg b/web/vendor/bootstrap-icons/filetype-ai.svg new file mode 100644 index 0000000..fe2bcaa --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-ai.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-bmp.svg b/web/vendor/bootstrap-icons/filetype-bmp.svg new file mode 100644 index 0000000..587381c --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-bmp.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-cs.svg b/web/vendor/bootstrap-icons/filetype-cs.svg new file mode 100644 index 0000000..90ed8de --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-cs.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-css.svg b/web/vendor/bootstrap-icons/filetype-css.svg new file mode 100644 index 0000000..8f0864f --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-css.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-csv.svg b/web/vendor/bootstrap-icons/filetype-csv.svg new file mode 100644 index 0000000..fa097aa --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-csv.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-doc.svg b/web/vendor/bootstrap-icons/filetype-doc.svg new file mode 100644 index 0000000..f75847f --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-doc.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-docx.svg b/web/vendor/bootstrap-icons/filetype-docx.svg new file mode 100644 index 0000000..1b6c172 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-docx.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-exe.svg b/web/vendor/bootstrap-icons/filetype-exe.svg new file mode 100644 index 0000000..cdafeb1 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-exe.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-gif.svg b/web/vendor/bootstrap-icons/filetype-gif.svg new file mode 100644 index 0000000..b39234f --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-gif.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-heic.svg b/web/vendor/bootstrap-icons/filetype-heic.svg new file mode 100644 index 0000000..a022060 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-heic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-html.svg b/web/vendor/bootstrap-icons/filetype-html.svg new file mode 100644 index 0000000..1661a94 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-html.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-java.svg b/web/vendor/bootstrap-icons/filetype-java.svg new file mode 100644 index 0000000..eeeab41 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-java.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-jpg.svg b/web/vendor/bootstrap-icons/filetype-jpg.svg new file mode 100644 index 0000000..7d939ec --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-jpg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-js.svg b/web/vendor/bootstrap-icons/filetype-js.svg new file mode 100644 index 0000000..4f4a00c --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-js.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-json.svg b/web/vendor/bootstrap-icons/filetype-json.svg new file mode 100644 index 0000000..a4ccced --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-json.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-jsx.svg b/web/vendor/bootstrap-icons/filetype-jsx.svg new file mode 100644 index 0000000..256b5c5 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-jsx.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-key.svg b/web/vendor/bootstrap-icons/filetype-key.svg new file mode 100644 index 0000000..d164bc8 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-key.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-m4p.svg b/web/vendor/bootstrap-icons/filetype-m4p.svg new file mode 100644 index 0000000..ae8f611 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-m4p.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-md.svg b/web/vendor/bootstrap-icons/filetype-md.svg new file mode 100644 index 0000000..40e5139 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-md.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-mdx.svg b/web/vendor/bootstrap-icons/filetype-mdx.svg new file mode 100644 index 0000000..43a9153 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-mdx.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-mov.svg b/web/vendor/bootstrap-icons/filetype-mov.svg new file mode 100644 index 0000000..27e6391 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-mov.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-mp3.svg b/web/vendor/bootstrap-icons/filetype-mp3.svg new file mode 100644 index 0000000..f25e6a8 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-mp3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-mp4.svg b/web/vendor/bootstrap-icons/filetype-mp4.svg new file mode 100644 index 0000000..d27e9ff --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-mp4.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-otf.svg b/web/vendor/bootstrap-icons/filetype-otf.svg new file mode 100644 index 0000000..f16eb61 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-otf.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-pdf.svg b/web/vendor/bootstrap-icons/filetype-pdf.svg new file mode 100644 index 0000000..e8bb772 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-pdf.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-php.svg b/web/vendor/bootstrap-icons/filetype-php.svg new file mode 100644 index 0000000..4d532df --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-php.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-png.svg b/web/vendor/bootstrap-icons/filetype-png.svg new file mode 100644 index 0000000..659e266 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-png.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-ppt.svg b/web/vendor/bootstrap-icons/filetype-ppt.svg new file mode 100644 index 0000000..e53b1ec --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-ppt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-pptx.svg b/web/vendor/bootstrap-icons/filetype-pptx.svg new file mode 100644 index 0000000..f68e939 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-pptx.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-psd.svg b/web/vendor/bootstrap-icons/filetype-psd.svg new file mode 100644 index 0000000..6fefd08 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-psd.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-py.svg b/web/vendor/bootstrap-icons/filetype-py.svg new file mode 100644 index 0000000..14fd4ef --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-py.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-raw.svg b/web/vendor/bootstrap-icons/filetype-raw.svg new file mode 100644 index 0000000..da98185 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-raw.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-rb.svg b/web/vendor/bootstrap-icons/filetype-rb.svg new file mode 100644 index 0000000..3466a1f --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-rb.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-sass.svg b/web/vendor/bootstrap-icons/filetype-sass.svg new file mode 100644 index 0000000..3632662 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-sass.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-scss.svg b/web/vendor/bootstrap-icons/filetype-scss.svg new file mode 100644 index 0000000..8d1935f --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-scss.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-sh.svg b/web/vendor/bootstrap-icons/filetype-sh.svg new file mode 100644 index 0000000..592c408 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-sh.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-sql.svg b/web/vendor/bootstrap-icons/filetype-sql.svg new file mode 100644 index 0000000..814137d --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-sql.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-svg.svg b/web/vendor/bootstrap-icons/filetype-svg.svg new file mode 100644 index 0000000..222d9ac --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-svg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-tiff.svg b/web/vendor/bootstrap-icons/filetype-tiff.svg new file mode 100644 index 0000000..e101575 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-tiff.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-tsx.svg b/web/vendor/bootstrap-icons/filetype-tsx.svg new file mode 100644 index 0000000..73dd643 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-tsx.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-ttf.svg b/web/vendor/bootstrap-icons/filetype-ttf.svg new file mode 100644 index 0000000..9c93584 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-ttf.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-txt.svg b/web/vendor/bootstrap-icons/filetype-txt.svg new file mode 100644 index 0000000..1e27bcf --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-txt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-wav.svg b/web/vendor/bootstrap-icons/filetype-wav.svg new file mode 100644 index 0000000..6725640 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-wav.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-woff.svg b/web/vendor/bootstrap-icons/filetype-woff.svg new file mode 100644 index 0000000..f29a4b1 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-woff.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-xls.svg b/web/vendor/bootstrap-icons/filetype-xls.svg new file mode 100644 index 0000000..5f79b16 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-xls.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-xlsx.svg b/web/vendor/bootstrap-icons/filetype-xlsx.svg new file mode 100644 index 0000000..5202bf7 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-xlsx.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-xml.svg b/web/vendor/bootstrap-icons/filetype-xml.svg new file mode 100644 index 0000000..ba9ffb6 --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-xml.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filetype-yml.svg b/web/vendor/bootstrap-icons/filetype-yml.svg new file mode 100644 index 0000000..17a9ebe --- /dev/null +++ b/web/vendor/bootstrap-icons/filetype-yml.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/film.svg b/web/vendor/bootstrap-icons/film.svg new file mode 100644 index 0000000..40c2eb9 --- /dev/null +++ b/web/vendor/bootstrap-icons/film.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filter-circle-fill.svg b/web/vendor/bootstrap-icons/filter-circle-fill.svg new file mode 100644 index 0000000..1aa0f39 --- /dev/null +++ b/web/vendor/bootstrap-icons/filter-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filter-circle.svg b/web/vendor/bootstrap-icons/filter-circle.svg new file mode 100644 index 0000000..42c1b84 --- /dev/null +++ b/web/vendor/bootstrap-icons/filter-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filter-left.svg b/web/vendor/bootstrap-icons/filter-left.svg new file mode 100644 index 0000000..bb1ee92 --- /dev/null +++ b/web/vendor/bootstrap-icons/filter-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filter-right.svg b/web/vendor/bootstrap-icons/filter-right.svg new file mode 100644 index 0000000..6a5083c --- /dev/null +++ b/web/vendor/bootstrap-icons/filter-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filter-square-fill.svg b/web/vendor/bootstrap-icons/filter-square-fill.svg new file mode 100644 index 0000000..438b8c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/filter-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filter-square.svg b/web/vendor/bootstrap-icons/filter-square.svg new file mode 100644 index 0000000..d243b0b --- /dev/null +++ b/web/vendor/bootstrap-icons/filter-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/filter.svg b/web/vendor/bootstrap-icons/filter.svg new file mode 100644 index 0000000..a7d7dac --- /dev/null +++ b/web/vendor/bootstrap-icons/filter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fingerprint.svg b/web/vendor/bootstrap-icons/fingerprint.svg new file mode 100644 index 0000000..08252bb --- /dev/null +++ b/web/vendor/bootstrap-icons/fingerprint.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fire.svg b/web/vendor/bootstrap-icons/fire.svg new file mode 100644 index 0000000..a58e6dd --- /dev/null +++ b/web/vendor/bootstrap-icons/fire.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flag-fill.svg b/web/vendor/bootstrap-icons/flag-fill.svg new file mode 100644 index 0000000..8b92331 --- /dev/null +++ b/web/vendor/bootstrap-icons/flag-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flag.svg b/web/vendor/bootstrap-icons/flag.svg new file mode 100644 index 0000000..f8b6dab --- /dev/null +++ b/web/vendor/bootstrap-icons/flag.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flask-fill.svg b/web/vendor/bootstrap-icons/flask-fill.svg new file mode 100644 index 0000000..61207af --- /dev/null +++ b/web/vendor/bootstrap-icons/flask-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flask-florence-fill.svg b/web/vendor/bootstrap-icons/flask-florence-fill.svg new file mode 100644 index 0000000..70896a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/flask-florence-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flask-florence.svg b/web/vendor/bootstrap-icons/flask-florence.svg new file mode 100644 index 0000000..6df75f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/flask-florence.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flask.svg b/web/vendor/bootstrap-icons/flask.svg new file mode 100644 index 0000000..01cc1ba --- /dev/null +++ b/web/vendor/bootstrap-icons/flask.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/floppy-fill.svg b/web/vendor/bootstrap-icons/floppy-fill.svg new file mode 100644 index 0000000..87a43ce --- /dev/null +++ b/web/vendor/bootstrap-icons/floppy-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/floppy.svg b/web/vendor/bootstrap-icons/floppy.svg new file mode 100644 index 0000000..65ae562 --- /dev/null +++ b/web/vendor/bootstrap-icons/floppy.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/floppy2-fill.svg b/web/vendor/bootstrap-icons/floppy2-fill.svg new file mode 100644 index 0000000..61a6cdb --- /dev/null +++ b/web/vendor/bootstrap-icons/floppy2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/floppy2.svg b/web/vendor/bootstrap-icons/floppy2.svg new file mode 100644 index 0000000..2412389 --- /dev/null +++ b/web/vendor/bootstrap-icons/floppy2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flower1.svg b/web/vendor/bootstrap-icons/flower1.svg new file mode 100644 index 0000000..3495858 --- /dev/null +++ b/web/vendor/bootstrap-icons/flower1.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flower2.svg b/web/vendor/bootstrap-icons/flower2.svg new file mode 100644 index 0000000..664a9c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/flower2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/flower3.svg b/web/vendor/bootstrap-icons/flower3.svg new file mode 100644 index 0000000..66845c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/flower3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder-check.svg b/web/vendor/bootstrap-icons/folder-check.svg new file mode 100644 index 0000000..57c6466 --- /dev/null +++ b/web/vendor/bootstrap-icons/folder-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder-fill.svg b/web/vendor/bootstrap-icons/folder-fill.svg new file mode 100644 index 0000000..113350c --- /dev/null +++ b/web/vendor/bootstrap-icons/folder-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder-minus.svg b/web/vendor/bootstrap-icons/folder-minus.svg new file mode 100644 index 0000000..41db30b --- /dev/null +++ b/web/vendor/bootstrap-icons/folder-minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder-plus.svg b/web/vendor/bootstrap-icons/folder-plus.svg new file mode 100644 index 0000000..85b5a18 --- /dev/null +++ b/web/vendor/bootstrap-icons/folder-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder-symlink-fill.svg b/web/vendor/bootstrap-icons/folder-symlink-fill.svg new file mode 100644 index 0000000..6407400 --- /dev/null +++ b/web/vendor/bootstrap-icons/folder-symlink-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder-symlink.svg b/web/vendor/bootstrap-icons/folder-symlink.svg new file mode 100644 index 0000000..7137637 --- /dev/null +++ b/web/vendor/bootstrap-icons/folder-symlink.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder-x.svg b/web/vendor/bootstrap-icons/folder-x.svg new file mode 100644 index 0000000..a6ed341 --- /dev/null +++ b/web/vendor/bootstrap-icons/folder-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder.svg b/web/vendor/bootstrap-icons/folder.svg new file mode 100644 index 0000000..a30c452 --- /dev/null +++ b/web/vendor/bootstrap-icons/folder.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder2-open.svg b/web/vendor/bootstrap-icons/folder2-open.svg new file mode 100644 index 0000000..7ffbb54 --- /dev/null +++ b/web/vendor/bootstrap-icons/folder2-open.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/folder2.svg b/web/vendor/bootstrap-icons/folder2.svg new file mode 100644 index 0000000..ce6a1af --- /dev/null +++ b/web/vendor/bootstrap-icons/folder2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fonts.svg b/web/vendor/bootstrap-icons/fonts.svg new file mode 100644 index 0000000..5e1f3c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/fonts.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fonts/bootstrap-icons.woff b/web/vendor/bootstrap-icons/fonts/bootstrap-icons.woff new file mode 100644 index 0000000..a4fa4f0 Binary files /dev/null and b/web/vendor/bootstrap-icons/fonts/bootstrap-icons.woff differ diff --git a/web/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 b/web/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 new file mode 100644 index 0000000..4d8c490 Binary files /dev/null and b/web/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 differ diff --git a/web/vendor/bootstrap-icons/fork-knife.svg b/web/vendor/bootstrap-icons/fork-knife.svg new file mode 100644 index 0000000..31277b4 --- /dev/null +++ b/web/vendor/bootstrap-icons/fork-knife.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/forward-fill.svg b/web/vendor/bootstrap-icons/forward-fill.svg new file mode 100644 index 0000000..27462e2 --- /dev/null +++ b/web/vendor/bootstrap-icons/forward-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/forward.svg b/web/vendor/bootstrap-icons/forward.svg new file mode 100644 index 0000000..ffe887c --- /dev/null +++ b/web/vendor/bootstrap-icons/forward.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/front.svg b/web/vendor/bootstrap-icons/front.svg new file mode 100644 index 0000000..59d854e --- /dev/null +++ b/web/vendor/bootstrap-icons/front.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fuel-pump-diesel-fill.svg b/web/vendor/bootstrap-icons/fuel-pump-diesel-fill.svg new file mode 100644 index 0000000..997d6f8 --- /dev/null +++ b/web/vendor/bootstrap-icons/fuel-pump-diesel-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fuel-pump-diesel.svg b/web/vendor/bootstrap-icons/fuel-pump-diesel.svg new file mode 100644 index 0000000..e170eeb --- /dev/null +++ b/web/vendor/bootstrap-icons/fuel-pump-diesel.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fuel-pump-fill.svg b/web/vendor/bootstrap-icons/fuel-pump-fill.svg new file mode 100644 index 0000000..46f92c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/fuel-pump-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fuel-pump.svg b/web/vendor/bootstrap-icons/fuel-pump.svg new file mode 100644 index 0000000..1704a6f --- /dev/null +++ b/web/vendor/bootstrap-icons/fuel-pump.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fullscreen-exit.svg b/web/vendor/bootstrap-icons/fullscreen-exit.svg new file mode 100644 index 0000000..cfde3a3 --- /dev/null +++ b/web/vendor/bootstrap-icons/fullscreen-exit.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/fullscreen.svg b/web/vendor/bootstrap-icons/fullscreen.svg new file mode 100644 index 0000000..d4f8a83 --- /dev/null +++ b/web/vendor/bootstrap-icons/fullscreen.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/funnel-fill.svg b/web/vendor/bootstrap-icons/funnel-fill.svg new file mode 100644 index 0000000..04d31a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/funnel-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/funnel.svg b/web/vendor/bootstrap-icons/funnel.svg new file mode 100644 index 0000000..28bfcf2 --- /dev/null +++ b/web/vendor/bootstrap-icons/funnel.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gear-fill.svg b/web/vendor/bootstrap-icons/gear-fill.svg new file mode 100644 index 0000000..ba8e2c5 --- /dev/null +++ b/web/vendor/bootstrap-icons/gear-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gear-wide-connected.svg b/web/vendor/bootstrap-icons/gear-wide-connected.svg new file mode 100644 index 0000000..19ddda9 --- /dev/null +++ b/web/vendor/bootstrap-icons/gear-wide-connected.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gear-wide.svg b/web/vendor/bootstrap-icons/gear-wide.svg new file mode 100644 index 0000000..c5de567 --- /dev/null +++ b/web/vendor/bootstrap-icons/gear-wide.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gear.svg b/web/vendor/bootstrap-icons/gear.svg new file mode 100644 index 0000000..30cfaa3 --- /dev/null +++ b/web/vendor/bootstrap-icons/gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gem.svg b/web/vendor/bootstrap-icons/gem.svg new file mode 100644 index 0000000..f56d871 --- /dev/null +++ b/web/vendor/bootstrap-icons/gem.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gender-ambiguous.svg b/web/vendor/bootstrap-icons/gender-ambiguous.svg new file mode 100644 index 0000000..674c526 --- /dev/null +++ b/web/vendor/bootstrap-icons/gender-ambiguous.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gender-female.svg b/web/vendor/bootstrap-icons/gender-female.svg new file mode 100644 index 0000000..ae6dc27 --- /dev/null +++ b/web/vendor/bootstrap-icons/gender-female.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gender-male.svg b/web/vendor/bootstrap-icons/gender-male.svg new file mode 100644 index 0000000..3931921 --- /dev/null +++ b/web/vendor/bootstrap-icons/gender-male.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gender-neuter.svg b/web/vendor/bootstrap-icons/gender-neuter.svg new file mode 100644 index 0000000..133db1a --- /dev/null +++ b/web/vendor/bootstrap-icons/gender-neuter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gender-trans.svg b/web/vendor/bootstrap-icons/gender-trans.svg new file mode 100644 index 0000000..005e6f8 --- /dev/null +++ b/web/vendor/bootstrap-icons/gender-trans.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/geo-alt-fill.svg b/web/vendor/bootstrap-icons/geo-alt-fill.svg new file mode 100644 index 0000000..77f0478 --- /dev/null +++ b/web/vendor/bootstrap-icons/geo-alt-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/geo-alt.svg b/web/vendor/bootstrap-icons/geo-alt.svg new file mode 100644 index 0000000..20e18ba --- /dev/null +++ b/web/vendor/bootstrap-icons/geo-alt.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/geo-fill.svg b/web/vendor/bootstrap-icons/geo-fill.svg new file mode 100644 index 0000000..9678fa0 --- /dev/null +++ b/web/vendor/bootstrap-icons/geo-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/geo.svg b/web/vendor/bootstrap-icons/geo.svg new file mode 100644 index 0000000..2382b91 --- /dev/null +++ b/web/vendor/bootstrap-icons/geo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gift-fill.svg b/web/vendor/bootstrap-icons/gift-fill.svg new file mode 100644 index 0000000..bd78a8c --- /dev/null +++ b/web/vendor/bootstrap-icons/gift-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gift.svg b/web/vendor/bootstrap-icons/gift.svg new file mode 100644 index 0000000..5921507 --- /dev/null +++ b/web/vendor/bootstrap-icons/gift.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/git.svg b/web/vendor/bootstrap-icons/git.svg new file mode 100644 index 0000000..0979cdb --- /dev/null +++ b/web/vendor/bootstrap-icons/git.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/github.svg b/web/vendor/bootstrap-icons/github.svg new file mode 100644 index 0000000..013e025 --- /dev/null +++ b/web/vendor/bootstrap-icons/github.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gitlab.svg b/web/vendor/bootstrap-icons/gitlab.svg new file mode 100644 index 0000000..f8875f8 --- /dev/null +++ b/web/vendor/bootstrap-icons/gitlab.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe-americas-fill.svg b/web/vendor/bootstrap-icons/globe-americas-fill.svg new file mode 100644 index 0000000..42259b2 --- /dev/null +++ b/web/vendor/bootstrap-icons/globe-americas-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe-americas.svg b/web/vendor/bootstrap-icons/globe-americas.svg new file mode 100644 index 0000000..5dcc7b2 --- /dev/null +++ b/web/vendor/bootstrap-icons/globe-americas.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe-asia-australia-fill.svg b/web/vendor/bootstrap-icons/globe-asia-australia-fill.svg new file mode 100644 index 0000000..8dfde0c --- /dev/null +++ b/web/vendor/bootstrap-icons/globe-asia-australia-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe-asia-australia.svg b/web/vendor/bootstrap-icons/globe-asia-australia.svg new file mode 100644 index 0000000..8c27539 --- /dev/null +++ b/web/vendor/bootstrap-icons/globe-asia-australia.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe-central-south-asia-fill.svg b/web/vendor/bootstrap-icons/globe-central-south-asia-fill.svg new file mode 100644 index 0000000..8f52f93 --- /dev/null +++ b/web/vendor/bootstrap-icons/globe-central-south-asia-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe-central-south-asia.svg b/web/vendor/bootstrap-icons/globe-central-south-asia.svg new file mode 100644 index 0000000..80a13c1 --- /dev/null +++ b/web/vendor/bootstrap-icons/globe-central-south-asia.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe-europe-africa-fill.svg b/web/vendor/bootstrap-icons/globe-europe-africa-fill.svg new file mode 100644 index 0000000..3fcdf0b --- /dev/null +++ b/web/vendor/bootstrap-icons/globe-europe-africa-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe-europe-africa.svg b/web/vendor/bootstrap-icons/globe-europe-africa.svg new file mode 100644 index 0000000..c1c5dcb --- /dev/null +++ b/web/vendor/bootstrap-icons/globe-europe-africa.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe.svg b/web/vendor/bootstrap-icons/globe.svg new file mode 100644 index 0000000..835ff66 --- /dev/null +++ b/web/vendor/bootstrap-icons/globe.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/globe2.svg b/web/vendor/bootstrap-icons/globe2.svg new file mode 100644 index 0000000..b302063 --- /dev/null +++ b/web/vendor/bootstrap-icons/globe2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/google-play.svg b/web/vendor/bootstrap-icons/google-play.svg new file mode 100644 index 0000000..0751c90 --- /dev/null +++ b/web/vendor/bootstrap-icons/google-play.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/google.svg b/web/vendor/bootstrap-icons/google.svg new file mode 100644 index 0000000..9f603de --- /dev/null +++ b/web/vendor/bootstrap-icons/google.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/gpu-card.svg b/web/vendor/bootstrap-icons/gpu-card.svg new file mode 100644 index 0000000..6ec3152 --- /dev/null +++ b/web/vendor/bootstrap-icons/gpu-card.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/graph-down-arrow.svg b/web/vendor/bootstrap-icons/graph-down-arrow.svg new file mode 100644 index 0000000..d811884 --- /dev/null +++ b/web/vendor/bootstrap-icons/graph-down-arrow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/graph-down.svg b/web/vendor/bootstrap-icons/graph-down.svg new file mode 100644 index 0000000..47dd647 --- /dev/null +++ b/web/vendor/bootstrap-icons/graph-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/graph-up-arrow.svg b/web/vendor/bootstrap-icons/graph-up-arrow.svg new file mode 100644 index 0000000..7eda5f4 --- /dev/null +++ b/web/vendor/bootstrap-icons/graph-up-arrow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/graph-up.svg b/web/vendor/bootstrap-icons/graph-up.svg new file mode 100644 index 0000000..15e0bbf --- /dev/null +++ b/web/vendor/bootstrap-icons/graph-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-1x2-fill.svg b/web/vendor/bootstrap-icons/grid-1x2-fill.svg new file mode 100644 index 0000000..cc568cb --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-1x2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-1x2.svg b/web/vendor/bootstrap-icons/grid-1x2.svg new file mode 100644 index 0000000..69ec79f --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-1x2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-3x2-gap-fill.svg b/web/vendor/bootstrap-icons/grid-3x2-gap-fill.svg new file mode 100644 index 0000000..9c0e855 --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-3x2-gap-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-3x2-gap.svg b/web/vendor/bootstrap-icons/grid-3x2-gap.svg new file mode 100644 index 0000000..730ae65 --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-3x2-gap.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-3x2.svg b/web/vendor/bootstrap-icons/grid-3x2.svg new file mode 100644 index 0000000..55ff9bb --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-3x2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-3x3-gap-fill.svg b/web/vendor/bootstrap-icons/grid-3x3-gap-fill.svg new file mode 100644 index 0000000..982cb70 --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-3x3-gap-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-3x3-gap.svg b/web/vendor/bootstrap-icons/grid-3x3-gap.svg new file mode 100644 index 0000000..bbf8ce4 --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-3x3-gap.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-3x3.svg b/web/vendor/bootstrap-icons/grid-3x3.svg new file mode 100644 index 0000000..d56aed7 --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-3x3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid-fill.svg b/web/vendor/bootstrap-icons/grid-fill.svg new file mode 100644 index 0000000..356cc8f --- /dev/null +++ b/web/vendor/bootstrap-icons/grid-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grid.svg b/web/vendor/bootstrap-icons/grid.svg new file mode 100644 index 0000000..eebab73 --- /dev/null +++ b/web/vendor/bootstrap-icons/grid.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grip-horizontal.svg b/web/vendor/bootstrap-icons/grip-horizontal.svg new file mode 100644 index 0000000..85f7e27 --- /dev/null +++ b/web/vendor/bootstrap-icons/grip-horizontal.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/grip-vertical.svg b/web/vendor/bootstrap-icons/grip-vertical.svg new file mode 100644 index 0000000..a8718ab --- /dev/null +++ b/web/vendor/bootstrap-icons/grip-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/h-circle-fill.svg b/web/vendor/bootstrap-icons/h-circle-fill.svg new file mode 100644 index 0000000..9106472 --- /dev/null +++ b/web/vendor/bootstrap-icons/h-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/h-circle.svg b/web/vendor/bootstrap-icons/h-circle.svg new file mode 100644 index 0000000..53c34b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/h-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/h-square-fill.svg b/web/vendor/bootstrap-icons/h-square-fill.svg new file mode 100644 index 0000000..06269e0 --- /dev/null +++ b/web/vendor/bootstrap-icons/h-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/h-square.svg b/web/vendor/bootstrap-icons/h-square.svg new file mode 100644 index 0000000..2c5ad2c --- /dev/null +++ b/web/vendor/bootstrap-icons/h-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hammer.svg b/web/vendor/bootstrap-icons/hammer.svg new file mode 100644 index 0000000..8e07b5b --- /dev/null +++ b/web/vendor/bootstrap-icons/hammer.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hand-index-fill.svg b/web/vendor/bootstrap-icons/hand-index-fill.svg new file mode 100644 index 0000000..b2a7d64 --- /dev/null +++ b/web/vendor/bootstrap-icons/hand-index-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hand-index-thumb-fill.svg b/web/vendor/bootstrap-icons/hand-index-thumb-fill.svg new file mode 100644 index 0000000..774b18e --- /dev/null +++ b/web/vendor/bootstrap-icons/hand-index-thumb-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hand-index-thumb.svg b/web/vendor/bootstrap-icons/hand-index-thumb.svg new file mode 100644 index 0000000..13b5475 --- /dev/null +++ b/web/vendor/bootstrap-icons/hand-index-thumb.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hand-index.svg b/web/vendor/bootstrap-icons/hand-index.svg new file mode 100644 index 0000000..725757b --- /dev/null +++ b/web/vendor/bootstrap-icons/hand-index.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hand-thumbs-down-fill.svg b/web/vendor/bootstrap-icons/hand-thumbs-down-fill.svg new file mode 100644 index 0000000..53584f3 --- /dev/null +++ b/web/vendor/bootstrap-icons/hand-thumbs-down-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hand-thumbs-down.svg b/web/vendor/bootstrap-icons/hand-thumbs-down.svg new file mode 100644 index 0000000..f87f523 --- /dev/null +++ b/web/vendor/bootstrap-icons/hand-thumbs-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hand-thumbs-up-fill.svg b/web/vendor/bootstrap-icons/hand-thumbs-up-fill.svg new file mode 100644 index 0000000..c68bc07 --- /dev/null +++ b/web/vendor/bootstrap-icons/hand-thumbs-up-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hand-thumbs-up.svg b/web/vendor/bootstrap-icons/hand-thumbs-up.svg new file mode 100644 index 0000000..dc46d4d --- /dev/null +++ b/web/vendor/bootstrap-icons/hand-thumbs-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/handbag-fill.svg b/web/vendor/bootstrap-icons/handbag-fill.svg new file mode 100644 index 0000000..5f8f23f --- /dev/null +++ b/web/vendor/bootstrap-icons/handbag-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/handbag.svg b/web/vendor/bootstrap-icons/handbag.svg new file mode 100644 index 0000000..c9ef874 --- /dev/null +++ b/web/vendor/bootstrap-icons/handbag.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hash.svg b/web/vendor/bootstrap-icons/hash.svg new file mode 100644 index 0000000..f67d000 --- /dev/null +++ b/web/vendor/bootstrap-icons/hash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdd-fill.svg b/web/vendor/bootstrap-icons/hdd-fill.svg new file mode 100644 index 0000000..dbeda24 --- /dev/null +++ b/web/vendor/bootstrap-icons/hdd-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdd-network-fill.svg b/web/vendor/bootstrap-icons/hdd-network-fill.svg new file mode 100644 index 0000000..a74874d --- /dev/null +++ b/web/vendor/bootstrap-icons/hdd-network-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdd-network.svg b/web/vendor/bootstrap-icons/hdd-network.svg new file mode 100644 index 0000000..7223542 --- /dev/null +++ b/web/vendor/bootstrap-icons/hdd-network.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdd-rack-fill.svg b/web/vendor/bootstrap-icons/hdd-rack-fill.svg new file mode 100644 index 0000000..7c33aec --- /dev/null +++ b/web/vendor/bootstrap-icons/hdd-rack-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdd-rack.svg b/web/vendor/bootstrap-icons/hdd-rack.svg new file mode 100644 index 0000000..d19a514 --- /dev/null +++ b/web/vendor/bootstrap-icons/hdd-rack.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdd-stack-fill.svg b/web/vendor/bootstrap-icons/hdd-stack-fill.svg new file mode 100644 index 0000000..27e8719 --- /dev/null +++ b/web/vendor/bootstrap-icons/hdd-stack-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdd-stack.svg b/web/vendor/bootstrap-icons/hdd-stack.svg new file mode 100644 index 0000000..f9095e5 --- /dev/null +++ b/web/vendor/bootstrap-icons/hdd-stack.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdd.svg b/web/vendor/bootstrap-icons/hdd.svg new file mode 100644 index 0000000..92358f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/hdd.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdmi-fill.svg b/web/vendor/bootstrap-icons/hdmi-fill.svg new file mode 100644 index 0000000..435b39d --- /dev/null +++ b/web/vendor/bootstrap-icons/hdmi-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hdmi.svg b/web/vendor/bootstrap-icons/hdmi.svg new file mode 100644 index 0000000..09d0849 --- /dev/null +++ b/web/vendor/bootstrap-icons/hdmi.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/headphones.svg b/web/vendor/bootstrap-icons/headphones.svg new file mode 100644 index 0000000..d50b3ee --- /dev/null +++ b/web/vendor/bootstrap-icons/headphones.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/headset-vr.svg b/web/vendor/bootstrap-icons/headset-vr.svg new file mode 100644 index 0000000..0498bd6 --- /dev/null +++ b/web/vendor/bootstrap-icons/headset-vr.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/headset.svg b/web/vendor/bootstrap-icons/headset.svg new file mode 100644 index 0000000..a921156 --- /dev/null +++ b/web/vendor/bootstrap-icons/headset.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heart-arrow.svg b/web/vendor/bootstrap-icons/heart-arrow.svg new file mode 100644 index 0000000..45eb6a5 --- /dev/null +++ b/web/vendor/bootstrap-icons/heart-arrow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heart-fill.svg b/web/vendor/bootstrap-icons/heart-fill.svg new file mode 100644 index 0000000..5e6b8ee --- /dev/null +++ b/web/vendor/bootstrap-icons/heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heart-half.svg b/web/vendor/bootstrap-icons/heart-half.svg new file mode 100644 index 0000000..2e0dd31 --- /dev/null +++ b/web/vendor/bootstrap-icons/heart-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heart-pulse-fill.svg b/web/vendor/bootstrap-icons/heart-pulse-fill.svg new file mode 100644 index 0000000..fc5e219 --- /dev/null +++ b/web/vendor/bootstrap-icons/heart-pulse-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heart-pulse.svg b/web/vendor/bootstrap-icons/heart-pulse.svg new file mode 100644 index 0000000..dd957b3 --- /dev/null +++ b/web/vendor/bootstrap-icons/heart-pulse.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heart.svg b/web/vendor/bootstrap-icons/heart.svg new file mode 100644 index 0000000..cd8eeb1 --- /dev/null +++ b/web/vendor/bootstrap-icons/heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heartbreak-fill.svg b/web/vendor/bootstrap-icons/heartbreak-fill.svg new file mode 100644 index 0000000..335cb6a --- /dev/null +++ b/web/vendor/bootstrap-icons/heartbreak-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heartbreak.svg b/web/vendor/bootstrap-icons/heartbreak.svg new file mode 100644 index 0000000..dcffc89 --- /dev/null +++ b/web/vendor/bootstrap-icons/heartbreak.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hearts.svg b/web/vendor/bootstrap-icons/hearts.svg new file mode 100644 index 0000000..ebd2dbb --- /dev/null +++ b/web/vendor/bootstrap-icons/hearts.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heptagon-fill.svg b/web/vendor/bootstrap-icons/heptagon-fill.svg new file mode 100644 index 0000000..a339328 --- /dev/null +++ b/web/vendor/bootstrap-icons/heptagon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heptagon-half.svg b/web/vendor/bootstrap-icons/heptagon-half.svg new file mode 100644 index 0000000..b4fadcf --- /dev/null +++ b/web/vendor/bootstrap-icons/heptagon-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/heptagon.svg b/web/vendor/bootstrap-icons/heptagon.svg new file mode 100644 index 0000000..3140b8b --- /dev/null +++ b/web/vendor/bootstrap-icons/heptagon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hexagon-fill.svg b/web/vendor/bootstrap-icons/hexagon-fill.svg new file mode 100644 index 0000000..50fdbfb --- /dev/null +++ b/web/vendor/bootstrap-icons/hexagon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hexagon-half.svg b/web/vendor/bootstrap-icons/hexagon-half.svg new file mode 100644 index 0000000..452cb6e --- /dev/null +++ b/web/vendor/bootstrap-icons/hexagon-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hexagon.svg b/web/vendor/bootstrap-icons/hexagon.svg new file mode 100644 index 0000000..6e83939 --- /dev/null +++ b/web/vendor/bootstrap-icons/hexagon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/highlighter.svg b/web/vendor/bootstrap-icons/highlighter.svg new file mode 100644 index 0000000..e26f1f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/highlighter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/highlights.svg b/web/vendor/bootstrap-icons/highlights.svg new file mode 100644 index 0000000..b43fca0 --- /dev/null +++ b/web/vendor/bootstrap-icons/highlights.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hospital-fill.svg b/web/vendor/bootstrap-icons/hospital-fill.svg new file mode 100644 index 0000000..55bde0f --- /dev/null +++ b/web/vendor/bootstrap-icons/hospital-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hospital.svg b/web/vendor/bootstrap-icons/hospital.svg new file mode 100644 index 0000000..f6bd0da --- /dev/null +++ b/web/vendor/bootstrap-icons/hospital.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hourglass-bottom.svg b/web/vendor/bootstrap-icons/hourglass-bottom.svg new file mode 100644 index 0000000..946f882 --- /dev/null +++ b/web/vendor/bootstrap-icons/hourglass-bottom.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hourglass-split.svg b/web/vendor/bootstrap-icons/hourglass-split.svg new file mode 100644 index 0000000..a9d1c51 --- /dev/null +++ b/web/vendor/bootstrap-icons/hourglass-split.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hourglass-top.svg b/web/vendor/bootstrap-icons/hourglass-top.svg new file mode 100644 index 0000000..6a8a4a3 --- /dev/null +++ b/web/vendor/bootstrap-icons/hourglass-top.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hourglass.svg b/web/vendor/bootstrap-icons/hourglass.svg new file mode 100644 index 0000000..44ede0a --- /dev/null +++ b/web/vendor/bootstrap-icons/hourglass.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-add-fill.svg b/web/vendor/bootstrap-icons/house-add-fill.svg new file mode 100644 index 0000000..1e814ee --- /dev/null +++ b/web/vendor/bootstrap-icons/house-add-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-add.svg b/web/vendor/bootstrap-icons/house-add.svg new file mode 100644 index 0000000..2a89bbe --- /dev/null +++ b/web/vendor/bootstrap-icons/house-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-check-fill.svg b/web/vendor/bootstrap-icons/house-check-fill.svg new file mode 100644 index 0000000..f2ddeb9 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-check-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-check.svg b/web/vendor/bootstrap-icons/house-check.svg new file mode 100644 index 0000000..5bd7900 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-dash-fill.svg b/web/vendor/bootstrap-icons/house-dash-fill.svg new file mode 100644 index 0000000..2fa8c57 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-dash-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-dash.svg b/web/vendor/bootstrap-icons/house-dash.svg new file mode 100644 index 0000000..b1cb832 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-door-fill.svg b/web/vendor/bootstrap-icons/house-door-fill.svg new file mode 100644 index 0000000..cf6d439 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-door-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-door.svg b/web/vendor/bootstrap-icons/house-door.svg new file mode 100644 index 0000000..daa093b --- /dev/null +++ b/web/vendor/bootstrap-icons/house-door.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-down-fill.svg b/web/vendor/bootstrap-icons/house-down-fill.svg new file mode 100644 index 0000000..351904f --- /dev/null +++ b/web/vendor/bootstrap-icons/house-down-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-down.svg b/web/vendor/bootstrap-icons/house-down.svg new file mode 100644 index 0000000..4d1d905 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-exclamation-fill.svg b/web/vendor/bootstrap-icons/house-exclamation-fill.svg new file mode 100644 index 0000000..52fc0ae --- /dev/null +++ b/web/vendor/bootstrap-icons/house-exclamation-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-exclamation.svg b/web/vendor/bootstrap-icons/house-exclamation.svg new file mode 100644 index 0000000..6d414f3 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-fill.svg b/web/vendor/bootstrap-icons/house-fill.svg new file mode 100644 index 0000000..89db164 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-gear-fill.svg b/web/vendor/bootstrap-icons/house-gear-fill.svg new file mode 100644 index 0000000..9003152 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-gear-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-gear.svg b/web/vendor/bootstrap-icons/house-gear.svg new file mode 100644 index 0000000..65b5abb --- /dev/null +++ b/web/vendor/bootstrap-icons/house-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-heart-fill.svg b/web/vendor/bootstrap-icons/house-heart-fill.svg new file mode 100644 index 0000000..be2e5f4 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-heart-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-heart.svg b/web/vendor/bootstrap-icons/house-heart.svg new file mode 100644 index 0000000..ece6c06 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-lock-fill.svg b/web/vendor/bootstrap-icons/house-lock-fill.svg new file mode 100644 index 0000000..90cc8c7 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-lock-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-lock.svg b/web/vendor/bootstrap-icons/house-lock.svg new file mode 100644 index 0000000..8dc5894 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-slash-fill.svg b/web/vendor/bootstrap-icons/house-slash-fill.svg new file mode 100644 index 0000000..df8d7c6 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-slash-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-slash.svg b/web/vendor/bootstrap-icons/house-slash.svg new file mode 100644 index 0000000..a81b0ed --- /dev/null +++ b/web/vendor/bootstrap-icons/house-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-up-fill.svg b/web/vendor/bootstrap-icons/house-up-fill.svg new file mode 100644 index 0000000..5e6a801 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-up-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-up.svg b/web/vendor/bootstrap-icons/house-up.svg new file mode 100644 index 0000000..da183d2 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-x-fill.svg b/web/vendor/bootstrap-icons/house-x-fill.svg new file mode 100644 index 0000000..729cdb5 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-x-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house-x.svg b/web/vendor/bootstrap-icons/house-x.svg new file mode 100644 index 0000000..68137c0 --- /dev/null +++ b/web/vendor/bootstrap-icons/house-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/house.svg b/web/vendor/bootstrap-icons/house.svg new file mode 100644 index 0000000..cb57f68 --- /dev/null +++ b/web/vendor/bootstrap-icons/house.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/houses-fill.svg b/web/vendor/bootstrap-icons/houses-fill.svg new file mode 100644 index 0000000..63047bb --- /dev/null +++ b/web/vendor/bootstrap-icons/houses-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/houses.svg b/web/vendor/bootstrap-icons/houses.svg new file mode 100644 index 0000000..3a906be --- /dev/null +++ b/web/vendor/bootstrap-icons/houses.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hr.svg b/web/vendor/bootstrap-icons/hr.svg new file mode 100644 index 0000000..6e9fbd8 --- /dev/null +++ b/web/vendor/bootstrap-icons/hr.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hurricane.svg b/web/vendor/bootstrap-icons/hurricane.svg new file mode 100644 index 0000000..5bd9ae1 --- /dev/null +++ b/web/vendor/bootstrap-icons/hurricane.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/hypnotize.svg b/web/vendor/bootstrap-icons/hypnotize.svg new file mode 100644 index 0000000..6a28997 --- /dev/null +++ b/web/vendor/bootstrap-icons/hypnotize.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/image-alt.svg b/web/vendor/bootstrap-icons/image-alt.svg new file mode 100644 index 0000000..e56ee20 --- /dev/null +++ b/web/vendor/bootstrap-icons/image-alt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/image-fill.svg b/web/vendor/bootstrap-icons/image-fill.svg new file mode 100644 index 0000000..db26ae8 --- /dev/null +++ b/web/vendor/bootstrap-icons/image-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/image.svg b/web/vendor/bootstrap-icons/image.svg new file mode 100644 index 0000000..152c333 --- /dev/null +++ b/web/vendor/bootstrap-icons/image.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/images.svg b/web/vendor/bootstrap-icons/images.svg new file mode 100644 index 0000000..adc7abf --- /dev/null +++ b/web/vendor/bootstrap-icons/images.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/inbox-fill.svg b/web/vendor/bootstrap-icons/inbox-fill.svg new file mode 100644 index 0000000..7e4816d --- /dev/null +++ b/web/vendor/bootstrap-icons/inbox-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/inbox.svg b/web/vendor/bootstrap-icons/inbox.svg new file mode 100644 index 0000000..b60e949 --- /dev/null +++ b/web/vendor/bootstrap-icons/inbox.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/inboxes-fill.svg b/web/vendor/bootstrap-icons/inboxes-fill.svg new file mode 100644 index 0000000..4d8c5f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/inboxes-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/inboxes.svg b/web/vendor/bootstrap-icons/inboxes.svg new file mode 100644 index 0000000..1a074ac --- /dev/null +++ b/web/vendor/bootstrap-icons/inboxes.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/incognito.svg b/web/vendor/bootstrap-icons/incognito.svg new file mode 100644 index 0000000..f2c8f9d --- /dev/null +++ b/web/vendor/bootstrap-icons/incognito.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/indent.svg b/web/vendor/bootstrap-icons/indent.svg new file mode 100644 index 0000000..0065aba --- /dev/null +++ b/web/vendor/bootstrap-icons/indent.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/infinity.svg b/web/vendor/bootstrap-icons/infinity.svg new file mode 100644 index 0000000..3dca19b --- /dev/null +++ b/web/vendor/bootstrap-icons/infinity.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/info-circle-fill.svg b/web/vendor/bootstrap-icons/info-circle-fill.svg new file mode 100644 index 0000000..d2e382b --- /dev/null +++ b/web/vendor/bootstrap-icons/info-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/info-circle.svg b/web/vendor/bootstrap-icons/info-circle.svg new file mode 100644 index 0000000..e2b50eb --- /dev/null +++ b/web/vendor/bootstrap-icons/info-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/info-lg.svg b/web/vendor/bootstrap-icons/info-lg.svg new file mode 100644 index 0000000..ac064b9 --- /dev/null +++ b/web/vendor/bootstrap-icons/info-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/info-square-fill.svg b/web/vendor/bootstrap-icons/info-square-fill.svg new file mode 100644 index 0000000..49196b4 --- /dev/null +++ b/web/vendor/bootstrap-icons/info-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/info-square.svg b/web/vendor/bootstrap-icons/info-square.svg new file mode 100644 index 0000000..b64cfa8 --- /dev/null +++ b/web/vendor/bootstrap-icons/info-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/info.svg b/web/vendor/bootstrap-icons/info.svg new file mode 100644 index 0000000..43dc242 --- /dev/null +++ b/web/vendor/bootstrap-icons/info.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/input-cursor-text.svg b/web/vendor/bootstrap-icons/input-cursor-text.svg new file mode 100644 index 0000000..fc910f3 --- /dev/null +++ b/web/vendor/bootstrap-icons/input-cursor-text.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/input-cursor.svg b/web/vendor/bootstrap-icons/input-cursor.svg new file mode 100644 index 0000000..de6a35f --- /dev/null +++ b/web/vendor/bootstrap-icons/input-cursor.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/instagram.svg b/web/vendor/bootstrap-icons/instagram.svg new file mode 100644 index 0000000..855e653 --- /dev/null +++ b/web/vendor/bootstrap-icons/instagram.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/intersect.svg b/web/vendor/bootstrap-icons/intersect.svg new file mode 100644 index 0000000..220141d --- /dev/null +++ b/web/vendor/bootstrap-icons/intersect.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/javascript.svg b/web/vendor/bootstrap-icons/javascript.svg new file mode 100644 index 0000000..8a7e716 --- /dev/null +++ b/web/vendor/bootstrap-icons/javascript.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-album.svg b/web/vendor/bootstrap-icons/journal-album.svg new file mode 100644 index 0000000..de49ccc --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-album.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-arrow-down.svg b/web/vendor/bootstrap-icons/journal-arrow-down.svg new file mode 100644 index 0000000..d922f3c --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-arrow-down.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-arrow-up.svg b/web/vendor/bootstrap-icons/journal-arrow-up.svg new file mode 100644 index 0000000..7edc400 --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-arrow-up.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-bookmark-fill.svg b/web/vendor/bootstrap-icons/journal-bookmark-fill.svg new file mode 100644 index 0000000..8e2f17a --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-bookmark-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-bookmark.svg b/web/vendor/bootstrap-icons/journal-bookmark.svg new file mode 100644 index 0000000..4a8f4bb --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-bookmark.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-check.svg b/web/vendor/bootstrap-icons/journal-check.svg new file mode 100644 index 0000000..eb398b9 --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-check.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-code.svg b/web/vendor/bootstrap-icons/journal-code.svg new file mode 100644 index 0000000..41430d2 --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-code.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-medical.svg b/web/vendor/bootstrap-icons/journal-medical.svg new file mode 100644 index 0000000..fb6d942 --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-medical.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-minus.svg b/web/vendor/bootstrap-icons/journal-minus.svg new file mode 100644 index 0000000..cbdfdd7 --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-minus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-plus.svg b/web/vendor/bootstrap-icons/journal-plus.svg new file mode 100644 index 0000000..5cb82c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-plus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-richtext.svg b/web/vendor/bootstrap-icons/journal-richtext.svg new file mode 100644 index 0000000..db92c70 --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-richtext.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-text.svg b/web/vendor/bootstrap-icons/journal-text.svg new file mode 100644 index 0000000..13c58bc --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-text.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal-x.svg b/web/vendor/bootstrap-icons/journal-x.svg new file mode 100644 index 0000000..fb3ea9f --- /dev/null +++ b/web/vendor/bootstrap-icons/journal-x.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journal.svg b/web/vendor/bootstrap-icons/journal.svg new file mode 100644 index 0000000..4c166e2 --- /dev/null +++ b/web/vendor/bootstrap-icons/journal.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/journals.svg b/web/vendor/bootstrap-icons/journals.svg new file mode 100644 index 0000000..6e5386a --- /dev/null +++ b/web/vendor/bootstrap-icons/journals.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/joystick.svg b/web/vendor/bootstrap-icons/joystick.svg new file mode 100644 index 0000000..909369e --- /dev/null +++ b/web/vendor/bootstrap-icons/joystick.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/justify-left.svg b/web/vendor/bootstrap-icons/justify-left.svg new file mode 100644 index 0000000..17b45e4 --- /dev/null +++ b/web/vendor/bootstrap-icons/justify-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/justify-right.svg b/web/vendor/bootstrap-icons/justify-right.svg new file mode 100644 index 0000000..4d96c43 --- /dev/null +++ b/web/vendor/bootstrap-icons/justify-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/justify.svg b/web/vendor/bootstrap-icons/justify.svg new file mode 100644 index 0000000..3eedc74 --- /dev/null +++ b/web/vendor/bootstrap-icons/justify.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/kanban-fill.svg b/web/vendor/bootstrap-icons/kanban-fill.svg new file mode 100644 index 0000000..a8ed5bb --- /dev/null +++ b/web/vendor/bootstrap-icons/kanban-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/kanban.svg b/web/vendor/bootstrap-icons/kanban.svg new file mode 100644 index 0000000..cd13b32 --- /dev/null +++ b/web/vendor/bootstrap-icons/kanban.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/key-fill.svg b/web/vendor/bootstrap-icons/key-fill.svg new file mode 100644 index 0000000..fdab8d6 --- /dev/null +++ b/web/vendor/bootstrap-icons/key-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/key.svg b/web/vendor/bootstrap-icons/key.svg new file mode 100644 index 0000000..b0d1e16 --- /dev/null +++ b/web/vendor/bootstrap-icons/key.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/keyboard-fill.svg b/web/vendor/bootstrap-icons/keyboard-fill.svg new file mode 100644 index 0000000..b46ad2d --- /dev/null +++ b/web/vendor/bootstrap-icons/keyboard-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/keyboard.svg b/web/vendor/bootstrap-icons/keyboard.svg new file mode 100644 index 0000000..8ba49b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/keyboard.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ladder.svg b/web/vendor/bootstrap-icons/ladder.svg new file mode 100644 index 0000000..7c6864f --- /dev/null +++ b/web/vendor/bootstrap-icons/ladder.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lamp-fill.svg b/web/vendor/bootstrap-icons/lamp-fill.svg new file mode 100644 index 0000000..836b0db --- /dev/null +++ b/web/vendor/bootstrap-icons/lamp-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lamp.svg b/web/vendor/bootstrap-icons/lamp.svg new file mode 100644 index 0000000..b3fa1d6 --- /dev/null +++ b/web/vendor/bootstrap-icons/lamp.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/laptop-fill.svg b/web/vendor/bootstrap-icons/laptop-fill.svg new file mode 100644 index 0000000..31e5880 --- /dev/null +++ b/web/vendor/bootstrap-icons/laptop-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/laptop.svg b/web/vendor/bootstrap-icons/laptop.svg new file mode 100644 index 0000000..8e71020 --- /dev/null +++ b/web/vendor/bootstrap-icons/laptop.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layer-backward.svg b/web/vendor/bootstrap-icons/layer-backward.svg new file mode 100644 index 0000000..ed43c70 --- /dev/null +++ b/web/vendor/bootstrap-icons/layer-backward.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layer-forward.svg b/web/vendor/bootstrap-icons/layer-forward.svg new file mode 100644 index 0000000..d0a4abf --- /dev/null +++ b/web/vendor/bootstrap-icons/layer-forward.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layers-fill.svg b/web/vendor/bootstrap-icons/layers-fill.svg new file mode 100644 index 0000000..3b6cdf6 --- /dev/null +++ b/web/vendor/bootstrap-icons/layers-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layers-half.svg b/web/vendor/bootstrap-icons/layers-half.svg new file mode 100644 index 0000000..8ceaaad --- /dev/null +++ b/web/vendor/bootstrap-icons/layers-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layers.svg b/web/vendor/bootstrap-icons/layers.svg new file mode 100644 index 0000000..52dbe79 --- /dev/null +++ b/web/vendor/bootstrap-icons/layers.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-sidebar-inset-reverse.svg b/web/vendor/bootstrap-icons/layout-sidebar-inset-reverse.svg new file mode 100644 index 0000000..0d8dc7f --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-sidebar-inset-reverse.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-sidebar-inset.svg b/web/vendor/bootstrap-icons/layout-sidebar-inset.svg new file mode 100644 index 0000000..cc19c86 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-sidebar-inset.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-sidebar-reverse.svg b/web/vendor/bootstrap-icons/layout-sidebar-reverse.svg new file mode 100644 index 0000000..7c03f73 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-sidebar-reverse.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-sidebar.svg b/web/vendor/bootstrap-icons/layout-sidebar.svg new file mode 100644 index 0000000..ff40858 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-sidebar.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-split.svg b/web/vendor/bootstrap-icons/layout-split.svg new file mode 100644 index 0000000..4805b25 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-split.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-text-sidebar-reverse.svg b/web/vendor/bootstrap-icons/layout-text-sidebar-reverse.svg new file mode 100644 index 0000000..9179285 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-text-sidebar-reverse.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-text-sidebar.svg b/web/vendor/bootstrap-icons/layout-text-sidebar.svg new file mode 100644 index 0000000..6d89f67 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-text-sidebar.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-text-window-reverse.svg b/web/vendor/bootstrap-icons/layout-text-window-reverse.svg new file mode 100644 index 0000000..8258ad3 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-text-window-reverse.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-text-window.svg b/web/vendor/bootstrap-icons/layout-text-window.svg new file mode 100644 index 0000000..4d27cf9 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-text-window.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-three-columns.svg b/web/vendor/bootstrap-icons/layout-three-columns.svg new file mode 100644 index 0000000..7117f07 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-three-columns.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/layout-wtf.svg b/web/vendor/bootstrap-icons/layout-wtf.svg new file mode 100644 index 0000000..fd8f5a1 --- /dev/null +++ b/web/vendor/bootstrap-icons/layout-wtf.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/leaf-fill.svg b/web/vendor/bootstrap-icons/leaf-fill.svg new file mode 100644 index 0000000..094ab20 --- /dev/null +++ b/web/vendor/bootstrap-icons/leaf-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/leaf.svg b/web/vendor/bootstrap-icons/leaf.svg new file mode 100644 index 0000000..37b51e3 --- /dev/null +++ b/web/vendor/bootstrap-icons/leaf.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/life-preserver.svg b/web/vendor/bootstrap-icons/life-preserver.svg new file mode 100644 index 0000000..7282baa --- /dev/null +++ b/web/vendor/bootstrap-icons/life-preserver.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lightbulb-fill.svg b/web/vendor/bootstrap-icons/lightbulb-fill.svg new file mode 100644 index 0000000..0ef90ea --- /dev/null +++ b/web/vendor/bootstrap-icons/lightbulb-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lightbulb-off-fill.svg b/web/vendor/bootstrap-icons/lightbulb-off-fill.svg new file mode 100644 index 0000000..e533739 --- /dev/null +++ b/web/vendor/bootstrap-icons/lightbulb-off-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lightbulb-off.svg b/web/vendor/bootstrap-icons/lightbulb-off.svg new file mode 100644 index 0000000..15e8200 --- /dev/null +++ b/web/vendor/bootstrap-icons/lightbulb-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lightbulb.svg b/web/vendor/bootstrap-icons/lightbulb.svg new file mode 100644 index 0000000..67bf5bd --- /dev/null +++ b/web/vendor/bootstrap-icons/lightbulb.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lightning-charge-fill.svg b/web/vendor/bootstrap-icons/lightning-charge-fill.svg new file mode 100644 index 0000000..5e197fb --- /dev/null +++ b/web/vendor/bootstrap-icons/lightning-charge-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lightning-charge.svg b/web/vendor/bootstrap-icons/lightning-charge.svg new file mode 100644 index 0000000..5352e72 --- /dev/null +++ b/web/vendor/bootstrap-icons/lightning-charge.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lightning-fill.svg b/web/vendor/bootstrap-icons/lightning-fill.svg new file mode 100644 index 0000000..b98af68 --- /dev/null +++ b/web/vendor/bootstrap-icons/lightning-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lightning.svg b/web/vendor/bootstrap-icons/lightning.svg new file mode 100644 index 0000000..d2faa0f --- /dev/null +++ b/web/vendor/bootstrap-icons/lightning.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/line.svg b/web/vendor/bootstrap-icons/line.svg new file mode 100644 index 0000000..3e4bfd3 --- /dev/null +++ b/web/vendor/bootstrap-icons/line.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/link-45deg.svg b/web/vendor/bootstrap-icons/link-45deg.svg new file mode 100644 index 0000000..abdc8cb --- /dev/null +++ b/web/vendor/bootstrap-icons/link-45deg.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/link.svg b/web/vendor/bootstrap-icons/link.svg new file mode 100644 index 0000000..823e4cd --- /dev/null +++ b/web/vendor/bootstrap-icons/link.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/linkedin.svg b/web/vendor/bootstrap-icons/linkedin.svg new file mode 100644 index 0000000..30fc0e3 --- /dev/null +++ b/web/vendor/bootstrap-icons/linkedin.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list-check.svg b/web/vendor/bootstrap-icons/list-check.svg new file mode 100644 index 0000000..e1db377 --- /dev/null +++ b/web/vendor/bootstrap-icons/list-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list-columns-reverse.svg b/web/vendor/bootstrap-icons/list-columns-reverse.svg new file mode 100644 index 0000000..f5e2876 --- /dev/null +++ b/web/vendor/bootstrap-icons/list-columns-reverse.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list-columns.svg b/web/vendor/bootstrap-icons/list-columns.svg new file mode 100644 index 0000000..07d0b1a --- /dev/null +++ b/web/vendor/bootstrap-icons/list-columns.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list-nested.svg b/web/vendor/bootstrap-icons/list-nested.svg new file mode 100644 index 0000000..26607c9 --- /dev/null +++ b/web/vendor/bootstrap-icons/list-nested.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list-ol.svg b/web/vendor/bootstrap-icons/list-ol.svg new file mode 100644 index 0000000..d111f73 --- /dev/null +++ b/web/vendor/bootstrap-icons/list-ol.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list-stars.svg b/web/vendor/bootstrap-icons/list-stars.svg new file mode 100644 index 0000000..c520bdf --- /dev/null +++ b/web/vendor/bootstrap-icons/list-stars.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list-task.svg b/web/vendor/bootstrap-icons/list-task.svg new file mode 100644 index 0000000..3905d7a --- /dev/null +++ b/web/vendor/bootstrap-icons/list-task.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list-ul.svg b/web/vendor/bootstrap-icons/list-ul.svg new file mode 100644 index 0000000..f1cc202 --- /dev/null +++ b/web/vendor/bootstrap-icons/list-ul.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/list.svg b/web/vendor/bootstrap-icons/list.svg new file mode 100644 index 0000000..de58858 --- /dev/null +++ b/web/vendor/bootstrap-icons/list.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lock-fill.svg b/web/vendor/bootstrap-icons/lock-fill.svg new file mode 100644 index 0000000..9e9ff14 --- /dev/null +++ b/web/vendor/bootstrap-icons/lock-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lock.svg b/web/vendor/bootstrap-icons/lock.svg new file mode 100644 index 0000000..c38aa42 --- /dev/null +++ b/web/vendor/bootstrap-icons/lock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/luggage-fill.svg b/web/vendor/bootstrap-icons/luggage-fill.svg new file mode 100644 index 0000000..eb7378f --- /dev/null +++ b/web/vendor/bootstrap-icons/luggage-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/luggage.svg b/web/vendor/bootstrap-icons/luggage.svg new file mode 100644 index 0000000..ad037ba --- /dev/null +++ b/web/vendor/bootstrap-icons/luggage.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lungs-fill.svg b/web/vendor/bootstrap-icons/lungs-fill.svg new file mode 100644 index 0000000..2880fa6 --- /dev/null +++ b/web/vendor/bootstrap-icons/lungs-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/lungs.svg b/web/vendor/bootstrap-icons/lungs.svg new file mode 100644 index 0000000..082e7de --- /dev/null +++ b/web/vendor/bootstrap-icons/lungs.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/magic.svg b/web/vendor/bootstrap-icons/magic.svg new file mode 100644 index 0000000..0b2f1fd --- /dev/null +++ b/web/vendor/bootstrap-icons/magic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/magnet-fill.svg b/web/vendor/bootstrap-icons/magnet-fill.svg new file mode 100644 index 0000000..026d0de --- /dev/null +++ b/web/vendor/bootstrap-icons/magnet-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/magnet.svg b/web/vendor/bootstrap-icons/magnet.svg new file mode 100644 index 0000000..36b238b --- /dev/null +++ b/web/vendor/bootstrap-icons/magnet.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mailbox-flag.svg b/web/vendor/bootstrap-icons/mailbox-flag.svg new file mode 100644 index 0000000..8e24db0 --- /dev/null +++ b/web/vendor/bootstrap-icons/mailbox-flag.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mailbox.svg b/web/vendor/bootstrap-icons/mailbox.svg new file mode 100644 index 0000000..1048e2a --- /dev/null +++ b/web/vendor/bootstrap-icons/mailbox.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mailbox2-flag.svg b/web/vendor/bootstrap-icons/mailbox2-flag.svg new file mode 100644 index 0000000..a253700 --- /dev/null +++ b/web/vendor/bootstrap-icons/mailbox2-flag.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mailbox2.svg b/web/vendor/bootstrap-icons/mailbox2.svg new file mode 100644 index 0000000..33e22a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/mailbox2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/map-fill.svg b/web/vendor/bootstrap-icons/map-fill.svg new file mode 100644 index 0000000..7134540 --- /dev/null +++ b/web/vendor/bootstrap-icons/map-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/map.svg b/web/vendor/bootstrap-icons/map.svg new file mode 100644 index 0000000..2b579ce --- /dev/null +++ b/web/vendor/bootstrap-icons/map.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/markdown-fill.svg b/web/vendor/bootstrap-icons/markdown-fill.svg new file mode 100644 index 0000000..a932fbb --- /dev/null +++ b/web/vendor/bootstrap-icons/markdown-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/markdown.svg b/web/vendor/bootstrap-icons/markdown.svg new file mode 100644 index 0000000..33962c6 --- /dev/null +++ b/web/vendor/bootstrap-icons/markdown.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/marker-tip.svg b/web/vendor/bootstrap-icons/marker-tip.svg new file mode 100644 index 0000000..e00f931 --- /dev/null +++ b/web/vendor/bootstrap-icons/marker-tip.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mask.svg b/web/vendor/bootstrap-icons/mask.svg new file mode 100644 index 0000000..b51158c --- /dev/null +++ b/web/vendor/bootstrap-icons/mask.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mastodon.svg b/web/vendor/bootstrap-icons/mastodon.svg new file mode 100644 index 0000000..a8c2a26 --- /dev/null +++ b/web/vendor/bootstrap-icons/mastodon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/measuring-cup-fill.svg b/web/vendor/bootstrap-icons/measuring-cup-fill.svg new file mode 100644 index 0000000..a62ef9e --- /dev/null +++ b/web/vendor/bootstrap-icons/measuring-cup-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/measuring-cup.svg b/web/vendor/bootstrap-icons/measuring-cup.svg new file mode 100644 index 0000000..6020df9 --- /dev/null +++ b/web/vendor/bootstrap-icons/measuring-cup.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/medium.svg b/web/vendor/bootstrap-icons/medium.svg new file mode 100644 index 0000000..065ace1 --- /dev/null +++ b/web/vendor/bootstrap-icons/medium.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/megaphone-fill.svg b/web/vendor/bootstrap-icons/megaphone-fill.svg new file mode 100644 index 0000000..9f44f2e --- /dev/null +++ b/web/vendor/bootstrap-icons/megaphone-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/megaphone.svg b/web/vendor/bootstrap-icons/megaphone.svg new file mode 100644 index 0000000..1cedb30 --- /dev/null +++ b/web/vendor/bootstrap-icons/megaphone.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/memory.svg b/web/vendor/bootstrap-icons/memory.svg new file mode 100644 index 0000000..48764d2 --- /dev/null +++ b/web/vendor/bootstrap-icons/memory.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/menu-app-fill.svg b/web/vendor/bootstrap-icons/menu-app-fill.svg new file mode 100644 index 0000000..65cfdcf --- /dev/null +++ b/web/vendor/bootstrap-icons/menu-app-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/menu-app.svg b/web/vendor/bootstrap-icons/menu-app.svg new file mode 100644 index 0000000..ecda144 --- /dev/null +++ b/web/vendor/bootstrap-icons/menu-app.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/menu-button-fill.svg b/web/vendor/bootstrap-icons/menu-button-fill.svg new file mode 100644 index 0000000..09b2805 --- /dev/null +++ b/web/vendor/bootstrap-icons/menu-button-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/menu-button-wide-fill.svg b/web/vendor/bootstrap-icons/menu-button-wide-fill.svg new file mode 100644 index 0000000..d97ce7f --- /dev/null +++ b/web/vendor/bootstrap-icons/menu-button-wide-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/menu-button-wide.svg b/web/vendor/bootstrap-icons/menu-button-wide.svg new file mode 100644 index 0000000..5636c10 --- /dev/null +++ b/web/vendor/bootstrap-icons/menu-button-wide.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/menu-button.svg b/web/vendor/bootstrap-icons/menu-button.svg new file mode 100644 index 0000000..ec4c70a --- /dev/null +++ b/web/vendor/bootstrap-icons/menu-button.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/menu-down.svg b/web/vendor/bootstrap-icons/menu-down.svg new file mode 100644 index 0000000..e53a5e9 --- /dev/null +++ b/web/vendor/bootstrap-icons/menu-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/menu-up.svg b/web/vendor/bootstrap-icons/menu-up.svg new file mode 100644 index 0000000..96ff58b --- /dev/null +++ b/web/vendor/bootstrap-icons/menu-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/messenger.svg b/web/vendor/bootstrap-icons/messenger.svg new file mode 100644 index 0000000..e896a79 --- /dev/null +++ b/web/vendor/bootstrap-icons/messenger.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/meta.svg b/web/vendor/bootstrap-icons/meta.svg new file mode 100644 index 0000000..7c7510b --- /dev/null +++ b/web/vendor/bootstrap-icons/meta.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mic-fill.svg b/web/vendor/bootstrap-icons/mic-fill.svg new file mode 100644 index 0000000..c92ade7 --- /dev/null +++ b/web/vendor/bootstrap-icons/mic-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mic-mute-fill.svg b/web/vendor/bootstrap-icons/mic-mute-fill.svg new file mode 100644 index 0000000..a10a1bc --- /dev/null +++ b/web/vendor/bootstrap-icons/mic-mute-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mic-mute.svg b/web/vendor/bootstrap-icons/mic-mute.svg new file mode 100644 index 0000000..59b04be --- /dev/null +++ b/web/vendor/bootstrap-icons/mic-mute.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mic.svg b/web/vendor/bootstrap-icons/mic.svg new file mode 100644 index 0000000..f07bf14 --- /dev/null +++ b/web/vendor/bootstrap-icons/mic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/microsoft-teams.svg b/web/vendor/bootstrap-icons/microsoft-teams.svg new file mode 100644 index 0000000..6bf3a0c --- /dev/null +++ b/web/vendor/bootstrap-icons/microsoft-teams.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/microsoft.svg b/web/vendor/bootstrap-icons/microsoft.svg new file mode 100644 index 0000000..8d2a03c --- /dev/null +++ b/web/vendor/bootstrap-icons/microsoft.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/minecart-loaded.svg b/web/vendor/bootstrap-icons/minecart-loaded.svg new file mode 100644 index 0000000..48e523f --- /dev/null +++ b/web/vendor/bootstrap-icons/minecart-loaded.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/minecart.svg b/web/vendor/bootstrap-icons/minecart.svg new file mode 100644 index 0000000..c4869c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/minecart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/modem-fill.svg b/web/vendor/bootstrap-icons/modem-fill.svg new file mode 100644 index 0000000..a5dd5e2 --- /dev/null +++ b/web/vendor/bootstrap-icons/modem-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/modem.svg b/web/vendor/bootstrap-icons/modem.svg new file mode 100644 index 0000000..f90ad6b --- /dev/null +++ b/web/vendor/bootstrap-icons/modem.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/moisture.svg b/web/vendor/bootstrap-icons/moisture.svg new file mode 100644 index 0000000..490fb49 --- /dev/null +++ b/web/vendor/bootstrap-icons/moisture.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/moon-fill.svg b/web/vendor/bootstrap-icons/moon-fill.svg new file mode 100644 index 0000000..67f6739 --- /dev/null +++ b/web/vendor/bootstrap-icons/moon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/moon-stars-fill.svg b/web/vendor/bootstrap-icons/moon-stars-fill.svg new file mode 100644 index 0000000..c50e070 --- /dev/null +++ b/web/vendor/bootstrap-icons/moon-stars-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/moon-stars.svg b/web/vendor/bootstrap-icons/moon-stars.svg new file mode 100644 index 0000000..ae138c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/moon-stars.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/moon.svg b/web/vendor/bootstrap-icons/moon.svg new file mode 100644 index 0000000..46458ec --- /dev/null +++ b/web/vendor/bootstrap-icons/moon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mortarboard-fill.svg b/web/vendor/bootstrap-icons/mortarboard-fill.svg new file mode 100644 index 0000000..02f6c8c --- /dev/null +++ b/web/vendor/bootstrap-icons/mortarboard-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mortarboard.svg b/web/vendor/bootstrap-icons/mortarboard.svg new file mode 100644 index 0000000..94f9e97 --- /dev/null +++ b/web/vendor/bootstrap-icons/mortarboard.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/motherboard-fill.svg b/web/vendor/bootstrap-icons/motherboard-fill.svg new file mode 100644 index 0000000..fabff97 --- /dev/null +++ b/web/vendor/bootstrap-icons/motherboard-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/motherboard.svg b/web/vendor/bootstrap-icons/motherboard.svg new file mode 100644 index 0000000..d29e255 --- /dev/null +++ b/web/vendor/bootstrap-icons/motherboard.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mouse-fill.svg b/web/vendor/bootstrap-icons/mouse-fill.svg new file mode 100644 index 0000000..24d275e --- /dev/null +++ b/web/vendor/bootstrap-icons/mouse-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mouse.svg b/web/vendor/bootstrap-icons/mouse.svg new file mode 100644 index 0000000..e018811 --- /dev/null +++ b/web/vendor/bootstrap-icons/mouse.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mouse2-fill.svg b/web/vendor/bootstrap-icons/mouse2-fill.svg new file mode 100644 index 0000000..6277b44 --- /dev/null +++ b/web/vendor/bootstrap-icons/mouse2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mouse2.svg b/web/vendor/bootstrap-icons/mouse2.svg new file mode 100644 index 0000000..fd15e7c --- /dev/null +++ b/web/vendor/bootstrap-icons/mouse2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mouse3-fill.svg b/web/vendor/bootstrap-icons/mouse3-fill.svg new file mode 100644 index 0000000..16c1705 --- /dev/null +++ b/web/vendor/bootstrap-icons/mouse3-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/mouse3.svg b/web/vendor/bootstrap-icons/mouse3.svg new file mode 100644 index 0000000..548b244 --- /dev/null +++ b/web/vendor/bootstrap-icons/mouse3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/music-note-beamed.svg b/web/vendor/bootstrap-icons/music-note-beamed.svg new file mode 100644 index 0000000..9eb1506 --- /dev/null +++ b/web/vendor/bootstrap-icons/music-note-beamed.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/music-note-list.svg b/web/vendor/bootstrap-icons/music-note-list.svg new file mode 100644 index 0000000..d33767d --- /dev/null +++ b/web/vendor/bootstrap-icons/music-note-list.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/music-note.svg b/web/vendor/bootstrap-icons/music-note.svg new file mode 100644 index 0000000..d6fe21e --- /dev/null +++ b/web/vendor/bootstrap-icons/music-note.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/music-player-fill.svg b/web/vendor/bootstrap-icons/music-player-fill.svg new file mode 100644 index 0000000..68a65b5 --- /dev/null +++ b/web/vendor/bootstrap-icons/music-player-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/music-player.svg b/web/vendor/bootstrap-icons/music-player.svg new file mode 100644 index 0000000..7eb9c92 --- /dev/null +++ b/web/vendor/bootstrap-icons/music-player.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/newspaper.svg b/web/vendor/bootstrap-icons/newspaper.svg new file mode 100644 index 0000000..9a1cf6d --- /dev/null +++ b/web/vendor/bootstrap-icons/newspaper.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/nintendo-switch.svg b/web/vendor/bootstrap-icons/nintendo-switch.svg new file mode 100644 index 0000000..5849493 --- /dev/null +++ b/web/vendor/bootstrap-icons/nintendo-switch.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/node-minus-fill.svg b/web/vendor/bootstrap-icons/node-minus-fill.svg new file mode 100644 index 0000000..802d678 --- /dev/null +++ b/web/vendor/bootstrap-icons/node-minus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/node-minus.svg b/web/vendor/bootstrap-icons/node-minus.svg new file mode 100644 index 0000000..8ffaa38 --- /dev/null +++ b/web/vendor/bootstrap-icons/node-minus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/node-plus-fill.svg b/web/vendor/bootstrap-icons/node-plus-fill.svg new file mode 100644 index 0000000..9559b26 --- /dev/null +++ b/web/vendor/bootstrap-icons/node-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/node-plus.svg b/web/vendor/bootstrap-icons/node-plus.svg new file mode 100644 index 0000000..028ef28 --- /dev/null +++ b/web/vendor/bootstrap-icons/node-plus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/noise-reduction.svg b/web/vendor/bootstrap-icons/noise-reduction.svg new file mode 100644 index 0000000..cd5e288 --- /dev/null +++ b/web/vendor/bootstrap-icons/noise-reduction.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/nut-fill.svg b/web/vendor/bootstrap-icons/nut-fill.svg new file mode 100644 index 0000000..18dfeb1 --- /dev/null +++ b/web/vendor/bootstrap-icons/nut-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/nut.svg b/web/vendor/bootstrap-icons/nut.svg new file mode 100644 index 0000000..75a401d --- /dev/null +++ b/web/vendor/bootstrap-icons/nut.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/nvidia.svg b/web/vendor/bootstrap-icons/nvidia.svg new file mode 100644 index 0000000..438a6fc --- /dev/null +++ b/web/vendor/bootstrap-icons/nvidia.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/nvme-fill.svg b/web/vendor/bootstrap-icons/nvme-fill.svg new file mode 100644 index 0000000..962c3c6 --- /dev/null +++ b/web/vendor/bootstrap-icons/nvme-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/nvme.svg b/web/vendor/bootstrap-icons/nvme.svg new file mode 100644 index 0000000..31a2fb6 --- /dev/null +++ b/web/vendor/bootstrap-icons/nvme.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/octagon-fill.svg b/web/vendor/bootstrap-icons/octagon-fill.svg new file mode 100644 index 0000000..73c80f0 --- /dev/null +++ b/web/vendor/bootstrap-icons/octagon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/octagon-half.svg b/web/vendor/bootstrap-icons/octagon-half.svg new file mode 100644 index 0000000..fe6eb41 --- /dev/null +++ b/web/vendor/bootstrap-icons/octagon-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/octagon.svg b/web/vendor/bootstrap-icons/octagon.svg new file mode 100644 index 0000000..d2d9c5c --- /dev/null +++ b/web/vendor/bootstrap-icons/octagon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/openai.svg b/web/vendor/bootstrap-icons/openai.svg new file mode 100644 index 0000000..ad018a2 --- /dev/null +++ b/web/vendor/bootstrap-icons/openai.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/opencollective.svg b/web/vendor/bootstrap-icons/opencollective.svg new file mode 100644 index 0000000..b9a0c9f --- /dev/null +++ b/web/vendor/bootstrap-icons/opencollective.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/optical-audio-fill.svg b/web/vendor/bootstrap-icons/optical-audio-fill.svg new file mode 100644 index 0000000..9b74060 --- /dev/null +++ b/web/vendor/bootstrap-icons/optical-audio-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/optical-audio.svg b/web/vendor/bootstrap-icons/optical-audio.svg new file mode 100644 index 0000000..253d1d7 --- /dev/null +++ b/web/vendor/bootstrap-icons/optical-audio.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/option.svg b/web/vendor/bootstrap-icons/option.svg new file mode 100644 index 0000000..32cce4c --- /dev/null +++ b/web/vendor/bootstrap-icons/option.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/outlet.svg b/web/vendor/bootstrap-icons/outlet.svg new file mode 100644 index 0000000..7787f35 --- /dev/null +++ b/web/vendor/bootstrap-icons/outlet.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/p-circle-fill.svg b/web/vendor/bootstrap-icons/p-circle-fill.svg new file mode 100644 index 0000000..e57d158 --- /dev/null +++ b/web/vendor/bootstrap-icons/p-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/p-circle.svg b/web/vendor/bootstrap-icons/p-circle.svg new file mode 100644 index 0000000..bfe68d9 --- /dev/null +++ b/web/vendor/bootstrap-icons/p-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/p-square-fill.svg b/web/vendor/bootstrap-icons/p-square-fill.svg new file mode 100644 index 0000000..164f5eb --- /dev/null +++ b/web/vendor/bootstrap-icons/p-square-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/p-square.svg b/web/vendor/bootstrap-icons/p-square.svg new file mode 100644 index 0000000..1f6335d --- /dev/null +++ b/web/vendor/bootstrap-icons/p-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/paint-bucket.svg b/web/vendor/bootstrap-icons/paint-bucket.svg new file mode 100644 index 0000000..9ac2df4 --- /dev/null +++ b/web/vendor/bootstrap-icons/paint-bucket.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/palette-fill.svg b/web/vendor/bootstrap-icons/palette-fill.svg new file mode 100644 index 0000000..d7a6a3b --- /dev/null +++ b/web/vendor/bootstrap-icons/palette-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/palette.svg b/web/vendor/bootstrap-icons/palette.svg new file mode 100644 index 0000000..1cd490f --- /dev/null +++ b/web/vendor/bootstrap-icons/palette.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/palette2.svg b/web/vendor/bootstrap-icons/palette2.svg new file mode 100644 index 0000000..ae65e88 --- /dev/null +++ b/web/vendor/bootstrap-icons/palette2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/paperclip.svg b/web/vendor/bootstrap-icons/paperclip.svg new file mode 100644 index 0000000..c02950b --- /dev/null +++ b/web/vendor/bootstrap-icons/paperclip.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/paragraph.svg b/web/vendor/bootstrap-icons/paragraph.svg new file mode 100644 index 0000000..38c65d4 --- /dev/null +++ b/web/vendor/bootstrap-icons/paragraph.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pass-fill.svg b/web/vendor/bootstrap-icons/pass-fill.svg new file mode 100644 index 0000000..1e15dd9 --- /dev/null +++ b/web/vendor/bootstrap-icons/pass-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pass.svg b/web/vendor/bootstrap-icons/pass.svg new file mode 100644 index 0000000..20a06bc --- /dev/null +++ b/web/vendor/bootstrap-icons/pass.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/passport-fill.svg b/web/vendor/bootstrap-icons/passport-fill.svg new file mode 100644 index 0000000..d42c1b9 --- /dev/null +++ b/web/vendor/bootstrap-icons/passport-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/passport.svg b/web/vendor/bootstrap-icons/passport.svg new file mode 100644 index 0000000..2ecee5a --- /dev/null +++ b/web/vendor/bootstrap-icons/passport.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-check-fill.svg b/web/vendor/bootstrap-icons/patch-check-fill.svg new file mode 100644 index 0000000..91283e2 --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-check-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-check.svg b/web/vendor/bootstrap-icons/patch-check.svg new file mode 100644 index 0000000..1fd0a2e --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-exclamation-fill.svg b/web/vendor/bootstrap-icons/patch-exclamation-fill.svg new file mode 100644 index 0000000..e745268 --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-exclamation-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-exclamation.svg b/web/vendor/bootstrap-icons/patch-exclamation.svg new file mode 100644 index 0000000..2372cc6 --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-minus-fill.svg b/web/vendor/bootstrap-icons/patch-minus-fill.svg new file mode 100644 index 0000000..bfeb96e --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-minus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-minus.svg b/web/vendor/bootstrap-icons/patch-minus.svg new file mode 100644 index 0000000..35a380c --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-plus-fill.svg b/web/vendor/bootstrap-icons/patch-plus-fill.svg new file mode 100644 index 0000000..b475098 --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-plus.svg b/web/vendor/bootstrap-icons/patch-plus.svg new file mode 100644 index 0000000..4f332da --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-question-fill.svg b/web/vendor/bootstrap-icons/patch-question-fill.svg new file mode 100644 index 0000000..101c255 --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-question-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/patch-question.svg b/web/vendor/bootstrap-icons/patch-question.svg new file mode 100644 index 0000000..a777cef --- /dev/null +++ b/web/vendor/bootstrap-icons/patch-question.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pause-btn-fill.svg b/web/vendor/bootstrap-icons/pause-btn-fill.svg new file mode 100644 index 0000000..81c0720 --- /dev/null +++ b/web/vendor/bootstrap-icons/pause-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pause-btn.svg b/web/vendor/bootstrap-icons/pause-btn.svg new file mode 100644 index 0000000..e2d68f9 --- /dev/null +++ b/web/vendor/bootstrap-icons/pause-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pause-circle-fill.svg b/web/vendor/bootstrap-icons/pause-circle-fill.svg new file mode 100644 index 0000000..90c4ca5 --- /dev/null +++ b/web/vendor/bootstrap-icons/pause-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pause-circle.svg b/web/vendor/bootstrap-icons/pause-circle.svg new file mode 100644 index 0000000..6d3aeff --- /dev/null +++ b/web/vendor/bootstrap-icons/pause-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pause-fill.svg b/web/vendor/bootstrap-icons/pause-fill.svg new file mode 100644 index 0000000..92e1588 --- /dev/null +++ b/web/vendor/bootstrap-icons/pause-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pause.svg b/web/vendor/bootstrap-icons/pause.svg new file mode 100644 index 0000000..7bfde2c --- /dev/null +++ b/web/vendor/bootstrap-icons/pause.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/paypal.svg b/web/vendor/bootstrap-icons/paypal.svg new file mode 100644 index 0000000..b2cec88 --- /dev/null +++ b/web/vendor/bootstrap-icons/paypal.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pc-display-horizontal.svg b/web/vendor/bootstrap-icons/pc-display-horizontal.svg new file mode 100644 index 0000000..724ba86 --- /dev/null +++ b/web/vendor/bootstrap-icons/pc-display-horizontal.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pc-display.svg b/web/vendor/bootstrap-icons/pc-display.svg new file mode 100644 index 0000000..c3cf9dd --- /dev/null +++ b/web/vendor/bootstrap-icons/pc-display.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pc-horizontal.svg b/web/vendor/bootstrap-icons/pc-horizontal.svg new file mode 100644 index 0000000..a8ae72f --- /dev/null +++ b/web/vendor/bootstrap-icons/pc-horizontal.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pc.svg b/web/vendor/bootstrap-icons/pc.svg new file mode 100644 index 0000000..a8c023a --- /dev/null +++ b/web/vendor/bootstrap-icons/pc.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pci-card-network.svg b/web/vendor/bootstrap-icons/pci-card-network.svg new file mode 100644 index 0000000..a2b4359 --- /dev/null +++ b/web/vendor/bootstrap-icons/pci-card-network.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pci-card-sound.svg b/web/vendor/bootstrap-icons/pci-card-sound.svg new file mode 100644 index 0000000..2bb98f2 --- /dev/null +++ b/web/vendor/bootstrap-icons/pci-card-sound.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pci-card.svg b/web/vendor/bootstrap-icons/pci-card.svg new file mode 100644 index 0000000..66ff052 --- /dev/null +++ b/web/vendor/bootstrap-icons/pci-card.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/peace-fill.svg b/web/vendor/bootstrap-icons/peace-fill.svg new file mode 100644 index 0000000..a93e64d --- /dev/null +++ b/web/vendor/bootstrap-icons/peace-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/peace.svg b/web/vendor/bootstrap-icons/peace.svg new file mode 100644 index 0000000..22367e0 --- /dev/null +++ b/web/vendor/bootstrap-icons/peace.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pen-fill.svg b/web/vendor/bootstrap-icons/pen-fill.svg new file mode 100644 index 0000000..59bbb2e --- /dev/null +++ b/web/vendor/bootstrap-icons/pen-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pen.svg b/web/vendor/bootstrap-icons/pen.svg new file mode 100644 index 0000000..a63b250 --- /dev/null +++ b/web/vendor/bootstrap-icons/pen.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pencil-fill.svg b/web/vendor/bootstrap-icons/pencil-fill.svg new file mode 100644 index 0000000..4b3bdd7 --- /dev/null +++ b/web/vendor/bootstrap-icons/pencil-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pencil-square.svg b/web/vendor/bootstrap-icons/pencil-square.svg new file mode 100644 index 0000000..95c0529 --- /dev/null +++ b/web/vendor/bootstrap-icons/pencil-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pencil.svg b/web/vendor/bootstrap-icons/pencil.svg new file mode 100644 index 0000000..0b84e36 --- /dev/null +++ b/web/vendor/bootstrap-icons/pencil.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pentagon-fill.svg b/web/vendor/bootstrap-icons/pentagon-fill.svg new file mode 100644 index 0000000..9c80789 --- /dev/null +++ b/web/vendor/bootstrap-icons/pentagon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pentagon-half.svg b/web/vendor/bootstrap-icons/pentagon-half.svg new file mode 100644 index 0000000..6811a93 --- /dev/null +++ b/web/vendor/bootstrap-icons/pentagon-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pentagon.svg b/web/vendor/bootstrap-icons/pentagon.svg new file mode 100644 index 0000000..b6f5fe3 --- /dev/null +++ b/web/vendor/bootstrap-icons/pentagon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/people-fill.svg b/web/vendor/bootstrap-icons/people-fill.svg new file mode 100644 index 0000000..2b9f768 --- /dev/null +++ b/web/vendor/bootstrap-icons/people-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/people.svg b/web/vendor/bootstrap-icons/people.svg new file mode 100644 index 0000000..341861a --- /dev/null +++ b/web/vendor/bootstrap-icons/people.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/percent.svg b/web/vendor/bootstrap-icons/percent.svg new file mode 100644 index 0000000..c0fd22d --- /dev/null +++ b/web/vendor/bootstrap-icons/percent.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/perplexity.svg b/web/vendor/bootstrap-icons/perplexity.svg new file mode 100644 index 0000000..8cab953 --- /dev/null +++ b/web/vendor/bootstrap-icons/perplexity.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-add.svg b/web/vendor/bootstrap-icons/person-add.svg new file mode 100644 index 0000000..66e2508 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-arms-up.svg b/web/vendor/bootstrap-icons/person-arms-up.svg new file mode 100644 index 0000000..deb50e8 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-arms-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-badge-fill.svg b/web/vendor/bootstrap-icons/person-badge-fill.svg new file mode 100644 index 0000000..7110ed3 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-badge-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-badge.svg b/web/vendor/bootstrap-icons/person-badge.svg new file mode 100644 index 0000000..680aee1 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-badge.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-bounding-box.svg b/web/vendor/bootstrap-icons/person-bounding-box.svg new file mode 100644 index 0000000..d9be675 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-bounding-box.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-check-fill.svg b/web/vendor/bootstrap-icons/person-check-fill.svg new file mode 100644 index 0000000..04b95d3 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-check-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-check.svg b/web/vendor/bootstrap-icons/person-check.svg new file mode 100644 index 0000000..39b4219 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-circle.svg b/web/vendor/bootstrap-icons/person-circle.svg new file mode 100644 index 0000000..a75f25f --- /dev/null +++ b/web/vendor/bootstrap-icons/person-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-dash-fill.svg b/web/vendor/bootstrap-icons/person-dash-fill.svg new file mode 100644 index 0000000..9879e6e --- /dev/null +++ b/web/vendor/bootstrap-icons/person-dash-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-dash.svg b/web/vendor/bootstrap-icons/person-dash.svg new file mode 100644 index 0000000..b61190b --- /dev/null +++ b/web/vendor/bootstrap-icons/person-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-down.svg b/web/vendor/bootstrap-icons/person-down.svg new file mode 100644 index 0000000..79cf29a --- /dev/null +++ b/web/vendor/bootstrap-icons/person-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-exclamation.svg b/web/vendor/bootstrap-icons/person-exclamation.svg new file mode 100644 index 0000000..46fb506 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-add.svg b/web/vendor/bootstrap-icons/person-fill-add.svg new file mode 100644 index 0000000..d6d15f9 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-check.svg b/web/vendor/bootstrap-icons/person-fill-check.svg new file mode 100644 index 0000000..19b88a4 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-dash.svg b/web/vendor/bootstrap-icons/person-fill-dash.svg new file mode 100644 index 0000000..24c2944 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-down.svg b/web/vendor/bootstrap-icons/person-fill-down.svg new file mode 100644 index 0000000..714ae50 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-exclamation.svg b/web/vendor/bootstrap-icons/person-fill-exclamation.svg new file mode 100644 index 0000000..5c3b7f5 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-gear.svg b/web/vendor/bootstrap-icons/person-fill-gear.svg new file mode 100644 index 0000000..33b120a --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-gear.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-lock.svg b/web/vendor/bootstrap-icons/person-fill-lock.svg new file mode 100644 index 0000000..adbccff --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-lock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-slash.svg b/web/vendor/bootstrap-icons/person-fill-slash.svg new file mode 100644 index 0000000..398d563 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-up.svg b/web/vendor/bootstrap-icons/person-fill-up.svg new file mode 100644 index 0000000..1edd97b --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill-x.svg b/web/vendor/bootstrap-icons/person-fill-x.svg new file mode 100644 index 0000000..e3a66ed --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-fill.svg b/web/vendor/bootstrap-icons/person-fill.svg new file mode 100644 index 0000000..46d1a75 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-gear.svg b/web/vendor/bootstrap-icons/person-gear.svg new file mode 100644 index 0000000..93ec4da --- /dev/null +++ b/web/vendor/bootstrap-icons/person-gear.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-heart.svg b/web/vendor/bootstrap-icons/person-heart.svg new file mode 100644 index 0000000..51b236e --- /dev/null +++ b/web/vendor/bootstrap-icons/person-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-hearts.svg b/web/vendor/bootstrap-icons/person-hearts.svg new file mode 100644 index 0000000..70bb2e0 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-hearts.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-lines-fill.svg b/web/vendor/bootstrap-icons/person-lines-fill.svg new file mode 100644 index 0000000..cbe6c68 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-lines-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-lock.svg b/web/vendor/bootstrap-icons/person-lock.svg new file mode 100644 index 0000000..d3672cd --- /dev/null +++ b/web/vendor/bootstrap-icons/person-lock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-plus-fill.svg b/web/vendor/bootstrap-icons/person-plus-fill.svg new file mode 100644 index 0000000..6c92aea --- /dev/null +++ b/web/vendor/bootstrap-icons/person-plus-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-plus.svg b/web/vendor/bootstrap-icons/person-plus.svg new file mode 100644 index 0000000..4b88424 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-raised-hand.svg b/web/vendor/bootstrap-icons/person-raised-hand.svg new file mode 100644 index 0000000..00ac301 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-raised-hand.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-rolodex.svg b/web/vendor/bootstrap-icons/person-rolodex.svg new file mode 100644 index 0000000..2039494 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-rolodex.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-slash.svg b/web/vendor/bootstrap-icons/person-slash.svg new file mode 100644 index 0000000..ab53647 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-square.svg b/web/vendor/bootstrap-icons/person-square.svg new file mode 100644 index 0000000..12a33c5 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-standing-dress.svg b/web/vendor/bootstrap-icons/person-standing-dress.svg new file mode 100644 index 0000000..4448606 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-standing-dress.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-standing.svg b/web/vendor/bootstrap-icons/person-standing.svg new file mode 100644 index 0000000..ccd7b35 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-standing.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-up.svg b/web/vendor/bootstrap-icons/person-up.svg new file mode 100644 index 0000000..93a430a --- /dev/null +++ b/web/vendor/bootstrap-icons/person-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-vcard-fill.svg b/web/vendor/bootstrap-icons/person-vcard-fill.svg new file mode 100644 index 0000000..9efb1b8 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-vcard-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-vcard.svg b/web/vendor/bootstrap-icons/person-vcard.svg new file mode 100644 index 0000000..40ec41e --- /dev/null +++ b/web/vendor/bootstrap-icons/person-vcard.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-video.svg b/web/vendor/bootstrap-icons/person-video.svg new file mode 100644 index 0000000..a991759 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-video.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-video2.svg b/web/vendor/bootstrap-icons/person-video2.svg new file mode 100644 index 0000000..80b3b8a --- /dev/null +++ b/web/vendor/bootstrap-icons/person-video2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-video3.svg b/web/vendor/bootstrap-icons/person-video3.svg new file mode 100644 index 0000000..472d998 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-video3.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-walking.svg b/web/vendor/bootstrap-icons/person-walking.svg new file mode 100644 index 0000000..16cc0b4 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-walking.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-wheelchair.svg b/web/vendor/bootstrap-icons/person-wheelchair.svg new file mode 100644 index 0000000..416cad2 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-wheelchair.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-workspace.svg b/web/vendor/bootstrap-icons/person-workspace.svg new file mode 100644 index 0000000..0b3cdce --- /dev/null +++ b/web/vendor/bootstrap-icons/person-workspace.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-x-fill.svg b/web/vendor/bootstrap-icons/person-x-fill.svg new file mode 100644 index 0000000..9e31903 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person-x.svg b/web/vendor/bootstrap-icons/person-x.svg new file mode 100644 index 0000000..7514c59 --- /dev/null +++ b/web/vendor/bootstrap-icons/person-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/person.svg b/web/vendor/bootstrap-icons/person.svg new file mode 100644 index 0000000..98ea060 --- /dev/null +++ b/web/vendor/bootstrap-icons/person.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/phone-fill.svg b/web/vendor/bootstrap-icons/phone-fill.svg new file mode 100644 index 0000000..f25bd51 --- /dev/null +++ b/web/vendor/bootstrap-icons/phone-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/phone-flip.svg b/web/vendor/bootstrap-icons/phone-flip.svg new file mode 100644 index 0000000..3ae28d3 --- /dev/null +++ b/web/vendor/bootstrap-icons/phone-flip.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/phone-landscape-fill.svg b/web/vendor/bootstrap-icons/phone-landscape-fill.svg new file mode 100644 index 0000000..669bf6e --- /dev/null +++ b/web/vendor/bootstrap-icons/phone-landscape-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/phone-landscape.svg b/web/vendor/bootstrap-icons/phone-landscape.svg new file mode 100644 index 0000000..4c30ef2 --- /dev/null +++ b/web/vendor/bootstrap-icons/phone-landscape.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/phone-vibrate-fill.svg b/web/vendor/bootstrap-icons/phone-vibrate-fill.svg new file mode 100644 index 0000000..dc35ca0 --- /dev/null +++ b/web/vendor/bootstrap-icons/phone-vibrate-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/phone-vibrate.svg b/web/vendor/bootstrap-icons/phone-vibrate.svg new file mode 100644 index 0000000..58acbf6 --- /dev/null +++ b/web/vendor/bootstrap-icons/phone-vibrate.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/phone.svg b/web/vendor/bootstrap-icons/phone.svg new file mode 100644 index 0000000..4839331 --- /dev/null +++ b/web/vendor/bootstrap-icons/phone.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pie-chart-fill.svg b/web/vendor/bootstrap-icons/pie-chart-fill.svg new file mode 100644 index 0000000..f667aea --- /dev/null +++ b/web/vendor/bootstrap-icons/pie-chart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pie-chart.svg b/web/vendor/bootstrap-icons/pie-chart.svg new file mode 100644 index 0000000..b49251b --- /dev/null +++ b/web/vendor/bootstrap-icons/pie-chart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/piggy-bank-fill.svg b/web/vendor/bootstrap-icons/piggy-bank-fill.svg new file mode 100644 index 0000000..592b31d --- /dev/null +++ b/web/vendor/bootstrap-icons/piggy-bank-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/piggy-bank.svg b/web/vendor/bootstrap-icons/piggy-bank.svg new file mode 100644 index 0000000..86e33eb --- /dev/null +++ b/web/vendor/bootstrap-icons/piggy-bank.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pin-angle-fill.svg b/web/vendor/bootstrap-icons/pin-angle-fill.svg new file mode 100644 index 0000000..bc3078c --- /dev/null +++ b/web/vendor/bootstrap-icons/pin-angle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pin-angle.svg b/web/vendor/bootstrap-icons/pin-angle.svg new file mode 100644 index 0000000..ecc4d16 --- /dev/null +++ b/web/vendor/bootstrap-icons/pin-angle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pin-fill.svg b/web/vendor/bootstrap-icons/pin-fill.svg new file mode 100644 index 0000000..f345059 --- /dev/null +++ b/web/vendor/bootstrap-icons/pin-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pin-map-fill.svg b/web/vendor/bootstrap-icons/pin-map-fill.svg new file mode 100644 index 0000000..9db0d29 --- /dev/null +++ b/web/vendor/bootstrap-icons/pin-map-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pin-map.svg b/web/vendor/bootstrap-icons/pin-map.svg new file mode 100644 index 0000000..0462a5a --- /dev/null +++ b/web/vendor/bootstrap-icons/pin-map.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pin.svg b/web/vendor/bootstrap-icons/pin.svg new file mode 100644 index 0000000..4655620 --- /dev/null +++ b/web/vendor/bootstrap-icons/pin.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pinterest.svg b/web/vendor/bootstrap-icons/pinterest.svg new file mode 100644 index 0000000..5c850b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/pinterest.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pip-fill.svg b/web/vendor/bootstrap-icons/pip-fill.svg new file mode 100644 index 0000000..4865244 --- /dev/null +++ b/web/vendor/bootstrap-icons/pip-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/pip.svg b/web/vendor/bootstrap-icons/pip.svg new file mode 100644 index 0000000..458c9c9 --- /dev/null +++ b/web/vendor/bootstrap-icons/pip.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/play-btn-fill.svg b/web/vendor/bootstrap-icons/play-btn-fill.svg new file mode 100644 index 0000000..3d0d1c3 --- /dev/null +++ b/web/vendor/bootstrap-icons/play-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/play-btn.svg b/web/vendor/bootstrap-icons/play-btn.svg new file mode 100644 index 0000000..2fcbc5e --- /dev/null +++ b/web/vendor/bootstrap-icons/play-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/play-circle-fill.svg b/web/vendor/bootstrap-icons/play-circle-fill.svg new file mode 100644 index 0000000..93eeb93 --- /dev/null +++ b/web/vendor/bootstrap-icons/play-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/play-circle.svg b/web/vendor/bootstrap-icons/play-circle.svg new file mode 100644 index 0000000..a1d742e --- /dev/null +++ b/web/vendor/bootstrap-icons/play-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/play-fill.svg b/web/vendor/bootstrap-icons/play-fill.svg new file mode 100644 index 0000000..e538083 --- /dev/null +++ b/web/vendor/bootstrap-icons/play-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/play.svg b/web/vendor/bootstrap-icons/play.svg new file mode 100644 index 0000000..98954e7 --- /dev/null +++ b/web/vendor/bootstrap-icons/play.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/playstation.svg b/web/vendor/bootstrap-icons/playstation.svg new file mode 100644 index 0000000..3275d59 --- /dev/null +++ b/web/vendor/bootstrap-icons/playstation.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plug-fill.svg b/web/vendor/bootstrap-icons/plug-fill.svg new file mode 100644 index 0000000..99858eb --- /dev/null +++ b/web/vendor/bootstrap-icons/plug-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plug.svg b/web/vendor/bootstrap-icons/plug.svg new file mode 100644 index 0000000..9d6a85b --- /dev/null +++ b/web/vendor/bootstrap-icons/plug.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plugin.svg b/web/vendor/bootstrap-icons/plugin.svg new file mode 100644 index 0000000..92e99d4 --- /dev/null +++ b/web/vendor/bootstrap-icons/plugin.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus-circle-dotted.svg b/web/vendor/bootstrap-icons/plus-circle-dotted.svg new file mode 100644 index 0000000..2a20e2d --- /dev/null +++ b/web/vendor/bootstrap-icons/plus-circle-dotted.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus-circle-fill.svg b/web/vendor/bootstrap-icons/plus-circle-fill.svg new file mode 100644 index 0000000..d1ec2d0 --- /dev/null +++ b/web/vendor/bootstrap-icons/plus-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus-circle.svg b/web/vendor/bootstrap-icons/plus-circle.svg new file mode 100644 index 0000000..2832373 --- /dev/null +++ b/web/vendor/bootstrap-icons/plus-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus-lg.svg b/web/vendor/bootstrap-icons/plus-lg.svg new file mode 100644 index 0000000..531e86c --- /dev/null +++ b/web/vendor/bootstrap-icons/plus-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus-slash-minus.svg b/web/vendor/bootstrap-icons/plus-slash-minus.svg new file mode 100644 index 0000000..e0fee7d --- /dev/null +++ b/web/vendor/bootstrap-icons/plus-slash-minus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus-square-dotted.svg b/web/vendor/bootstrap-icons/plus-square-dotted.svg new file mode 100644 index 0000000..e230a08 --- /dev/null +++ b/web/vendor/bootstrap-icons/plus-square-dotted.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus-square-fill.svg b/web/vendor/bootstrap-icons/plus-square-fill.svg new file mode 100644 index 0000000..1dddd13 --- /dev/null +++ b/web/vendor/bootstrap-icons/plus-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus-square.svg b/web/vendor/bootstrap-icons/plus-square.svg new file mode 100644 index 0000000..ef11b48 --- /dev/null +++ b/web/vendor/bootstrap-icons/plus-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/plus.svg b/web/vendor/bootstrap-icons/plus.svg new file mode 100644 index 0000000..9012271 --- /dev/null +++ b/web/vendor/bootstrap-icons/plus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/postage-fill.svg b/web/vendor/bootstrap-icons/postage-fill.svg new file mode 100644 index 0000000..861a3a1 --- /dev/null +++ b/web/vendor/bootstrap-icons/postage-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/postage-heart-fill.svg b/web/vendor/bootstrap-icons/postage-heart-fill.svg new file mode 100644 index 0000000..4737a4d --- /dev/null +++ b/web/vendor/bootstrap-icons/postage-heart-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/postage-heart.svg b/web/vendor/bootstrap-icons/postage-heart.svg new file mode 100644 index 0000000..aa35a6c --- /dev/null +++ b/web/vendor/bootstrap-icons/postage-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/postage.svg b/web/vendor/bootstrap-icons/postage.svg new file mode 100644 index 0000000..54dcfa6 --- /dev/null +++ b/web/vendor/bootstrap-icons/postage.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/postcard-fill.svg b/web/vendor/bootstrap-icons/postcard-fill.svg new file mode 100644 index 0000000..aeba518 --- /dev/null +++ b/web/vendor/bootstrap-icons/postcard-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/postcard-heart-fill.svg b/web/vendor/bootstrap-icons/postcard-heart-fill.svg new file mode 100644 index 0000000..1e371b8 --- /dev/null +++ b/web/vendor/bootstrap-icons/postcard-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/postcard-heart.svg b/web/vendor/bootstrap-icons/postcard-heart.svg new file mode 100644 index 0000000..52c0053 --- /dev/null +++ b/web/vendor/bootstrap-icons/postcard-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/postcard.svg b/web/vendor/bootstrap-icons/postcard.svg new file mode 100644 index 0000000..43ba40b --- /dev/null +++ b/web/vendor/bootstrap-icons/postcard.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/power.svg b/web/vendor/bootstrap-icons/power.svg new file mode 100644 index 0000000..937b842 --- /dev/null +++ b/web/vendor/bootstrap-icons/power.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/prescription.svg b/web/vendor/bootstrap-icons/prescription.svg new file mode 100644 index 0000000..b895b2b --- /dev/null +++ b/web/vendor/bootstrap-icons/prescription.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/prescription2.svg b/web/vendor/bootstrap-icons/prescription2.svg new file mode 100644 index 0000000..cb278b0 --- /dev/null +++ b/web/vendor/bootstrap-icons/prescription2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/printer-fill.svg b/web/vendor/bootstrap-icons/printer-fill.svg new file mode 100644 index 0000000..43cee36 --- /dev/null +++ b/web/vendor/bootstrap-icons/printer-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/printer.svg b/web/vendor/bootstrap-icons/printer.svg new file mode 100644 index 0000000..0886a57 --- /dev/null +++ b/web/vendor/bootstrap-icons/printer.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/projector-fill.svg b/web/vendor/bootstrap-icons/projector-fill.svg new file mode 100644 index 0000000..046166c --- /dev/null +++ b/web/vendor/bootstrap-icons/projector-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/projector.svg b/web/vendor/bootstrap-icons/projector.svg new file mode 100644 index 0000000..77e68b0 --- /dev/null +++ b/web/vendor/bootstrap-icons/projector.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/puzzle-fill.svg b/web/vendor/bootstrap-icons/puzzle-fill.svg new file mode 100644 index 0000000..92c4ea0 --- /dev/null +++ b/web/vendor/bootstrap-icons/puzzle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/puzzle.svg b/web/vendor/bootstrap-icons/puzzle.svg new file mode 100644 index 0000000..44903f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/puzzle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/qr-code-scan.svg b/web/vendor/bootstrap-icons/qr-code-scan.svg new file mode 100644 index 0000000..3c53387 --- /dev/null +++ b/web/vendor/bootstrap-icons/qr-code-scan.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/qr-code.svg b/web/vendor/bootstrap-icons/qr-code.svg new file mode 100644 index 0000000..e09157a --- /dev/null +++ b/web/vendor/bootstrap-icons/qr-code.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-circle-fill.svg b/web/vendor/bootstrap-icons/question-circle-fill.svg new file mode 100644 index 0000000..8b2a2c0 --- /dev/null +++ b/web/vendor/bootstrap-icons/question-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-circle.svg b/web/vendor/bootstrap-icons/question-circle.svg new file mode 100644 index 0000000..283e653 --- /dev/null +++ b/web/vendor/bootstrap-icons/question-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-diamond-fill.svg b/web/vendor/bootstrap-icons/question-diamond-fill.svg new file mode 100644 index 0000000..6bf3512 --- /dev/null +++ b/web/vendor/bootstrap-icons/question-diamond-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-diamond.svg b/web/vendor/bootstrap-icons/question-diamond.svg new file mode 100644 index 0000000..a777b47 --- /dev/null +++ b/web/vendor/bootstrap-icons/question-diamond.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-lg.svg b/web/vendor/bootstrap-icons/question-lg.svg new file mode 100644 index 0000000..756ea0f --- /dev/null +++ b/web/vendor/bootstrap-icons/question-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-octagon-fill.svg b/web/vendor/bootstrap-icons/question-octagon-fill.svg new file mode 100644 index 0000000..c0c43ef --- /dev/null +++ b/web/vendor/bootstrap-icons/question-octagon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-octagon.svg b/web/vendor/bootstrap-icons/question-octagon.svg new file mode 100644 index 0000000..5116862 --- /dev/null +++ b/web/vendor/bootstrap-icons/question-octagon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-square-fill.svg b/web/vendor/bootstrap-icons/question-square-fill.svg new file mode 100644 index 0000000..a266f9f --- /dev/null +++ b/web/vendor/bootstrap-icons/question-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question-square.svg b/web/vendor/bootstrap-icons/question-square.svg new file mode 100644 index 0000000..ad44a20 --- /dev/null +++ b/web/vendor/bootstrap-icons/question-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/question.svg b/web/vendor/bootstrap-icons/question.svg new file mode 100644 index 0000000..ba185ad --- /dev/null +++ b/web/vendor/bootstrap-icons/question.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/quora.svg b/web/vendor/bootstrap-icons/quora.svg new file mode 100644 index 0000000..85ca1bd --- /dev/null +++ b/web/vendor/bootstrap-icons/quora.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/quote.svg b/web/vendor/bootstrap-icons/quote.svg new file mode 100644 index 0000000..0aa0e17 --- /dev/null +++ b/web/vendor/bootstrap-icons/quote.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/r-circle-fill.svg b/web/vendor/bootstrap-icons/r-circle-fill.svg new file mode 100644 index 0000000..810423e --- /dev/null +++ b/web/vendor/bootstrap-icons/r-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/r-circle.svg b/web/vendor/bootstrap-icons/r-circle.svg new file mode 100644 index 0000000..bf2d8d6 --- /dev/null +++ b/web/vendor/bootstrap-icons/r-circle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/r-square-fill.svg b/web/vendor/bootstrap-icons/r-square-fill.svg new file mode 100644 index 0000000..b1151f3 --- /dev/null +++ b/web/vendor/bootstrap-icons/r-square-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/r-square.svg b/web/vendor/bootstrap-icons/r-square.svg new file mode 100644 index 0000000..e19e688 --- /dev/null +++ b/web/vendor/bootstrap-icons/r-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/radar.svg b/web/vendor/bootstrap-icons/radar.svg new file mode 100644 index 0000000..024f3fd --- /dev/null +++ b/web/vendor/bootstrap-icons/radar.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/radioactive.svg b/web/vendor/bootstrap-icons/radioactive.svg new file mode 100644 index 0000000..3eaaa56 --- /dev/null +++ b/web/vendor/bootstrap-icons/radioactive.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rainbow.svg b/web/vendor/bootstrap-icons/rainbow.svg new file mode 100644 index 0000000..e864abf --- /dev/null +++ b/web/vendor/bootstrap-icons/rainbow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/receipt-cutoff.svg b/web/vendor/bootstrap-icons/receipt-cutoff.svg new file mode 100644 index 0000000..21c3bc8 --- /dev/null +++ b/web/vendor/bootstrap-icons/receipt-cutoff.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/receipt.svg b/web/vendor/bootstrap-icons/receipt.svg new file mode 100644 index 0000000..ab29fe6 --- /dev/null +++ b/web/vendor/bootstrap-icons/receipt.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reception-0.svg b/web/vendor/bootstrap-icons/reception-0.svg new file mode 100644 index 0000000..a7c7872 --- /dev/null +++ b/web/vendor/bootstrap-icons/reception-0.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reception-1.svg b/web/vendor/bootstrap-icons/reception-1.svg new file mode 100644 index 0000000..4081ceb --- /dev/null +++ b/web/vendor/bootstrap-icons/reception-1.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reception-2.svg b/web/vendor/bootstrap-icons/reception-2.svg new file mode 100644 index 0000000..7e1acc5 --- /dev/null +++ b/web/vendor/bootstrap-icons/reception-2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reception-3.svg b/web/vendor/bootstrap-icons/reception-3.svg new file mode 100644 index 0000000..e9ea476 --- /dev/null +++ b/web/vendor/bootstrap-icons/reception-3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reception-4.svg b/web/vendor/bootstrap-icons/reception-4.svg new file mode 100644 index 0000000..7791e4b --- /dev/null +++ b/web/vendor/bootstrap-icons/reception-4.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/record-btn-fill.svg b/web/vendor/bootstrap-icons/record-btn-fill.svg new file mode 100644 index 0000000..83ee303 --- /dev/null +++ b/web/vendor/bootstrap-icons/record-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/record-btn.svg b/web/vendor/bootstrap-icons/record-btn.svg new file mode 100644 index 0000000..7ba84ce --- /dev/null +++ b/web/vendor/bootstrap-icons/record-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/record-circle-fill.svg b/web/vendor/bootstrap-icons/record-circle-fill.svg new file mode 100644 index 0000000..0db59d1 --- /dev/null +++ b/web/vendor/bootstrap-icons/record-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/record-circle.svg b/web/vendor/bootstrap-icons/record-circle.svg new file mode 100644 index 0000000..5dad17b --- /dev/null +++ b/web/vendor/bootstrap-icons/record-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/record-fill.svg b/web/vendor/bootstrap-icons/record-fill.svg new file mode 100644 index 0000000..ef31df0 --- /dev/null +++ b/web/vendor/bootstrap-icons/record-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/record.svg b/web/vendor/bootstrap-icons/record.svg new file mode 100644 index 0000000..5ec840c --- /dev/null +++ b/web/vendor/bootstrap-icons/record.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/record2-fill.svg b/web/vendor/bootstrap-icons/record2-fill.svg new file mode 100644 index 0000000..8ad4fe3 --- /dev/null +++ b/web/vendor/bootstrap-icons/record2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/record2.svg b/web/vendor/bootstrap-icons/record2.svg new file mode 100644 index 0000000..3b5c5ca --- /dev/null +++ b/web/vendor/bootstrap-icons/record2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/recycle.svg b/web/vendor/bootstrap-icons/recycle.svg new file mode 100644 index 0000000..21d1bd9 --- /dev/null +++ b/web/vendor/bootstrap-icons/recycle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reddit.svg b/web/vendor/bootstrap-icons/reddit.svg new file mode 100644 index 0000000..777aead --- /dev/null +++ b/web/vendor/bootstrap-icons/reddit.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/regex.svg b/web/vendor/bootstrap-icons/regex.svg new file mode 100644 index 0000000..ec8bf00 --- /dev/null +++ b/web/vendor/bootstrap-icons/regex.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/repeat-1.svg b/web/vendor/bootstrap-icons/repeat-1.svg new file mode 100644 index 0000000..9357fcf --- /dev/null +++ b/web/vendor/bootstrap-icons/repeat-1.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/repeat.svg b/web/vendor/bootstrap-icons/repeat.svg new file mode 100644 index 0000000..51765c9 --- /dev/null +++ b/web/vendor/bootstrap-icons/repeat.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reply-all-fill.svg b/web/vendor/bootstrap-icons/reply-all-fill.svg new file mode 100644 index 0000000..95e18a2 --- /dev/null +++ b/web/vendor/bootstrap-icons/reply-all-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reply-all.svg b/web/vendor/bootstrap-icons/reply-all.svg new file mode 100644 index 0000000..decad51 --- /dev/null +++ b/web/vendor/bootstrap-icons/reply-all.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reply-fill.svg b/web/vendor/bootstrap-icons/reply-fill.svg new file mode 100644 index 0000000..82358b1 --- /dev/null +++ b/web/vendor/bootstrap-icons/reply-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/reply.svg b/web/vendor/bootstrap-icons/reply.svg new file mode 100644 index 0000000..5bb432e --- /dev/null +++ b/web/vendor/bootstrap-icons/reply.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rewind-btn-fill.svg b/web/vendor/bootstrap-icons/rewind-btn-fill.svg new file mode 100644 index 0000000..8ea4155 --- /dev/null +++ b/web/vendor/bootstrap-icons/rewind-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rewind-btn.svg b/web/vendor/bootstrap-icons/rewind-btn.svg new file mode 100644 index 0000000..47bd717 --- /dev/null +++ b/web/vendor/bootstrap-icons/rewind-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rewind-circle-fill.svg b/web/vendor/bootstrap-icons/rewind-circle-fill.svg new file mode 100644 index 0000000..b972044 --- /dev/null +++ b/web/vendor/bootstrap-icons/rewind-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rewind-circle.svg b/web/vendor/bootstrap-icons/rewind-circle.svg new file mode 100644 index 0000000..4952147 --- /dev/null +++ b/web/vendor/bootstrap-icons/rewind-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rewind-fill.svg b/web/vendor/bootstrap-icons/rewind-fill.svg new file mode 100644 index 0000000..5919f7e --- /dev/null +++ b/web/vendor/bootstrap-icons/rewind-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rewind.svg b/web/vendor/bootstrap-icons/rewind.svg new file mode 100644 index 0000000..bc731e7 --- /dev/null +++ b/web/vendor/bootstrap-icons/rewind.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/robot.svg b/web/vendor/bootstrap-icons/robot.svg new file mode 100644 index 0000000..a224202 --- /dev/null +++ b/web/vendor/bootstrap-icons/robot.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rocket-fill.svg b/web/vendor/bootstrap-icons/rocket-fill.svg new file mode 100644 index 0000000..f319029 --- /dev/null +++ b/web/vendor/bootstrap-icons/rocket-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rocket-takeoff-fill.svg b/web/vendor/bootstrap-icons/rocket-takeoff-fill.svg new file mode 100644 index 0000000..707d205 --- /dev/null +++ b/web/vendor/bootstrap-icons/rocket-takeoff-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rocket-takeoff.svg b/web/vendor/bootstrap-icons/rocket-takeoff.svg new file mode 100644 index 0000000..2abc6d4 --- /dev/null +++ b/web/vendor/bootstrap-icons/rocket-takeoff.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rocket.svg b/web/vendor/bootstrap-icons/rocket.svg new file mode 100644 index 0000000..b760e1f --- /dev/null +++ b/web/vendor/bootstrap-icons/rocket.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/router-fill.svg b/web/vendor/bootstrap-icons/router-fill.svg new file mode 100644 index 0000000..74d1469 --- /dev/null +++ b/web/vendor/bootstrap-icons/router-fill.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/router.svg b/web/vendor/bootstrap-icons/router.svg new file mode 100644 index 0000000..62fac78 --- /dev/null +++ b/web/vendor/bootstrap-icons/router.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rss-fill.svg b/web/vendor/bootstrap-icons/rss-fill.svg new file mode 100644 index 0000000..50d7cfd --- /dev/null +++ b/web/vendor/bootstrap-icons/rss-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rss.svg b/web/vendor/bootstrap-icons/rss.svg new file mode 100644 index 0000000..18dc9f1 --- /dev/null +++ b/web/vendor/bootstrap-icons/rss.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/rulers.svg b/web/vendor/bootstrap-icons/rulers.svg new file mode 100644 index 0000000..90fb01c --- /dev/null +++ b/web/vendor/bootstrap-icons/rulers.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/safe-fill.svg b/web/vendor/bootstrap-icons/safe-fill.svg new file mode 100644 index 0000000..6da7a7d --- /dev/null +++ b/web/vendor/bootstrap-icons/safe-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/safe.svg b/web/vendor/bootstrap-icons/safe.svg new file mode 100644 index 0000000..d6d24c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/safe.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/safe2-fill.svg b/web/vendor/bootstrap-icons/safe2-fill.svg new file mode 100644 index 0000000..064e073 --- /dev/null +++ b/web/vendor/bootstrap-icons/safe2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/safe2.svg b/web/vendor/bootstrap-icons/safe2.svg new file mode 100644 index 0000000..9c80f55 --- /dev/null +++ b/web/vendor/bootstrap-icons/safe2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/save-fill.svg b/web/vendor/bootstrap-icons/save-fill.svg new file mode 100644 index 0000000..1c42812 --- /dev/null +++ b/web/vendor/bootstrap-icons/save-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/save.svg b/web/vendor/bootstrap-icons/save.svg new file mode 100644 index 0000000..9dd7b2f --- /dev/null +++ b/web/vendor/bootstrap-icons/save.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/save2-fill.svg b/web/vendor/bootstrap-icons/save2-fill.svg new file mode 100644 index 0000000..207f91b --- /dev/null +++ b/web/vendor/bootstrap-icons/save2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/save2.svg b/web/vendor/bootstrap-icons/save2.svg new file mode 100644 index 0000000..988c4f1 --- /dev/null +++ b/web/vendor/bootstrap-icons/save2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/scissors.svg b/web/vendor/bootstrap-icons/scissors.svg new file mode 100644 index 0000000..2f566e4 --- /dev/null +++ b/web/vendor/bootstrap-icons/scissors.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/scooter.svg b/web/vendor/bootstrap-icons/scooter.svg new file mode 100644 index 0000000..8828452 --- /dev/null +++ b/web/vendor/bootstrap-icons/scooter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/screwdriver.svg b/web/vendor/bootstrap-icons/screwdriver.svg new file mode 100644 index 0000000..54d5a2c --- /dev/null +++ b/web/vendor/bootstrap-icons/screwdriver.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sd-card-fill.svg b/web/vendor/bootstrap-icons/sd-card-fill.svg new file mode 100644 index 0000000..655a96d --- /dev/null +++ b/web/vendor/bootstrap-icons/sd-card-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sd-card.svg b/web/vendor/bootstrap-icons/sd-card.svg new file mode 100644 index 0000000..564661a --- /dev/null +++ b/web/vendor/bootstrap-icons/sd-card.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/search-heart-fill.svg b/web/vendor/bootstrap-icons/search-heart-fill.svg new file mode 100644 index 0000000..c57bb48 --- /dev/null +++ b/web/vendor/bootstrap-icons/search-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/search-heart.svg b/web/vendor/bootstrap-icons/search-heart.svg new file mode 100644 index 0000000..d76bfe5 --- /dev/null +++ b/web/vendor/bootstrap-icons/search-heart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/search.svg b/web/vendor/bootstrap-icons/search.svg new file mode 100644 index 0000000..3318054 --- /dev/null +++ b/web/vendor/bootstrap-icons/search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/segmented-nav.svg b/web/vendor/bootstrap-icons/segmented-nav.svg new file mode 100644 index 0000000..b274b68 --- /dev/null +++ b/web/vendor/bootstrap-icons/segmented-nav.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-arrow-down-fill.svg b/web/vendor/bootstrap-icons/send-arrow-down-fill.svg new file mode 100644 index 0000000..6d43965 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-arrow-down-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-arrow-down.svg b/web/vendor/bootstrap-icons/send-arrow-down.svg new file mode 100644 index 0000000..dcbae56 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-arrow-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-arrow-up-fill.svg b/web/vendor/bootstrap-icons/send-arrow-up-fill.svg new file mode 100644 index 0000000..19abab7 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-arrow-up-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-arrow-up.svg b/web/vendor/bootstrap-icons/send-arrow-up.svg new file mode 100644 index 0000000..a642dac --- /dev/null +++ b/web/vendor/bootstrap-icons/send-arrow-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-check-fill.svg b/web/vendor/bootstrap-icons/send-check-fill.svg new file mode 100644 index 0000000..c4259c8 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-check-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-check.svg b/web/vendor/bootstrap-icons/send-check.svg new file mode 100644 index 0000000..5221868 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-dash-fill.svg b/web/vendor/bootstrap-icons/send-dash-fill.svg new file mode 100644 index 0000000..12a82df --- /dev/null +++ b/web/vendor/bootstrap-icons/send-dash-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-dash.svg b/web/vendor/bootstrap-icons/send-dash.svg new file mode 100644 index 0000000..63fc38c --- /dev/null +++ b/web/vendor/bootstrap-icons/send-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-exclamation-fill.svg b/web/vendor/bootstrap-icons/send-exclamation-fill.svg new file mode 100644 index 0000000..fce810f --- /dev/null +++ b/web/vendor/bootstrap-icons/send-exclamation-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-exclamation.svg b/web/vendor/bootstrap-icons/send-exclamation.svg new file mode 100644 index 0000000..8a72f31 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-fill.svg b/web/vendor/bootstrap-icons/send-fill.svg new file mode 100644 index 0000000..6e95d27 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-plus-fill.svg b/web/vendor/bootstrap-icons/send-plus-fill.svg new file mode 100644 index 0000000..63b0482 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-plus-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-plus.svg b/web/vendor/bootstrap-icons/send-plus.svg new file mode 100644 index 0000000..350b388 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-slash-fill.svg b/web/vendor/bootstrap-icons/send-slash-fill.svg new file mode 100644 index 0000000..e98aa92 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-slash-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-slash.svg b/web/vendor/bootstrap-icons/send-slash.svg new file mode 100644 index 0000000..e434afe --- /dev/null +++ b/web/vendor/bootstrap-icons/send-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-x-fill.svg b/web/vendor/bootstrap-icons/send-x-fill.svg new file mode 100644 index 0000000..45a98a4 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-x-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send-x.svg b/web/vendor/bootstrap-icons/send-x.svg new file mode 100644 index 0000000..5b854c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/send-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/send.svg b/web/vendor/bootstrap-icons/send.svg new file mode 100644 index 0000000..8db355e --- /dev/null +++ b/web/vendor/bootstrap-icons/send.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/server.svg b/web/vendor/bootstrap-icons/server.svg new file mode 100644 index 0000000..bb8ca8f --- /dev/null +++ b/web/vendor/bootstrap-icons/server.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shadows.svg b/web/vendor/bootstrap-icons/shadows.svg new file mode 100644 index 0000000..6b09f0f --- /dev/null +++ b/web/vendor/bootstrap-icons/shadows.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/share-fill.svg b/web/vendor/bootstrap-icons/share-fill.svg new file mode 100644 index 0000000..bdc07ca --- /dev/null +++ b/web/vendor/bootstrap-icons/share-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/share.svg b/web/vendor/bootstrap-icons/share.svg new file mode 100644 index 0000000..bc62b93 --- /dev/null +++ b/web/vendor/bootstrap-icons/share.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-check.svg b/web/vendor/bootstrap-icons/shield-check.svg new file mode 100644 index 0000000..3908fca --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-exclamation.svg b/web/vendor/bootstrap-icons/shield-exclamation.svg new file mode 100644 index 0000000..9826504 --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-fill-check.svg b/web/vendor/bootstrap-icons/shield-fill-check.svg new file mode 100644 index 0000000..f914f1f --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-fill-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-fill-exclamation.svg b/web/vendor/bootstrap-icons/shield-fill-exclamation.svg new file mode 100644 index 0000000..99a6bf9 --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-fill-exclamation.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-fill-minus.svg b/web/vendor/bootstrap-icons/shield-fill-minus.svg new file mode 100644 index 0000000..584f5ae --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-fill-minus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-fill-plus.svg b/web/vendor/bootstrap-icons/shield-fill-plus.svg new file mode 100644 index 0000000..43a3169 --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-fill-plus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-fill-x.svg b/web/vendor/bootstrap-icons/shield-fill-x.svg new file mode 100644 index 0000000..42267cf --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-fill-x.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-fill.svg b/web/vendor/bootstrap-icons/shield-fill.svg new file mode 100644 index 0000000..12a61bc --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-lock-fill.svg b/web/vendor/bootstrap-icons/shield-lock-fill.svg new file mode 100644 index 0000000..0fccf6f --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-lock-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-lock.svg b/web/vendor/bootstrap-icons/shield-lock.svg new file mode 100644 index 0000000..316fb3c --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-minus.svg b/web/vendor/bootstrap-icons/shield-minus.svg new file mode 100644 index 0000000..9fb8712 --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-plus.svg b/web/vendor/bootstrap-icons/shield-plus.svg new file mode 100644 index 0000000..3b19b28 --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-shaded.svg b/web/vendor/bootstrap-icons/shield-shaded.svg new file mode 100644 index 0000000..4908f5d --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-shaded.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-slash-fill.svg b/web/vendor/bootstrap-icons/shield-slash-fill.svg new file mode 100644 index 0000000..d270d6d --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-slash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-slash.svg b/web/vendor/bootstrap-icons/shield-slash.svg new file mode 100644 index 0000000..abc01b1 --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield-x.svg b/web/vendor/bootstrap-icons/shield-x.svg new file mode 100644 index 0000000..cc9c59d --- /dev/null +++ b/web/vendor/bootstrap-icons/shield-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shield.svg b/web/vendor/bootstrap-icons/shield.svg new file mode 100644 index 0000000..7e18d1b --- /dev/null +++ b/web/vendor/bootstrap-icons/shield.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shift-fill.svg b/web/vendor/bootstrap-icons/shift-fill.svg new file mode 100644 index 0000000..37583e1 --- /dev/null +++ b/web/vendor/bootstrap-icons/shift-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shift.svg b/web/vendor/bootstrap-icons/shift.svg new file mode 100644 index 0000000..5d8a6e3 --- /dev/null +++ b/web/vendor/bootstrap-icons/shift.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shop-window.svg b/web/vendor/bootstrap-icons/shop-window.svg new file mode 100644 index 0000000..14e0d42 --- /dev/null +++ b/web/vendor/bootstrap-icons/shop-window.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shop.svg b/web/vendor/bootstrap-icons/shop.svg new file mode 100644 index 0000000..e6bb8c0 --- /dev/null +++ b/web/vendor/bootstrap-icons/shop.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/shuffle.svg b/web/vendor/bootstrap-icons/shuffle.svg new file mode 100644 index 0000000..2787bf2 --- /dev/null +++ b/web/vendor/bootstrap-icons/shuffle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-dead-end-fill.svg b/web/vendor/bootstrap-icons/sign-dead-end-fill.svg new file mode 100644 index 0000000..b362833 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-dead-end-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-dead-end.svg b/web/vendor/bootstrap-icons/sign-dead-end.svg new file mode 100644 index 0000000..b87d368 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-dead-end.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-do-not-enter-fill.svg b/web/vendor/bootstrap-icons/sign-do-not-enter-fill.svg new file mode 100644 index 0000000..f86ebfa --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-do-not-enter-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-do-not-enter.svg b/web/vendor/bootstrap-icons/sign-do-not-enter.svg new file mode 100644 index 0000000..2e2c877 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-do-not-enter.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-intersection-fill.svg b/web/vendor/bootstrap-icons/sign-intersection-fill.svg new file mode 100644 index 0000000..7fd8f3f --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-intersection-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-intersection-side-fill.svg b/web/vendor/bootstrap-icons/sign-intersection-side-fill.svg new file mode 100644 index 0000000..38870b5 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-intersection-side-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-intersection-side.svg b/web/vendor/bootstrap-icons/sign-intersection-side.svg new file mode 100644 index 0000000..df9015a --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-intersection-side.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-intersection-t-fill.svg b/web/vendor/bootstrap-icons/sign-intersection-t-fill.svg new file mode 100644 index 0000000..15a007e --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-intersection-t-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-intersection-t.svg b/web/vendor/bootstrap-icons/sign-intersection-t.svg new file mode 100644 index 0000000..4ba9f6f --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-intersection-t.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-intersection-y-fill.svg b/web/vendor/bootstrap-icons/sign-intersection-y-fill.svg new file mode 100644 index 0000000..01a03d0 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-intersection-y-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-intersection-y.svg b/web/vendor/bootstrap-icons/sign-intersection-y.svg new file mode 100644 index 0000000..e0e387f --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-intersection-y.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-intersection.svg b/web/vendor/bootstrap-icons/sign-intersection.svg new file mode 100644 index 0000000..be2ffdc --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-intersection.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-merge-left-fill.svg b/web/vendor/bootstrap-icons/sign-merge-left-fill.svg new file mode 100644 index 0000000..1408133 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-merge-left-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-merge-left.svg b/web/vendor/bootstrap-icons/sign-merge-left.svg new file mode 100644 index 0000000..3447bcf --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-merge-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-merge-right-fill.svg b/web/vendor/bootstrap-icons/sign-merge-right-fill.svg new file mode 100644 index 0000000..a952bb5 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-merge-right-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-merge-right.svg b/web/vendor/bootstrap-icons/sign-merge-right.svg new file mode 100644 index 0000000..ab3e08a --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-merge-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-no-left-turn-fill.svg b/web/vendor/bootstrap-icons/sign-no-left-turn-fill.svg new file mode 100644 index 0000000..85f421a --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-no-left-turn-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-no-left-turn.svg b/web/vendor/bootstrap-icons/sign-no-left-turn.svg new file mode 100644 index 0000000..d45f090 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-no-left-turn.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-no-parking-fill.svg b/web/vendor/bootstrap-icons/sign-no-parking-fill.svg new file mode 100644 index 0000000..c4100d9 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-no-parking-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-no-parking.svg b/web/vendor/bootstrap-icons/sign-no-parking.svg new file mode 100644 index 0000000..1679603 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-no-parking.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-no-right-turn-fill.svg b/web/vendor/bootstrap-icons/sign-no-right-turn-fill.svg new file mode 100644 index 0000000..c3883da --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-no-right-turn-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-no-right-turn.svg b/web/vendor/bootstrap-icons/sign-no-right-turn.svg new file mode 100644 index 0000000..209b918 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-no-right-turn.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-railroad-fill.svg b/web/vendor/bootstrap-icons/sign-railroad-fill.svg new file mode 100644 index 0000000..61d88a9 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-railroad-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-railroad.svg b/web/vendor/bootstrap-icons/sign-railroad.svg new file mode 100644 index 0000000..b5d7339 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-railroad.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-stop-fill.svg b/web/vendor/bootstrap-icons/sign-stop-fill.svg new file mode 100644 index 0000000..08efb9a --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-stop-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-stop-lights-fill.svg b/web/vendor/bootstrap-icons/sign-stop-lights-fill.svg new file mode 100644 index 0000000..9be8e0c --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-stop-lights-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-stop-lights.svg b/web/vendor/bootstrap-icons/sign-stop-lights.svg new file mode 100644 index 0000000..85918cf --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-stop-lights.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-stop.svg b/web/vendor/bootstrap-icons/sign-stop.svg new file mode 100644 index 0000000..49128dc --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-stop.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-turn-left-fill.svg b/web/vendor/bootstrap-icons/sign-turn-left-fill.svg new file mode 100644 index 0000000..4b8358e --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-turn-left-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-turn-left.svg b/web/vendor/bootstrap-icons/sign-turn-left.svg new file mode 100644 index 0000000..c1b34ed --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-turn-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-turn-right-fill.svg b/web/vendor/bootstrap-icons/sign-turn-right-fill.svg new file mode 100644 index 0000000..29d8d2c --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-turn-right-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-turn-right.svg b/web/vendor/bootstrap-icons/sign-turn-right.svg new file mode 100644 index 0000000..956614a --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-turn-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-turn-slight-left-fill.svg b/web/vendor/bootstrap-icons/sign-turn-slight-left-fill.svg new file mode 100644 index 0000000..80b2977 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-turn-slight-left-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-turn-slight-left.svg b/web/vendor/bootstrap-icons/sign-turn-slight-left.svg new file mode 100644 index 0000000..98f0a0a --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-turn-slight-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-turn-slight-right-fill.svg b/web/vendor/bootstrap-icons/sign-turn-slight-right-fill.svg new file mode 100644 index 0000000..2734952 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-turn-slight-right-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-turn-slight-right.svg b/web/vendor/bootstrap-icons/sign-turn-slight-right.svg new file mode 100644 index 0000000..c462f19 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-turn-slight-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-yield-fill.svg b/web/vendor/bootstrap-icons/sign-yield-fill.svg new file mode 100644 index 0000000..79fa190 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-yield-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sign-yield.svg b/web/vendor/bootstrap-icons/sign-yield.svg new file mode 100644 index 0000000..23bd623 --- /dev/null +++ b/web/vendor/bootstrap-icons/sign-yield.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/signal.svg b/web/vendor/bootstrap-icons/signal.svg new file mode 100644 index 0000000..1583f97 --- /dev/null +++ b/web/vendor/bootstrap-icons/signal.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/signpost-2-fill.svg b/web/vendor/bootstrap-icons/signpost-2-fill.svg new file mode 100644 index 0000000..58c05a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/signpost-2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/signpost-2.svg b/web/vendor/bootstrap-icons/signpost-2.svg new file mode 100644 index 0000000..e3454bd --- /dev/null +++ b/web/vendor/bootstrap-icons/signpost-2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/signpost-fill.svg b/web/vendor/bootstrap-icons/signpost-fill.svg new file mode 100644 index 0000000..00989a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/signpost-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/signpost-split-fill.svg b/web/vendor/bootstrap-icons/signpost-split-fill.svg new file mode 100644 index 0000000..9b720f0 --- /dev/null +++ b/web/vendor/bootstrap-icons/signpost-split-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/signpost-split.svg b/web/vendor/bootstrap-icons/signpost-split.svg new file mode 100644 index 0000000..7fb69b7 --- /dev/null +++ b/web/vendor/bootstrap-icons/signpost-split.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/signpost.svg b/web/vendor/bootstrap-icons/signpost.svg new file mode 100644 index 0000000..940e664 --- /dev/null +++ b/web/vendor/bootstrap-icons/signpost.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sim-fill.svg b/web/vendor/bootstrap-icons/sim-fill.svg new file mode 100644 index 0000000..c7922b6 --- /dev/null +++ b/web/vendor/bootstrap-icons/sim-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sim-slash-fill.svg b/web/vendor/bootstrap-icons/sim-slash-fill.svg new file mode 100644 index 0000000..b608de7 --- /dev/null +++ b/web/vendor/bootstrap-icons/sim-slash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sim-slash.svg b/web/vendor/bootstrap-icons/sim-slash.svg new file mode 100644 index 0000000..c8e0dc4 --- /dev/null +++ b/web/vendor/bootstrap-icons/sim-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sim.svg b/web/vendor/bootstrap-icons/sim.svg new file mode 100644 index 0000000..0d71a11 --- /dev/null +++ b/web/vendor/bootstrap-icons/sim.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sina-weibo.svg b/web/vendor/bootstrap-icons/sina-weibo.svg new file mode 100644 index 0000000..6d484d2 --- /dev/null +++ b/web/vendor/bootstrap-icons/sina-weibo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-backward-btn-fill.svg b/web/vendor/bootstrap-icons/skip-backward-btn-fill.svg new file mode 100644 index 0000000..2029489 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-backward-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-backward-btn.svg b/web/vendor/bootstrap-icons/skip-backward-btn.svg new file mode 100644 index 0000000..7cae681 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-backward-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-backward-circle-fill.svg b/web/vendor/bootstrap-icons/skip-backward-circle-fill.svg new file mode 100644 index 0000000..7526356 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-backward-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-backward-circle.svg b/web/vendor/bootstrap-icons/skip-backward-circle.svg new file mode 100644 index 0000000..8739f31 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-backward-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-backward-fill.svg b/web/vendor/bootstrap-icons/skip-backward-fill.svg new file mode 100644 index 0000000..bf8f63e --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-backward-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-backward.svg b/web/vendor/bootstrap-icons/skip-backward.svg new file mode 100644 index 0000000..ff5b821 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-backward.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-end-btn-fill.svg b/web/vendor/bootstrap-icons/skip-end-btn-fill.svg new file mode 100644 index 0000000..e721821 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-end-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-end-btn.svg b/web/vendor/bootstrap-icons/skip-end-btn.svg new file mode 100644 index 0000000..6815577 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-end-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-end-circle-fill.svg b/web/vendor/bootstrap-icons/skip-end-circle-fill.svg new file mode 100644 index 0000000..63c0f68 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-end-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-end-circle.svg b/web/vendor/bootstrap-icons/skip-end-circle.svg new file mode 100644 index 0000000..50f41d9 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-end-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-end-fill.svg b/web/vendor/bootstrap-icons/skip-end-fill.svg new file mode 100644 index 0000000..afa88e3 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-end-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-end.svg b/web/vendor/bootstrap-icons/skip-end.svg new file mode 100644 index 0000000..b2dfde6 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-end.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-forward-btn-fill.svg b/web/vendor/bootstrap-icons/skip-forward-btn-fill.svg new file mode 100644 index 0000000..9547957 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-forward-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-forward-btn.svg b/web/vendor/bootstrap-icons/skip-forward-btn.svg new file mode 100644 index 0000000..46a61b0 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-forward-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-forward-circle-fill.svg b/web/vendor/bootstrap-icons/skip-forward-circle-fill.svg new file mode 100644 index 0000000..aefb633 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-forward-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-forward-circle.svg b/web/vendor/bootstrap-icons/skip-forward-circle.svg new file mode 100644 index 0000000..7ebc928 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-forward-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-forward-fill.svg b/web/vendor/bootstrap-icons/skip-forward-fill.svg new file mode 100644 index 0000000..6c54d79 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-forward-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-forward.svg b/web/vendor/bootstrap-icons/skip-forward.svg new file mode 100644 index 0000000..c69cfc5 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-forward.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-start-btn-fill.svg b/web/vendor/bootstrap-icons/skip-start-btn-fill.svg new file mode 100644 index 0000000..a0af702 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-start-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-start-btn.svg b/web/vendor/bootstrap-icons/skip-start-btn.svg new file mode 100644 index 0000000..b829fdd --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-start-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-start-circle-fill.svg b/web/vendor/bootstrap-icons/skip-start-circle-fill.svg new file mode 100644 index 0000000..bb33ab0 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-start-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-start-circle.svg b/web/vendor/bootstrap-icons/skip-start-circle.svg new file mode 100644 index 0000000..d0e3323 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-start-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-start-fill.svg b/web/vendor/bootstrap-icons/skip-start-fill.svg new file mode 100644 index 0000000..56cccc3 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-start-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skip-start.svg b/web/vendor/bootstrap-icons/skip-start.svg new file mode 100644 index 0000000..76811a9 --- /dev/null +++ b/web/vendor/bootstrap-icons/skip-start.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/skype.svg b/web/vendor/bootstrap-icons/skype.svg new file mode 100644 index 0000000..ad4be4d --- /dev/null +++ b/web/vendor/bootstrap-icons/skype.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/slack.svg b/web/vendor/bootstrap-icons/slack.svg new file mode 100644 index 0000000..d914abe --- /dev/null +++ b/web/vendor/bootstrap-icons/slack.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/slash-circle-fill.svg b/web/vendor/bootstrap-icons/slash-circle-fill.svg new file mode 100644 index 0000000..5f71707 --- /dev/null +++ b/web/vendor/bootstrap-icons/slash-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/slash-circle.svg b/web/vendor/bootstrap-icons/slash-circle.svg new file mode 100644 index 0000000..eb26f19 --- /dev/null +++ b/web/vendor/bootstrap-icons/slash-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/slash-lg.svg b/web/vendor/bootstrap-icons/slash-lg.svg new file mode 100644 index 0000000..8b8b132 --- /dev/null +++ b/web/vendor/bootstrap-icons/slash-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/slash-square-fill.svg b/web/vendor/bootstrap-icons/slash-square-fill.svg new file mode 100644 index 0000000..6fc9153 --- /dev/null +++ b/web/vendor/bootstrap-icons/slash-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/slash-square.svg b/web/vendor/bootstrap-icons/slash-square.svg new file mode 100644 index 0000000..0757006 --- /dev/null +++ b/web/vendor/bootstrap-icons/slash-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/slash.svg b/web/vendor/bootstrap-icons/slash.svg new file mode 100644 index 0000000..6d18af4 --- /dev/null +++ b/web/vendor/bootstrap-icons/slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sliders.svg b/web/vendor/bootstrap-icons/sliders.svg new file mode 100644 index 0000000..c64a06c --- /dev/null +++ b/web/vendor/bootstrap-icons/sliders.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sliders2-vertical.svg b/web/vendor/bootstrap-icons/sliders2-vertical.svg new file mode 100644 index 0000000..4fcb8ba --- /dev/null +++ b/web/vendor/bootstrap-icons/sliders2-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sliders2.svg b/web/vendor/bootstrap-icons/sliders2.svg new file mode 100644 index 0000000..975861e --- /dev/null +++ b/web/vendor/bootstrap-icons/sliders2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/smartwatch.svg b/web/vendor/bootstrap-icons/smartwatch.svg new file mode 100644 index 0000000..0a11991 --- /dev/null +++ b/web/vendor/bootstrap-icons/smartwatch.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/snapchat.svg b/web/vendor/bootstrap-icons/snapchat.svg new file mode 100644 index 0000000..01d3684 --- /dev/null +++ b/web/vendor/bootstrap-icons/snapchat.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/snow.svg b/web/vendor/bootstrap-icons/snow.svg new file mode 100644 index 0000000..9b648a5 --- /dev/null +++ b/web/vendor/bootstrap-icons/snow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/snow2.svg b/web/vendor/bootstrap-icons/snow2.svg new file mode 100644 index 0000000..6533d63 --- /dev/null +++ b/web/vendor/bootstrap-icons/snow2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/snow3.svg b/web/vendor/bootstrap-icons/snow3.svg new file mode 100644 index 0000000..01c0d73 --- /dev/null +++ b/web/vendor/bootstrap-icons/snow3.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-alpha-down-alt.svg b/web/vendor/bootstrap-icons/sort-alpha-down-alt.svg new file mode 100644 index 0000000..d03f1aa --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-alpha-down-alt.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-alpha-down.svg b/web/vendor/bootstrap-icons/sort-alpha-down.svg new file mode 100644 index 0000000..6ac3e84 --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-alpha-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-alpha-up-alt.svg b/web/vendor/bootstrap-icons/sort-alpha-up-alt.svg new file mode 100644 index 0000000..a7b332e --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-alpha-up-alt.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-alpha-up.svg b/web/vendor/bootstrap-icons/sort-alpha-up.svg new file mode 100644 index 0000000..c5f0e3a --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-alpha-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-down-alt.svg b/web/vendor/bootstrap-icons/sort-down-alt.svg new file mode 100644 index 0000000..86a1bf6 --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-down-alt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-down.svg b/web/vendor/bootstrap-icons/sort-down.svg new file mode 100644 index 0000000..8cfdf23 --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-numeric-down-alt.svg b/web/vendor/bootstrap-icons/sort-numeric-down-alt.svg new file mode 100644 index 0000000..ce4e3c5 --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-numeric-down-alt.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-numeric-down.svg b/web/vendor/bootstrap-icons/sort-numeric-down.svg new file mode 100644 index 0000000..afa87be --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-numeric-down.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-numeric-up-alt.svg b/web/vendor/bootstrap-icons/sort-numeric-up-alt.svg new file mode 100644 index 0000000..d83cbf9 --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-numeric-up-alt.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-numeric-up.svg b/web/vendor/bootstrap-icons/sort-numeric-up.svg new file mode 100644 index 0000000..25a1e54 --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-numeric-up.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-up-alt.svg b/web/vendor/bootstrap-icons/sort-up-alt.svg new file mode 100644 index 0000000..9f78a20 --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-up-alt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sort-up.svg b/web/vendor/bootstrap-icons/sort-up.svg new file mode 100644 index 0000000..cda9ac7 --- /dev/null +++ b/web/vendor/bootstrap-icons/sort-up.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/soundwave.svg b/web/vendor/bootstrap-icons/soundwave.svg new file mode 100644 index 0000000..1444777 --- /dev/null +++ b/web/vendor/bootstrap-icons/soundwave.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sourceforge.svg b/web/vendor/bootstrap-icons/sourceforge.svg new file mode 100644 index 0000000..13d0c5f --- /dev/null +++ b/web/vendor/bootstrap-icons/sourceforge.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/speaker-fill.svg b/web/vendor/bootstrap-icons/speaker-fill.svg new file mode 100644 index 0000000..f6d9e33 --- /dev/null +++ b/web/vendor/bootstrap-icons/speaker-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/speaker.svg b/web/vendor/bootstrap-icons/speaker.svg new file mode 100644 index 0000000..1415b5d --- /dev/null +++ b/web/vendor/bootstrap-icons/speaker.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/speedometer.svg b/web/vendor/bootstrap-icons/speedometer.svg new file mode 100644 index 0000000..f6e3e61 --- /dev/null +++ b/web/vendor/bootstrap-icons/speedometer.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/speedometer2.svg b/web/vendor/bootstrap-icons/speedometer2.svg new file mode 100644 index 0000000..75e79c8 --- /dev/null +++ b/web/vendor/bootstrap-icons/speedometer2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/spellcheck.svg b/web/vendor/bootstrap-icons/spellcheck.svg new file mode 100644 index 0000000..69fec76 --- /dev/null +++ b/web/vendor/bootstrap-icons/spellcheck.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/spotify.svg b/web/vendor/bootstrap-icons/spotify.svg new file mode 100644 index 0000000..09d0e9f --- /dev/null +++ b/web/vendor/bootstrap-icons/spotify.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/square-fill.svg b/web/vendor/bootstrap-icons/square-fill.svg new file mode 100644 index 0000000..1e72d5e --- /dev/null +++ b/web/vendor/bootstrap-icons/square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/square-half.svg b/web/vendor/bootstrap-icons/square-half.svg new file mode 100644 index 0000000..aa3e349 --- /dev/null +++ b/web/vendor/bootstrap-icons/square-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/square.svg b/web/vendor/bootstrap-icons/square.svg new file mode 100644 index 0000000..0f086de --- /dev/null +++ b/web/vendor/bootstrap-icons/square.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stack-overflow.svg b/web/vendor/bootstrap-icons/stack-overflow.svg new file mode 100644 index 0000000..c5e5be6 --- /dev/null +++ b/web/vendor/bootstrap-icons/stack-overflow.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stack.svg b/web/vendor/bootstrap-icons/stack.svg new file mode 100644 index 0000000..3cf0eca --- /dev/null +++ b/web/vendor/bootstrap-icons/stack.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/star-fill.svg b/web/vendor/bootstrap-icons/star-fill.svg new file mode 100644 index 0000000..de09c4a --- /dev/null +++ b/web/vendor/bootstrap-icons/star-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/star-half.svg b/web/vendor/bootstrap-icons/star-half.svg new file mode 100644 index 0000000..8a70f53 --- /dev/null +++ b/web/vendor/bootstrap-icons/star-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/star.svg b/web/vendor/bootstrap-icons/star.svg new file mode 100644 index 0000000..fcdcb1c --- /dev/null +++ b/web/vendor/bootstrap-icons/star.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stars.svg b/web/vendor/bootstrap-icons/stars.svg new file mode 100644 index 0000000..b6fb4f2 --- /dev/null +++ b/web/vendor/bootstrap-icons/stars.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/steam.svg b/web/vendor/bootstrap-icons/steam.svg new file mode 100644 index 0000000..9daa3d3 --- /dev/null +++ b/web/vendor/bootstrap-icons/steam.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stickies-fill.svg b/web/vendor/bootstrap-icons/stickies-fill.svg new file mode 100644 index 0000000..039c3b8 --- /dev/null +++ b/web/vendor/bootstrap-icons/stickies-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stickies.svg b/web/vendor/bootstrap-icons/stickies.svg new file mode 100644 index 0000000..24e6492 --- /dev/null +++ b/web/vendor/bootstrap-icons/stickies.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sticky-fill.svg b/web/vendor/bootstrap-icons/sticky-fill.svg new file mode 100644 index 0000000..b36dcb1 --- /dev/null +++ b/web/vendor/bootstrap-icons/sticky-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sticky.svg b/web/vendor/bootstrap-icons/sticky.svg new file mode 100644 index 0000000..0d50e88 --- /dev/null +++ b/web/vendor/bootstrap-icons/sticky.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stop-btn-fill.svg b/web/vendor/bootstrap-icons/stop-btn-fill.svg new file mode 100644 index 0000000..70e562e --- /dev/null +++ b/web/vendor/bootstrap-icons/stop-btn-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stop-btn.svg b/web/vendor/bootstrap-icons/stop-btn.svg new file mode 100644 index 0000000..26348d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/stop-btn.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stop-circle-fill.svg b/web/vendor/bootstrap-icons/stop-circle-fill.svg new file mode 100644 index 0000000..141668e --- /dev/null +++ b/web/vendor/bootstrap-icons/stop-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stop-circle.svg b/web/vendor/bootstrap-icons/stop-circle.svg new file mode 100644 index 0000000..3e1933a --- /dev/null +++ b/web/vendor/bootstrap-icons/stop-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stop-fill.svg b/web/vendor/bootstrap-icons/stop-fill.svg new file mode 100644 index 0000000..ca1b957 --- /dev/null +++ b/web/vendor/bootstrap-icons/stop-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stop.svg b/web/vendor/bootstrap-icons/stop.svg new file mode 100644 index 0000000..27f1fb6 --- /dev/null +++ b/web/vendor/bootstrap-icons/stop.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stoplights-fill.svg b/web/vendor/bootstrap-icons/stoplights-fill.svg new file mode 100644 index 0000000..f0b2d76 --- /dev/null +++ b/web/vendor/bootstrap-icons/stoplights-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stoplights.svg b/web/vendor/bootstrap-icons/stoplights.svg new file mode 100644 index 0000000..6db3e12 --- /dev/null +++ b/web/vendor/bootstrap-icons/stoplights.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stopwatch-fill.svg b/web/vendor/bootstrap-icons/stopwatch-fill.svg new file mode 100644 index 0000000..1228cf2 --- /dev/null +++ b/web/vendor/bootstrap-icons/stopwatch-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stopwatch.svg b/web/vendor/bootstrap-icons/stopwatch.svg new file mode 100644 index 0000000..aff8c33 --- /dev/null +++ b/web/vendor/bootstrap-icons/stopwatch.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/strava.svg b/web/vendor/bootstrap-icons/strava.svg new file mode 100644 index 0000000..0ed6bab --- /dev/null +++ b/web/vendor/bootstrap-icons/strava.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/stripe.svg b/web/vendor/bootstrap-icons/stripe.svg new file mode 100644 index 0000000..ba961a0 --- /dev/null +++ b/web/vendor/bootstrap-icons/stripe.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/subscript.svg b/web/vendor/bootstrap-icons/subscript.svg new file mode 100644 index 0000000..51f5eea --- /dev/null +++ b/web/vendor/bootstrap-icons/subscript.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/substack.svg b/web/vendor/bootstrap-icons/substack.svg new file mode 100644 index 0000000..e54179a --- /dev/null +++ b/web/vendor/bootstrap-icons/substack.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/subtract.svg b/web/vendor/bootstrap-icons/subtract.svg new file mode 100644 index 0000000..129c3d5 --- /dev/null +++ b/web/vendor/bootstrap-icons/subtract.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suit-club-fill.svg b/web/vendor/bootstrap-icons/suit-club-fill.svg new file mode 100644 index 0000000..a787160 --- /dev/null +++ b/web/vendor/bootstrap-icons/suit-club-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suit-club.svg b/web/vendor/bootstrap-icons/suit-club.svg new file mode 100644 index 0000000..3fbf98b --- /dev/null +++ b/web/vendor/bootstrap-icons/suit-club.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suit-diamond-fill.svg b/web/vendor/bootstrap-icons/suit-diamond-fill.svg new file mode 100644 index 0000000..67617d6 --- /dev/null +++ b/web/vendor/bootstrap-icons/suit-diamond-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suit-diamond.svg b/web/vendor/bootstrap-icons/suit-diamond.svg new file mode 100644 index 0000000..79b54c1 --- /dev/null +++ b/web/vendor/bootstrap-icons/suit-diamond.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suit-heart-fill.svg b/web/vendor/bootstrap-icons/suit-heart-fill.svg new file mode 100644 index 0000000..d09850c --- /dev/null +++ b/web/vendor/bootstrap-icons/suit-heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suit-heart.svg b/web/vendor/bootstrap-icons/suit-heart.svg new file mode 100644 index 0000000..173b32f --- /dev/null +++ b/web/vendor/bootstrap-icons/suit-heart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suit-spade-fill.svg b/web/vendor/bootstrap-icons/suit-spade-fill.svg new file mode 100644 index 0000000..cc465e5 --- /dev/null +++ b/web/vendor/bootstrap-icons/suit-spade-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suit-spade.svg b/web/vendor/bootstrap-icons/suit-spade.svg new file mode 100644 index 0000000..7123c10 --- /dev/null +++ b/web/vendor/bootstrap-icons/suit-spade.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suitcase-fill.svg b/web/vendor/bootstrap-icons/suitcase-fill.svg new file mode 100644 index 0000000..df62651 --- /dev/null +++ b/web/vendor/bootstrap-icons/suitcase-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suitcase-lg-fill.svg b/web/vendor/bootstrap-icons/suitcase-lg-fill.svg new file mode 100644 index 0000000..cef1da9 --- /dev/null +++ b/web/vendor/bootstrap-icons/suitcase-lg-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suitcase-lg.svg b/web/vendor/bootstrap-icons/suitcase-lg.svg new file mode 100644 index 0000000..ea447d2 --- /dev/null +++ b/web/vendor/bootstrap-icons/suitcase-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suitcase.svg b/web/vendor/bootstrap-icons/suitcase.svg new file mode 100644 index 0000000..65e619d --- /dev/null +++ b/web/vendor/bootstrap-icons/suitcase.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suitcase2-fill.svg b/web/vendor/bootstrap-icons/suitcase2-fill.svg new file mode 100644 index 0000000..a2cb410 --- /dev/null +++ b/web/vendor/bootstrap-icons/suitcase2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/suitcase2.svg b/web/vendor/bootstrap-icons/suitcase2.svg new file mode 100644 index 0000000..e6ea533 --- /dev/null +++ b/web/vendor/bootstrap-icons/suitcase2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sun-fill.svg b/web/vendor/bootstrap-icons/sun-fill.svg new file mode 100644 index 0000000..c83f69a --- /dev/null +++ b/web/vendor/bootstrap-icons/sun-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sun.svg b/web/vendor/bootstrap-icons/sun.svg new file mode 100644 index 0000000..3777f07 --- /dev/null +++ b/web/vendor/bootstrap-icons/sun.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sunglasses.svg b/web/vendor/bootstrap-icons/sunglasses.svg new file mode 100644 index 0000000..1ff81f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/sunglasses.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sunrise-fill.svg b/web/vendor/bootstrap-icons/sunrise-fill.svg new file mode 100644 index 0000000..c922d7c --- /dev/null +++ b/web/vendor/bootstrap-icons/sunrise-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sunrise.svg b/web/vendor/bootstrap-icons/sunrise.svg new file mode 100644 index 0000000..98adcfb --- /dev/null +++ b/web/vendor/bootstrap-icons/sunrise.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sunset-fill.svg b/web/vendor/bootstrap-icons/sunset-fill.svg new file mode 100644 index 0000000..91a8d0e --- /dev/null +++ b/web/vendor/bootstrap-icons/sunset-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/sunset.svg b/web/vendor/bootstrap-icons/sunset.svg new file mode 100644 index 0000000..e72d634 --- /dev/null +++ b/web/vendor/bootstrap-icons/sunset.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/superscript.svg b/web/vendor/bootstrap-icons/superscript.svg new file mode 100644 index 0000000..81543ae --- /dev/null +++ b/web/vendor/bootstrap-icons/superscript.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/symmetry-horizontal.svg b/web/vendor/bootstrap-icons/symmetry-horizontal.svg new file mode 100644 index 0000000..594735b --- /dev/null +++ b/web/vendor/bootstrap-icons/symmetry-horizontal.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/symmetry-vertical.svg b/web/vendor/bootstrap-icons/symmetry-vertical.svg new file mode 100644 index 0000000..6907280 --- /dev/null +++ b/web/vendor/bootstrap-icons/symmetry-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/table.svg b/web/vendor/bootstrap-icons/table.svg new file mode 100644 index 0000000..8f70585 --- /dev/null +++ b/web/vendor/bootstrap-icons/table.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tablet-fill.svg b/web/vendor/bootstrap-icons/tablet-fill.svg new file mode 100644 index 0000000..0746ead --- /dev/null +++ b/web/vendor/bootstrap-icons/tablet-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tablet-landscape-fill.svg b/web/vendor/bootstrap-icons/tablet-landscape-fill.svg new file mode 100644 index 0000000..6290024 --- /dev/null +++ b/web/vendor/bootstrap-icons/tablet-landscape-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tablet-landscape.svg b/web/vendor/bootstrap-icons/tablet-landscape.svg new file mode 100644 index 0000000..438d4d1 --- /dev/null +++ b/web/vendor/bootstrap-icons/tablet-landscape.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tablet.svg b/web/vendor/bootstrap-icons/tablet.svg new file mode 100644 index 0000000..eebeee3 --- /dev/null +++ b/web/vendor/bootstrap-icons/tablet.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tag-fill.svg b/web/vendor/bootstrap-icons/tag-fill.svg new file mode 100644 index 0000000..6a95e2d --- /dev/null +++ b/web/vendor/bootstrap-icons/tag-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tag.svg b/web/vendor/bootstrap-icons/tag.svg new file mode 100644 index 0000000..01d19b5 --- /dev/null +++ b/web/vendor/bootstrap-icons/tag.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tags-fill.svg b/web/vendor/bootstrap-icons/tags-fill.svg new file mode 100644 index 0000000..1673abb --- /dev/null +++ b/web/vendor/bootstrap-icons/tags-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tags.svg b/web/vendor/bootstrap-icons/tags.svg new file mode 100644 index 0000000..ade5519 --- /dev/null +++ b/web/vendor/bootstrap-icons/tags.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/taxi-front-fill.svg b/web/vendor/bootstrap-icons/taxi-front-fill.svg new file mode 100644 index 0000000..ef7f45b --- /dev/null +++ b/web/vendor/bootstrap-icons/taxi-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/taxi-front.svg b/web/vendor/bootstrap-icons/taxi-front.svg new file mode 100644 index 0000000..1b4337a --- /dev/null +++ b/web/vendor/bootstrap-icons/taxi-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telegram.svg b/web/vendor/bootstrap-icons/telegram.svg new file mode 100644 index 0000000..d260266 --- /dev/null +++ b/web/vendor/bootstrap-icons/telegram.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-fill.svg b/web/vendor/bootstrap-icons/telephone-fill.svg new file mode 100644 index 0000000..2e9de2e --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-forward-fill.svg b/web/vendor/bootstrap-icons/telephone-forward-fill.svg new file mode 100644 index 0000000..26fc35f --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-forward-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-forward.svg b/web/vendor/bootstrap-icons/telephone-forward.svg new file mode 100644 index 0000000..08c07bd --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-forward.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-inbound-fill.svg b/web/vendor/bootstrap-icons/telephone-inbound-fill.svg new file mode 100644 index 0000000..85434d0 --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-inbound-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-inbound.svg b/web/vendor/bootstrap-icons/telephone-inbound.svg new file mode 100644 index 0000000..8ec20a6 --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-inbound.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-minus-fill.svg b/web/vendor/bootstrap-icons/telephone-minus-fill.svg new file mode 100644 index 0000000..7b2fe9d --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-minus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-minus.svg b/web/vendor/bootstrap-icons/telephone-minus.svg new file mode 100644 index 0000000..6ebc50e --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-outbound-fill.svg b/web/vendor/bootstrap-icons/telephone-outbound-fill.svg new file mode 100644 index 0000000..0a18bda --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-outbound-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-outbound.svg b/web/vendor/bootstrap-icons/telephone-outbound.svg new file mode 100644 index 0000000..566eb46 --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-outbound.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-plus-fill.svg b/web/vendor/bootstrap-icons/telephone-plus-fill.svg new file mode 100644 index 0000000..b02874f --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-plus.svg b/web/vendor/bootstrap-icons/telephone-plus.svg new file mode 100644 index 0000000..787e0c6 --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-x-fill.svg b/web/vendor/bootstrap-icons/telephone-x-fill.svg new file mode 100644 index 0000000..5410e16 --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-x-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone-x.svg b/web/vendor/bootstrap-icons/telephone-x.svg new file mode 100644 index 0000000..3f483a1 --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/telephone.svg b/web/vendor/bootstrap-icons/telephone.svg new file mode 100644 index 0000000..679e8a9 --- /dev/null +++ b/web/vendor/bootstrap-icons/telephone.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tencent-qq.svg b/web/vendor/bootstrap-icons/tencent-qq.svg new file mode 100644 index 0000000..0d5cd23 --- /dev/null +++ b/web/vendor/bootstrap-icons/tencent-qq.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/terminal-dash.svg b/web/vendor/bootstrap-icons/terminal-dash.svg new file mode 100644 index 0000000..9f46e8e --- /dev/null +++ b/web/vendor/bootstrap-icons/terminal-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/terminal-fill.svg b/web/vendor/bootstrap-icons/terminal-fill.svg new file mode 100644 index 0000000..fabd075 --- /dev/null +++ b/web/vendor/bootstrap-icons/terminal-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/terminal-plus.svg b/web/vendor/bootstrap-icons/terminal-plus.svg new file mode 100644 index 0000000..32c6432 --- /dev/null +++ b/web/vendor/bootstrap-icons/terminal-plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/terminal-split.svg b/web/vendor/bootstrap-icons/terminal-split.svg new file mode 100644 index 0000000..a378c37 --- /dev/null +++ b/web/vendor/bootstrap-icons/terminal-split.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/terminal-x.svg b/web/vendor/bootstrap-icons/terminal-x.svg new file mode 100644 index 0000000..aa59e7f --- /dev/null +++ b/web/vendor/bootstrap-icons/terminal-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/terminal.svg b/web/vendor/bootstrap-icons/terminal.svg new file mode 100644 index 0000000..44aef95 --- /dev/null +++ b/web/vendor/bootstrap-icons/terminal.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/text-center.svg b/web/vendor/bootstrap-icons/text-center.svg new file mode 100644 index 0000000..12d9e29 --- /dev/null +++ b/web/vendor/bootstrap-icons/text-center.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/text-indent-left.svg b/web/vendor/bootstrap-icons/text-indent-left.svg new file mode 100644 index 0000000..5a607af --- /dev/null +++ b/web/vendor/bootstrap-icons/text-indent-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/text-indent-right.svg b/web/vendor/bootstrap-icons/text-indent-right.svg new file mode 100644 index 0000000..de91d9e --- /dev/null +++ b/web/vendor/bootstrap-icons/text-indent-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/text-left.svg b/web/vendor/bootstrap-icons/text-left.svg new file mode 100644 index 0000000..36ae0d3 --- /dev/null +++ b/web/vendor/bootstrap-icons/text-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/text-paragraph.svg b/web/vendor/bootstrap-icons/text-paragraph.svg new file mode 100644 index 0000000..035a1c8 --- /dev/null +++ b/web/vendor/bootstrap-icons/text-paragraph.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/text-right.svg b/web/vendor/bootstrap-icons/text-right.svg new file mode 100644 index 0000000..98178e7 --- /dev/null +++ b/web/vendor/bootstrap-icons/text-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/text-wrap.svg b/web/vendor/bootstrap-icons/text-wrap.svg new file mode 100644 index 0000000..4c732d6 --- /dev/null +++ b/web/vendor/bootstrap-icons/text-wrap.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/textarea-resize.svg b/web/vendor/bootstrap-icons/textarea-resize.svg new file mode 100644 index 0000000..6401320 --- /dev/null +++ b/web/vendor/bootstrap-icons/textarea-resize.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/textarea-t.svg b/web/vendor/bootstrap-icons/textarea-t.svg new file mode 100644 index 0000000..145cbb7 --- /dev/null +++ b/web/vendor/bootstrap-icons/textarea-t.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/textarea.svg b/web/vendor/bootstrap-icons/textarea.svg new file mode 100644 index 0000000..176ca25 --- /dev/null +++ b/web/vendor/bootstrap-icons/textarea.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/thermometer-half.svg b/web/vendor/bootstrap-icons/thermometer-half.svg new file mode 100644 index 0000000..018eab1 --- /dev/null +++ b/web/vendor/bootstrap-icons/thermometer-half.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/thermometer-high.svg b/web/vendor/bootstrap-icons/thermometer-high.svg new file mode 100644 index 0000000..22e77d1 --- /dev/null +++ b/web/vendor/bootstrap-icons/thermometer-high.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/thermometer-low.svg b/web/vendor/bootstrap-icons/thermometer-low.svg new file mode 100644 index 0000000..1f0f5e1 --- /dev/null +++ b/web/vendor/bootstrap-icons/thermometer-low.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/thermometer-snow.svg b/web/vendor/bootstrap-icons/thermometer-snow.svg new file mode 100644 index 0000000..df7c1d1 --- /dev/null +++ b/web/vendor/bootstrap-icons/thermometer-snow.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/thermometer-sun.svg b/web/vendor/bootstrap-icons/thermometer-sun.svg new file mode 100644 index 0000000..c453dee --- /dev/null +++ b/web/vendor/bootstrap-icons/thermometer-sun.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/thermometer.svg b/web/vendor/bootstrap-icons/thermometer.svg new file mode 100644 index 0000000..8a5529b --- /dev/null +++ b/web/vendor/bootstrap-icons/thermometer.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/threads-fill.svg b/web/vendor/bootstrap-icons/threads-fill.svg new file mode 100644 index 0000000..b19666d --- /dev/null +++ b/web/vendor/bootstrap-icons/threads-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/threads.svg b/web/vendor/bootstrap-icons/threads.svg new file mode 100644 index 0000000..13c9e7a --- /dev/null +++ b/web/vendor/bootstrap-icons/threads.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/three-dots-vertical.svg b/web/vendor/bootstrap-icons/three-dots-vertical.svg new file mode 100644 index 0000000..f5ef7d4 --- /dev/null +++ b/web/vendor/bootstrap-icons/three-dots-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/three-dots.svg b/web/vendor/bootstrap-icons/three-dots.svg new file mode 100644 index 0000000..4706f52 --- /dev/null +++ b/web/vendor/bootstrap-icons/three-dots.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/thunderbolt-fill.svg b/web/vendor/bootstrap-icons/thunderbolt-fill.svg new file mode 100644 index 0000000..1faea43 --- /dev/null +++ b/web/vendor/bootstrap-icons/thunderbolt-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/thunderbolt.svg b/web/vendor/bootstrap-icons/thunderbolt.svg new file mode 100644 index 0000000..3655659 --- /dev/null +++ b/web/vendor/bootstrap-icons/thunderbolt.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ticket-detailed-fill.svg b/web/vendor/bootstrap-icons/ticket-detailed-fill.svg new file mode 100644 index 0000000..cd7a3a7 --- /dev/null +++ b/web/vendor/bootstrap-icons/ticket-detailed-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ticket-detailed.svg b/web/vendor/bootstrap-icons/ticket-detailed.svg new file mode 100644 index 0000000..cf32e88 --- /dev/null +++ b/web/vendor/bootstrap-icons/ticket-detailed.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ticket-fill.svg b/web/vendor/bootstrap-icons/ticket-fill.svg new file mode 100644 index 0000000..01e9108 --- /dev/null +++ b/web/vendor/bootstrap-icons/ticket-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ticket-perforated-fill.svg b/web/vendor/bootstrap-icons/ticket-perforated-fill.svg new file mode 100644 index 0000000..38c18dc --- /dev/null +++ b/web/vendor/bootstrap-icons/ticket-perforated-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ticket-perforated.svg b/web/vendor/bootstrap-icons/ticket-perforated.svg new file mode 100644 index 0000000..da44537 --- /dev/null +++ b/web/vendor/bootstrap-icons/ticket-perforated.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ticket.svg b/web/vendor/bootstrap-icons/ticket.svg new file mode 100644 index 0000000..eb813a1 --- /dev/null +++ b/web/vendor/bootstrap-icons/ticket.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tiktok.svg b/web/vendor/bootstrap-icons/tiktok.svg new file mode 100644 index 0000000..04c6679 --- /dev/null +++ b/web/vendor/bootstrap-icons/tiktok.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/toggle-off.svg b/web/vendor/bootstrap-icons/toggle-off.svg new file mode 100644 index 0000000..e1e89ad --- /dev/null +++ b/web/vendor/bootstrap-icons/toggle-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/toggle-on.svg b/web/vendor/bootstrap-icons/toggle-on.svg new file mode 100644 index 0000000..7cd6eb3 --- /dev/null +++ b/web/vendor/bootstrap-icons/toggle-on.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/toggle2-off.svg b/web/vendor/bootstrap-icons/toggle2-off.svg new file mode 100644 index 0000000..61739ce --- /dev/null +++ b/web/vendor/bootstrap-icons/toggle2-off.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/toggle2-on.svg b/web/vendor/bootstrap-icons/toggle2-on.svg new file mode 100644 index 0000000..d752ce8 --- /dev/null +++ b/web/vendor/bootstrap-icons/toggle2-on.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/toggles.svg b/web/vendor/bootstrap-icons/toggles.svg new file mode 100644 index 0000000..659c185 --- /dev/null +++ b/web/vendor/bootstrap-icons/toggles.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/toggles2.svg b/web/vendor/bootstrap-icons/toggles2.svg new file mode 100644 index 0000000..2f90344 --- /dev/null +++ b/web/vendor/bootstrap-icons/toggles2.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tools.svg b/web/vendor/bootstrap-icons/tools.svg new file mode 100644 index 0000000..f6efdcc --- /dev/null +++ b/web/vendor/bootstrap-icons/tools.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tornado.svg b/web/vendor/bootstrap-icons/tornado.svg new file mode 100644 index 0000000..2a6397c --- /dev/null +++ b/web/vendor/bootstrap-icons/tornado.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/train-freight-front-fill.svg b/web/vendor/bootstrap-icons/train-freight-front-fill.svg new file mode 100644 index 0000000..e272051 --- /dev/null +++ b/web/vendor/bootstrap-icons/train-freight-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/train-freight-front.svg b/web/vendor/bootstrap-icons/train-freight-front.svg new file mode 100644 index 0000000..097c960 --- /dev/null +++ b/web/vendor/bootstrap-icons/train-freight-front.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/train-front-fill.svg b/web/vendor/bootstrap-icons/train-front-fill.svg new file mode 100644 index 0000000..4acad08 --- /dev/null +++ b/web/vendor/bootstrap-icons/train-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/train-front.svg b/web/vendor/bootstrap-icons/train-front.svg new file mode 100644 index 0000000..81ce139 --- /dev/null +++ b/web/vendor/bootstrap-icons/train-front.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/train-lightrail-front-fill.svg b/web/vendor/bootstrap-icons/train-lightrail-front-fill.svg new file mode 100644 index 0000000..7bd87fb --- /dev/null +++ b/web/vendor/bootstrap-icons/train-lightrail-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/train-lightrail-front.svg b/web/vendor/bootstrap-icons/train-lightrail-front.svg new file mode 100644 index 0000000..d7aa87f --- /dev/null +++ b/web/vendor/bootstrap-icons/train-lightrail-front.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/translate.svg b/web/vendor/bootstrap-icons/translate.svg new file mode 100644 index 0000000..2e0754e --- /dev/null +++ b/web/vendor/bootstrap-icons/translate.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/transparency.svg b/web/vendor/bootstrap-icons/transparency.svg new file mode 100644 index 0000000..289a4b9 --- /dev/null +++ b/web/vendor/bootstrap-icons/transparency.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trash-fill.svg b/web/vendor/bootstrap-icons/trash-fill.svg new file mode 100644 index 0000000..b67453a --- /dev/null +++ b/web/vendor/bootstrap-icons/trash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trash.svg b/web/vendor/bootstrap-icons/trash.svg new file mode 100644 index 0000000..3020264 --- /dev/null +++ b/web/vendor/bootstrap-icons/trash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trash2-fill.svg b/web/vendor/bootstrap-icons/trash2-fill.svg new file mode 100644 index 0000000..fb1d90d --- /dev/null +++ b/web/vendor/bootstrap-icons/trash2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trash2.svg b/web/vendor/bootstrap-icons/trash2.svg new file mode 100644 index 0000000..0cabe8d --- /dev/null +++ b/web/vendor/bootstrap-icons/trash2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trash3-fill.svg b/web/vendor/bootstrap-icons/trash3-fill.svg new file mode 100644 index 0000000..42fbfc5 --- /dev/null +++ b/web/vendor/bootstrap-icons/trash3-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trash3.svg b/web/vendor/bootstrap-icons/trash3.svg new file mode 100644 index 0000000..5194bf0 --- /dev/null +++ b/web/vendor/bootstrap-icons/trash3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tree-fill.svg b/web/vendor/bootstrap-icons/tree-fill.svg new file mode 100644 index 0000000..d00e733 --- /dev/null +++ b/web/vendor/bootstrap-icons/tree-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tree.svg b/web/vendor/bootstrap-icons/tree.svg new file mode 100644 index 0000000..17a5efe --- /dev/null +++ b/web/vendor/bootstrap-icons/tree.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trello.svg b/web/vendor/bootstrap-icons/trello.svg new file mode 100644 index 0000000..bd88732 --- /dev/null +++ b/web/vendor/bootstrap-icons/trello.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/triangle-fill.svg b/web/vendor/bootstrap-icons/triangle-fill.svg new file mode 100644 index 0000000..474c8bb --- /dev/null +++ b/web/vendor/bootstrap-icons/triangle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/triangle-half.svg b/web/vendor/bootstrap-icons/triangle-half.svg new file mode 100644 index 0000000..a495ca0 --- /dev/null +++ b/web/vendor/bootstrap-icons/triangle-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/triangle.svg b/web/vendor/bootstrap-icons/triangle.svg new file mode 100644 index 0000000..95a6a9b --- /dev/null +++ b/web/vendor/bootstrap-icons/triangle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trophy-fill.svg b/web/vendor/bootstrap-icons/trophy-fill.svg new file mode 100644 index 0000000..f469737 --- /dev/null +++ b/web/vendor/bootstrap-icons/trophy-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/trophy.svg b/web/vendor/bootstrap-icons/trophy.svg new file mode 100644 index 0000000..ae13957 --- /dev/null +++ b/web/vendor/bootstrap-icons/trophy.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tropical-storm.svg b/web/vendor/bootstrap-icons/tropical-storm.svg new file mode 100644 index 0000000..9eb3354 --- /dev/null +++ b/web/vendor/bootstrap-icons/tropical-storm.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/truck-flatbed.svg b/web/vendor/bootstrap-icons/truck-flatbed.svg new file mode 100644 index 0000000..4b38155 --- /dev/null +++ b/web/vendor/bootstrap-icons/truck-flatbed.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/truck-front-fill.svg b/web/vendor/bootstrap-icons/truck-front-fill.svg new file mode 100644 index 0000000..39f72d0 --- /dev/null +++ b/web/vendor/bootstrap-icons/truck-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/truck-front.svg b/web/vendor/bootstrap-icons/truck-front.svg new file mode 100644 index 0000000..d805db5 --- /dev/null +++ b/web/vendor/bootstrap-icons/truck-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/truck.svg b/web/vendor/bootstrap-icons/truck.svg new file mode 100644 index 0000000..72c5439 --- /dev/null +++ b/web/vendor/bootstrap-icons/truck.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tsunami.svg b/web/vendor/bootstrap-icons/tsunami.svg new file mode 100644 index 0000000..be5f9be --- /dev/null +++ b/web/vendor/bootstrap-icons/tsunami.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tux.svg b/web/vendor/bootstrap-icons/tux.svg new file mode 100644 index 0000000..6a3242c --- /dev/null +++ b/web/vendor/bootstrap-icons/tux.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tv-fill.svg b/web/vendor/bootstrap-icons/tv-fill.svg new file mode 100644 index 0000000..483c9fd --- /dev/null +++ b/web/vendor/bootstrap-icons/tv-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/tv.svg b/web/vendor/bootstrap-icons/tv.svg new file mode 100644 index 0000000..fa8b3c1 --- /dev/null +++ b/web/vendor/bootstrap-icons/tv.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/twitch.svg b/web/vendor/bootstrap-icons/twitch.svg new file mode 100644 index 0000000..b2c8ff5 --- /dev/null +++ b/web/vendor/bootstrap-icons/twitch.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/twitter-x.svg b/web/vendor/bootstrap-icons/twitter-x.svg new file mode 100644 index 0000000..2fafcc2 --- /dev/null +++ b/web/vendor/bootstrap-icons/twitter-x.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/twitter.svg b/web/vendor/bootstrap-icons/twitter.svg new file mode 100644 index 0000000..3001352 --- /dev/null +++ b/web/vendor/bootstrap-icons/twitter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-bold.svg b/web/vendor/bootstrap-icons/type-bold.svg new file mode 100644 index 0000000..0814a2e --- /dev/null +++ b/web/vendor/bootstrap-icons/type-bold.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-h1.svg b/web/vendor/bootstrap-icons/type-h1.svg new file mode 100644 index 0000000..0df41f6 --- /dev/null +++ b/web/vendor/bootstrap-icons/type-h1.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-h2.svg b/web/vendor/bootstrap-icons/type-h2.svg new file mode 100644 index 0000000..03379ed --- /dev/null +++ b/web/vendor/bootstrap-icons/type-h2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-h3.svg b/web/vendor/bootstrap-icons/type-h3.svg new file mode 100644 index 0000000..97de531 --- /dev/null +++ b/web/vendor/bootstrap-icons/type-h3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-h4.svg b/web/vendor/bootstrap-icons/type-h4.svg new file mode 100644 index 0000000..a7ddc81 --- /dev/null +++ b/web/vendor/bootstrap-icons/type-h4.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-h5.svg b/web/vendor/bootstrap-icons/type-h5.svg new file mode 100644 index 0000000..776bfa3 --- /dev/null +++ b/web/vendor/bootstrap-icons/type-h5.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-h6.svg b/web/vendor/bootstrap-icons/type-h6.svg new file mode 100644 index 0000000..9267192 --- /dev/null +++ b/web/vendor/bootstrap-icons/type-h6.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-italic.svg b/web/vendor/bootstrap-icons/type-italic.svg new file mode 100644 index 0000000..3ac6b09 --- /dev/null +++ b/web/vendor/bootstrap-icons/type-italic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-strikethrough.svg b/web/vendor/bootstrap-icons/type-strikethrough.svg new file mode 100644 index 0000000..c64eba3 --- /dev/null +++ b/web/vendor/bootstrap-icons/type-strikethrough.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type-underline.svg b/web/vendor/bootstrap-icons/type-underline.svg new file mode 100644 index 0000000..1c0b6c4 --- /dev/null +++ b/web/vendor/bootstrap-icons/type-underline.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/type.svg b/web/vendor/bootstrap-icons/type.svg new file mode 100644 index 0000000..8c1fde1 --- /dev/null +++ b/web/vendor/bootstrap-icons/type.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/typescript.svg b/web/vendor/bootstrap-icons/typescript.svg new file mode 100644 index 0000000..12429d8 --- /dev/null +++ b/web/vendor/bootstrap-icons/typescript.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ubuntu.svg b/web/vendor/bootstrap-icons/ubuntu.svg new file mode 100644 index 0000000..89c8830 --- /dev/null +++ b/web/vendor/bootstrap-icons/ubuntu.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ui-checks-grid.svg b/web/vendor/bootstrap-icons/ui-checks-grid.svg new file mode 100644 index 0000000..e5d1ed9 --- /dev/null +++ b/web/vendor/bootstrap-icons/ui-checks-grid.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ui-checks.svg b/web/vendor/bootstrap-icons/ui-checks.svg new file mode 100644 index 0000000..5d02869 --- /dev/null +++ b/web/vendor/bootstrap-icons/ui-checks.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ui-radios-grid.svg b/web/vendor/bootstrap-icons/ui-radios-grid.svg new file mode 100644 index 0000000..9f9aae0 --- /dev/null +++ b/web/vendor/bootstrap-icons/ui-radios-grid.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/ui-radios.svg b/web/vendor/bootstrap-icons/ui-radios.svg new file mode 100644 index 0000000..9165340 --- /dev/null +++ b/web/vendor/bootstrap-icons/ui-radios.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/umbrella-fill.svg b/web/vendor/bootstrap-icons/umbrella-fill.svg new file mode 100644 index 0000000..3efaf13 --- /dev/null +++ b/web/vendor/bootstrap-icons/umbrella-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/umbrella.svg b/web/vendor/bootstrap-icons/umbrella.svg new file mode 100644 index 0000000..f7b698c --- /dev/null +++ b/web/vendor/bootstrap-icons/umbrella.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/unindent.svg b/web/vendor/bootstrap-icons/unindent.svg new file mode 100644 index 0000000..9e68255 --- /dev/null +++ b/web/vendor/bootstrap-icons/unindent.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/union.svg b/web/vendor/bootstrap-icons/union.svg new file mode 100644 index 0000000..ba23f54 --- /dev/null +++ b/web/vendor/bootstrap-icons/union.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/unity.svg b/web/vendor/bootstrap-icons/unity.svg new file mode 100644 index 0000000..8b84508 --- /dev/null +++ b/web/vendor/bootstrap-icons/unity.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/universal-access-circle.svg b/web/vendor/bootstrap-icons/universal-access-circle.svg new file mode 100644 index 0000000..e5ea936 --- /dev/null +++ b/web/vendor/bootstrap-icons/universal-access-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/universal-access.svg b/web/vendor/bootstrap-icons/universal-access.svg new file mode 100644 index 0000000..0d0d6ef --- /dev/null +++ b/web/vendor/bootstrap-icons/universal-access.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/unlock-fill.svg b/web/vendor/bootstrap-icons/unlock-fill.svg new file mode 100644 index 0000000..c130330 --- /dev/null +++ b/web/vendor/bootstrap-icons/unlock-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/unlock.svg b/web/vendor/bootstrap-icons/unlock.svg new file mode 100644 index 0000000..73625c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/unlock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/unlock2-fill.svg b/web/vendor/bootstrap-icons/unlock2-fill.svg new file mode 100644 index 0000000..1d8ef92 --- /dev/null +++ b/web/vendor/bootstrap-icons/unlock2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/unlock2.svg b/web/vendor/bootstrap-icons/unlock2.svg new file mode 100644 index 0000000..0815a16 --- /dev/null +++ b/web/vendor/bootstrap-icons/unlock2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/upc-scan.svg b/web/vendor/bootstrap-icons/upc-scan.svg new file mode 100644 index 0000000..1a89554 --- /dev/null +++ b/web/vendor/bootstrap-icons/upc-scan.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/upc.svg b/web/vendor/bootstrap-icons/upc.svg new file mode 100644 index 0000000..785297d --- /dev/null +++ b/web/vendor/bootstrap-icons/upc.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/upload.svg b/web/vendor/bootstrap-icons/upload.svg new file mode 100644 index 0000000..9a4a363 --- /dev/null +++ b/web/vendor/bootstrap-icons/upload.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-c-fill.svg b/web/vendor/bootstrap-icons/usb-c-fill.svg new file mode 100644 index 0000000..759eee2 --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-c-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-c.svg b/web/vendor/bootstrap-icons/usb-c.svg new file mode 100644 index 0000000..1198332 --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-c.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-drive-fill.svg b/web/vendor/bootstrap-icons/usb-drive-fill.svg new file mode 100644 index 0000000..2f656ee --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-drive-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-drive.svg b/web/vendor/bootstrap-icons/usb-drive.svg new file mode 100644 index 0000000..739051d --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-drive.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-fill.svg b/web/vendor/bootstrap-icons/usb-fill.svg new file mode 100644 index 0000000..a3b17fa --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-micro-fill.svg b/web/vendor/bootstrap-icons/usb-micro-fill.svg new file mode 100644 index 0000000..1469a9b --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-micro-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-micro.svg b/web/vendor/bootstrap-icons/usb-micro.svg new file mode 100644 index 0000000..ece7da7 --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-micro.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-mini-fill.svg b/web/vendor/bootstrap-icons/usb-mini-fill.svg new file mode 100644 index 0000000..3ab0747 --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-mini-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-mini.svg b/web/vendor/bootstrap-icons/usb-mini.svg new file mode 100644 index 0000000..f095b67 --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-mini.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-plug-fill.svg b/web/vendor/bootstrap-icons/usb-plug-fill.svg new file mode 100644 index 0000000..d1dc518 --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-plug-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-plug.svg b/web/vendor/bootstrap-icons/usb-plug.svg new file mode 100644 index 0000000..f3d7220 --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-plug.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb-symbol.svg b/web/vendor/bootstrap-icons/usb-symbol.svg new file mode 100644 index 0000000..457f93f --- /dev/null +++ b/web/vendor/bootstrap-icons/usb-symbol.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/usb.svg b/web/vendor/bootstrap-icons/usb.svg new file mode 100644 index 0000000..737bef5 --- /dev/null +++ b/web/vendor/bootstrap-icons/usb.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/valentine.svg b/web/vendor/bootstrap-icons/valentine.svg new file mode 100644 index 0000000..7b8f0a3 --- /dev/null +++ b/web/vendor/bootstrap-icons/valentine.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/valentine2.svg b/web/vendor/bootstrap-icons/valentine2.svg new file mode 100644 index 0000000..6d95a2d --- /dev/null +++ b/web/vendor/bootstrap-icons/valentine2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/vector-pen.svg b/web/vendor/bootstrap-icons/vector-pen.svg new file mode 100644 index 0000000..60115b7 --- /dev/null +++ b/web/vendor/bootstrap-icons/vector-pen.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/view-list.svg b/web/vendor/bootstrap-icons/view-list.svg new file mode 100644 index 0000000..9211543 --- /dev/null +++ b/web/vendor/bootstrap-icons/view-list.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/view-stacked.svg b/web/vendor/bootstrap-icons/view-stacked.svg new file mode 100644 index 0000000..84b5ccf --- /dev/null +++ b/web/vendor/bootstrap-icons/view-stacked.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/vignette.svg b/web/vendor/bootstrap-icons/vignette.svg new file mode 100644 index 0000000..d179290 --- /dev/null +++ b/web/vendor/bootstrap-icons/vignette.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/vimeo.svg b/web/vendor/bootstrap-icons/vimeo.svg new file mode 100644 index 0000000..6b8e4b5 --- /dev/null +++ b/web/vendor/bootstrap-icons/vimeo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/vinyl-fill.svg b/web/vendor/bootstrap-icons/vinyl-fill.svg new file mode 100644 index 0000000..546d7bb --- /dev/null +++ b/web/vendor/bootstrap-icons/vinyl-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/vinyl.svg b/web/vendor/bootstrap-icons/vinyl.svg new file mode 100644 index 0000000..63647e5 --- /dev/null +++ b/web/vendor/bootstrap-icons/vinyl.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/virus.svg b/web/vendor/bootstrap-icons/virus.svg new file mode 100644 index 0000000..fd291a5 --- /dev/null +++ b/web/vendor/bootstrap-icons/virus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/virus2.svg b/web/vendor/bootstrap-icons/virus2.svg new file mode 100644 index 0000000..53f44e9 --- /dev/null +++ b/web/vendor/bootstrap-icons/virus2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/voicemail.svg b/web/vendor/bootstrap-icons/voicemail.svg new file mode 100644 index 0000000..ba22eb1 --- /dev/null +++ b/web/vendor/bootstrap-icons/voicemail.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/volume-down-fill.svg b/web/vendor/bootstrap-icons/volume-down-fill.svg new file mode 100644 index 0000000..681d349 --- /dev/null +++ b/web/vendor/bootstrap-icons/volume-down-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/volume-down.svg b/web/vendor/bootstrap-icons/volume-down.svg new file mode 100644 index 0000000..3ca7e6a --- /dev/null +++ b/web/vendor/bootstrap-icons/volume-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/volume-mute-fill.svg b/web/vendor/bootstrap-icons/volume-mute-fill.svg new file mode 100644 index 0000000..148628c --- /dev/null +++ b/web/vendor/bootstrap-icons/volume-mute-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/volume-mute.svg b/web/vendor/bootstrap-icons/volume-mute.svg new file mode 100644 index 0000000..d06d3dc --- /dev/null +++ b/web/vendor/bootstrap-icons/volume-mute.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/volume-off-fill.svg b/web/vendor/bootstrap-icons/volume-off-fill.svg new file mode 100644 index 0000000..315110a --- /dev/null +++ b/web/vendor/bootstrap-icons/volume-off-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/volume-off.svg b/web/vendor/bootstrap-icons/volume-off.svg new file mode 100644 index 0000000..e5f82cc --- /dev/null +++ b/web/vendor/bootstrap-icons/volume-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/volume-up-fill.svg b/web/vendor/bootstrap-icons/volume-up-fill.svg new file mode 100644 index 0000000..0f94073 --- /dev/null +++ b/web/vendor/bootstrap-icons/volume-up-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/volume-up.svg b/web/vendor/bootstrap-icons/volume-up.svg new file mode 100644 index 0000000..6347f42 --- /dev/null +++ b/web/vendor/bootstrap-icons/volume-up.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/vr.svg b/web/vendor/bootstrap-icons/vr.svg new file mode 100644 index 0000000..5ad5438 --- /dev/null +++ b/web/vendor/bootstrap-icons/vr.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wallet-fill.svg b/web/vendor/bootstrap-icons/wallet-fill.svg new file mode 100644 index 0000000..ee1c27d --- /dev/null +++ b/web/vendor/bootstrap-icons/wallet-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wallet.svg b/web/vendor/bootstrap-icons/wallet.svg new file mode 100644 index 0000000..6c9d247 --- /dev/null +++ b/web/vendor/bootstrap-icons/wallet.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wallet2.svg b/web/vendor/bootstrap-icons/wallet2.svg new file mode 100644 index 0000000..b127b0e --- /dev/null +++ b/web/vendor/bootstrap-icons/wallet2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/watch.svg b/web/vendor/bootstrap-icons/watch.svg new file mode 100644 index 0000000..542d4d8 --- /dev/null +++ b/web/vendor/bootstrap-icons/watch.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/water.svg b/web/vendor/bootstrap-icons/water.svg new file mode 100644 index 0000000..666653b --- /dev/null +++ b/web/vendor/bootstrap-icons/water.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/webcam-fill.svg b/web/vendor/bootstrap-icons/webcam-fill.svg new file mode 100644 index 0000000..e8db7ba --- /dev/null +++ b/web/vendor/bootstrap-icons/webcam-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/webcam.svg b/web/vendor/bootstrap-icons/webcam.svg new file mode 100644 index 0000000..0d23803 --- /dev/null +++ b/web/vendor/bootstrap-icons/webcam.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wechat.svg b/web/vendor/bootstrap-icons/wechat.svg new file mode 100644 index 0000000..3bc67dd --- /dev/null +++ b/web/vendor/bootstrap-icons/wechat.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/whatsapp.svg b/web/vendor/bootstrap-icons/whatsapp.svg new file mode 100644 index 0000000..5cde6f7 --- /dev/null +++ b/web/vendor/bootstrap-icons/whatsapp.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wifi-1.svg b/web/vendor/bootstrap-icons/wifi-1.svg new file mode 100644 index 0000000..5f3d340 --- /dev/null +++ b/web/vendor/bootstrap-icons/wifi-1.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wifi-2.svg b/web/vendor/bootstrap-icons/wifi-2.svg new file mode 100644 index 0000000..09d26c2 --- /dev/null +++ b/web/vendor/bootstrap-icons/wifi-2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wifi-off.svg b/web/vendor/bootstrap-icons/wifi-off.svg new file mode 100644 index 0000000..2f5e61f --- /dev/null +++ b/web/vendor/bootstrap-icons/wifi-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wifi.svg b/web/vendor/bootstrap-icons/wifi.svg new file mode 100644 index 0000000..773e027 --- /dev/null +++ b/web/vendor/bootstrap-icons/wifi.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wikipedia.svg b/web/vendor/bootstrap-icons/wikipedia.svg new file mode 100644 index 0000000..11f2fc6 --- /dev/null +++ b/web/vendor/bootstrap-icons/wikipedia.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wind.svg b/web/vendor/bootstrap-icons/wind.svg new file mode 100644 index 0000000..2ac05cd --- /dev/null +++ b/web/vendor/bootstrap-icons/wind.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-dash.svg b/web/vendor/bootstrap-icons/window-dash.svg new file mode 100644 index 0000000..5e157af --- /dev/null +++ b/web/vendor/bootstrap-icons/window-dash.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-desktop.svg b/web/vendor/bootstrap-icons/window-desktop.svg new file mode 100644 index 0000000..fa17523 --- /dev/null +++ b/web/vendor/bootstrap-icons/window-desktop.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-dock.svg b/web/vendor/bootstrap-icons/window-dock.svg new file mode 100644 index 0000000..41cdf69 --- /dev/null +++ b/web/vendor/bootstrap-icons/window-dock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-fullscreen.svg b/web/vendor/bootstrap-icons/window-fullscreen.svg new file mode 100644 index 0000000..421c4c5 --- /dev/null +++ b/web/vendor/bootstrap-icons/window-fullscreen.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-plus.svg b/web/vendor/bootstrap-icons/window-plus.svg new file mode 100644 index 0000000..e24ce0c --- /dev/null +++ b/web/vendor/bootstrap-icons/window-plus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-sidebar.svg b/web/vendor/bootstrap-icons/window-sidebar.svg new file mode 100644 index 0000000..d020d13 --- /dev/null +++ b/web/vendor/bootstrap-icons/window-sidebar.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-split.svg b/web/vendor/bootstrap-icons/window-split.svg new file mode 100644 index 0000000..96bdd24 --- /dev/null +++ b/web/vendor/bootstrap-icons/window-split.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-stack.svg b/web/vendor/bootstrap-icons/window-stack.svg new file mode 100644 index 0000000..8862976 --- /dev/null +++ b/web/vendor/bootstrap-icons/window-stack.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window-x.svg b/web/vendor/bootstrap-icons/window-x.svg new file mode 100644 index 0000000..c45e078 --- /dev/null +++ b/web/vendor/bootstrap-icons/window-x.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/window.svg b/web/vendor/bootstrap-icons/window.svg new file mode 100644 index 0000000..9bd2a2a --- /dev/null +++ b/web/vendor/bootstrap-icons/window.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/windows.svg b/web/vendor/bootstrap-icons/windows.svg new file mode 100644 index 0000000..af3b18c --- /dev/null +++ b/web/vendor/bootstrap-icons/windows.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wordpress.svg b/web/vendor/bootstrap-icons/wordpress.svg new file mode 100644 index 0000000..7d5808c --- /dev/null +++ b/web/vendor/bootstrap-icons/wordpress.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wrench-adjustable-circle-fill.svg b/web/vendor/bootstrap-icons/wrench-adjustable-circle-fill.svg new file mode 100644 index 0000000..33156c7 --- /dev/null +++ b/web/vendor/bootstrap-icons/wrench-adjustable-circle-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wrench-adjustable-circle.svg b/web/vendor/bootstrap-icons/wrench-adjustable-circle.svg new file mode 100644 index 0000000..381fb30 --- /dev/null +++ b/web/vendor/bootstrap-icons/wrench-adjustable-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wrench-adjustable.svg b/web/vendor/bootstrap-icons/wrench-adjustable.svg new file mode 100644 index 0000000..e7456d7 --- /dev/null +++ b/web/vendor/bootstrap-icons/wrench-adjustable.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/wrench.svg b/web/vendor/bootstrap-icons/wrench.svg new file mode 100644 index 0000000..806cca0 --- /dev/null +++ b/web/vendor/bootstrap-icons/wrench.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-circle-fill.svg b/web/vendor/bootstrap-icons/x-circle-fill.svg new file mode 100644 index 0000000..4070fb3 --- /dev/null +++ b/web/vendor/bootstrap-icons/x-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-circle.svg b/web/vendor/bootstrap-icons/x-circle.svg new file mode 100644 index 0000000..0e8c641 --- /dev/null +++ b/web/vendor/bootstrap-icons/x-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-diamond-fill.svg b/web/vendor/bootstrap-icons/x-diamond-fill.svg new file mode 100644 index 0000000..6ec461a --- /dev/null +++ b/web/vendor/bootstrap-icons/x-diamond-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-diamond.svg b/web/vendor/bootstrap-icons/x-diamond.svg new file mode 100644 index 0000000..b93295e --- /dev/null +++ b/web/vendor/bootstrap-icons/x-diamond.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-lg.svg b/web/vendor/bootstrap-icons/x-lg.svg new file mode 100644 index 0000000..b689cbb --- /dev/null +++ b/web/vendor/bootstrap-icons/x-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-octagon-fill.svg b/web/vendor/bootstrap-icons/x-octagon-fill.svg new file mode 100644 index 0000000..dd9fc6a --- /dev/null +++ b/web/vendor/bootstrap-icons/x-octagon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-octagon.svg b/web/vendor/bootstrap-icons/x-octagon.svg new file mode 100644 index 0000000..181a39f --- /dev/null +++ b/web/vendor/bootstrap-icons/x-octagon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-square-fill.svg b/web/vendor/bootstrap-icons/x-square-fill.svg new file mode 100644 index 0000000..5499578 --- /dev/null +++ b/web/vendor/bootstrap-icons/x-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x-square.svg b/web/vendor/bootstrap-icons/x-square.svg new file mode 100644 index 0000000..eb62b61 --- /dev/null +++ b/web/vendor/bootstrap-icons/x-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/x.svg b/web/vendor/bootstrap-icons/x.svg new file mode 100644 index 0000000..fdcc4e8 --- /dev/null +++ b/web/vendor/bootstrap-icons/x.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/xbox.svg b/web/vendor/bootstrap-icons/xbox.svg new file mode 100644 index 0000000..c0672b2 --- /dev/null +++ b/web/vendor/bootstrap-icons/xbox.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/yelp.svg b/web/vendor/bootstrap-icons/yelp.svg new file mode 100644 index 0000000..76e8884 --- /dev/null +++ b/web/vendor/bootstrap-icons/yelp.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/yin-yang.svg b/web/vendor/bootstrap-icons/yin-yang.svg new file mode 100644 index 0000000..1f50275 --- /dev/null +++ b/web/vendor/bootstrap-icons/yin-yang.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/youtube.svg b/web/vendor/bootstrap-icons/youtube.svg new file mode 100644 index 0000000..3c9c0be --- /dev/null +++ b/web/vendor/bootstrap-icons/youtube.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/zoom-in.svg b/web/vendor/bootstrap-icons/zoom-in.svg new file mode 100644 index 0000000..438e9bc --- /dev/null +++ b/web/vendor/bootstrap-icons/zoom-in.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/bootstrap-icons/zoom-out.svg b/web/vendor/bootstrap-icons/zoom-out.svg new file mode 100644 index 0000000..8be9f29 --- /dev/null +++ b/web/vendor/bootstrap-icons/zoom-out.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/web/vendor/editorjs/editorjs.mjs b/web/vendor/editorjs/editorjs.mjs new file mode 100644 index 0000000..a7c80b7 --- /dev/null +++ b/web/vendor/editorjs/editorjs.mjs @@ -0,0 +1,11208 @@ +(function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(".ce-hint--align-start{text-align:left}.ce-hint--align-center{text-align:center}.ce-hint__description{opacity:.6;margin-top:3px}")),document.head.appendChild(e)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})(); +var Ce = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {}; +function Ke(n) { + return n && n.__esModule && Object.prototype.hasOwnProperty.call(n, "default") ? n.default : n; +} +function Xn(n) { + if (n.__esModule) + return n; + var e = n.default; + if (typeof e == "function") { + var t = function o() { + return this instanceof o ? Reflect.construct(e, arguments, this.constructor) : e.apply(this, arguments); + }; + t.prototype = e.prototype; + } else + t = {}; + return Object.defineProperty(t, "__esModule", { value: !0 }), Object.keys(n).forEach(function(o) { + var i = Object.getOwnPropertyDescriptor(n, o); + Object.defineProperty(t, o, i.get ? i : { + enumerable: !0, + get: function() { + return n[o]; + } + }); + }), t; +} +function ot() { +} +Object.assign(ot, { + default: ot, + register: ot, + revert: function() { + }, + __esModule: !0 +}); +Element.prototype.matches || (Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function(n) { + const e = (this.document || this.ownerDocument).querySelectorAll(n); + let t = e.length; + for (; --t >= 0 && e.item(t) !== this; ) + ; + return t > -1; +}); +Element.prototype.closest || (Element.prototype.closest = function(n) { + let e = this; + if (!document.documentElement.contains(e)) + return null; + do { + if (e.matches(n)) + return e; + e = e.parentElement || e.parentNode; + } while (e !== null); + return null; +}); +Element.prototype.prepend || (Element.prototype.prepend = function(e) { + const t = document.createDocumentFragment(); + Array.isArray(e) || (e = [e]), e.forEach((o) => { + const i = o instanceof Node; + t.appendChild(i ? o : document.createTextNode(o)); + }), this.insertBefore(t, this.firstChild); +}); +Element.prototype.scrollIntoViewIfNeeded || (Element.prototype.scrollIntoViewIfNeeded = function(n) { + n = arguments.length === 0 ? !0 : !!n; + const e = this.parentNode, t = window.getComputedStyle(e, null), o = parseInt(t.getPropertyValue("border-top-width")), i = parseInt(t.getPropertyValue("border-left-width")), s = this.offsetTop - e.offsetTop < e.scrollTop, r = this.offsetTop - e.offsetTop + this.clientHeight - o > e.scrollTop + e.clientHeight, a = this.offsetLeft - e.offsetLeft < e.scrollLeft, l = this.offsetLeft - e.offsetLeft + this.clientWidth - i > e.scrollLeft + e.clientWidth, c = s && !r; + (s || r) && n && (e.scrollTop = this.offsetTop - e.offsetTop - e.clientHeight / 2 - o + this.clientHeight / 2), (a || l) && n && (e.scrollLeft = this.offsetLeft - e.offsetLeft - e.clientWidth / 2 - i + this.clientWidth / 2), (s || r || a || l) && !n && this.scrollIntoView(c); +}); +window.requestIdleCallback = window.requestIdleCallback || function(n) { + const e = Date.now(); + return setTimeout(function() { + n({ + didTimeout: !1, + timeRemaining: function() { + return Math.max(0, 50 - (Date.now() - e)); + } + }); + }, 1); +}; +window.cancelIdleCallback = window.cancelIdleCallback || function(n) { + clearTimeout(n); +}; +let Vn = (n = 21) => crypto.getRandomValues(new Uint8Array(n)).reduce((e, t) => (t &= 63, t < 36 ? e += t.toString(36) : t < 62 ? e += (t - 26).toString(36).toUpperCase() : t > 62 ? e += "-" : e += "_", e), ""); +var Lo = /* @__PURE__ */ ((n) => (n.VERBOSE = "VERBOSE", n.INFO = "INFO", n.WARN = "WARN", n.ERROR = "ERROR", n))(Lo || {}); +const y = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + SHIFT: 16, + CTRL: 17, + ALT: 18, + ESC: 27, + SPACE: 32, + LEFT: 37, + UP: 38, + DOWN: 40, + RIGHT: 39, + DELETE: 46, + META: 91, + SLASH: 191 +}, qn = { + LEFT: 0, + WHEEL: 1, + RIGHT: 2, + BACKWARD: 3, + FORWARD: 4 +}; +function Ie(n, e, t = "log", o, i = "color: inherit") { + if (!("console" in window) || !window.console[t]) + return; + const s = ["info", "log", "warn", "error"].includes(t), r = []; + switch (Ie.logLevel) { + case "ERROR": + if (t !== "error") + return; + break; + case "WARN": + if (!["error", "warn"].includes(t)) + return; + break; + case "INFO": + if (!s || n) + return; + break; + } + o && r.push(o); + const a = "Editor.js 2.31.1", l = `line-height: 1em; + color: #006FEA; + display: inline-block; + font-size: 11px; + line-height: 1em; + background-color: #fff; + padding: 4px 9px; + border-radius: 30px; + border: 1px solid rgba(56, 138, 229, 0.16); + margin: 4px 5px 4px 0;`; + n && (s ? (r.unshift(l, i), e = `%c${a}%c ${e}`) : e = `( ${a} )${e}`); + try { + s ? o ? console[t](`${e} %o`, ...r) : console[t](e, ...r) : console[t](e); + } catch { + } +} +Ie.logLevel = "VERBOSE"; +function Zn(n) { + Ie.logLevel = n; +} +const S = Ie.bind(window, !1), X = Ie.bind(window, !0); +function le(n) { + return Object.prototype.toString.call(n).match(/\s([a-zA-Z]+)/)[1].toLowerCase(); +} +function A(n) { + return le(n) === "function" || le(n) === "asyncfunction"; +} +function D(n) { + return le(n) === "object"; +} +function te(n) { + return le(n) === "string"; +} +function Gn(n) { + return le(n) === "boolean"; +} +function yo(n) { + return le(n) === "number"; +} +function wo(n) { + return le(n) === "undefined"; +} +function V(n) { + return n ? Object.keys(n).length === 0 && n.constructor === Object : !0; +} +function Po(n) { + return n > 47 && n < 58 || // number keys + n === 32 || n === 13 || // Space bar & return key(s) + n === 229 || // processing key input for certain languages — Chinese, Japanese, etc. + n > 64 && n < 91 || // letter keys + n > 95 && n < 112 || // Numpad keys + n > 185 && n < 193 || // ;=,-./` (in order) + n > 218 && n < 223; +} +async function Qn(n, e = () => { +}, t = () => { +}) { + async function o(i, s, r) { + try { + await i.function(i.data), await s(wo(i.data) ? {} : i.data); + } catch { + r(wo(i.data) ? {} : i.data); + } + } + return n.reduce(async (i, s) => (await i, o(s, e, t)), Promise.resolve()); +} +function No(n) { + return Array.prototype.slice.call(n); +} +function Fe(n, e) { + return function() { + const t = this, o = arguments; + window.setTimeout(() => n.apply(t, o), e); + }; +} +function Jn(n) { + return n.name.split(".").pop(); +} +function ei(n) { + return /^[-\w]+\/([-+\w]+|\*)$/.test(n); +} +function Eo(n, e, t) { + let o; + return (...i) => { + const s = this, r = () => { + o = null, t || n.apply(s, i); + }, a = t && !o; + window.clearTimeout(o), o = window.setTimeout(r, e), a && n.apply(s, i); + }; +} +function dt(n, e, t = void 0) { + let o, i, s, r = null, a = 0; + t || (t = {}); + const l = function() { + a = t.leading === !1 ? 0 : Date.now(), r = null, s = n.apply(o, i), r || (o = i = null); + }; + return function() { + const c = Date.now(); + !a && t.leading === !1 && (a = c); + const d = e - (c - a); + return o = this, i = arguments, d <= 0 || d > e ? (r && (clearTimeout(r), r = null), a = c, s = n.apply(o, i), r || (o = i = null)) : !r && t.trailing !== !1 && (r = setTimeout(l, d)), s; + }; +} +function ti() { + const n = { + win: !1, + mac: !1, + x11: !1, + linux: !1 + }, e = Object.keys(n).find((t) => window.navigator.appVersion.toLowerCase().indexOf(t) !== -1); + return e && (n[e] = !0), n; +} +function je(n) { + return n[0].toUpperCase() + n.slice(1); +} +function ut(n, ...e) { + if (!e.length) + return n; + const t = e.shift(); + if (D(n) && D(t)) + for (const o in t) + D(t[o]) ? (n[o] || Object.assign(n, { [o]: {} }), ut(n[o], t[o])) : Object.assign(n, { [o]: t[o] }); + return ut(n, ...e); +} +function vt(n) { + const e = ti(); + return n = n.replace(/shift/gi, "⇧").replace(/backspace/gi, "⌫").replace(/enter/gi, "⏎").replace(/up/gi, "↑").replace(/left/gi, "→").replace(/down/gi, "↓").replace(/right/gi, "←").replace(/escape/gi, "⎋").replace(/insert/gi, "Ins").replace(/delete/gi, "␡").replace(/\+/gi, " + "), e.mac ? n = n.replace(/ctrl|cmd/gi, "⌘").replace(/alt/gi, "⌥") : n = n.replace(/cmd/gi, "Ctrl").replace(/windows/gi, "WIN"), n; +} +function oi(n) { + try { + return new URL(n).href; + } catch { + } + return n.substring(0, 2) === "//" ? window.location.protocol + n : window.location.origin + n; +} +function ni() { + return Vn(10); +} +function ii(n) { + window.open(n, "_blank"); +} +function si(n = "") { + return `${n}${Math.floor(Math.random() * 1e8).toString(16)}`; +} +function ht(n, e, t) { + const o = `«${e}» is deprecated and will be removed in the next major release. Please use the «${t}» instead.`; + n && X(o, "warn"); +} +function me(n, e, t) { + const o = t.value ? "value" : "get", i = t[o], s = `#${e}Cache`; + if (t[o] = function(...r) { + return this[s] === void 0 && (this[s] = i.apply(this, ...r)), this[s]; + }, o === "get" && t.set) { + const r = t.set; + t.set = function(a) { + delete n[s], r.apply(this, a); + }; + } + return t; +} +const Ro = 650; +function be() { + return window.matchMedia(`(max-width: ${Ro}px)`).matches; +} +const pt = typeof window < "u" && window.navigator && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === "MacIntel" && window.navigator.maxTouchPoints > 1); +function ri(n, e) { + const t = Array.isArray(n) || D(n), o = Array.isArray(e) || D(e); + return t || o ? JSON.stringify(n) === JSON.stringify(e) : n === e; +} +class u { + /** + * Check if passed tag has no closed tag + * + * @param {HTMLElement} tag - element to check + * @returns {boolean} + */ + static isSingleTag(e) { + return e.tagName && [ + "AREA", + "BASE", + "BR", + "COL", + "COMMAND", + "EMBED", + "HR", + "IMG", + "INPUT", + "KEYGEN", + "LINK", + "META", + "PARAM", + "SOURCE", + "TRACK", + "WBR" + ].includes(e.tagName); + } + /** + * Check if element is BR or WBR + * + * @param {HTMLElement} element - element to check + * @returns {boolean} + */ + static isLineBreakTag(e) { + return e && e.tagName && [ + "BR", + "WBR" + ].includes(e.tagName); + } + /** + * Helper for making Elements with class name and attributes + * + * @param {string} tagName - new Element tag name + * @param {string[]|string} [classNames] - list or name of CSS class name(s) + * @param {object} [attributes] - any attributes + * @returns {HTMLElement} + */ + static make(e, t = null, o = {}) { + const i = document.createElement(e); + if (Array.isArray(t)) { + const s = t.filter((r) => r !== void 0); + i.classList.add(...s); + } else + t && i.classList.add(t); + for (const s in o) + Object.prototype.hasOwnProperty.call(o, s) && (i[s] = o[s]); + return i; + } + /** + * Creates Text Node with the passed content + * + * @param {string} content - text content + * @returns {Text} + */ + static text(e) { + return document.createTextNode(e); + } + /** + * Append one or several elements to the parent + * + * @param {Element|DocumentFragment} parent - where to append + * @param {Element|Element[]|DocumentFragment|Text|Text[]} elements - element or elements list + */ + static append(e, t) { + Array.isArray(t) ? t.forEach((o) => e.appendChild(o)) : e.appendChild(t); + } + /** + * Append element or a couple to the beginning of the parent elements + * + * @param {Element} parent - where to append + * @param {Element|Element[]} elements - element or elements list + */ + static prepend(e, t) { + Array.isArray(t) ? (t = t.reverse(), t.forEach((o) => e.prepend(o))) : e.prepend(t); + } + /** + * Swap two elements in parent + * + * @param {HTMLElement} el1 - from + * @param {HTMLElement} el2 - to + * @deprecated + */ + static swap(e, t) { + const o = document.createElement("div"), i = e.parentNode; + i.insertBefore(o, e), i.insertBefore(e, t), i.insertBefore(t, o), i.removeChild(o); + } + /** + * Selector Decorator + * + * Returns first match + * + * @param {Element} el - element we searching inside. Default - DOM Document + * @param {string} selector - searching string + * @returns {Element} + */ + static find(e = document, t) { + return e.querySelector(t); + } + /** + * Get Element by Id + * + * @param {string} id - id to find + * @returns {HTMLElement | null} + */ + static get(e) { + return document.getElementById(e); + } + /** + * Selector Decorator. + * + * Returns all matches + * + * @param {Element|Document} el - element we searching inside. Default - DOM Document + * @param {string} selector - searching string + * @returns {NodeList} + */ + static findAll(e = document, t) { + return e.querySelectorAll(t); + } + /** + * Returns CSS selector for all text inputs + */ + static get allInputsSelector() { + return "[contenteditable=true], textarea, input:not([type]), " + ["text", "password", "email", "number", "search", "tel", "url"].map((t) => `input[type="${t}"]`).join(", "); + } + /** + * Find all contenteditable, textarea and editable input elements passed holder contains + * + * @param holder - element where to find inputs + */ + static findAllInputs(e) { + return No(e.querySelectorAll(u.allInputsSelector)).reduce((t, o) => u.isNativeInput(o) || u.containsOnlyInlineElements(o) ? [...t, o] : [...t, ...u.getDeepestBlockElements(o)], []); + } + /** + * Search for deepest node which is Leaf. + * Leaf is the vertex that doesn't have any child nodes + * + * @description Method recursively goes throw the all Node until it finds the Leaf + * @param {Node} node - root Node. From this vertex we start Deep-first search + * {@link https://en.wikipedia.org/wiki/Depth-first_search} + * @param {boolean} [atLast] - find last text node + * @returns - it can be text Node or Element Node, so that caret will able to work with it + * Can return null if node is Document or DocumentFragment, or node is not attached to the DOM + */ + static getDeepestNode(e, t = !1) { + const o = t ? "lastChild" : "firstChild", i = t ? "previousSibling" : "nextSibling"; + if (e && e.nodeType === Node.ELEMENT_NODE && e[o]) { + let s = e[o]; + if (u.isSingleTag(s) && !u.isNativeInput(s) && !u.isLineBreakTag(s)) + if (s[i]) + s = s[i]; + else if (s.parentNode[i]) + s = s.parentNode[i]; + else + return s.parentNode; + return this.getDeepestNode(s, t); + } + return e; + } + /** + * Check if object is DOM node + * + * @param {*} node - object to check + * @returns {boolean} + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static isElement(e) { + return yo(e) ? !1 : e && e.nodeType && e.nodeType === Node.ELEMENT_NODE; + } + /** + * Check if object is DocumentFragment node + * + * @param {object} node - object to check + * @returns {boolean} + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static isFragment(e) { + return yo(e) ? !1 : e && e.nodeType && e.nodeType === Node.DOCUMENT_FRAGMENT_NODE; + } + /** + * Check if passed element is contenteditable + * + * @param {HTMLElement} element - html element to check + * @returns {boolean} + */ + static isContentEditable(e) { + return e.contentEditable === "true"; + } + /** + * Checks target if it is native input + * + * @param {*} target - HTML element or string + * @returns {boolean} + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static isNativeInput(e) { + const t = [ + "INPUT", + "TEXTAREA" + ]; + return e && e.tagName ? t.includes(e.tagName) : !1; + } + /** + * Checks if we can set caret + * + * @param {HTMLElement} target - target to check + * @returns {boolean} + */ + static canSetCaret(e) { + let t = !0; + if (u.isNativeInput(e)) + switch (e.type) { + case "file": + case "checkbox": + case "radio": + case "hidden": + case "submit": + case "button": + case "image": + case "reset": + t = !1; + break; + } + else + t = u.isContentEditable(e); + return t; + } + /** + * Checks node if it is empty + * + * @description Method checks simple Node without any childs for emptiness + * If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method + * @param {Node} node - node to check + * @param {string} [ignoreChars] - char or substring to treat as empty + * @returns {boolean} true if it is empty + */ + static isNodeEmpty(e, t) { + let o; + return this.isSingleTag(e) && !this.isLineBreakTag(e) ? !1 : (this.isElement(e) && this.isNativeInput(e) ? o = e.value : o = e.textContent.replace("​", ""), t && (o = o.replace(new RegExp(t, "g"), "")), o.length === 0); + } + /** + * checks node if it is doesn't have any child nodes + * + * @param {Node} node - node to check + * @returns {boolean} + */ + static isLeaf(e) { + return e ? e.childNodes.length === 0 : !1; + } + /** + * breadth-first search (BFS) + * {@link https://en.wikipedia.org/wiki/Breadth-first_search} + * + * @description Pushes to stack all DOM leafs and checks for emptiness + * @param {Node} node - node to check + * @param {string} [ignoreChars] - char or substring to treat as empty + * @returns {boolean} + */ + static isEmpty(e, t) { + const o = [e]; + for (; o.length > 0; ) + if (e = o.shift(), !!e) { + if (this.isLeaf(e) && !this.isNodeEmpty(e, t)) + return !1; + e.childNodes && o.push(...Array.from(e.childNodes)); + } + return !0; + } + /** + * Check if string contains html elements + * + * @param {string} str - string to check + * @returns {boolean} + */ + static isHTMLString(e) { + const t = u.make("div"); + return t.innerHTML = e, t.childElementCount > 0; + } + /** + * Return length of node`s text content + * + * @param {Node} node - node with content + * @returns {number} + */ + static getContentLength(e) { + return u.isNativeInput(e) ? e.value.length : e.nodeType === Node.TEXT_NODE ? e.length : e.textContent.length; + } + /** + * Return array of names of block html elements + * + * @returns {string[]} + */ + static get blockElements() { + return [ + "address", + "article", + "aside", + "blockquote", + "canvas", + "div", + "dl", + "dt", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "header", + "hgroup", + "hr", + "li", + "main", + "nav", + "noscript", + "ol", + "output", + "p", + "pre", + "ruby", + "section", + "table", + "tbody", + "thead", + "tr", + "tfoot", + "ul", + "video" + ]; + } + /** + * Check if passed content includes only inline elements + * + * @param {string|HTMLElement} data - element or html string + * @returns {boolean} + */ + static containsOnlyInlineElements(e) { + let t; + te(e) ? (t = document.createElement("div"), t.innerHTML = e) : t = e; + const o = (i) => !u.blockElements.includes(i.tagName.toLowerCase()) && Array.from(i.children).every(o); + return Array.from(t.children).every(o); + } + /** + * Find and return all block elements in the passed parent (including subtree) + * + * @param {HTMLElement} parent - root element + * @returns {HTMLElement[]} + */ + static getDeepestBlockElements(e) { + return u.containsOnlyInlineElements(e) ? [e] : Array.from(e.children).reduce((t, o) => [...t, ...u.getDeepestBlockElements(o)], []); + } + /** + * Helper for get holder from {string} or return HTMLElement + * + * @param {string | HTMLElement} element - holder's id or holder's HTML Element + * @returns {HTMLElement} + */ + static getHolder(e) { + return te(e) ? document.getElementById(e) : e; + } + /** + * Returns true if element is anchor (is A tag) + * + * @param {Element} element - element to check + * @returns {boolean} + */ + static isAnchor(e) { + return e.tagName.toLowerCase() === "a"; + } + /** + * Return element's offset related to the document + * + * @todo handle case when editor initialized in scrollable popup + * @param el - element to compute offset + */ + static offset(e) { + const t = e.getBoundingClientRect(), o = window.pageXOffset || document.documentElement.scrollLeft, i = window.pageYOffset || document.documentElement.scrollTop, s = t.top + i, r = t.left + o; + return { + top: s, + left: r, + bottom: s + t.height, + right: r + t.width + }; + } + /** + * Find text node and offset by total content offset + * + * @param {Node} root - root node to start search from + * @param {number} totalOffset - offset relative to the root node content + * @returns {{node: Node | null, offset: number}} - node and offset inside node + */ + static getNodeByOffset(e, t) { + let o = 0, i = null; + const s = document.createTreeWalker( + e, + NodeFilter.SHOW_TEXT, + null + ); + let r = s.nextNode(); + for (; r; ) { + const c = r.textContent, d = c === null ? 0 : c.length; + if (i = r, o + d >= t) + break; + o += d, r = s.nextNode(); + } + if (!i) + return { + node: null, + offset: 0 + }; + const a = i.textContent; + if (a === null || a.length === 0) + return { + node: null, + offset: 0 + }; + const l = Math.min(t - o, a.length); + return { + node: i, + offset: l + }; + } +} +function ai(n) { + return !/[^\t\n\r ]/.test(n); +} +function li(n) { + const e = window.getComputedStyle(n), t = parseFloat(e.fontSize), o = parseFloat(e.lineHeight) || t * 1.2, i = parseFloat(e.paddingTop), s = parseFloat(e.borderTopWidth), r = parseFloat(e.marginTop), a = t * 0.8, l = (o - t) / 2; + return r + s + i + l + a; +} +function Do(n) { + n.dataset.empty = u.isEmpty(n) ? "true" : "false"; +} +const ci = { + blockTunes: { + toggler: { + "Click to tune": "", + "or drag to move": "" + } + }, + inlineToolbar: { + converter: { + "Convert to": "" + } + }, + toolbar: { + toolbox: { + Add: "" + } + }, + popover: { + Filter: "", + "Nothing found": "", + "Convert to": "" + } +}, di = { + Text: "", + Link: "", + Bold: "", + Italic: "" +}, ui = { + link: { + "Add a link": "" + }, + stub: { + "The block can not be displayed correctly.": "" + } +}, hi = { + delete: { + Delete: "", + "Click to delete": "" + }, + moveUp: { + "Move up": "" + }, + moveDown: { + "Move down": "" + } +}, Fo = { + ui: ci, + toolNames: di, + tools: ui, + blockTunes: hi +}, jo = class he { + /** + * Type-safe translation for internal UI texts: + * Perform translation of the string by namespace and a key + * + * @example I18n.ui(I18nInternalNS.ui.blockTunes.toggler, 'Click to tune') + * @param internalNamespace - path to translated string in dictionary + * @param dictKey - dictionary key. Better to use default locale original text + */ + static ui(e, t) { + return he._t(e, t); + } + /** + * Translate for external strings that is not presented in default dictionary. + * For example, for user-specified tool names + * + * @param namespace - path to translated string in dictionary + * @param dictKey - dictionary key. Better to use default locale original text + */ + static t(e, t) { + return he._t(e, t); + } + /** + * Adjust module for using external dictionary + * + * @param dictionary - new messages list to override default + */ + static setDictionary(e) { + he.currentDictionary = e; + } + /** + * Perform translation both for internal and external namespaces + * If there is no translation found, returns passed key as a translated message + * + * @param namespace - path to translated string in dictionary + * @param dictKey - dictionary key. Better to use default locale original text + */ + static _t(e, t) { + const o = he.getNamespace(e); + return !o || !o[t] ? t : o[t]; + } + /** + * Find messages section by namespace path + * + * @param namespace - path to section + */ + static getNamespace(e) { + return e.split(".").reduce((o, i) => !o || !Object.keys(o).length ? {} : o[i], he.currentDictionary); + } +}; +jo.currentDictionary = Fo; +let z = jo; +class Ho extends Error { +} +class Oe { + constructor() { + this.subscribers = {}; + } + /** + * Subscribe any event on callback + * + * @param eventName - event name + * @param callback - subscriber + */ + on(e, t) { + e in this.subscribers || (this.subscribers[e] = []), this.subscribers[e].push(t); + } + /** + * Subscribe any event on callback. Callback will be called once and be removed from subscribers array after call. + * + * @param eventName - event name + * @param callback - subscriber + */ + once(e, t) { + e in this.subscribers || (this.subscribers[e] = []); + const o = (i) => { + const s = t(i), r = this.subscribers[e].indexOf(o); + return r !== -1 && this.subscribers[e].splice(r, 1), s; + }; + this.subscribers[e].push(o); + } + /** + * Emit callbacks with passed data + * + * @param eventName - event name + * @param data - subscribers get this data when they were fired + */ + emit(e, t) { + V(this.subscribers) || !this.subscribers[e] || this.subscribers[e].reduce((o, i) => { + const s = i(o); + return s !== void 0 ? s : o; + }, t); + } + /** + * Unsubscribe callback from event + * + * @param eventName - event name + * @param callback - event handler + */ + off(e, t) { + if (this.subscribers[e] === void 0) { + console.warn(`EventDispatcher .off(): there is no subscribers for event "${e.toString()}". Probably, .off() called before .on()`); + return; + } + for (let o = 0; o < this.subscribers[e].length; o++) + if (this.subscribers[e][o] === t) { + delete this.subscribers[e][o]; + break; + } + } + /** + * Destroyer + * clears subscribers list + */ + destroy() { + this.subscribers = {}; + } +} +function J(n) { + Object.setPrototypeOf(this, { + /** + * Block id + * + * @returns {string} + */ + get id() { + return n.id; + }, + /** + * Tool name + * + * @returns {string} + */ + get name() { + return n.name; + }, + /** + * Tool config passed on Editor's initialization + * + * @returns {ToolConfig} + */ + get config() { + return n.config; + }, + /** + * .ce-block element, that wraps plugin contents + * + * @returns {HTMLElement} + */ + get holder() { + return n.holder; + }, + /** + * True if Block content is empty + * + * @returns {boolean} + */ + get isEmpty() { + return n.isEmpty; + }, + /** + * True if Block is selected with Cross-Block selection + * + * @returns {boolean} + */ + get selected() { + return n.selected; + }, + /** + * Set Block's stretch state + * + * @param {boolean} state — state to set + */ + set stretched(t) { + n.stretched = t; + }, + /** + * True if Block is stretched + * + * @returns {boolean} + */ + get stretched() { + return n.stretched; + }, + /** + * True if Block has inputs to be focused + */ + get focusable() { + return n.focusable; + }, + /** + * Call Tool method with errors handler under-the-hood + * + * @param {string} methodName - method to call + * @param {object} param - object with parameters + * @returns {unknown} + */ + call(t, o) { + return n.call(t, o); + }, + /** + * Save Block content + * + * @returns {Promise} + */ + save() { + return n.save(); + }, + /** + * Validate Block data + * + * @param {BlockToolData} data - data to validate + * @returns {Promise} + */ + validate(t) { + return n.validate(t); + }, + /** + * Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback + * Can be useful for block changes invisible for editor core. + */ + dispatchChange() { + n.dispatchChange(); + }, + /** + * Tool could specify several entries to be displayed at the Toolbox (for example, "Heading 1", "Heading 2", "Heading 3") + * This method returns the entry that is related to the Block (depended on the Block data) + */ + getActiveToolboxEntry() { + return n.getActiveToolboxEntry(); + } + }); +} +class _e { + constructor() { + this.allListeners = []; + } + /** + * Assigns event listener on element and returns unique identifier + * + * @param {EventTarget} element - DOM element that needs to be listened + * @param {string} eventType - event type + * @param {Function} handler - method that will be fired on event + * @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once} + */ + on(e, t, o, i = !1) { + const s = si("l"), r = { + id: s, + element: e, + eventType: t, + handler: o, + options: i + }; + if (!this.findOne(e, t, o)) + return this.allListeners.push(r), e.addEventListener(t, o, i), s; + } + /** + * Removes event listener from element + * + * @param {EventTarget} element - DOM element that we removing listener + * @param {string} eventType - event type + * @param {Function} handler - remove handler, if element listens several handlers on the same event type + * @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once} + */ + off(e, t, o, i) { + const s = this.findAll(e, t, o); + s.forEach((r, a) => { + const l = this.allListeners.indexOf(s[a]); + l > -1 && (this.allListeners.splice(l, 1), r.element.removeEventListener(r.eventType, r.handler, r.options)); + }); + } + /** + * Removes listener by id + * + * @param {string} id - listener identifier + */ + offById(e) { + const t = this.findById(e); + t && t.element.removeEventListener(t.eventType, t.handler, t.options); + } + /** + * Finds and returns first listener by passed params + * + * @param {EventTarget} element - event target + * @param {string} [eventType] - event type + * @param {Function} [handler] - event handler + * @returns {ListenerData|null} + */ + findOne(e, t, o) { + const i = this.findAll(e, t, o); + return i.length > 0 ? i[0] : null; + } + /** + * Return all stored listeners by passed params + * + * @param {EventTarget} element - event target + * @param {string} eventType - event type + * @param {Function} handler - event handler + * @returns {ListenerData[]} + */ + findAll(e, t, o) { + let i; + const s = e ? this.findByEventTarget(e) : []; + return e && t && o ? i = s.filter((r) => r.eventType === t && r.handler === o) : e && t ? i = s.filter((r) => r.eventType === t) : i = s, i; + } + /** + * Removes all listeners + */ + removeAll() { + this.allListeners.map((e) => { + e.element.removeEventListener(e.eventType, e.handler, e.options); + }), this.allListeners = []; + } + /** + * Module cleanup on destruction + */ + destroy() { + this.removeAll(); + } + /** + * Search method: looks for listener by passed element + * + * @param {EventTarget} element - searching element + * @returns {Array} listeners that found on element + */ + findByEventTarget(e) { + return this.allListeners.filter((t) => { + if (t.element === e) + return t; + }); + } + /** + * Search method: looks for listener by passed event type + * + * @param {string} eventType - event type + * @returns {ListenerData[]} listeners that found on element + */ + findByType(e) { + return this.allListeners.filter((t) => { + if (t.eventType === e) + return t; + }); + } + /** + * Search method: looks for listener by passed handler + * + * @param {Function} handler - event handler + * @returns {ListenerData[]} listeners that found on element + */ + findByHandler(e) { + return this.allListeners.filter((t) => { + if (t.handler === e) + return t; + }); + } + /** + * Returns listener data found by id + * + * @param {string} id - listener identifier + * @returns {ListenerData} + */ + findById(e) { + return this.allListeners.find((t) => t.id === e); + } +} +class E { + /** + * @class + * @param options - Module options + * @param options.config - Module config + * @param options.eventsDispatcher - Common event bus + */ + constructor({ config: e, eventsDispatcher: t }) { + if (this.nodes = {}, this.listeners = new _e(), this.readOnlyMutableListeners = { + /** + * Assigns event listener on DOM element and pushes into special array that might be removed + * + * @param {EventTarget} element - DOM Element + * @param {string} eventType - Event name + * @param {Function} handler - Event handler + * @param {boolean|AddEventListenerOptions} options - Listening options + */ + on: (o, i, s, r = !1) => { + this.mutableListenerIds.push( + this.listeners.on(o, i, s, r) + ); + }, + /** + * Clears all mutable listeners + */ + clearAll: () => { + for (const o of this.mutableListenerIds) + this.listeners.offById(o); + this.mutableListenerIds = []; + } + }, this.mutableListenerIds = [], new.target === E) + throw new TypeError("Constructors for abstract class Module are not allowed."); + this.config = e, this.eventsDispatcher = t; + } + /** + * Editor modules setter + * + * @param {EditorModules} Editor - Editor's Modules + */ + set state(e) { + this.Editor = e; + } + /** + * Remove memorized nodes + */ + removeAllNodes() { + for (const e in this.nodes) { + const t = this.nodes[e]; + t instanceof HTMLElement && t.remove(); + } + } + /** + * Returns true if current direction is RTL (Right-To-Left) + */ + get isRtl() { + return this.config.i18n.direction === "rtl"; + } +} +class b { + constructor() { + this.instance = null, this.selection = null, this.savedSelectionRange = null, this.isFakeBackgroundEnabled = !1, this.commandBackground = "backColor", this.commandRemoveFormat = "removeFormat"; + } + /** + * Editor styles + * + * @returns {{editorWrapper: string, editorZone: string}} + */ + static get CSS() { + return { + editorWrapper: "codex-editor", + editorZone: "codex-editor__redactor" + }; + } + /** + * Returns selected anchor + * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorNode} + * + * @returns {Node|null} + */ + static get anchorNode() { + const e = window.getSelection(); + return e ? e.anchorNode : null; + } + /** + * Returns selected anchor element + * + * @returns {Element|null} + */ + static get anchorElement() { + const e = window.getSelection(); + if (!e) + return null; + const t = e.anchorNode; + return t ? u.isElement(t) ? t : t.parentElement : null; + } + /** + * Returns selection offset according to the anchor node + * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorOffset} + * + * @returns {number|null} + */ + static get anchorOffset() { + const e = window.getSelection(); + return e ? e.anchorOffset : null; + } + /** + * Is current selection range collapsed + * + * @returns {boolean|null} + */ + static get isCollapsed() { + const e = window.getSelection(); + return e ? e.isCollapsed : null; + } + /** + * Check current selection if it is at Editor's zone + * + * @returns {boolean} + */ + static get isAtEditor() { + return this.isSelectionAtEditor(b.get()); + } + /** + * Check if passed selection is at Editor's zone + * + * @param selection - Selection object to check + */ + static isSelectionAtEditor(e) { + if (!e) + return !1; + let t = e.anchorNode || e.focusNode; + t && t.nodeType === Node.TEXT_NODE && (t = t.parentNode); + let o = null; + return t && t instanceof Element && (o = t.closest(`.${b.CSS.editorZone}`)), o ? o.nodeType === Node.ELEMENT_NODE : !1; + } + /** + * Check if passed range at Editor zone + * + * @param range - range to check + */ + static isRangeAtEditor(e) { + if (!e) + return; + let t = e.startContainer; + t && t.nodeType === Node.TEXT_NODE && (t = t.parentNode); + let o = null; + return t && t instanceof Element && (o = t.closest(`.${b.CSS.editorZone}`)), o ? o.nodeType === Node.ELEMENT_NODE : !1; + } + /** + * Methods return boolean that true if selection exists on the page + */ + static get isSelectionExists() { + return !!b.get().anchorNode; + } + /** + * Return first range + * + * @returns {Range|null} + */ + static get range() { + return this.getRangeFromSelection(this.get()); + } + /** + * Returns range from passed Selection object + * + * @param selection - Selection object to get Range from + */ + static getRangeFromSelection(e) { + return e && e.rangeCount ? e.getRangeAt(0) : null; + } + /** + * Calculates position and size of selected text + * + * @returns {DOMRect | ClientRect} + */ + static get rect() { + let e = document.selection, t, o = { + x: 0, + y: 0, + width: 0, + height: 0 + }; + if (e && e.type !== "Control") + return e = e, t = e.createRange(), o.x = t.boundingLeft, o.y = t.boundingTop, o.width = t.boundingWidth, o.height = t.boundingHeight, o; + if (!window.getSelection) + return S("Method window.getSelection is not supported", "warn"), o; + if (e = window.getSelection(), e.rangeCount === null || isNaN(e.rangeCount)) + return S("Method SelectionUtils.rangeCount is not supported", "warn"), o; + if (e.rangeCount === 0) + return o; + if (t = e.getRangeAt(0).cloneRange(), t.getBoundingClientRect && (o = t.getBoundingClientRect()), o.x === 0 && o.y === 0) { + const i = document.createElement("span"); + if (i.getBoundingClientRect) { + i.appendChild(document.createTextNode("​")), t.insertNode(i), o = i.getBoundingClientRect(); + const s = i.parentNode; + s.removeChild(i), s.normalize(); + } + } + return o; + } + /** + * Returns selected text as String + * + * @returns {string} + */ + static get text() { + return window.getSelection ? window.getSelection().toString() : ""; + } + /** + * Returns window SelectionUtils + * {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection} + * + * @returns {Selection} + */ + static get() { + return window.getSelection(); + } + /** + * Set focus to contenteditable or native input element + * + * @param element - element where to set focus + * @param offset - offset of cursor + */ + static setCursor(e, t = 0) { + const o = document.createRange(), i = window.getSelection(); + return u.isNativeInput(e) ? u.canSetCaret(e) ? (e.focus(), e.selectionStart = e.selectionEnd = t, e.getBoundingClientRect()) : void 0 : (o.setStart(e, t), o.setEnd(e, t), i.removeAllRanges(), i.addRange(o), o.getBoundingClientRect()); + } + /** + * Check if current range exists and belongs to container + * + * @param container - where range should be + */ + static isRangeInsideContainer(e) { + const t = b.range; + return t === null ? !1 : e.contains(t.startContainer); + } + /** + * Adds fake cursor to the current range + */ + static addFakeCursor() { + const e = b.range; + if (e === null) + return; + const t = u.make("span", "codex-editor__fake-cursor"); + t.dataset.mutationFree = "true", e.collapse(), e.insertNode(t); + } + /** + * Check if passed element contains a fake cursor + * + * @param el - where to check + */ + static isFakeCursorInsideContainer(e) { + return u.find(e, ".codex-editor__fake-cursor") !== null; + } + /** + * Removes fake cursor from a container + * + * @param container - container to look for + */ + static removeFakeCursor(e = document.body) { + const t = u.find(e, ".codex-editor__fake-cursor"); + t && t.remove(); + } + /** + * Removes fake background + */ + removeFakeBackground() { + this.isFakeBackgroundEnabled && (this.isFakeBackgroundEnabled = !1, document.execCommand(this.commandRemoveFormat)); + } + /** + * Sets fake background + */ + setFakeBackground() { + document.execCommand(this.commandBackground, !1, "#a8d6ff"), this.isFakeBackgroundEnabled = !0; + } + /** + * Save SelectionUtils's range + */ + save() { + this.savedSelectionRange = b.range; + } + /** + * Restore saved SelectionUtils's range + */ + restore() { + if (!this.savedSelectionRange) + return; + const e = window.getSelection(); + e.removeAllRanges(), e.addRange(this.savedSelectionRange); + } + /** + * Clears saved selection + */ + clearSaved() { + this.savedSelectionRange = null; + } + /** + * Collapse current selection + */ + collapseToEnd() { + const e = window.getSelection(), t = document.createRange(); + t.selectNodeContents(e.focusNode), t.collapse(!1), e.removeAllRanges(), e.addRange(t); + } + /** + * Looks ahead to find passed tag from current selection + * + * @param {string} tagName - tag to found + * @param {string} [className] - tag's class name + * @param {number} [searchDepth] - count of tags that can be included. For better performance. + * @returns {HTMLElement|null} + */ + findParentTag(e, t, o = 10) { + const i = window.getSelection(); + let s = null; + return !i || !i.anchorNode || !i.focusNode ? null : ([ + /** the Node in which the selection begins */ + i.anchorNode, + /** the Node in which the selection ends */ + i.focusNode + ].forEach((a) => { + let l = o; + for (; l > 0 && a.parentNode && !(a.tagName === e && (s = a, t && a.classList && !a.classList.contains(t) && (s = null), s)); ) + a = a.parentNode, l--; + }), s); + } + /** + * Expands selection range to the passed parent node + * + * @param {HTMLElement} element - element which contents should be selected + */ + expandToTag(e) { + const t = window.getSelection(); + t.removeAllRanges(); + const o = document.createRange(); + o.selectNodeContents(e), t.addRange(o); + } +} +function pi(n, e) { + const { type: t, target: o, addedNodes: i, removedNodes: s } = n; + return n.type === "attributes" && n.attributeName === "data-empty" ? !1 : !!(e.contains(o) || t === "childList" && (Array.from(i).some((l) => l === e) || Array.from(s).some((l) => l === e))); +} +const ft = "redactor dom changed", $o = "block changed", zo = "fake cursor is about to be toggled", Uo = "fake cursor have been set", Te = "editor mobile layout toggled"; +function gt(n, e) { + if (!n.conversionConfig) + return !1; + const t = n.conversionConfig[e]; + return A(t) || te(t); +} +function He(n, e) { + return gt(n.tool, e); +} +function Wo(n, e) { + return Object.entries(n).some(([t, o]) => e[t] && ri(e[t], o)); +} +async function Yo(n, e) { + const o = (await n.save()).data, i = e.find((s) => s.name === n.name); + return i !== void 0 && !gt(i, "export") ? [] : e.reduce((s, r) => { + if (!gt(r, "import") || r.toolbox === void 0) + return s; + const a = r.toolbox.filter((l) => { + if (V(l) || l.icon === void 0) + return !1; + if (l.data !== void 0) { + if (Wo(l.data, o)) + return !1; + } else if (r.name === n.name) + return !1; + return !0; + }); + return s.push({ + ...r, + toolbox: a + }), s; + }, []); +} +function xo(n, e) { + return n.mergeable ? n.name === e.name ? !0 : He(e, "export") && He(n, "import") : !1; +} +function fi(n, e) { + const t = e == null ? void 0 : e.export; + return A(t) ? t(n) : te(t) ? n[t] : (t !== void 0 && S("Conversion «export» property must be a string or function. String means key of saved data object to export. Function should export processed string to export."), ""); +} +function Bo(n, e, t) { + const o = e == null ? void 0 : e.import; + return A(o) ? o(n, t) : te(o) ? { + [o]: n + } : (o !== void 0 && S("Conversion «import» property must be a string or function. String means key of tool data to import. Function accepts a imported string and return composed tool data."), {}); +} +var _ = /* @__PURE__ */ ((n) => (n.Default = "default", n.Separator = "separator", n.Html = "html", n))(_ || {}), ee = /* @__PURE__ */ ((n) => (n.APPEND_CALLBACK = "appendCallback", n.RENDERED = "rendered", n.MOVED = "moved", n.UPDATED = "updated", n.REMOVED = "removed", n.ON_PASTE = "onPaste", n))(ee || {}); +class R extends Oe { + /** + * @param options - block constructor options + * @param [options.id] - block's id. Will be generated if omitted. + * @param options.data - Tool's initial data + * @param options.tool — block's tool + * @param options.api - Editor API module for pass it to the Block Tunes + * @param options.readOnly - Read-Only flag + * @param [eventBus] - Editor common event bus. Allows to subscribe on some Editor events. Could be omitted when "virtual" Block is created. See BlocksAPI@composeBlockData. + */ + constructor({ + id: e = ni(), + data: t, + tool: o, + readOnly: i, + tunesData: s + }, r) { + super(), this.cachedInputs = [], this.toolRenderedElement = null, this.tunesInstances = /* @__PURE__ */ new Map(), this.defaultTunesInstances = /* @__PURE__ */ new Map(), this.unavailableTunesData = {}, this.inputIndex = 0, this.editorEventBus = null, this.handleFocus = () => { + this.dropInputsCache(), this.updateCurrentInput(); + }, this.didMutated = (a = void 0) => { + const l = a === void 0, c = a instanceof InputEvent; + !l && !c && this.detectToolRootChange(a); + let d; + l || c ? d = !0 : d = !(a.length > 0 && a.every((p) => { + const { addedNodes: g, removedNodes: f, target: v } = p; + return [ + ...Array.from(g), + ...Array.from(f), + v + ].some((T) => (u.isElement(T) || (T = T.parentElement), T && T.closest('[data-mutation-free="true"]') !== null)); + })), d && (this.dropInputsCache(), this.updateCurrentInput(), this.toggleInputsEmptyMark(), this.call( + "updated" + /* UPDATED */ + ), this.emit("didMutated", this)); + }, this.name = o.name, this.id = e, this.settings = o.settings, this.config = o.settings.config || {}, this.editorEventBus = r || null, this.blockAPI = new J(this), this.tool = o, this.toolInstance = o.create(t, this.blockAPI, i), this.tunes = o.tunes, this.composeTunes(s), this.holder = this.compose(), window.requestIdleCallback(() => { + this.watchBlockMutations(), this.addInputEvents(), this.toggleInputsEmptyMark(); + }); + } + /** + * CSS classes for the Block + * + * @returns {{wrapper: string, content: string}} + */ + static get CSS() { + return { + wrapper: "ce-block", + wrapperStretched: "ce-block--stretched", + content: "ce-block__content", + selected: "ce-block--selected", + dropTarget: "ce-block--drop-target" + }; + } + /** + * Find and return all editable elements (contenteditable and native inputs) in the Tool HTML + */ + get inputs() { + if (this.cachedInputs.length !== 0) + return this.cachedInputs; + const e = u.findAllInputs(this.holder); + return this.inputIndex > e.length - 1 && (this.inputIndex = e.length - 1), this.cachedInputs = e, e; + } + /** + * Return current Tool`s input + * If Block doesn't contain inputs, return undefined + */ + get currentInput() { + return this.inputs[this.inputIndex]; + } + /** + * Set input index to the passed element + * + * @param element - HTML Element to set as current input + */ + set currentInput(e) { + const t = this.inputs.findIndex((o) => o === e || o.contains(e)); + t !== -1 && (this.inputIndex = t); + } + /** + * Return first Tool`s input + * If Block doesn't contain inputs, return undefined + */ + get firstInput() { + return this.inputs[0]; + } + /** + * Return first Tool`s input + * If Block doesn't contain inputs, return undefined + */ + get lastInput() { + const e = this.inputs; + return e[e.length - 1]; + } + /** + * Return next Tool`s input or undefined if it doesn't exist + * If Block doesn't contain inputs, return undefined + */ + get nextInput() { + return this.inputs[this.inputIndex + 1]; + } + /** + * Return previous Tool`s input or undefined if it doesn't exist + * If Block doesn't contain inputs, return undefined + */ + get previousInput() { + return this.inputs[this.inputIndex - 1]; + } + /** + * Get Block's JSON data + * + * @returns {object} + */ + get data() { + return this.save().then((e) => e && !V(e.data) ? e.data : {}); + } + /** + * Returns tool's sanitizer config + * + * @returns {object} + */ + get sanitize() { + return this.tool.sanitizeConfig; + } + /** + * is block mergeable + * We plugin have merge function then we call it mergeable + * + * @returns {boolean} + */ + get mergeable() { + return A(this.toolInstance.merge); + } + /** + * If Block contains inputs, it is focusable + */ + get focusable() { + return this.inputs.length !== 0; + } + /** + * Check block for emptiness + * + * @returns {boolean} + */ + get isEmpty() { + const e = u.isEmpty(this.pluginsContent, "/"), t = !this.hasMedia; + return e && t; + } + /** + * Check if block has a media content such as images, iframe and other + * + * @returns {boolean} + */ + get hasMedia() { + const e = [ + "img", + "iframe", + "video", + "audio", + "source", + "input", + "textarea", + "twitterwidget" + ]; + return !!this.holder.querySelector(e.join(",")); + } + /** + * Set selected state + * We don't need to mark Block as Selected when it is empty + * + * @param {boolean} state - 'true' to select, 'false' to remove selection + */ + set selected(e) { + var i, s; + this.holder.classList.toggle(R.CSS.selected, e); + const t = e === !0 && b.isRangeInsideContainer(this.holder), o = e === !1 && b.isFakeCursorInsideContainer(this.holder); + (t || o) && ((i = this.editorEventBus) == null || i.emit(zo, { state: e }), t ? b.addFakeCursor() : b.removeFakeCursor(this.holder), (s = this.editorEventBus) == null || s.emit(Uo, { state: e })); + } + /** + * Returns True if it is Selected + * + * @returns {boolean} + */ + get selected() { + return this.holder.classList.contains(R.CSS.selected); + } + /** + * Set stretched state + * + * @param {boolean} state - 'true' to enable, 'false' to disable stretched state + */ + set stretched(e) { + this.holder.classList.toggle(R.CSS.wrapperStretched, e); + } + /** + * Return Block's stretched state + * + * @returns {boolean} + */ + get stretched() { + return this.holder.classList.contains(R.CSS.wrapperStretched); + } + /** + * Toggle drop target state + * + * @param {boolean} state - 'true' if block is drop target, false otherwise + */ + set dropTarget(e) { + this.holder.classList.toggle(R.CSS.dropTarget, e); + } + /** + * Returns Plugins content + * + * @returns {HTMLElement} + */ + get pluginsContent() { + return this.toolRenderedElement; + } + /** + * Calls Tool's method + * + * Method checks tool property {MethodName}. Fires method with passes params If it is instance of Function + * + * @param {string} methodName - method to call + * @param {object} params - method argument + */ + call(e, t) { + if (A(this.toolInstance[e])) { + e === "appendCallback" && S( + "`appendCallback` hook is deprecated and will be removed in the next major release. Use `rendered` hook instead", + "warn" + ); + try { + this.toolInstance[e].call(this.toolInstance, t); + } catch (o) { + S(`Error during '${e}' call: ${o.message}`, "error"); + } + } + } + /** + * Call plugins merge method + * + * @param {BlockToolData} data - data to merge + */ + async mergeWith(e) { + await this.toolInstance.merge(e); + } + /** + * Extracts data from Block + * Groups Tool's save processing time + * + * @returns {object} + */ + async save() { + const e = await this.toolInstance.save(this.pluginsContent), t = this.unavailableTunesData; + [ + ...this.tunesInstances.entries(), + ...this.defaultTunesInstances.entries() + ].forEach(([s, r]) => { + if (A(r.save)) + try { + t[s] = r.save(); + } catch (a) { + S(`Tune ${r.constructor.name} save method throws an Error %o`, "warn", a); + } + }); + const o = window.performance.now(); + let i; + return Promise.resolve(e).then((s) => (i = window.performance.now(), { + id: this.id, + tool: this.name, + data: s, + tunes: t, + time: i - o + })).catch((s) => { + S(`Saving process for ${this.name} tool failed due to the ${s}`, "log", "red"); + }); + } + /** + * Uses Tool's validation method to check the correctness of output data + * Tool's validation method is optional + * + * @description Method returns true|false whether data passed the validation or not + * @param {BlockToolData} data - data to validate + * @returns {Promise} valid + */ + async validate(e) { + let t = !0; + return this.toolInstance.validate instanceof Function && (t = await this.toolInstance.validate(e)), t; + } + /** + * Returns data to render in Block Tunes menu. + * Splits block tunes into 2 groups: block specific tunes and common tunes + */ + getTunes() { + const e = [], t = [], o = typeof this.toolInstance.renderSettings == "function" ? this.toolInstance.renderSettings() : []; + return u.isElement(o) ? e.push({ + type: _.Html, + element: o + }) : Array.isArray(o) ? e.push(...o) : e.push(o), [ + ...this.tunesInstances.values(), + ...this.defaultTunesInstances.values() + ].map((s) => s.render()).forEach((s) => { + u.isElement(s) ? t.push({ + type: _.Html, + element: s + }) : Array.isArray(s) ? t.push(...s) : t.push(s); + }), { + toolTunes: e, + commonTunes: t + }; + } + /** + * Update current input index with selection anchor node + */ + updateCurrentInput() { + this.currentInput = u.isNativeInput(document.activeElement) || !b.anchorNode ? document.activeElement : b.anchorNode; + } + /** + * Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback + * Can be useful for block changes invisible for editor core. + */ + dispatchChange() { + this.didMutated(); + } + /** + * Call Tool instance destroy method + */ + destroy() { + this.unwatchBlockMutations(), this.removeInputEvents(), super.destroy(), A(this.toolInstance.destroy) && this.toolInstance.destroy(); + } + /** + * Tool could specify several entries to be displayed at the Toolbox (for example, "Heading 1", "Heading 2", "Heading 3") + * This method returns the entry that is related to the Block (depended on the Block data) + */ + async getActiveToolboxEntry() { + const e = this.tool.toolbox; + if (e.length === 1) + return Promise.resolve(this.tool.toolbox[0]); + const t = await this.data, o = e; + return o == null ? void 0 : o.find((i) => Wo(i.data, t)); + } + /** + * Exports Block data as string using conversion config + */ + async exportDataAsString() { + const e = await this.data; + return fi(e, this.tool.conversionConfig); + } + /** + * Make default Block wrappers and put Tool`s content there + * + * @returns {HTMLDivElement} + */ + compose() { + const e = u.make("div", R.CSS.wrapper), t = u.make("div", R.CSS.content), o = this.toolInstance.render(); + e.dataset.id = this.id, this.toolRenderedElement = o, t.appendChild(this.toolRenderedElement); + let i = t; + return [...this.tunesInstances.values(), ...this.defaultTunesInstances.values()].forEach((s) => { + if (A(s.wrap)) + try { + i = s.wrap(i); + } catch (r) { + S(`Tune ${s.constructor.name} wrap method throws an Error %o`, "warn", r); + } + }), e.appendChild(i), e; + } + /** + * Instantiate Block Tunes + * + * @param tunesData - current Block tunes data + * @private + */ + composeTunes(e) { + Array.from(this.tunes.values()).forEach((t) => { + (t.isInternal ? this.defaultTunesInstances : this.tunesInstances).set(t.name, t.create(e[t.name], this.blockAPI)); + }), Object.entries(e).forEach(([t, o]) => { + this.tunesInstances.has(t) || (this.unavailableTunesData[t] = o); + }); + } + /** + * Adds focus event listeners to all inputs and contenteditable + */ + addInputEvents() { + this.inputs.forEach((e) => { + e.addEventListener("focus", this.handleFocus), u.isNativeInput(e) && e.addEventListener("input", this.didMutated); + }); + } + /** + * removes focus event listeners from all inputs and contenteditable + */ + removeInputEvents() { + this.inputs.forEach((e) => { + e.removeEventListener("focus", this.handleFocus), u.isNativeInput(e) && e.removeEventListener("input", this.didMutated); + }); + } + /** + * Listen common editor Dom Changed event and detect mutations related to the Block + */ + watchBlockMutations() { + var e; + this.redactorDomChangedCallback = (t) => { + const { mutations: o } = t; + o.some((s) => pi(s, this.toolRenderedElement)) && this.didMutated(o); + }, (e = this.editorEventBus) == null || e.on(ft, this.redactorDomChangedCallback); + } + /** + * Remove redactor dom change event listener + */ + unwatchBlockMutations() { + var e; + (e = this.editorEventBus) == null || e.off(ft, this.redactorDomChangedCallback); + } + /** + * Sometimes Tool can replace own main element, for example H2 -> H4 or UL -> OL + * We need to detect such changes and update a link to tools main element with the new one + * + * @param mutations - records of block content mutations + */ + detectToolRootChange(e) { + e.forEach((t) => { + if (Array.from(t.removedNodes).includes(this.toolRenderedElement)) { + const i = t.addedNodes[t.addedNodes.length - 1]; + this.toolRenderedElement = i; + } + }); + } + /** + * Clears inputs cached value + */ + dropInputsCache() { + this.cachedInputs = []; + } + /** + * Mark inputs with 'data-empty' attribute with the empty state + */ + toggleInputsEmptyMark() { + this.inputs.forEach(Do); + } +} +class gi extends E { + constructor() { + super(...arguments), this.insert = (e = this.config.defaultBlock, t = {}, o = {}, i, s, r, a) => { + const l = this.Editor.BlockManager.insert({ + id: a, + tool: e, + data: t, + index: i, + needToFocus: s, + replace: r + }); + return new J(l); + }, this.composeBlockData = async (e) => { + const t = this.Editor.Tools.blockTools.get(e); + return new R({ + tool: t, + api: this.Editor.API, + readOnly: !0, + data: {}, + tunesData: {} + }).data; + }, this.update = async (e, t, o) => { + const { BlockManager: i } = this.Editor, s = i.getBlockById(e); + if (s === void 0) + throw new Error(`Block with id "${e}" not found`); + const r = await i.update(s, t, o); + return new J(r); + }, this.convert = async (e, t, o) => { + var h, p; + const { BlockManager: i, Tools: s } = this.Editor, r = i.getBlockById(e); + if (!r) + throw new Error(`Block with id "${e}" not found`); + const a = s.blockTools.get(r.name), l = s.blockTools.get(t); + if (!l) + throw new Error(`Block Tool with type "${t}" not found`); + const c = ((h = a == null ? void 0 : a.conversionConfig) == null ? void 0 : h.export) !== void 0, d = ((p = l.conversionConfig) == null ? void 0 : p.import) !== void 0; + if (c && d) { + const g = await i.convert(r, t, o); + return new J(g); + } else { + const g = [ + c ? !1 : je(r.name), + d ? !1 : je(t) + ].filter(Boolean).join(" and "); + throw new Error(`Conversion from "${r.name}" to "${t}" is not possible. ${g} tool(s) should provide a "conversionConfig"`); + } + }, this.insertMany = (e, t = this.Editor.BlockManager.blocks.length - 1) => { + this.validateIndex(t); + const o = e.map(({ id: i, type: s, data: r }) => this.Editor.BlockManager.composeBlock({ + id: i, + tool: s || this.config.defaultBlock, + data: r + })); + return this.Editor.BlockManager.insertMany(o, t), o.map((i) => new J(i)); + }; + } + /** + * Available methods + * + * @returns {Blocks} + */ + get methods() { + return { + clear: () => this.clear(), + render: (e) => this.render(e), + renderFromHTML: (e) => this.renderFromHTML(e), + delete: (e) => this.delete(e), + swap: (e, t) => this.swap(e, t), + move: (e, t) => this.move(e, t), + getBlockByIndex: (e) => this.getBlockByIndex(e), + getById: (e) => this.getById(e), + getCurrentBlockIndex: () => this.getCurrentBlockIndex(), + getBlockIndex: (e) => this.getBlockIndex(e), + getBlocksCount: () => this.getBlocksCount(), + getBlockByElement: (e) => this.getBlockByElement(e), + stretchBlock: (e, t = !0) => this.stretchBlock(e, t), + insertNewBlock: () => this.insertNewBlock(), + insert: this.insert, + insertMany: this.insertMany, + update: this.update, + composeBlockData: this.composeBlockData, + convert: this.convert + }; + } + /** + * Returns Blocks count + * + * @returns {number} + */ + getBlocksCount() { + return this.Editor.BlockManager.blocks.length; + } + /** + * Returns current block index + * + * @returns {number} + */ + getCurrentBlockIndex() { + return this.Editor.BlockManager.currentBlockIndex; + } + /** + * Returns the index of Block by id; + * + * @param id - block id + */ + getBlockIndex(e) { + const t = this.Editor.BlockManager.getBlockById(e); + if (!t) { + X("There is no block with id `" + e + "`", "warn"); + return; + } + return this.Editor.BlockManager.getBlockIndex(t); + } + /** + * Returns BlockAPI object by Block index + * + * @param {number} index - index to get + */ + getBlockByIndex(e) { + const t = this.Editor.BlockManager.getBlockByIndex(e); + if (t === void 0) { + X("There is no block at index `" + e + "`", "warn"); + return; + } + return new J(t); + } + /** + * Returns BlockAPI object by Block id + * + * @param id - id of block to get + */ + getById(e) { + const t = this.Editor.BlockManager.getBlockById(e); + return t === void 0 ? (X("There is no block with id `" + e + "`", "warn"), null) : new J(t); + } + /** + * Get Block API object by any child html element + * + * @param element - html element to get Block by + */ + getBlockByElement(e) { + const t = this.Editor.BlockManager.getBlock(e); + if (t === void 0) { + X("There is no block corresponding to element `" + e + "`", "warn"); + return; + } + return new J(t); + } + /** + * Call Block Manager method that swap Blocks + * + * @param {number} fromIndex - position of first Block + * @param {number} toIndex - position of second Block + * @deprecated — use 'move' instead + */ + swap(e, t) { + S( + "`blocks.swap()` method is deprecated and will be removed in the next major release. Use `block.move()` method instead", + "info" + ), this.Editor.BlockManager.swap(e, t); + } + /** + * Move block from one index to another + * + * @param {number} toIndex - index to move to + * @param {number} fromIndex - index to move from + */ + move(e, t) { + this.Editor.BlockManager.move(e, t); + } + /** + * Deletes Block + * + * @param {number} blockIndex - index of Block to delete + */ + delete(e = this.Editor.BlockManager.currentBlockIndex) { + try { + const t = this.Editor.BlockManager.getBlockByIndex(e); + this.Editor.BlockManager.removeBlock(t); + } catch (t) { + X(t, "warn"); + return; + } + this.Editor.BlockManager.blocks.length === 0 && this.Editor.BlockManager.insert(), this.Editor.BlockManager.currentBlock && this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock, this.Editor.Caret.positions.END), this.Editor.Toolbar.close(); + } + /** + * Clear Editor's area + */ + async clear() { + await this.Editor.BlockManager.clear(!0), this.Editor.InlineToolbar.close(); + } + /** + * Fills Editor with Blocks data + * + * @param {OutputData} data — Saved Editor data + */ + async render(e) { + if (e === void 0 || e.blocks === void 0) + throw new Error("Incorrect data passed to the render() method"); + this.Editor.ModificationsObserver.disable(), await this.Editor.BlockManager.clear(), await this.Editor.Renderer.render(e.blocks), this.Editor.ModificationsObserver.enable(); + } + /** + * Render passed HTML string + * + * @param {string} data - HTML string to render + * @returns {Promise} + */ + async renderFromHTML(e) { + return await this.Editor.BlockManager.clear(), this.Editor.Paste.processText(e, !0); + } + /** + * Stretch Block's content + * + * @param {number} index - index of Block to stretch + * @param {boolean} status - true to enable, false to disable + * @deprecated Use BlockAPI interface to stretch Blocks + */ + stretchBlock(e, t = !0) { + ht( + !0, + "blocks.stretchBlock()", + "BlockAPI" + ); + const o = this.Editor.BlockManager.getBlockByIndex(e); + o && (o.stretched = t); + } + /** + * Insert new Block + * After set caret to this Block + * + * @todo remove in 3.0.0 + * @deprecated with insert() method + */ + insertNewBlock() { + S("Method blocks.insertNewBlock() is deprecated and it will be removed in the next major release. Use blocks.insert() instead.", "warn"), this.insert(); + } + /** + * Validated block index and throws an error if it's invalid + * + * @param index - index to validate + */ + validateIndex(e) { + if (typeof e != "number") + throw new Error("Index should be a number"); + if (e < 0) + throw new Error("Index should be greater than or equal to 0"); + if (e === null) + throw new Error("Index should be greater than or equal to 0"); + } +} +function mi(n, e) { + return typeof n == "number" ? e.BlockManager.getBlockByIndex(n) : typeof n == "string" ? e.BlockManager.getBlockById(n) : e.BlockManager.getBlockById(n.id); +} +class bi extends E { + constructor() { + super(...arguments), this.setToFirstBlock = (e = this.Editor.Caret.positions.DEFAULT, t = 0) => this.Editor.BlockManager.firstBlock ? (this.Editor.Caret.setToBlock(this.Editor.BlockManager.firstBlock, e, t), !0) : !1, this.setToLastBlock = (e = this.Editor.Caret.positions.DEFAULT, t = 0) => this.Editor.BlockManager.lastBlock ? (this.Editor.Caret.setToBlock(this.Editor.BlockManager.lastBlock, e, t), !0) : !1, this.setToPreviousBlock = (e = this.Editor.Caret.positions.DEFAULT, t = 0) => this.Editor.BlockManager.previousBlock ? (this.Editor.Caret.setToBlock(this.Editor.BlockManager.previousBlock, e, t), !0) : !1, this.setToNextBlock = (e = this.Editor.Caret.positions.DEFAULT, t = 0) => this.Editor.BlockManager.nextBlock ? (this.Editor.Caret.setToBlock(this.Editor.BlockManager.nextBlock, e, t), !0) : !1, this.setToBlock = (e, t = this.Editor.Caret.positions.DEFAULT, o = 0) => { + const i = mi(e, this.Editor); + return i === void 0 ? !1 : (this.Editor.Caret.setToBlock(i, t, o), !0); + }, this.focus = (e = !1) => e ? this.setToLastBlock(this.Editor.Caret.positions.END) : this.setToFirstBlock(this.Editor.Caret.positions.START); + } + /** + * Available methods + * + * @returns {Caret} + */ + get methods() { + return { + setToFirstBlock: this.setToFirstBlock, + setToLastBlock: this.setToLastBlock, + setToPreviousBlock: this.setToPreviousBlock, + setToNextBlock: this.setToNextBlock, + setToBlock: this.setToBlock, + focus: this.focus + }; + } +} +class vi extends E { + /** + * Available methods + * + * @returns {Events} + */ + get methods() { + return { + emit: (e, t) => this.emit(e, t), + off: (e, t) => this.off(e, t), + on: (e, t) => this.on(e, t) + }; + } + /** + * Subscribe on Events + * + * @param {string} eventName - event name to subscribe + * @param {Function} callback - event handler + */ + on(e, t) { + this.eventsDispatcher.on(e, t); + } + /** + * Emit event with data + * + * @param {string} eventName - event to emit + * @param {object} data - event's data + */ + emit(e, t) { + this.eventsDispatcher.emit(e, t); + } + /** + * Unsubscribe from Event + * + * @param {string} eventName - event to unsubscribe + * @param {Function} callback - event handler + */ + off(e, t) { + this.eventsDispatcher.off(e, t); + } +} +class kt extends E { + /** + * Return namespace section for tool or block tune + * + * @param toolName - tool name + * @param isTune - is tool a block tune + */ + static getNamespace(e, t) { + return t ? `blockTunes.${e}` : `tools.${e}`; + } + /** + * Return I18n API methods with global dictionary access + */ + get methods() { + return { + t: () => { + X("I18n.t() method can be accessed only from Tools", "warn"); + } + }; + } + /** + * Return I18n API methods with tool namespaced dictionary + * + * @param toolName - tool name + * @param isTune - is tool a block tune + */ + getMethodsForTool(e, t) { + return Object.assign( + this.methods, + { + t: (o) => z.t(kt.getNamespace(e, t), o) + } + ); + } +} +class ki extends E { + /** + * Editor.js Core API modules + */ + get methods() { + return { + blocks: this.Editor.BlocksAPI.methods, + caret: this.Editor.CaretAPI.methods, + tools: this.Editor.ToolsAPI.methods, + events: this.Editor.EventsAPI.methods, + listeners: this.Editor.ListenersAPI.methods, + notifier: this.Editor.NotifierAPI.methods, + sanitizer: this.Editor.SanitizerAPI.methods, + saver: this.Editor.SaverAPI.methods, + selection: this.Editor.SelectionAPI.methods, + styles: this.Editor.StylesAPI.classes, + toolbar: this.Editor.ToolbarAPI.methods, + inlineToolbar: this.Editor.InlineToolbarAPI.methods, + tooltip: this.Editor.TooltipAPI.methods, + i18n: this.Editor.I18nAPI.methods, + readOnly: this.Editor.ReadOnlyAPI.methods, + ui: this.Editor.UiAPI.methods + }; + } + /** + * Returns Editor.js Core API methods for passed tool + * + * @param toolName - tool name + * @param isTune - is tool a block tune + */ + getMethodsForTool(e, t) { + return Object.assign( + this.methods, + { + i18n: this.Editor.I18nAPI.getMethodsForTool(e, t) + } + ); + } +} +class yi extends E { + /** + * Available methods + * + * @returns {InlineToolbar} + */ + get methods() { + return { + close: () => this.close(), + open: () => this.open() + }; + } + /** + * Open Inline Toolbar + */ + open() { + this.Editor.InlineToolbar.tryToShow(); + } + /** + * Close Inline Toolbar + */ + close() { + this.Editor.InlineToolbar.close(); + } +} +class wi extends E { + /** + * Available methods + * + * @returns {Listeners} + */ + get methods() { + return { + on: (e, t, o, i) => this.on(e, t, o, i), + off: (e, t, o, i) => this.off(e, t, o, i), + offById: (e) => this.offById(e) + }; + } + /** + * Ads a DOM event listener. Return it's id. + * + * @param {HTMLElement} element - Element to set handler to + * @param {string} eventType - event type + * @param {() => void} handler - event handler + * @param {boolean} useCapture - capture event or not + */ + on(e, t, o, i) { + return this.listeners.on(e, t, o, i); + } + /** + * Removes DOM listener from element + * + * @param {Element} element - Element to remove handler from + * @param eventType - event type + * @param handler - event handler + * @param {boolean} useCapture - capture event or not + */ + off(e, t, o, i) { + this.listeners.off(e, t, o, i); + } + /** + * Removes DOM listener by the listener id + * + * @param id - id of the listener to remove + */ + offById(e) { + this.listeners.offById(e); + } +} +var Ko = { exports: {} }; +(function(n, e) { + (function(t, o) { + n.exports = o(); + })(window, function() { + return function(t) { + var o = {}; + function i(s) { + if (o[s]) + return o[s].exports; + var r = o[s] = { i: s, l: !1, exports: {} }; + return t[s].call(r.exports, r, r.exports, i), r.l = !0, r.exports; + } + return i.m = t, i.c = o, i.d = function(s, r, a) { + i.o(s, r) || Object.defineProperty(s, r, { enumerable: !0, get: a }); + }, i.r = function(s) { + typeof Symbol < "u" && Symbol.toStringTag && Object.defineProperty(s, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(s, "__esModule", { value: !0 }); + }, i.t = function(s, r) { + if (1 & r && (s = i(s)), 8 & r || 4 & r && typeof s == "object" && s && s.__esModule) + return s; + var a = /* @__PURE__ */ Object.create(null); + if (i.r(a), Object.defineProperty(a, "default", { enumerable: !0, value: s }), 2 & r && typeof s != "string") + for (var l in s) + i.d(a, l, (function(c) { + return s[c]; + }).bind(null, l)); + return a; + }, i.n = function(s) { + var r = s && s.__esModule ? function() { + return s.default; + } : function() { + return s; + }; + return i.d(r, "a", r), r; + }, i.o = function(s, r) { + return Object.prototype.hasOwnProperty.call(s, r); + }, i.p = "/", i(i.s = 0); + }([function(t, o, i) { + i(1), /*! + * Codex JavaScript Notification module + * https://github.com/codex-team/js-notifier + */ + t.exports = function() { + var s = i(6), r = "cdx-notify--bounce-in", a = null; + return { show: function(l) { + if (l.message) { + (function() { + if (a) + return !0; + a = s.getWrapper(), document.body.appendChild(a); + })(); + var c = null, d = l.time || 8e3; + switch (l.type) { + case "confirm": + c = s.confirm(l); + break; + case "prompt": + c = s.prompt(l); + break; + default: + c = s.alert(l), window.setTimeout(function() { + c.remove(); + }, d); + } + a.appendChild(c), c.classList.add(r); + } + } }; + }(); + }, function(t, o, i) { + var s = i(2); + typeof s == "string" && (s = [[t.i, s, ""]]); + var r = { hmr: !0, transform: void 0, insertInto: void 0 }; + i(4)(s, r), s.locals && (t.exports = s.locals); + }, function(t, o, i) { + (t.exports = i(3)(!1)).push([t.i, `.cdx-notify--error{background:#fffbfb!important}.cdx-notify--error::before{background:#fb5d5d!important}.cdx-notify__input{max-width:130px;padding:5px 10px;background:#f7f7f7;border:0;border-radius:3px;font-size:13px;color:#656b7c;outline:0}.cdx-notify__input:-ms-input-placeholder{color:#656b7c}.cdx-notify__input::placeholder{color:#656b7c}.cdx-notify__input:focus:-ms-input-placeholder{color:rgba(101,107,124,.3)}.cdx-notify__input:focus::placeholder{color:rgba(101,107,124,.3)}.cdx-notify__button{border:none;border-radius:3px;font-size:13px;padding:5px 10px;cursor:pointer}.cdx-notify__button:last-child{margin-left:10px}.cdx-notify__button--cancel{background:#f2f5f7;box-shadow:0 2px 1px 0 rgba(16,19,29,0);color:#656b7c}.cdx-notify__button--cancel:hover{background:#eee}.cdx-notify__button--confirm{background:#34c992;box-shadow:0 1px 1px 0 rgba(18,49,35,.05);color:#fff}.cdx-notify__button--confirm:hover{background:#33b082}.cdx-notify__btns-wrapper{display:-ms-flexbox;display:flex;-ms-flex-flow:row nowrap;flex-flow:row nowrap;margin-top:5px}.cdx-notify__cross{position:absolute;top:5px;right:5px;width:10px;height:10px;padding:5px;opacity:.54;cursor:pointer}.cdx-notify__cross::after,.cdx-notify__cross::before{content:'';position:absolute;left:9px;top:5px;height:12px;width:2px;background:#575d67}.cdx-notify__cross::before{transform:rotate(-45deg)}.cdx-notify__cross::after{transform:rotate(45deg)}.cdx-notify__cross:hover{opacity:1}.cdx-notifies{position:fixed;z-index:2;bottom:20px;left:20px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif}.cdx-notify{position:relative;width:220px;margin-top:15px;padding:13px 16px;background:#fff;box-shadow:0 11px 17px 0 rgba(23,32,61,.13);border-radius:5px;font-size:14px;line-height:1.4em;word-wrap:break-word}.cdx-notify::before{content:'';position:absolute;display:block;top:0;left:0;width:3px;height:calc(100% - 6px);margin:3px;border-radius:5px;background:0 0}@keyframes bounceIn{0%{opacity:0;transform:scale(.3)}50%{opacity:1;transform:scale(1.05)}70%{transform:scale(.9)}100%{transform:scale(1)}}.cdx-notify--bounce-in{animation-name:bounceIn;animation-duration:.6s;animation-iteration-count:1}.cdx-notify--success{background:#fafffe!important}.cdx-notify--success::before{background:#41ffb1!important}`, ""]); + }, function(t, o) { + t.exports = function(i) { + var s = []; + return s.toString = function() { + return this.map(function(r) { + var a = function(l, c) { + var d = l[1] || "", h = l[3]; + if (!h) + return d; + if (c && typeof btoa == "function") { + var p = (f = h, "/*# sourceMappingURL=data:application/json;charset=utf-8;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(f)))) + " */"), g = h.sources.map(function(v) { + return "/*# sourceURL=" + h.sourceRoot + v + " */"; + }); + return [d].concat(g).concat([p]).join(` +`); + } + var f; + return [d].join(` +`); + }(r, i); + return r[2] ? "@media " + r[2] + "{" + a + "}" : a; + }).join(""); + }, s.i = function(r, a) { + typeof r == "string" && (r = [[null, r, ""]]); + for (var l = {}, c = 0; c < this.length; c++) { + var d = this[c][0]; + typeof d == "number" && (l[d] = !0); + } + for (c = 0; c < r.length; c++) { + var h = r[c]; + typeof h[0] == "number" && l[h[0]] || (a && !h[2] ? h[2] = a : a && (h[2] = "(" + h[2] + ") and (" + a + ")"), s.push(h)); + } + }, s; + }; + }, function(t, o, i) { + var s, r, a = {}, l = (s = function() { + return window && document && document.all && !window.atob; + }, function() { + return r === void 0 && (r = s.apply(this, arguments)), r; + }), c = function(k) { + var m = {}; + return function(w) { + if (typeof w == "function") + return w(); + if (m[w] === void 0) { + var x = (function(I) { + return document.querySelector(I); + }).call(this, w); + if (window.HTMLIFrameElement && x instanceof window.HTMLIFrameElement) + try { + x = x.contentDocument.head; + } catch { + x = null; + } + m[w] = x; + } + return m[w]; + }; + }(), d = null, h = 0, p = [], g = i(5); + function f(k, m) { + for (var w = 0; w < k.length; w++) { + var x = k[w], I = a[x.id]; + if (I) { + I.refs++; + for (var C = 0; C < I.parts.length; C++) + I.parts[C](x.parts[C]); + for (; C < x.parts.length; C++) + I.parts.push(F(x.parts[C], m)); + } else { + var N = []; + for (C = 0; C < x.parts.length; C++) + N.push(F(x.parts[C], m)); + a[x.id] = { id: x.id, refs: 1, parts: N }; + } + } + } + function v(k, m) { + for (var w = [], x = {}, I = 0; I < k.length; I++) { + var C = k[I], N = m.base ? C[0] + m.base : C[0], B = { css: C[1], media: C[2], sourceMap: C[3] }; + x[N] ? x[N].parts.push(B) : w.push(x[N] = { id: N, parts: [B] }); + } + return w; + } + function O(k, m) { + var w = c(k.insertInto); + if (!w) + throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid."); + var x = p[p.length - 1]; + if (k.insertAt === "top") + x ? x.nextSibling ? w.insertBefore(m, x.nextSibling) : w.appendChild(m) : w.insertBefore(m, w.firstChild), p.push(m); + else if (k.insertAt === "bottom") + w.appendChild(m); + else { + if (typeof k.insertAt != "object" || !k.insertAt.before) + throw new Error(`[Style Loader] + + Invalid value for parameter 'insertAt' ('options.insertAt') found. + Must be 'top', 'bottom', or Object. + (https://github.com/webpack-contrib/style-loader#insertat) +`); + var I = c(k.insertInto + " " + k.insertAt.before); + w.insertBefore(m, I); + } + } + function T(k) { + if (k.parentNode === null) + return !1; + k.parentNode.removeChild(k); + var m = p.indexOf(k); + m >= 0 && p.splice(m, 1); + } + function M(k) { + var m = document.createElement("style"); + return k.attrs.type === void 0 && (k.attrs.type = "text/css"), q(m, k.attrs), O(k, m), m; + } + function q(k, m) { + Object.keys(m).forEach(function(w) { + k.setAttribute(w, m[w]); + }); + } + function F(k, m) { + var w, x, I, C; + if (m.transform && k.css) { + if (!(C = m.transform(k.css))) + return function() { + }; + k.css = C; + } + if (m.singleton) { + var N = h++; + w = d || (d = M(m)), x = ie.bind(null, w, N, !1), I = ie.bind(null, w, N, !0); + } else + k.sourceMap && typeof URL == "function" && typeof URL.createObjectURL == "function" && typeof URL.revokeObjectURL == "function" && typeof Blob == "function" && typeof btoa == "function" ? (w = function(B) { + var W = document.createElement("link"); + return B.attrs.type === void 0 && (B.attrs.type = "text/css"), B.attrs.rel = "stylesheet", q(W, B.attrs), O(B, W), W; + }(m), x = (function(B, W, ve) { + var se = ve.css, tt = ve.sourceMap, Yn = W.convertToAbsoluteUrls === void 0 && tt; + (W.convertToAbsoluteUrls || Yn) && (se = g(se)), tt && (se += ` +/*# sourceMappingURL=data:application/json;base64,` + btoa(unescape(encodeURIComponent(JSON.stringify(tt)))) + " */"); + var Kn = new Blob([se], { type: "text/css" }), ko = B.href; + B.href = URL.createObjectURL(Kn), ko && URL.revokeObjectURL(ko); + }).bind(null, w, m), I = function() { + T(w), w.href && URL.revokeObjectURL(w.href); + }) : (w = M(m), x = (function(B, W) { + var ve = W.css, se = W.media; + if (se && B.setAttribute("media", se), B.styleSheet) + B.styleSheet.cssText = ve; + else { + for (; B.firstChild; ) + B.removeChild(B.firstChild); + B.appendChild(document.createTextNode(ve)); + } + }).bind(null, w), I = function() { + T(w); + }); + return x(k), function(B) { + if (B) { + if (B.css === k.css && B.media === k.media && B.sourceMap === k.sourceMap) + return; + x(k = B); + } else + I(); + }; + } + t.exports = function(k, m) { + if (typeof DEBUG < "u" && DEBUG && typeof document != "object") + throw new Error("The style-loader cannot be used in a non-browser environment"); + (m = m || {}).attrs = typeof m.attrs == "object" ? m.attrs : {}, m.singleton || typeof m.singleton == "boolean" || (m.singleton = l()), m.insertInto || (m.insertInto = "head"), m.insertAt || (m.insertAt = "bottom"); + var w = v(k, m); + return f(w, m), function(x) { + for (var I = [], C = 0; C < w.length; C++) { + var N = w[C]; + (B = a[N.id]).refs--, I.push(B); + } + for (x && f(v(x, m), m), C = 0; C < I.length; C++) { + var B; + if ((B = I[C]).refs === 0) { + for (var W = 0; W < B.parts.length; W++) + B.parts[W](); + delete a[B.id]; + } + } + }; + }; + var H, Q = (H = [], function(k, m) { + return H[k] = m, H.filter(Boolean).join(` +`); + }); + function ie(k, m, w, x) { + var I = w ? "" : x.css; + if (k.styleSheet) + k.styleSheet.cssText = Q(m, I); + else { + var C = document.createTextNode(I), N = k.childNodes; + N[m] && k.removeChild(N[m]), N.length ? k.insertBefore(C, N[m]) : k.appendChild(C); + } + } + }, function(t, o) { + t.exports = function(i) { + var s = typeof window < "u" && window.location; + if (!s) + throw new Error("fixUrls requires window.location"); + if (!i || typeof i != "string") + return i; + var r = s.protocol + "//" + s.host, a = r + s.pathname.replace(/\/[^\/]*$/, "/"); + return i.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(l, c) { + var d, h = c.trim().replace(/^"(.*)"$/, function(p, g) { + return g; + }).replace(/^'(.*)'$/, function(p, g) { + return g; + }); + return /^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(h) ? l : (d = h.indexOf("//") === 0 ? h : h.indexOf("/") === 0 ? r + h : a + h.replace(/^\.\//, ""), "url(" + JSON.stringify(d) + ")"); + }); + }; + }, function(t, o, i) { + var s, r, a, l, c, d, h, p, g; + t.exports = (s = "cdx-notifies", r = "cdx-notify", a = "cdx-notify__cross", l = "cdx-notify__button--confirm", c = "cdx-notify__button--cancel", d = "cdx-notify__input", h = "cdx-notify__button", p = "cdx-notify__btns-wrapper", { alert: g = function(f) { + var v = document.createElement("DIV"), O = document.createElement("DIV"), T = f.message, M = f.style; + return v.classList.add(r), M && v.classList.add(r + "--" + M), v.innerHTML = T, O.classList.add(a), O.addEventListener("click", v.remove.bind(v)), v.appendChild(O), v; + }, confirm: function(f) { + var v = g(f), O = document.createElement("div"), T = document.createElement("button"), M = document.createElement("button"), q = v.querySelector("." + a), F = f.cancelHandler, H = f.okHandler; + return O.classList.add(p), T.innerHTML = f.okText || "Confirm", M.innerHTML = f.cancelText || "Cancel", T.classList.add(h), M.classList.add(h), T.classList.add(l), M.classList.add(c), F && typeof F == "function" && (M.addEventListener("click", F), q.addEventListener("click", F)), H && typeof H == "function" && T.addEventListener("click", H), T.addEventListener("click", v.remove.bind(v)), M.addEventListener("click", v.remove.bind(v)), O.appendChild(T), O.appendChild(M), v.appendChild(O), v; + }, prompt: function(f) { + var v = g(f), O = document.createElement("div"), T = document.createElement("button"), M = document.createElement("input"), q = v.querySelector("." + a), F = f.cancelHandler, H = f.okHandler; + return O.classList.add(p), T.innerHTML = f.okText || "Ok", T.classList.add(h), T.classList.add(l), M.classList.add(d), f.placeholder && M.setAttribute("placeholder", f.placeholder), f.default && (M.value = f.default), f.inputType && (M.type = f.inputType), F && typeof F == "function" && q.addEventListener("click", F), H && typeof H == "function" && T.addEventListener("click", function() { + H(M.value); + }), T.addEventListener("click", v.remove.bind(v)), O.appendChild(M), O.appendChild(T), v.appendChild(O), v; + }, getWrapper: function() { + var f = document.createElement("DIV"); + return f.classList.add(s), f; + } }); + }]); + }); +})(Ko); +var Ei = Ko.exports; +const xi = /* @__PURE__ */ Ke(Ei); +class Bi { + /** + * Show web notification + * + * @param {NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions} options - notification options + */ + show(e) { + xi.show(e); + } +} +class Ci extends E { + /** + * @param moduleConfiguration - Module Configuration + * @param moduleConfiguration.config - Editor's config + * @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher + */ + constructor({ config: e, eventsDispatcher: t }) { + super({ + config: e, + eventsDispatcher: t + }), this.notifier = new Bi(); + } + /** + * Available methods + */ + get methods() { + return { + show: (e) => this.show(e) + }; + } + /** + * Show notification + * + * @param {NotifierOptions} options - message option + */ + show(e) { + return this.notifier.show(e); + } +} +class Ti extends E { + /** + * Available methods + */ + get methods() { + const e = () => this.isEnabled; + return { + toggle: (t) => this.toggle(t), + get isEnabled() { + return e(); + } + }; + } + /** + * Set or toggle read-only state + * + * @param {boolean|undefined} state - set or toggle state + * @returns {boolean} current value + */ + toggle(e) { + return this.Editor.ReadOnly.toggle(e); + } + /** + * Returns current read-only state + */ + get isEnabled() { + return this.Editor.ReadOnly.isEnabled; + } +} +var Xo = { exports: {} }; +(function(n, e) { + (function(t, o) { + n.exports = o(); + })(Ce, function() { + function t(h) { + var p = h.tags, g = Object.keys(p), f = g.map(function(v) { + return typeof p[v]; + }).every(function(v) { + return v === "object" || v === "boolean" || v === "function"; + }); + if (!f) + throw new Error("The configuration was invalid"); + this.config = h; + } + var o = ["P", "LI", "TD", "TH", "DIV", "H1", "H2", "H3", "H4", "H5", "H6", "PRE"]; + function i(h) { + return o.indexOf(h.nodeName) !== -1; + } + var s = ["A", "B", "STRONG", "I", "EM", "SUB", "SUP", "U", "STRIKE"]; + function r(h) { + return s.indexOf(h.nodeName) !== -1; + } + t.prototype.clean = function(h) { + const p = document.implementation.createHTMLDocument(), g = p.createElement("div"); + return g.innerHTML = h, this._sanitize(p, g), g.innerHTML; + }, t.prototype._sanitize = function(h, p) { + var g = a(h, p), f = g.firstChild(); + if (f) + do { + if (f.nodeType === Node.TEXT_NODE) + if (f.data.trim() === "" && (f.previousElementSibling && i(f.previousElementSibling) || f.nextElementSibling && i(f.nextElementSibling))) { + p.removeChild(f), this._sanitize(h, p); + break; + } else + continue; + if (f.nodeType === Node.COMMENT_NODE) { + p.removeChild(f), this._sanitize(h, p); + break; + } + var v = r(f), O; + v && (O = Array.prototype.some.call(f.childNodes, i)); + var T = !!p.parentNode, M = i(p) && i(f) && T, q = f.nodeName.toLowerCase(), F = l(this.config, q, f), H = v && O; + if (H || c(f, F) || !this.config.keepNestedBlockElements && M) { + if (!(f.nodeName === "SCRIPT" || f.nodeName === "STYLE")) + for (; f.childNodes.length > 0; ) + p.insertBefore(f.childNodes[0], f); + p.removeChild(f), this._sanitize(h, p); + break; + } + for (var Q = 0; Q < f.attributes.length; Q += 1) { + var ie = f.attributes[Q]; + d(ie, F, f) && (f.removeAttribute(ie.name), Q = Q - 1); + } + this._sanitize(h, f); + } while (f = g.nextSibling()); + }; + function a(h, p) { + return h.createTreeWalker( + p, + NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT, + null, + !1 + ); + } + function l(h, p, g) { + return typeof h.tags[p] == "function" ? h.tags[p](g) : h.tags[p]; + } + function c(h, p) { + return typeof p > "u" ? !0 : typeof p == "boolean" ? !p : !1; + } + function d(h, p, g) { + var f = h.name.toLowerCase(); + return p === !0 ? !1 : typeof p[f] == "function" ? !p[f](h.value, g) : typeof p[f] > "u" || p[f] === !1 ? !0 : typeof p[f] == "string" ? p[f] !== h.value : !1; + } + return t; + }); +})(Xo); +var Si = Xo.exports; +const Ii = /* @__PURE__ */ Ke(Si); +function yt(n, e) { + return n.map((t) => { + const o = A(e) ? e(t.tool) : e; + return V(o) || (t.data = wt(t.data, o)), t; + }); +} +function Z(n, e = {}) { + const t = { + tags: e + }; + return new Ii(t).clean(n); +} +function wt(n, e) { + return Array.isArray(n) ? Oi(n, e) : D(n) ? _i(n, e) : te(n) ? Mi(n, e) : n; +} +function Oi(n, e) { + return n.map((t) => wt(t, e)); +} +function _i(n, e) { + const t = {}; + for (const o in n) { + if (!Object.prototype.hasOwnProperty.call(n, o)) + continue; + const i = n[o], s = Ai(e[o]) ? e[o] : e; + t[o] = wt(i, s); + } + return t; +} +function Mi(n, e) { + return D(e) ? Z(n, e) : e === !1 ? Z(n, {}) : n; +} +function Ai(n) { + return D(n) || Gn(n) || A(n); +} +class Li extends E { + /** + * Available methods + * + * @returns {SanitizerConfig} + */ + get methods() { + return { + clean: (e, t) => this.clean(e, t) + }; + } + /** + * Perform sanitizing of a string + * + * @param {string} taintString - what to sanitize + * @param {SanitizerConfig} config - sanitizer config + * @returns {string} + */ + clean(e, t) { + return Z(e, t); + } +} +class Pi extends E { + /** + * Available methods + * + * @returns {Saver} + */ + get methods() { + return { + save: () => this.save() + }; + } + /** + * Return Editor's data + * + * @returns {OutputData} + */ + save() { + const e = "Editor's content can not be saved in read-only mode"; + return this.Editor.ReadOnly.isEnabled ? (X(e, "warn"), Promise.reject(new Error(e))) : this.Editor.Saver.save(); + } +} +class Ni extends E { + constructor() { + super(...arguments), this.selectionUtils = new b(); + } + /** + * Available methods + * + * @returns {SelectionAPIInterface} + */ + get methods() { + return { + findParentTag: (e, t) => this.findParentTag(e, t), + expandToTag: (e) => this.expandToTag(e), + save: () => this.selectionUtils.save(), + restore: () => this.selectionUtils.restore(), + setFakeBackground: () => this.selectionUtils.setFakeBackground(), + removeFakeBackground: () => this.selectionUtils.removeFakeBackground() + }; + } + /** + * Looks ahead from selection and find passed tag with class name + * + * @param {string} tagName - tag to find + * @param {string} className - tag's class name + * @returns {HTMLElement|null} + */ + findParentTag(e, t) { + return this.selectionUtils.findParentTag(e, t); + } + /** + * Expand selection to passed tag + * + * @param {HTMLElement} node - tag that should contain selection + */ + expandToTag(e) { + this.selectionUtils.expandToTag(e); + } +} +class Ri extends E { + /** + * Available methods + */ + get methods() { + return { + getBlockTools: () => Array.from(this.Editor.Tools.blockTools.values()) + }; + } +} +class Di extends E { + /** + * Exported classes + */ + get classes() { + return { + /** + * Base Block styles + */ + block: "cdx-block", + /** + * Inline Tools styles + */ + inlineToolButton: "ce-inline-tool", + inlineToolButtonActive: "ce-inline-tool--active", + /** + * UI elements + */ + input: "cdx-input", + loader: "cdx-loader", + button: "cdx-button", + /** + * Settings styles + */ + settingsButton: "cdx-settings-button", + settingsButtonActive: "cdx-settings-button--active" + }; + } +} +class Fi extends E { + /** + * Available methods + * + * @returns {Toolbar} + */ + get methods() { + return { + close: () => this.close(), + open: () => this.open(), + toggleBlockSettings: (e) => this.toggleBlockSettings(e), + toggleToolbox: (e) => this.toggleToolbox(e) + }; + } + /** + * Open toolbar + */ + open() { + this.Editor.Toolbar.moveAndOpen(); + } + /** + * Close toolbar and all included elements + */ + close() { + this.Editor.Toolbar.close(); + } + /** + * Toggles Block Setting of the current block + * + * @param {boolean} openingState — opening state of Block Setting + */ + toggleBlockSettings(e) { + if (this.Editor.BlockManager.currentBlockIndex === -1) { + X("Could't toggle the Toolbar because there is no block selected ", "warn"); + return; + } + e ?? !this.Editor.BlockSettings.opened ? (this.Editor.Toolbar.moveAndOpen(), this.Editor.BlockSettings.open()) : this.Editor.BlockSettings.close(); + } + /** + * Open toolbox + * + * @param {boolean} openingState - Opening state of toolbox + */ + toggleToolbox(e) { + if (this.Editor.BlockManager.currentBlockIndex === -1) { + X("Could't toggle the Toolbox because there is no block selected ", "warn"); + return; + } + e ?? !this.Editor.Toolbar.toolbox.opened ? (this.Editor.Toolbar.moveAndOpen(), this.Editor.Toolbar.toolbox.open()) : this.Editor.Toolbar.toolbox.close(); + } +} +var Vo = { exports: {} }; +/*! + * CodeX.Tooltips + * + * @version 1.0.5 + * + * @licence MIT + * @author CodeX + * + * + */ +(function(n, e) { + (function(t, o) { + n.exports = o(); + })(window, function() { + return function(t) { + var o = {}; + function i(s) { + if (o[s]) + return o[s].exports; + var r = o[s] = { i: s, l: !1, exports: {} }; + return t[s].call(r.exports, r, r.exports, i), r.l = !0, r.exports; + } + return i.m = t, i.c = o, i.d = function(s, r, a) { + i.o(s, r) || Object.defineProperty(s, r, { enumerable: !0, get: a }); + }, i.r = function(s) { + typeof Symbol < "u" && Symbol.toStringTag && Object.defineProperty(s, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(s, "__esModule", { value: !0 }); + }, i.t = function(s, r) { + if (1 & r && (s = i(s)), 8 & r || 4 & r && typeof s == "object" && s && s.__esModule) + return s; + var a = /* @__PURE__ */ Object.create(null); + if (i.r(a), Object.defineProperty(a, "default", { enumerable: !0, value: s }), 2 & r && typeof s != "string") + for (var l in s) + i.d(a, l, (function(c) { + return s[c]; + }).bind(null, l)); + return a; + }, i.n = function(s) { + var r = s && s.__esModule ? function() { + return s.default; + } : function() { + return s; + }; + return i.d(r, "a", r), r; + }, i.o = function(s, r) { + return Object.prototype.hasOwnProperty.call(s, r); + }, i.p = "", i(i.s = 0); + }([function(t, o, i) { + t.exports = i(1); + }, function(t, o, i) { + i.r(o), i.d(o, "default", function() { + return s; + }); + class s { + constructor() { + this.nodes = { wrapper: null, content: null }, this.showed = !1, this.offsetTop = 10, this.offsetLeft = 10, this.offsetRight = 10, this.hidingDelay = 0, this.handleWindowScroll = () => { + this.showed && this.hide(!0); + }, this.loadStyles(), this.prepare(), window.addEventListener("scroll", this.handleWindowScroll, { passive: !0 }); + } + get CSS() { + return { tooltip: "ct", tooltipContent: "ct__content", tooltipShown: "ct--shown", placement: { left: "ct--left", bottom: "ct--bottom", right: "ct--right", top: "ct--top" } }; + } + show(a, l, c) { + this.nodes.wrapper || this.prepare(), this.hidingTimeout && clearTimeout(this.hidingTimeout); + const d = Object.assign({ placement: "bottom", marginTop: 0, marginLeft: 0, marginRight: 0, marginBottom: 0, delay: 70, hidingDelay: 0 }, c); + if (d.hidingDelay && (this.hidingDelay = d.hidingDelay), this.nodes.content.innerHTML = "", typeof l == "string") + this.nodes.content.appendChild(document.createTextNode(l)); + else { + if (!(l instanceof Node)) + throw Error("[CodeX Tooltip] Wrong type of «content» passed. It should be an instance of Node or String. But " + typeof l + " given."); + this.nodes.content.appendChild(l); + } + switch (this.nodes.wrapper.classList.remove(...Object.values(this.CSS.placement)), d.placement) { + case "top": + this.placeTop(a, d); + break; + case "left": + this.placeLeft(a, d); + break; + case "right": + this.placeRight(a, d); + break; + case "bottom": + default: + this.placeBottom(a, d); + } + d && d.delay ? this.showingTimeout = setTimeout(() => { + this.nodes.wrapper.classList.add(this.CSS.tooltipShown), this.showed = !0; + }, d.delay) : (this.nodes.wrapper.classList.add(this.CSS.tooltipShown), this.showed = !0); + } + hide(a = !1) { + if (this.hidingDelay && !a) + return this.hidingTimeout && clearTimeout(this.hidingTimeout), void (this.hidingTimeout = setTimeout(() => { + this.hide(!0); + }, this.hidingDelay)); + this.nodes.wrapper.classList.remove(this.CSS.tooltipShown), this.showed = !1, this.showingTimeout && clearTimeout(this.showingTimeout); + } + onHover(a, l, c) { + a.addEventListener("mouseenter", () => { + this.show(a, l, c); + }), a.addEventListener("mouseleave", () => { + this.hide(); + }); + } + destroy() { + this.nodes.wrapper.remove(), window.removeEventListener("scroll", this.handleWindowScroll); + } + prepare() { + this.nodes.wrapper = this.make("div", this.CSS.tooltip), this.nodes.content = this.make("div", this.CSS.tooltipContent), this.append(this.nodes.wrapper, this.nodes.content), this.append(document.body, this.nodes.wrapper); + } + loadStyles() { + const a = "codex-tooltips-style"; + if (document.getElementById(a)) + return; + const l = i(2), c = this.make("style", null, { textContent: l.toString(), id: a }); + this.prepend(document.head, c); + } + placeBottom(a, l) { + const c = a.getBoundingClientRect(), d = c.left + a.clientWidth / 2 - this.nodes.wrapper.offsetWidth / 2, h = c.bottom + window.pageYOffset + this.offsetTop + l.marginTop; + this.applyPlacement("bottom", d, h); + } + placeTop(a, l) { + const c = a.getBoundingClientRect(), d = c.left + a.clientWidth / 2 - this.nodes.wrapper.offsetWidth / 2, h = c.top + window.pageYOffset - this.nodes.wrapper.clientHeight - this.offsetTop; + this.applyPlacement("top", d, h); + } + placeLeft(a, l) { + const c = a.getBoundingClientRect(), d = c.left - this.nodes.wrapper.offsetWidth - this.offsetLeft - l.marginLeft, h = c.top + window.pageYOffset + a.clientHeight / 2 - this.nodes.wrapper.offsetHeight / 2; + this.applyPlacement("left", d, h); + } + placeRight(a, l) { + const c = a.getBoundingClientRect(), d = c.right + this.offsetRight + l.marginRight, h = c.top + window.pageYOffset + a.clientHeight / 2 - this.nodes.wrapper.offsetHeight / 2; + this.applyPlacement("right", d, h); + } + applyPlacement(a, l, c) { + this.nodes.wrapper.classList.add(this.CSS.placement[a]), this.nodes.wrapper.style.left = l + "px", this.nodes.wrapper.style.top = c + "px"; + } + make(a, l = null, c = {}) { + const d = document.createElement(a); + Array.isArray(l) ? d.classList.add(...l) : l && d.classList.add(l); + for (const h in c) + c.hasOwnProperty(h) && (d[h] = c[h]); + return d; + } + append(a, l) { + Array.isArray(l) ? l.forEach((c) => a.appendChild(c)) : a.appendChild(l); + } + prepend(a, l) { + Array.isArray(l) ? (l = l.reverse()).forEach((c) => a.prepend(c)) : a.prepend(l); + } + } + }, function(t, o) { + t.exports = `.ct{z-index:999;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-transition:opacity 50ms ease-in,-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,transform 70ms cubic-bezier(.215,.61,.355,1),-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);will-change:opacity,top,left;-webkit-box-shadow:0 8px 12px 0 rgba(29,32,43,.17),0 4px 5px -3px rgba(5,6,12,.49);box-shadow:0 8px 12px 0 rgba(29,32,43,.17),0 4px 5px -3px rgba(5,6,12,.49);border-radius:9px}.ct,.ct:before{position:absolute;top:0;left:0}.ct:before{content:"";bottom:0;right:0;background-color:#1d202b;z-index:-1;border-radius:4px}@supports(-webkit-mask-box-image:url("")){.ct:before{border-radius:0;-webkit-mask-box-image:url('data:image/svg+xml;charset=utf-8,') 48% 41% 37.9% 53.3%}}@media (--mobile){.ct{display:none}}.ct__content{padding:6px 10px;color:#cdd1e0;font-size:12px;text-align:center;letter-spacing:.02em;line-height:1em}.ct:after{content:"";width:8px;height:8px;position:absolute;background-color:#1d202b;z-index:-1}.ct--bottom{-webkit-transform:translateY(5px);transform:translateY(5px)}.ct--bottom:after{top:-3px;left:50%;-webkit-transform:translateX(-50%) rotate(-45deg);transform:translateX(-50%) rotate(-45deg)}.ct--top{-webkit-transform:translateY(-5px);transform:translateY(-5px)}.ct--top:after{top:auto;bottom:-3px;left:50%;-webkit-transform:translateX(-50%) rotate(-45deg);transform:translateX(-50%) rotate(-45deg)}.ct--left{-webkit-transform:translateX(-5px);transform:translateX(-5px)}.ct--left:after{top:50%;left:auto;right:0;-webkit-transform:translate(41.6%,-50%) rotate(-45deg);transform:translate(41.6%,-50%) rotate(-45deg)}.ct--right{-webkit-transform:translateX(5px);transform:translateX(5px)}.ct--right:after{top:50%;left:0;-webkit-transform:translate(-41.6%,-50%) rotate(-45deg);transform:translate(-41.6%,-50%) rotate(-45deg)}.ct--shown{opacity:1;-webkit-transform:none;transform:none}`; + }]).default; + }); +})(Vo); +var ji = Vo.exports; +const Hi = /* @__PURE__ */ Ke(ji); +let U = null; +function Et() { + U || (U = new Hi()); +} +function $i(n, e, t) { + Et(), U == null || U.show(n, e, t); +} +function $e(n = !1) { + Et(), U == null || U.hide(n); +} +function ze(n, e, t) { + Et(), U == null || U.onHover(n, e, t); +} +function zi() { + U == null || U.destroy(), U = null; +} +class Ui extends E { + /** + * @class + * @param moduleConfiguration - Module Configuration + * @param moduleConfiguration.config - Editor's config + * @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher + */ + constructor({ config: e, eventsDispatcher: t }) { + super({ + config: e, + eventsDispatcher: t + }); + } + /** + * Available methods + */ + get methods() { + return { + show: (e, t, o) => this.show(e, t, o), + hide: () => this.hide(), + onHover: (e, t, o) => this.onHover(e, t, o) + }; + } + /** + * Method show tooltip on element with passed HTML content + * + * @param {HTMLElement} element - element on which tooltip should be shown + * @param {TooltipContent} content - tooltip content + * @param {TooltipOptions} options - tooltip options + */ + show(e, t, o) { + $i(e, t, o); + } + /** + * Method hides tooltip on HTML page + */ + hide() { + $e(); + } + /** + * Decorator for showing Tooltip by mouseenter/mouseleave + * + * @param {HTMLElement} element - element on which tooltip should be shown + * @param {TooltipContent} content - tooltip content + * @param {TooltipOptions} options - tooltip options + */ + onHover(e, t, o) { + ze(e, t, o); + } +} +class Wi extends E { + /** + * Available methods / getters + */ + get methods() { + return { + nodes: this.editorNodes + /** + * There can be added some UI methods, like toggleThinMode() etc + */ + }; + } + /** + * Exported classes + */ + get editorNodes() { + return { + /** + * Top-level editor instance wrapper + */ + wrapper: this.Editor.UI.nodes.wrapper, + /** + * Element that holds all the Blocks + */ + redactor: this.Editor.UI.nodes.redactor + }; + } +} +function qo(n, e) { + const t = {}; + return Object.entries(n).forEach(([o, i]) => { + if (D(i)) { + const s = e ? `${e}.${o}` : o; + Object.values(i).every((a) => te(a)) ? t[o] = s : t[o] = qo(i, s); + return; + } + t[o] = i; + }), t; +} +const K = qo(Fo); +function Yi(n, e) { + const t = {}; + return Object.keys(n).forEach((o) => { + const i = e[o]; + i !== void 0 ? t[i] = n[o] : t[o] = n[o]; + }), t; +} +const Zo = class Ee { + /** + * @param {HTMLElement[]} nodeList — the list of iterable HTML-items + * @param {string} focusedCssClass - user-provided CSS-class that will be set in flipping process + */ + constructor(e, t) { + this.cursor = -1, this.items = [], this.items = e || [], this.focusedCssClass = t; + } + /** + * Returns Focused button Node + * + * @returns {HTMLElement} + */ + get currentItem() { + return this.cursor === -1 ? null : this.items[this.cursor]; + } + /** + * Sets cursor to specified position + * + * @param cursorPosition - new cursor position + */ + setCursor(e) { + e < this.items.length && e >= -1 && (this.dropCursor(), this.cursor = e, this.items[this.cursor].classList.add(this.focusedCssClass)); + } + /** + * Sets items. Can be used when iterable items changed dynamically + * + * @param {HTMLElement[]} nodeList - nodes to iterate + */ + setItems(e) { + this.items = e; + } + /** + * Sets cursor next to the current + */ + next() { + this.cursor = this.leafNodesAndReturnIndex(Ee.directions.RIGHT); + } + /** + * Sets cursor before current + */ + previous() { + this.cursor = this.leafNodesAndReturnIndex(Ee.directions.LEFT); + } + /** + * Sets cursor to the default position and removes CSS-class from previously focused item + */ + dropCursor() { + this.cursor !== -1 && (this.items[this.cursor].classList.remove(this.focusedCssClass), this.cursor = -1); + } + /** + * Leafs nodes inside the target list from active element + * + * @param {string} direction - leaf direction. Can be 'left' or 'right' + * @returns {number} index of focused node + */ + leafNodesAndReturnIndex(e) { + if (this.items.length === 0) + return this.cursor; + let t = this.cursor; + return t === -1 ? t = e === Ee.directions.RIGHT ? -1 : 0 : this.items[t].classList.remove(this.focusedCssClass), e === Ee.directions.RIGHT ? t = (t + 1) % this.items.length : t = (this.items.length + t - 1) % this.items.length, u.canSetCaret(this.items[t]) && Fe(() => b.setCursor(this.items[t]), 50)(), this.items[t].classList.add(this.focusedCssClass), t; + } +}; +Zo.directions = { + RIGHT: "right", + LEFT: "left" +}; +let ke = Zo; +class ce { + /** + * @param options - different constructing settings + */ + constructor(e) { + this.iterator = null, this.activated = !1, this.flipCallbacks = [], this.onKeyDown = (t) => { + if (!(!this.isEventReadyForHandling(t) || t.shiftKey === !0)) + switch (ce.usedKeys.includes(t.keyCode) && t.preventDefault(), t.keyCode) { + case y.TAB: + this.handleTabPress(t); + break; + case y.LEFT: + case y.UP: + this.flipLeft(); + break; + case y.RIGHT: + case y.DOWN: + this.flipRight(); + break; + case y.ENTER: + this.handleEnterPress(t); + break; + } + }, this.iterator = new ke(e.items, e.focusedItemClass), this.activateCallback = e.activateCallback, this.allowedKeys = e.allowedKeys || ce.usedKeys; + } + /** + * True if flipper is currently activated + */ + get isActivated() { + return this.activated; + } + /** + * Array of keys (codes) that is handled by Flipper + * Used to: + * - preventDefault only for this keys, not all keydowns (@see constructor) + * - to skip external behaviours only for these keys, when filler is activated (@see BlockEvents@arrowRightAndDown) + */ + static get usedKeys() { + return [ + y.TAB, + y.LEFT, + y.RIGHT, + y.ENTER, + y.UP, + y.DOWN + ]; + } + /** + * Active tab/arrows handling by flipper + * + * @param items - Some modules (like, InlineToolbar, BlockSettings) might refresh buttons dynamically + * @param cursorPosition - index of the item that should be focused once flipper is activated + */ + activate(e, t) { + this.activated = !0, e && this.iterator.setItems(e), t !== void 0 && this.iterator.setCursor(t), document.addEventListener("keydown", this.onKeyDown, !0); + } + /** + * Disable tab/arrows handling by flipper + */ + deactivate() { + this.activated = !1, this.dropCursor(), document.removeEventListener("keydown", this.onKeyDown); + } + /** + * Focus first item + */ + focusFirst() { + this.dropCursor(), this.flipRight(); + } + /** + * Focuses previous flipper iterator item + */ + flipLeft() { + this.iterator.previous(), this.flipCallback(); + } + /** + * Focuses next flipper iterator item + */ + flipRight() { + this.iterator.next(), this.flipCallback(); + } + /** + * Return true if some button is focused + */ + hasFocus() { + return !!this.iterator.currentItem; + } + /** + * Registeres function that should be executed on each navigation action + * + * @param cb - function to execute + */ + onFlip(e) { + this.flipCallbacks.push(e); + } + /** + * Unregisteres function that is executed on each navigation action + * + * @param cb - function to stop executing + */ + removeOnFlip(e) { + this.flipCallbacks = this.flipCallbacks.filter((t) => t !== e); + } + /** + * Drops flipper's iterator cursor + * + * @see DomIterator#dropCursor + */ + dropCursor() { + this.iterator.dropCursor(); + } + /** + * This function is fired before handling flipper keycodes + * The result of this function defines if it is need to be handled or not + * + * @param {KeyboardEvent} event - keydown keyboard event + * @returns {boolean} + */ + isEventReadyForHandling(e) { + return this.activated && this.allowedKeys.includes(e.keyCode); + } + /** + * When flipper is activated tab press will leaf the items + * + * @param {KeyboardEvent} event - tab keydown event + */ + handleTabPress(e) { + switch (e.shiftKey ? ke.directions.LEFT : ke.directions.RIGHT) { + case ke.directions.RIGHT: + this.flipRight(); + break; + case ke.directions.LEFT: + this.flipLeft(); + break; + } + } + /** + * Enter press will click current item if flipper is activated + * + * @param {KeyboardEvent} event - enter keydown event + */ + handleEnterPress(e) { + this.activated && (this.iterator.currentItem && (e.stopPropagation(), e.preventDefault(), this.iterator.currentItem.click()), A(this.activateCallback) && this.activateCallback(this.iterator.currentItem)); + } + /** + * Fired after flipping in any direction + */ + flipCallback() { + this.iterator.currentItem && this.iterator.currentItem.scrollIntoViewIfNeeded(), this.flipCallbacks.forEach((e) => e()); + } +} +const Ki = '', Xi = '', Vi = '', qi = '', Zi = '', Gi = '', Qi = '', Ji = '', Co = '', es = '', ts = '', Go = '', os = '', ns = '', is = '', ss = "__", rs = "--"; +function ne(n) { + return (e, t) => [[n, e].filter((i) => !!i).join(ss), t].filter((i) => !!i).join(rs); +} +const ye = ne("ce-hint"), we = { + root: ye(), + alignedStart: ye(null, "align-left"), + alignedCenter: ye(null, "align-center"), + title: ye("title"), + description: ye("description") +}; +class as { + /** + * Constructs the hint content instance + * + * @param params - hint content parameters + */ + constructor(e) { + this.nodes = { + root: u.make("div", [we.root, e.alignment === "center" ? we.alignedCenter : we.alignedStart]), + title: u.make("div", we.title, { textContent: e.title }) + }, this.nodes.root.appendChild(this.nodes.title), e.description !== void 0 && (this.nodes.description = u.make("div", we.description, { textContent: e.description }), this.nodes.root.appendChild(this.nodes.description)); + } + /** + * Returns the root element of the hint content + */ + getElement() { + return this.nodes.root; + } +} +class xt { + /** + * Constructs the instance + * + * @param params - instance parameters + */ + constructor(e) { + this.params = e; + } + /** + * Item name if exists + */ + get name() { + if (this.params !== void 0 && "name" in this.params) + return this.params.name; + } + /** + * Destroys the instance + */ + destroy() { + $e(); + } + /** + * Called when children popover is opened (if exists) + */ + onChildrenOpen() { + var e; + this.params !== void 0 && "children" in this.params && typeof ((e = this.params.children) == null ? void 0 : e.onOpen) == "function" && this.params.children.onOpen(); + } + /** + * Called when children popover is closed (if exists) + */ + onChildrenClose() { + var e; + this.params !== void 0 && "children" in this.params && typeof ((e = this.params.children) == null ? void 0 : e.onClose) == "function" && this.params.children.onClose(); + } + /** + * Called on popover item click + */ + handleClick() { + var e, t; + this.params !== void 0 && "onActivate" in this.params && ((t = (e = this.params).onActivate) == null || t.call(e, this.params)); + } + /** + * Adds hint to the item element if hint data is provided + * + * @param itemElement - popover item root element to add hint to + * @param hintData - hint data + */ + addHint(e, t) { + const o = new as(t); + ze(e, o.getElement(), { + placement: t.position, + hidingDelay: 100 + }); + } + /** + * Returns item children that are represented as popover items + */ + get children() { + var e; + return this.params !== void 0 && "children" in this.params && ((e = this.params.children) == null ? void 0 : e.items) !== void 0 ? this.params.children.items : []; + } + /** + * Returns true if item has any type of children + */ + get hasChildren() { + return this.children.length > 0; + } + /** + * Returns true if item children should be open instantly after popover is opened and not on item click/hover + */ + get isChildrenOpen() { + var e; + return this.params !== void 0 && "children" in this.params && ((e = this.params.children) == null ? void 0 : e.isOpen) === !0; + } + /** + * True if item children items should be navigatable via keyboard + */ + get isChildrenFlippable() { + var e; + return !(this.params === void 0 || !("children" in this.params) || ((e = this.params.children) == null ? void 0 : e.isFlippable) === !1); + } + /** + * Returns true if item has children that should be searchable + */ + get isChildrenSearchable() { + var e; + return this.params !== void 0 && "children" in this.params && ((e = this.params.children) == null ? void 0 : e.searchable) === !0; + } + /** + * True if popover should close once item is activated + */ + get closeOnActivate() { + return this.params !== void 0 && "closeOnActivate" in this.params && this.params.closeOnActivate; + } + /** + * True if item is active + */ + get isActive() { + return this.params === void 0 || !("isActive" in this.params) ? !1 : typeof this.params.isActive == "function" ? this.params.isActive() : this.params.isActive === !0; + } +} +const Y = ne("ce-popover-item"), L = { + container: Y(), + active: Y(null, "active"), + disabled: Y(null, "disabled"), + focused: Y(null, "focused"), + hidden: Y(null, "hidden"), + confirmationState: Y(null, "confirmation"), + noHover: Y(null, "no-hover"), + noFocus: Y(null, "no-focus"), + title: Y("title"), + secondaryTitle: Y("secondary-title"), + icon: Y("icon"), + iconTool: Y("icon", "tool"), + iconChevronRight: Y("icon", "chevron-right"), + wobbleAnimation: ne("wobble")() +}; +class re extends xt { + /** + * Constructs popover item instance + * + * @param params - popover item construction params + * @param renderParams - popover item render params. + * The parameters that are not set by user via popover api but rather depend on technical implementation + */ + constructor(e, t) { + super(e), this.params = e, this.nodes = { + root: null, + icon: null + }, this.confirmationState = null, this.removeSpecialFocusBehavior = () => { + var o; + (o = this.nodes.root) == null || o.classList.remove(L.noFocus); + }, this.removeSpecialHoverBehavior = () => { + var o; + (o = this.nodes.root) == null || o.classList.remove(L.noHover); + }, this.onErrorAnimationEnd = () => { + var o, i; + (o = this.nodes.icon) == null || o.classList.remove(L.wobbleAnimation), (i = this.nodes.icon) == null || i.removeEventListener("animationend", this.onErrorAnimationEnd); + }, this.nodes.root = this.make(e, t); + } + /** + * True if item is disabled and hence not clickable + */ + get isDisabled() { + return this.params.isDisabled === !0; + } + /** + * Exposes popover item toggle parameter + */ + get toggle() { + return this.params.toggle; + } + /** + * Item title + */ + get title() { + return this.params.title; + } + /** + * True if confirmation state is enabled for popover item + */ + get isConfirmationStateEnabled() { + return this.confirmationState !== null; + } + /** + * True if item is focused in keyboard navigation process + */ + get isFocused() { + return this.nodes.root === null ? !1 : this.nodes.root.classList.contains(L.focused); + } + /** + * Returns popover item root element + */ + getElement() { + return this.nodes.root; + } + /** + * Called on popover item click + */ + handleClick() { + if (this.isConfirmationStateEnabled && this.confirmationState !== null) { + this.activateOrEnableConfirmationMode(this.confirmationState); + return; + } + this.activateOrEnableConfirmationMode(this.params); + } + /** + * Toggles item active state + * + * @param isActive - true if item should strictly should become active + */ + toggleActive(e) { + var t; + (t = this.nodes.root) == null || t.classList.toggle(L.active, e); + } + /** + * Toggles item hidden state + * + * @param isHidden - true if item should be hidden + */ + toggleHidden(e) { + var t; + (t = this.nodes.root) == null || t.classList.toggle(L.hidden, e); + } + /** + * Resets popover item to its original state + */ + reset() { + this.isConfirmationStateEnabled && this.disableConfirmationMode(); + } + /** + * Method called once item becomes focused during keyboard navigation + */ + onFocus() { + this.disableSpecialHoverAndFocusBehavior(); + } + /** + * Constructs HTML element corresponding to popover item params + * + * @param params - item construction params + * @param renderParams - popover item render params + */ + make(e, t) { + var s, r; + const o = (t == null ? void 0 : t.wrapperTag) || "div", i = u.make(o, L.container, { + type: o === "button" ? "button" : void 0 + }); + return e.name && (i.dataset.itemName = e.name), this.nodes.icon = u.make("div", [L.icon, L.iconTool], { + innerHTML: e.icon || Qi + }), i.appendChild(this.nodes.icon), e.title !== void 0 && i.appendChild(u.make("div", L.title, { + innerHTML: e.title || "" + })), e.secondaryLabel && i.appendChild(u.make("div", L.secondaryTitle, { + textContent: e.secondaryLabel + })), this.hasChildren && i.appendChild(u.make("div", [L.icon, L.iconChevronRight], { + innerHTML: qi + })), this.isActive && i.classList.add(L.active), e.isDisabled && i.classList.add(L.disabled), e.hint !== void 0 && ((s = t == null ? void 0 : t.hint) == null ? void 0 : s.enabled) !== !1 && this.addHint(i, { + ...e.hint, + position: ((r = t == null ? void 0 : t.hint) == null ? void 0 : r.position) || "right" + }), i; + } + /** + * Activates confirmation mode for the item. + * + * @param newState - new popover item params that should be applied + */ + enableConfirmationMode(e) { + if (this.nodes.root === null) + return; + const t = { + ...this.params, + ...e, + confirmation: "confirmation" in e ? e.confirmation : void 0 + }, o = this.make(t); + this.nodes.root.innerHTML = o.innerHTML, this.nodes.root.classList.add(L.confirmationState), this.confirmationState = e, this.enableSpecialHoverAndFocusBehavior(); + } + /** + * Returns item to its original state + */ + disableConfirmationMode() { + if (this.nodes.root === null) + return; + const e = this.make(this.params); + this.nodes.root.innerHTML = e.innerHTML, this.nodes.root.classList.remove(L.confirmationState), this.confirmationState = null, this.disableSpecialHoverAndFocusBehavior(); + } + /** + * Enables special focus and hover behavior for item in confirmation state. + * This is needed to prevent item from being highlighted as hovered/focused just after click. + */ + enableSpecialHoverAndFocusBehavior() { + var e, t, o; + (e = this.nodes.root) == null || e.classList.add(L.noHover), (t = this.nodes.root) == null || t.classList.add(L.noFocus), (o = this.nodes.root) == null || o.addEventListener("mouseleave", this.removeSpecialHoverBehavior, { once: !0 }); + } + /** + * Disables special focus and hover behavior + */ + disableSpecialHoverAndFocusBehavior() { + var e; + this.removeSpecialFocusBehavior(), this.removeSpecialHoverBehavior(), (e = this.nodes.root) == null || e.removeEventListener("mouseleave", this.removeSpecialHoverBehavior); + } + /** + * Executes item's onActivate callback if the item has no confirmation configured + * + * @param item - item to activate or bring to confirmation mode + */ + activateOrEnableConfirmationMode(e) { + var t; + if (!("confirmation" in e) || e.confirmation === void 0) + try { + (t = e.onActivate) == null || t.call(e, e), this.disableConfirmationMode(); + } catch { + this.animateError(); + } + else + this.enableConfirmationMode(e.confirmation); + } + /** + * Animates item which symbolizes that error occured while executing 'onActivate()' callback + */ + animateError() { + var e, t, o; + (e = this.nodes.icon) != null && e.classList.contains(L.wobbleAnimation) || ((t = this.nodes.icon) == null || t.classList.add(L.wobbleAnimation), (o = this.nodes.icon) == null || o.addEventListener("animationend", this.onErrorAnimationEnd)); + } +} +const nt = ne("ce-popover-item-separator"), it = { + container: nt(), + line: nt("line"), + hidden: nt(null, "hidden") +}; +class Qo extends xt { + /** + * Constructs the instance + */ + constructor() { + super(), this.nodes = { + root: u.make("div", it.container), + line: u.make("div", it.line) + }, this.nodes.root.appendChild(this.nodes.line); + } + /** + * Returns popover separator root element + */ + getElement() { + return this.nodes.root; + } + /** + * Toggles item hidden state + * + * @param isHidden - true if item should be hidden + */ + toggleHidden(e) { + var t; + (t = this.nodes.root) == null || t.classList.toggle(it.hidden, e); + } +} +var G = /* @__PURE__ */ ((n) => (n.Closed = "closed", n.ClosedOnActivate = "closed-on-activate", n))(G || {}); +const $ = ne("ce-popover"), P = { + popover: $(), + popoverContainer: $("container"), + popoverOpenTop: $(null, "open-top"), + popoverOpenLeft: $(null, "open-left"), + popoverOpened: $(null, "opened"), + search: $("search"), + nothingFoundMessage: $("nothing-found-message"), + nothingFoundMessageDisplayed: $("nothing-found-message", "displayed"), + items: $("items"), + overlay: $("overlay"), + overlayHidden: $("overlay", "hidden"), + popoverNested: $(null, "nested"), + getPopoverNestedClass: (n) => $(null, `nested-level-${n.toString()}`), + popoverInline: $(null, "inline"), + popoverHeader: $("header") +}; +var fe = /* @__PURE__ */ ((n) => (n.NestingLevel = "--nesting-level", n.PopoverHeight = "--popover-height", n.InlinePopoverWidth = "--inline-popover-width", n.TriggerItemLeft = "--trigger-item-left", n.TriggerItemTop = "--trigger-item-top", n))(fe || {}); +const To = ne("ce-popover-item-html"), So = { + root: To(), + hidden: To(null, "hidden") +}; +class Se extends xt { + /** + * Constructs the instance + * + * @param params – instance parameters + * @param renderParams – popover item render params. + * The parameters that are not set by user via popover api but rather depend on technical implementation + */ + constructor(e, t) { + var o, i; + super(e), this.nodes = { + root: u.make("div", So.root) + }, this.nodes.root.appendChild(e.element), e.name && (this.nodes.root.dataset.itemName = e.name), e.hint !== void 0 && ((o = t == null ? void 0 : t.hint) == null ? void 0 : o.enabled) !== !1 && this.addHint(this.nodes.root, { + ...e.hint, + position: ((i = t == null ? void 0 : t.hint) == null ? void 0 : i.position) || "right" + }); + } + /** + * Returns popover item root element + */ + getElement() { + return this.nodes.root; + } + /** + * Toggles item hidden state + * + * @param isHidden - true if item should be hidden + */ + toggleHidden(e) { + var t; + (t = this.nodes.root) == null || t.classList.toggle(So.hidden, e); + } + /** + * Returns list of buttons and inputs inside custom content + */ + getControls() { + const e = this.nodes.root.querySelectorAll( + `button, ${u.allInputsSelector}` + ); + return Array.from(e); + } +} +class Jo extends Oe { + /** + * Constructs the instance + * + * @param params - popover construction params + * @param itemsRenderParams - popover item render params. + * The parameters that are not set by user via popover api but rather depend on technical implementation + */ + constructor(e, t = {}) { + super(), this.params = e, this.itemsRenderParams = t, this.listeners = new _e(), this.messages = { + nothingFound: "Nothing found", + search: "Search" + }, this.items = this.buildItems(e.items), e.messages && (this.messages = { + ...this.messages, + ...e.messages + }), this.nodes = {}, this.nodes.popoverContainer = u.make("div", [P.popoverContainer]), this.nodes.nothingFoundMessage = u.make("div", [P.nothingFoundMessage], { + textContent: this.messages.nothingFound + }), this.nodes.popoverContainer.appendChild(this.nodes.nothingFoundMessage), this.nodes.items = u.make("div", [P.items]), this.items.forEach((o) => { + const i = o.getElement(); + i !== null && this.nodes.items.appendChild(i); + }), this.nodes.popoverContainer.appendChild(this.nodes.items), this.listeners.on(this.nodes.popoverContainer, "click", (o) => this.handleClick(o)), this.nodes.popover = u.make("div", [ + P.popover, + this.params.class + ]), this.nodes.popover.appendChild(this.nodes.popoverContainer); + } + /** + * List of default popover items that are searchable and may have confirmation state + */ + get itemsDefault() { + return this.items.filter((e) => e instanceof re); + } + /** + * Returns HTML element corresponding to the popover + */ + getElement() { + return this.nodes.popover; + } + /** + * Open popover + */ + show() { + this.nodes.popover.classList.add(P.popoverOpened), this.search !== void 0 && this.search.focus(); + } + /** + * Closes popover + */ + hide() { + this.nodes.popover.classList.remove(P.popoverOpened), this.nodes.popover.classList.remove(P.popoverOpenTop), this.itemsDefault.forEach((e) => e.reset()), this.search !== void 0 && this.search.clear(), this.emit(G.Closed); + } + /** + * Clears memory + */ + destroy() { + var e; + this.items.forEach((t) => t.destroy()), this.nodes.popover.remove(), this.listeners.removeAll(), (e = this.search) == null || e.destroy(); + } + /** + * Looks for the item by name and imitates click on it + * + * @param name - name of the item to activate + */ + activateItemByName(e) { + const t = this.items.find((o) => o.name === e); + this.handleItemClick(t); + } + /** + * Factory method for creating popover items + * + * @param items - list of items params + */ + buildItems(e) { + return e.map((t) => { + switch (t.type) { + case _.Separator: + return new Qo(); + case _.Html: + return new Se(t, this.itemsRenderParams[_.Html]); + default: + return new re(t, this.itemsRenderParams[_.Default]); + } + }); + } + /** + * Retrieves popover item that is the target of the specified event + * + * @param event - event to retrieve popover item from + */ + getTargetItem(e) { + return this.items.filter((t) => t instanceof re || t instanceof Se).find((t) => { + const o = t.getElement(); + return o === null ? !1 : e.composedPath().includes(o); + }); + } + /** + * Handles popover item click + * + * @param item - item to handle click of + */ + handleItemClick(e) { + if (!("isDisabled" in e && e.isDisabled)) { + if (e.hasChildren) { + this.showNestedItems(e), "handleClick" in e && typeof e.handleClick == "function" && e.handleClick(); + return; + } + this.itemsDefault.filter((t) => t !== e).forEach((t) => t.reset()), "handleClick" in e && typeof e.handleClick == "function" && e.handleClick(), this.toggleItemActivenessIfNeeded(e), e.closeOnActivate && (this.hide(), this.emit(G.ClosedOnActivate)); + } + } + /** + * Handles clicks inside popover + * + * @param event - item to handle click of + */ + handleClick(e) { + const t = this.getTargetItem(e); + t !== void 0 && this.handleItemClick(t); + } + /** + * - Toggles item active state, if clicked popover item has property 'toggle' set to true. + * + * - Performs radiobutton-like behavior if the item has property 'toggle' set to string key. + * (All the other items with the same key get inactive, and the item gets active) + * + * @param clickedItem - popover item that was clicked + */ + toggleItemActivenessIfNeeded(e) { + if (e instanceof re && (e.toggle === !0 && e.toggleActive(), typeof e.toggle == "string")) { + const t = this.itemsDefault.filter((o) => o.toggle === e.toggle); + if (t.length === 1) { + e.toggleActive(); + return; + } + t.forEach((o) => { + o.toggleActive(o === e); + }); + } + } +} +var Ue = /* @__PURE__ */ ((n) => (n.Search = "search", n))(Ue || {}); +const st = ne("cdx-search-field"), rt = { + wrapper: st(), + icon: st("icon"), + input: st("input") +}; +class ls extends Oe { + /** + * @param options - available config + * @param options.items - searchable items list + * @param options.placeholder - input placeholder + */ + constructor({ items: e, placeholder: t }) { + super(), this.listeners = new _e(), this.items = e, this.wrapper = u.make("div", rt.wrapper); + const o = u.make("div", rt.icon, { + innerHTML: os + }); + this.input = u.make("input", rt.input, { + placeholder: t, + /** + * Used to prevent focusing on the input by Tab key + * (Popover in the Toolbar lays below the blocks, + * so Tab in the last block will focus this hidden input if this property is not set) + */ + tabIndex: -1 + }), this.wrapper.appendChild(o), this.wrapper.appendChild(this.input), this.listeners.on(this.input, "input", () => { + this.searchQuery = this.input.value, this.emit(Ue.Search, { + query: this.searchQuery, + items: this.foundItems + }); + }); + } + /** + * Returns search field element + */ + getElement() { + return this.wrapper; + } + /** + * Sets focus to the input + */ + focus() { + this.input.focus(); + } + /** + * Clears search query and results + */ + clear() { + this.input.value = "", this.searchQuery = "", this.emit(Ue.Search, { + query: "", + items: this.foundItems + }); + } + /** + * Clears memory + */ + destroy() { + this.listeners.removeAll(); + } + /** + * Returns list of found items for the current search query + */ + get foundItems() { + return this.items.filter((e) => this.checkItem(e)); + } + /** + * Contains logic for checking whether passed item conforms the search query + * + * @param item - item to be checked + */ + checkItem(e) { + var i, s; + const t = ((i = e.title) == null ? void 0 : i.toLowerCase()) || "", o = (s = this.searchQuery) == null ? void 0 : s.toLowerCase(); + return o !== void 0 ? t.includes(o) : !1; + } +} +var cs = Object.defineProperty, ds = Object.getOwnPropertyDescriptor, us = (n, e, t, o) => { + for (var i = o > 1 ? void 0 : o ? ds(e, t) : e, s = n.length - 1, r; s >= 0; s--) + (r = n[s]) && (i = (o ? r(e, t, i) : r(i)) || i); + return o && i && cs(e, t, i), i; +}; +const en = class tn extends Jo { + /** + * Construct the instance + * + * @param params - popover params + * @param itemsRenderParams – popover item render params. + * The parameters that are not set by user via popover api but rather depend on technical implementation + */ + constructor(e, t) { + super(e, t), this.nestingLevel = 0, this.nestedPopoverTriggerItem = null, this.previouslyHoveredItem = null, this.scopeElement = document.body, this.hide = () => { + var o; + super.hide(), this.destroyNestedPopoverIfExists(), (o = this.flipper) == null || o.deactivate(), this.previouslyHoveredItem = null; + }, this.onFlip = () => { + const o = this.itemsDefault.find((i) => i.isFocused); + o == null || o.onFocus(); + }, this.onSearch = (o) => { + var a; + const i = o.query === "", s = o.items.length === 0; + this.items.forEach((l) => { + let c = !1; + l instanceof re ? c = !o.items.includes(l) : (l instanceof Qo || l instanceof Se) && (c = s || !i), l.toggleHidden(c); + }), this.toggleNothingFoundMessage(s); + const r = o.query === "" ? this.flippableElements : o.items.map((l) => l.getElement()); + (a = this.flipper) != null && a.isActivated && (this.flipper.deactivate(), this.flipper.activate(r)); + }, e.nestingLevel !== void 0 && (this.nestingLevel = e.nestingLevel), this.nestingLevel > 0 && this.nodes.popover.classList.add(P.popoverNested), e.scopeElement !== void 0 && (this.scopeElement = e.scopeElement), this.nodes.popoverContainer !== null && this.listeners.on(this.nodes.popoverContainer, "mouseover", (o) => this.handleHover(o)), e.searchable && this.addSearch(), e.flippable !== !1 && (this.flipper = new ce({ + items: this.flippableElements, + focusedItemClass: L.focused, + allowedKeys: [ + y.TAB, + y.UP, + y.DOWN, + y.ENTER + ] + }), this.flipper.onFlip(this.onFlip)); + } + /** + * Returns true if some item inside popover is focused + */ + hasFocus() { + return this.flipper === void 0 ? !1 : this.flipper.hasFocus(); + } + /** + * Scroll position inside items container of the popover + */ + get scrollTop() { + return this.nodes.items === null ? 0 : this.nodes.items.scrollTop; + } + /** + * Returns visible element offset top + */ + get offsetTop() { + return this.nodes.popoverContainer === null ? 0 : this.nodes.popoverContainer.offsetTop; + } + /** + * Open popover + */ + show() { + var e; + this.nodes.popover.style.setProperty(fe.PopoverHeight, this.size.height + "px"), this.shouldOpenBottom || this.nodes.popover.classList.add(P.popoverOpenTop), this.shouldOpenRight || this.nodes.popover.classList.add(P.popoverOpenLeft), super.show(), (e = this.flipper) == null || e.activate(this.flippableElements); + } + /** + * Clears memory + */ + destroy() { + this.hide(), super.destroy(); + } + /** + * Handles displaying nested items for the item. + * + * @param item – item to show nested popover for + */ + showNestedItems(e) { + this.nestedPopover !== null && this.nestedPopover !== void 0 || (this.nestedPopoverTriggerItem = e, this.showNestedPopoverForItem(e)); + } + /** + * Handles hover events inside popover items container + * + * @param event - hover event data + */ + handleHover(e) { + const t = this.getTargetItem(e); + t !== void 0 && this.previouslyHoveredItem !== t && (this.destroyNestedPopoverIfExists(), this.previouslyHoveredItem = t, t.hasChildren && this.showNestedPopoverForItem(t)); + } + /** + * Sets CSS variable with position of item near which nested popover should be displayed. + * Is used for correct positioning of the nested popover + * + * @param nestedPopoverEl - nested popover element + * @param item – item near which nested popover should be displayed + */ + setTriggerItemPosition(e, t) { + const o = t.getElement(), i = (o ? o.offsetTop : 0) - this.scrollTop, s = this.offsetTop + i; + e.style.setProperty(fe.TriggerItemTop, s + "px"); + } + /** + * Destroys existing nested popover + */ + destroyNestedPopoverIfExists() { + var e, t; + this.nestedPopover === void 0 || this.nestedPopover === null || (this.nestedPopover.off(G.ClosedOnActivate, this.hide), this.nestedPopover.hide(), this.nestedPopover.destroy(), this.nestedPopover.getElement().remove(), this.nestedPopover = null, (e = this.flipper) == null || e.activate(this.flippableElements), (t = this.nestedPopoverTriggerItem) == null || t.onChildrenClose()); + } + /** + * Creates and displays nested popover for specified item. + * Is used only on desktop + * + * @param item - item to display nested popover by + */ + showNestedPopoverForItem(e) { + var o; + this.nestedPopover = new tn({ + searchable: e.isChildrenSearchable, + items: e.children, + nestingLevel: this.nestingLevel + 1, + flippable: e.isChildrenFlippable, + messages: this.messages + }), e.onChildrenOpen(), this.nestedPopover.on(G.ClosedOnActivate, this.hide); + const t = this.nestedPopover.getElement(); + return this.nodes.popover.appendChild(t), this.setTriggerItemPosition(t, e), t.style.setProperty(fe.NestingLevel, this.nestedPopover.nestingLevel.toString()), this.nestedPopover.show(), (o = this.flipper) == null || o.deactivate(), this.nestedPopover; + } + /** + * Checks if popover should be opened bottom. + * It should happen when there is enough space below or not enough space above + */ + get shouldOpenBottom() { + if (this.nodes.popover === void 0 || this.nodes.popover === null) + return !1; + const e = this.nodes.popoverContainer.getBoundingClientRect(), t = this.scopeElement.getBoundingClientRect(), o = this.size.height, i = e.top + o, s = e.top - o, r = Math.min(window.innerHeight, t.bottom); + return s < t.top || i <= r; + } + /** + * Checks if popover should be opened left. + * It should happen when there is enough space in the right or not enough space in the left + */ + get shouldOpenRight() { + if (this.nodes.popover === void 0 || this.nodes.popover === null) + return !1; + const e = this.nodes.popover.getBoundingClientRect(), t = this.scopeElement.getBoundingClientRect(), o = this.size.width, i = e.right + o, s = e.left - o, r = Math.min(window.innerWidth, t.right); + return s < t.left || i <= r; + } + get size() { + var i; + const e = { + height: 0, + width: 0 + }; + if (this.nodes.popover === null) + return e; + const t = this.nodes.popover.cloneNode(!0); + t.style.visibility = "hidden", t.style.position = "absolute", t.style.top = "-1000px", t.classList.add(P.popoverOpened), (i = t.querySelector("." + P.popoverNested)) == null || i.remove(), document.body.appendChild(t); + const o = t.querySelector("." + P.popoverContainer); + return e.height = o.offsetHeight, e.width = o.offsetWidth, t.remove(), e; + } + /** + * Returns list of elements available for keyboard navigation. + */ + get flippableElements() { + return this.items.map((t) => { + if (t instanceof re) + return t.getElement(); + if (t instanceof Se) + return t.getControls(); + }).flat().filter((t) => t != null); + } + /** + * Adds search to the popover + */ + addSearch() { + this.search = new ls({ + items: this.itemsDefault, + placeholder: this.messages.search + }), this.search.on(Ue.Search, this.onSearch); + const e = this.search.getElement(); + e.classList.add(P.search), this.nodes.popoverContainer.insertBefore(e, this.nodes.popoverContainer.firstChild); + } + /** + * Toggles nothing found message visibility + * + * @param isDisplayed - true if the message should be displayed + */ + toggleNothingFoundMessage(e) { + this.nodes.nothingFoundMessage.classList.toggle(P.nothingFoundMessageDisplayed, e); + } +}; +us([ + me +], en.prototype, "size", 1); +let Bt = en; +class hs extends Bt { + /** + * Constructs the instance + * + * @param params - instance parameters + */ + constructor(e) { + const t = !be(); + super( + { + ...e, + class: P.popoverInline + }, + { + [_.Default]: { + /** + * We use button instead of div here to fix bug associated with focus loss (which leads to selection change) on click in safari + * + * @todo figure out better way to solve the issue + */ + wrapperTag: "button", + hint: { + position: "top", + alignment: "center", + enabled: t + } + }, + [_.Html]: { + hint: { + position: "top", + alignment: "center", + enabled: t + } + } + } + ), this.items.forEach((o) => { + !(o instanceof re) && !(o instanceof Se) || o.hasChildren && o.isChildrenOpen && this.showNestedItems(o); + }); + } + /** + * Returns visible element offset top + */ + get offsetLeft() { + return this.nodes.popoverContainer === null ? 0 : this.nodes.popoverContainer.offsetLeft; + } + /** + * Open popover + */ + show() { + this.nestingLevel === 0 && this.nodes.popover.style.setProperty( + fe.InlinePopoverWidth, + this.size.width + "px" + ), super.show(); + } + /** + * Disable hover event handling. + * Overrides parent's class behavior + */ + handleHover() { + } + /** + * Sets CSS variable with position of item near which nested popover should be displayed. + * Is used to position nested popover right below clicked item + * + * @param nestedPopoverEl - nested popover element + * @param item – item near which nested popover should be displayed + */ + setTriggerItemPosition(e, t) { + const o = t.getElement(), i = o ? o.offsetLeft : 0, s = this.offsetLeft + i; + e.style.setProperty( + fe.TriggerItemLeft, + s + "px" + ); + } + /** + * Handles displaying nested items for the item. + * Overriding in order to add toggling behaviour + * + * @param item – item to toggle nested popover for + */ + showNestedItems(e) { + if (this.nestedPopoverTriggerItem === e) { + this.destroyNestedPopoverIfExists(), this.nestedPopoverTriggerItem = null; + return; + } + super.showNestedItems(e); + } + /** + * Creates and displays nested popover for specified item. + * Is used only on desktop + * + * @param item - item to display nested popover by + */ + showNestedPopoverForItem(e) { + const t = super.showNestedPopoverForItem(e); + return t.getElement().classList.add(P.getPopoverNestedClass(t.nestingLevel)), t; + } + /** + * Overrides default item click handling. + * Helps to close nested popover once other item is clicked. + * + * @param item - clicked item + */ + handleItemClick(e) { + var t; + e !== this.nestedPopoverTriggerItem && ((t = this.nestedPopoverTriggerItem) == null || t.handleClick(), super.destroyNestedPopoverIfExists()), super.handleItemClick(e); + } +} +const on = class xe { + constructor() { + this.scrollPosition = null; + } + /** + * Locks body element scroll + */ + lock() { + pt ? this.lockHard() : document.body.classList.add(xe.CSS.scrollLocked); + } + /** + * Unlocks body element scroll + */ + unlock() { + pt ? this.unlockHard() : document.body.classList.remove(xe.CSS.scrollLocked); + } + /** + * Locks scroll in a hard way (via setting fixed position to body element) + */ + lockHard() { + this.scrollPosition = window.pageYOffset, document.documentElement.style.setProperty( + "--window-scroll-offset", + `${this.scrollPosition}px` + ), document.body.classList.add(xe.CSS.scrollLockedHard); + } + /** + * Unlocks hard scroll lock + */ + unlockHard() { + document.body.classList.remove(xe.CSS.scrollLockedHard), this.scrollPosition !== null && window.scrollTo(0, this.scrollPosition), this.scrollPosition = null; + } +}; +on.CSS = { + scrollLocked: "ce-scroll-locked", + scrollLockedHard: "ce-scroll-locked--hard" +}; +let ps = on; +const at = ne("ce-popover-header"), lt = { + root: at(), + text: at("text"), + backButton: at("back-button") +}; +class fs { + /** + * Constructs the instance + * + * @param params - popover header params + */ + constructor({ text: e, onBackButtonClick: t }) { + this.listeners = new _e(), this.text = e, this.onBackButtonClick = t, this.nodes = { + root: u.make("div", [lt.root]), + backButton: u.make("button", [lt.backButton]), + text: u.make("div", [lt.text]) + }, this.nodes.backButton.innerHTML = Vi, this.nodes.root.appendChild(this.nodes.backButton), this.listeners.on(this.nodes.backButton, "click", this.onBackButtonClick), this.nodes.text.innerText = this.text, this.nodes.root.appendChild(this.nodes.text); + } + /** + * Returns popover header root html element + */ + getElement() { + return this.nodes.root; + } + /** + * Destroys the instance + */ + destroy() { + this.nodes.root.remove(), this.listeners.destroy(); + } +} +class gs { + constructor() { + this.history = []; + } + /** + * Push new popover state + * + * @param state - new state + */ + push(e) { + this.history.push(e); + } + /** + * Pop last popover state + */ + pop() { + return this.history.pop(); + } + /** + * Title retrieved from the current state + */ + get currentTitle() { + return this.history.length === 0 ? "" : this.history[this.history.length - 1].title; + } + /** + * Items list retrieved from the current state + */ + get currentItems() { + return this.history.length === 0 ? [] : this.history[this.history.length - 1].items; + } + /** + * Returns history to initial popover state + */ + reset() { + for (; this.history.length > 1; ) + this.pop(); + } +} +class nn extends Jo { + /** + * Construct the instance + * + * @param params - popover params + */ + constructor(e) { + super(e, { + [_.Default]: { + hint: { + enabled: !1 + } + }, + [_.Html]: { + hint: { + enabled: !1 + } + } + }), this.scrollLocker = new ps(), this.history = new gs(), this.isHidden = !0, this.nodes.overlay = u.make("div", [P.overlay, P.overlayHidden]), this.nodes.popover.insertBefore(this.nodes.overlay, this.nodes.popover.firstChild), this.listeners.on(this.nodes.overlay, "click", () => { + this.hide(); + }), this.history.push({ items: e.items }); + } + /** + * Open popover + */ + show() { + this.nodes.overlay.classList.remove(P.overlayHidden), super.show(), this.scrollLocker.lock(), this.isHidden = !1; + } + /** + * Closes popover + */ + hide() { + this.isHidden || (super.hide(), this.nodes.overlay.classList.add(P.overlayHidden), this.scrollLocker.unlock(), this.history.reset(), this.isHidden = !0); + } + /** + * Clears memory + */ + destroy() { + super.destroy(), this.scrollLocker.unlock(); + } + /** + * Handles displaying nested items for the item + * + * @param item – item to show nested popover for + */ + showNestedItems(e) { + this.updateItemsAndHeader(e.children, e.title), this.history.push({ + title: e.title, + items: e.children + }); + } + /** + * Removes rendered popover items and header and displays new ones + * + * @param items - new popover items + * @param title - new popover header text + */ + updateItemsAndHeader(e, t) { + if (this.header !== null && this.header !== void 0 && (this.header.destroy(), this.header = null), t !== void 0) { + this.header = new fs({ + text: t, + onBackButtonClick: () => { + this.history.pop(), this.updateItemsAndHeader(this.history.currentItems, this.history.currentTitle); + } + }); + const o = this.header.getElement(); + o !== null && this.nodes.popoverContainer.insertBefore(o, this.nodes.popoverContainer.firstChild); + } + this.items.forEach((o) => { + var i; + return (i = o.getElement()) == null ? void 0 : i.remove(); + }), this.items = this.buildItems(e), this.items.forEach((o) => { + var s; + const i = o.getElement(); + i !== null && ((s = this.nodes.items) == null || s.appendChild(i)); + }); + } +} +class ms extends E { + constructor() { + super(...arguments), this.opened = !1, this.hasMobileLayoutToggleListener = !1, this.selection = new b(), this.popover = null, this.close = () => { + this.opened && (this.opened = !1, b.isAtEditor || this.selection.restore(), this.selection.clearSaved(), !this.Editor.CrossBlockSelection.isCrossBlockSelectionStarted && this.Editor.BlockManager.currentBlock && this.Editor.BlockSelection.unselectBlock(this.Editor.BlockManager.currentBlock), this.eventsDispatcher.emit(this.events.closed), this.popover && (this.popover.off(G.Closed, this.onPopoverClose), this.popover.destroy(), this.popover.getElement().remove(), this.popover = null)); + }, this.onPopoverClose = () => { + this.close(); + }; + } + /** + * Module Events + */ + get events() { + return { + opened: "block-settings-opened", + closed: "block-settings-closed" + }; + } + /** + * Block Settings CSS + */ + get CSS() { + return { + settings: "ce-settings" + }; + } + /** + * Getter for inner popover's flipper instance + * + * @todo remove once BlockSettings becomes standalone non-module class + */ + get flipper() { + var e; + if (this.popover !== null) + return "flipper" in this.popover ? (e = this.popover) == null ? void 0 : e.flipper : void 0; + } + /** + * Panel with block settings with 2 sections: + * - Tool's Settings + * - Default Settings [Move, Remove, etc] + */ + make() { + this.nodes.wrapper = u.make("div", [this.CSS.settings]), this.eventsDispatcher.on(Te, this.close), this.hasMobileLayoutToggleListener = !0; + } + /** + * Destroys module + */ + destroy() { + this.removeAllNodes(), this.listeners.destroy(), this.hasMobileLayoutToggleListener && (this.eventsDispatcher.off(Te, this.close), this.hasMobileLayoutToggleListener = !1); + } + /** + * Open Block Settings pane + * + * @param targetBlock - near which Block we should open BlockSettings + */ + async open(e = this.Editor.BlockManager.currentBlock) { + var s; + this.opened = !0, this.selection.save(), this.Editor.BlockSelection.selectBlock(e), this.Editor.BlockSelection.clearCache(); + const { toolTunes: t, commonTunes: o } = e.getTunes(); + this.eventsDispatcher.emit(this.events.opened); + const i = be() ? nn : Bt; + this.popover = new i({ + searchable: !0, + items: await this.getTunesItems(e, o, t), + scopeElement: this.Editor.API.methods.ui.nodes.redactor, + messages: { + nothingFound: z.ui(K.ui.popover, "Nothing found"), + search: z.ui(K.ui.popover, "Filter") + } + }), this.popover.on(G.Closed, this.onPopoverClose), (s = this.nodes.wrapper) == null || s.append(this.popover.getElement()), this.popover.show(); + } + /** + * Returns root block settings element + */ + getElement() { + return this.nodes.wrapper; + } + /** + * Returns list of items to be displayed in block tunes menu. + * Merges tool specific tunes, conversion menu and common tunes in one list in predefined order + * + * @param currentBlock – block we are about to open block tunes for + * @param commonTunes – common tunes + * @param toolTunes - tool specific tunes + */ + async getTunesItems(e, t, o) { + const i = []; + o !== void 0 && o.length > 0 && (i.push(...o), i.push({ + type: _.Separator + })); + const s = Array.from(this.Editor.Tools.blockTools.values()), a = (await Yo(e, s)).reduce((l, c) => (c.toolbox.forEach((d) => { + l.push({ + icon: d.icon, + title: z.t(K.toolNames, d.title), + name: c.name, + closeOnActivate: !0, + onActivate: async () => { + const { BlockManager: h, Caret: p, Toolbar: g } = this.Editor, f = await h.convert(e, c.name, d.data); + g.close(), p.setToBlock(f, p.positions.END); + } + }); + }), l), []); + return a.length > 0 && (i.push({ + icon: Go, + name: "convert-to", + title: z.ui(K.ui.popover, "Convert to"), + children: { + searchable: !0, + items: a + } + }), i.push({ + type: _.Separator + })), i.push(...t), i.map((l) => this.resolveTuneAliases(l)); + } + /** + * Resolves aliases in tunes menu items + * + * @param item - item with resolved aliases + */ + resolveTuneAliases(e) { + if (e.type === _.Separator || e.type === _.Html) + return e; + const t = Yi(e, { label: "title" }); + return e.confirmation && (t.confirmation = this.resolveTuneAliases(e.confirmation)), t; + } +} +var sn = { exports: {} }; +/*! + * Library for handling keyboard shortcuts + * @copyright CodeX (https://codex.so) + * @license MIT + * @author CodeX (https://codex.so) + * @version 1.2.0 + */ +(function(n, e) { + (function(t, o) { + n.exports = o(); + })(window, function() { + return function(t) { + var o = {}; + function i(s) { + if (o[s]) + return o[s].exports; + var r = o[s] = { i: s, l: !1, exports: {} }; + return t[s].call(r.exports, r, r.exports, i), r.l = !0, r.exports; + } + return i.m = t, i.c = o, i.d = function(s, r, a) { + i.o(s, r) || Object.defineProperty(s, r, { enumerable: !0, get: a }); + }, i.r = function(s) { + typeof Symbol < "u" && Symbol.toStringTag && Object.defineProperty(s, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(s, "__esModule", { value: !0 }); + }, i.t = function(s, r) { + if (1 & r && (s = i(s)), 8 & r || 4 & r && typeof s == "object" && s && s.__esModule) + return s; + var a = /* @__PURE__ */ Object.create(null); + if (i.r(a), Object.defineProperty(a, "default", { enumerable: !0, value: s }), 2 & r && typeof s != "string") + for (var l in s) + i.d(a, l, (function(c) { + return s[c]; + }).bind(null, l)); + return a; + }, i.n = function(s) { + var r = s && s.__esModule ? function() { + return s.default; + } : function() { + return s; + }; + return i.d(r, "a", r), r; + }, i.o = function(s, r) { + return Object.prototype.hasOwnProperty.call(s, r); + }, i.p = "", i(i.s = 0); + }([function(t, o, i) { + function s(l, c) { + for (var d = 0; d < c.length; d++) { + var h = c[d]; + h.enumerable = h.enumerable || !1, h.configurable = !0, "value" in h && (h.writable = !0), Object.defineProperty(l, h.key, h); + } + } + function r(l, c, d) { + return c && s(l.prototype, c), d && s(l, d), l; + } + i.r(o); + var a = function() { + function l(c) { + var d = this; + (function(h, p) { + if (!(h instanceof p)) + throw new TypeError("Cannot call a class as a function"); + })(this, l), this.commands = {}, this.keys = {}, this.name = c.name, this.parseShortcutName(c.name), this.element = c.on, this.callback = c.callback, this.executeShortcut = function(h) { + d.execute(h); + }, this.element.addEventListener("keydown", this.executeShortcut, !1); + } + return r(l, null, [{ key: "supportedCommands", get: function() { + return { SHIFT: ["SHIFT"], CMD: ["CMD", "CONTROL", "COMMAND", "WINDOWS", "CTRL"], ALT: ["ALT", "OPTION"] }; + } }, { key: "keyCodes", get: function() { + return { 0: 48, 1: 49, 2: 50, 3: 51, 4: 52, 5: 53, 6: 54, 7: 55, 8: 56, 9: 57, A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90, BACKSPACE: 8, ENTER: 13, ESCAPE: 27, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, INSERT: 45, DELETE: 46, ".": 190 }; + } }]), r(l, [{ key: "parseShortcutName", value: function(c) { + c = c.split("+"); + for (var d = 0; d < c.length; d++) { + c[d] = c[d].toUpperCase(); + var h = !1; + for (var p in l.supportedCommands) + if (l.supportedCommands[p].includes(c[d])) { + h = this.commands[p] = !0; + break; + } + h || (this.keys[c[d]] = !0); + } + for (var g in l.supportedCommands) + this.commands[g] || (this.commands[g] = !1); + } }, { key: "execute", value: function(c) { + var d, h = { CMD: c.ctrlKey || c.metaKey, SHIFT: c.shiftKey, ALT: c.altKey }, p = !0; + for (d in this.commands) + this.commands[d] !== h[d] && (p = !1); + var g, f = !0; + for (g in this.keys) + f = f && c.keyCode === l.keyCodes[g]; + p && f && this.callback(c); + } }, { key: "remove", value: function() { + this.element.removeEventListener("keydown", this.executeShortcut); + } }]), l; + }(); + o.default = a; + }]).default; + }); +})(sn); +var bs = sn.exports; +const vs = /* @__PURE__ */ Ke(bs); +class ks { + constructor() { + this.registeredShortcuts = /* @__PURE__ */ new Map(); + } + /** + * Register shortcut + * + * @param shortcut - shortcut options + */ + add(e) { + if (this.findShortcut(e.on, e.name)) + throw Error( + `Shortcut ${e.name} is already registered for ${e.on}. Please remove it before add a new handler.` + ); + const o = new vs({ + name: e.name, + on: e.on, + callback: e.handler + }), i = this.registeredShortcuts.get(e.on) || []; + this.registeredShortcuts.set(e.on, [...i, o]); + } + /** + * Remove shortcut + * + * @param element - Element shortcut is set for + * @param name - shortcut name + */ + remove(e, t) { + const o = this.findShortcut(e, t); + if (!o) + return; + o.remove(); + const s = this.registeredShortcuts.get(e).filter((r) => r !== o); + if (s.length === 0) { + this.registeredShortcuts.delete(e); + return; + } + this.registeredShortcuts.set(e, s); + } + /** + * Get Shortcut instance if exist + * + * @param element - Element shorcut is set for + * @param shortcut - shortcut name + * @returns {number} index - shortcut index if exist + */ + findShortcut(e, t) { + return (this.registeredShortcuts.get(e) || []).find(({ name: i }) => i === t); + } +} +const ge = new ks(); +var ys = Object.defineProperty, ws = Object.getOwnPropertyDescriptor, rn = (n, e, t, o) => { + for (var i = o > 1 ? void 0 : o ? ws(e, t) : e, s = n.length - 1, r; s >= 0; s--) + (r = n[s]) && (i = (o ? r(e, t, i) : r(i)) || i); + return o && i && ys(e, t, i), i; +}, Le = /* @__PURE__ */ ((n) => (n.Opened = "toolbox-opened", n.Closed = "toolbox-closed", n.BlockAdded = "toolbox-block-added", n))(Le || {}); +const Ct = class an extends Oe { + /** + * Toolbox constructor + * + * @param options - available parameters + * @param options.api - Editor API methods + * @param options.tools - Tools available to check whether some of them should be displayed at the Toolbox or not + */ + constructor({ api: e, tools: t, i18nLabels: o }) { + super(), this.opened = !1, this.listeners = new _e(), this.popover = null, this.handleMobileLayoutToggle = () => { + this.destroyPopover(), this.initPopover(); + }, this.onPopoverClose = () => { + this.opened = !1, this.emit( + "toolbox-closed" + /* Closed */ + ); + }, this.api = e, this.tools = t, this.i18nLabels = o, this.enableShortcuts(), this.nodes = { + toolbox: u.make("div", an.CSS.toolbox) + }, this.initPopover(), this.api.events.on(Te, this.handleMobileLayoutToggle); + } + /** + * Returns True if Toolbox is Empty and nothing to show + * + * @returns {boolean} + */ + get isEmpty() { + return this.toolsToBeDisplayed.length === 0; + } + /** + * CSS styles + */ + static get CSS() { + return { + toolbox: "ce-toolbox" + }; + } + /** + * Returns root block settings element + */ + getElement() { + return this.nodes.toolbox; + } + /** + * Returns true if the Toolbox has the Flipper activated and the Flipper has selected button + */ + hasFocus() { + if (this.popover !== null) + return "hasFocus" in this.popover ? this.popover.hasFocus() : void 0; + } + /** + * Destroy Module + */ + destroy() { + var e; + super.destroy(), this.nodes && this.nodes.toolbox && this.nodes.toolbox.remove(), this.removeAllShortcuts(), (e = this.popover) == null || e.off(G.Closed, this.onPopoverClose), this.listeners.destroy(), this.api.events.off(Te, this.handleMobileLayoutToggle); + } + /** + * Toolbox Tool's button click handler + * + * @param toolName - tool type to be activated + * @param blockDataOverrides - Block data predefined by the activated Toolbox item + */ + toolButtonActivated(e, t) { + this.insertNewBlock(e, t); + } + /** + * Open Toolbox with Tools + */ + open() { + var e; + this.isEmpty || ((e = this.popover) == null || e.show(), this.opened = !0, this.emit( + "toolbox-opened" + /* Opened */ + )); + } + /** + * Close Toolbox + */ + close() { + var e; + (e = this.popover) == null || e.hide(), this.opened = !1, this.emit( + "toolbox-closed" + /* Closed */ + ); + } + /** + * Close Toolbox + */ + toggle() { + this.opened ? this.close() : this.open(); + } + /** + * Creates toolbox popover and appends it inside wrapper element + */ + initPopover() { + var t; + const e = be() ? nn : Bt; + this.popover = new e({ + scopeElement: this.api.ui.nodes.redactor, + searchable: !0, + messages: { + nothingFound: this.i18nLabels.nothingFound, + search: this.i18nLabels.filter + }, + items: this.toolboxItemsToBeDisplayed + }), this.popover.on(G.Closed, this.onPopoverClose), (t = this.nodes.toolbox) == null || t.append(this.popover.getElement()); + } + /** + * Destroys popover instance and removes it from DOM + */ + destroyPopover() { + this.popover !== null && (this.popover.hide(), this.popover.off(G.Closed, this.onPopoverClose), this.popover.destroy(), this.popover = null), this.nodes.toolbox !== null && (this.nodes.toolbox.innerHTML = ""); + } + get toolsToBeDisplayed() { + const e = []; + return this.tools.forEach((t) => { + t.toolbox && e.push(t); + }), e; + } + get toolboxItemsToBeDisplayed() { + const e = (t, o, i = !0) => ({ + icon: t.icon, + title: z.t(K.toolNames, t.title || je(o.name)), + name: o.name, + onActivate: () => { + this.toolButtonActivated(o.name, t.data); + }, + secondaryLabel: o.shortcut && i ? vt(o.shortcut) : "" + }); + return this.toolsToBeDisplayed.reduce((t, o) => (Array.isArray(o.toolbox) ? o.toolbox.forEach((i, s) => { + t.push(e(i, o, s === 0)); + }) : o.toolbox !== void 0 && t.push(e(o.toolbox, o)), t), []); + } + /** + * Iterate all tools and enable theirs shortcuts if specified + */ + enableShortcuts() { + this.toolsToBeDisplayed.forEach((e) => { + const t = e.shortcut; + t && this.enableShortcutForTool(e.name, t); + }); + } + /** + * Enable shortcut Block Tool implemented shortcut + * + * @param {string} toolName - Tool name + * @param {string} shortcut - shortcut according to the ShortcutData Module format + */ + enableShortcutForTool(e, t) { + ge.add({ + name: t, + on: this.api.ui.nodes.redactor, + handler: async (o) => { + o.preventDefault(); + const i = this.api.blocks.getCurrentBlockIndex(), s = this.api.blocks.getBlockByIndex(i); + if (s) + try { + const r = await this.api.blocks.convert(s.id, e); + this.api.caret.setToBlock(r, "end"); + return; + } catch { + } + this.insertNewBlock(e); + } + }); + } + /** + * Removes all added shortcuts + * Fired when the Read-Only mode is activated + */ + removeAllShortcuts() { + this.toolsToBeDisplayed.forEach((e) => { + const t = e.shortcut; + t && ge.remove(this.api.ui.nodes.redactor, t); + }); + } + /** + * Inserts new block + * Can be called when button clicked on Toolbox or by ShortcutData + * + * @param {string} toolName - Tool name + * @param blockDataOverrides - predefined Block data + */ + async insertNewBlock(e, t) { + const o = this.api.blocks.getCurrentBlockIndex(), i = this.api.blocks.getBlockByIndex(o); + if (!i) + return; + const s = i.isEmpty ? o : o + 1; + let r; + if (t) { + const l = await this.api.blocks.composeBlockData(e); + r = Object.assign(l, t); + } + const a = this.api.blocks.insert( + e, + r, + void 0, + s, + void 0, + i.isEmpty + ); + a.call(ee.APPEND_CALLBACK), this.api.caret.setToBlock(s), this.emit("toolbox-block-added", { + block: a + }), this.api.toolbar.close(); + } +}; +rn([ + me +], Ct.prototype, "toolsToBeDisplayed", 1); +rn([ + me +], Ct.prototype, "toolboxItemsToBeDisplayed", 1); +let Es = Ct; +const ln = "block hovered"; +async function xs(n, e) { + const t = navigator.keyboard; + if (!t) + return e; + try { + return (await t.getLayoutMap()).get(n) || e; + } catch (o) { + return console.error(o), e; + } +} +class Bs extends E { + /** + * @class + * @param moduleConfiguration - Module Configuration + * @param moduleConfiguration.config - Editor's config + * @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher + */ + constructor({ config: e, eventsDispatcher: t }) { + super({ + config: e, + eventsDispatcher: t + }), this.toolboxInstance = null; + } + /** + * CSS styles + * + * @returns {object} + */ + get CSS() { + return { + toolbar: "ce-toolbar", + content: "ce-toolbar__content", + actions: "ce-toolbar__actions", + actionsOpened: "ce-toolbar__actions--opened", + toolbarOpened: "ce-toolbar--opened", + openedToolboxHolderModifier: "codex-editor--toolbox-opened", + plusButton: "ce-toolbar__plus", + plusButtonShortcut: "ce-toolbar__plus-shortcut", + settingsToggler: "ce-toolbar__settings-btn", + settingsTogglerHidden: "ce-toolbar__settings-btn--hidden" + }; + } + /** + * Returns the Toolbar opening state + * + * @returns {boolean} + */ + get opened() { + return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened); + } + /** + * Public interface for accessing the Toolbox + */ + get toolbox() { + var e; + return { + opened: (e = this.toolboxInstance) == null ? void 0 : e.opened, + close: () => { + var t; + (t = this.toolboxInstance) == null || t.close(); + }, + open: () => { + if (this.toolboxInstance === null) { + S("toolbox.open() called before initialization is finished", "warn"); + return; + } + this.Editor.BlockManager.currentBlock = this.hoveredBlock, this.toolboxInstance.open(); + }, + toggle: () => { + if (this.toolboxInstance === null) { + S("toolbox.toggle() called before initialization is finished", "warn"); + return; + } + this.toolboxInstance.toggle(); + }, + hasFocus: () => { + var t; + return (t = this.toolboxInstance) == null ? void 0 : t.hasFocus(); + } + }; + } + /** + * Block actions appearance manipulations + */ + get blockActions() { + return { + hide: () => { + this.nodes.actions.classList.remove(this.CSS.actionsOpened); + }, + show: () => { + this.nodes.actions.classList.add(this.CSS.actionsOpened); + } + }; + } + /** + * Methods for working with Block Tunes toggler + */ + get blockTunesToggler() { + return { + hide: () => this.nodes.settingsToggler.classList.add(this.CSS.settingsTogglerHidden), + show: () => this.nodes.settingsToggler.classList.remove(this.CSS.settingsTogglerHidden) + }; + } + /** + * Toggles read-only mode + * + * @param {boolean} readOnlyEnabled - read-only mode + */ + toggleReadOnly(e) { + e ? (this.destroy(), this.Editor.BlockSettings.destroy(), this.disableModuleBindings()) : window.requestIdleCallback(() => { + this.drawUI(), this.enableModuleBindings(); + }, { timeout: 2e3 }); + } + /** + * Move Toolbar to the passed (or current) Block + * + * @param block - block to move Toolbar near it + */ + moveAndOpen(e = this.Editor.BlockManager.currentBlock) { + if (this.toolboxInstance === null) { + S("Can't open Toolbar since Editor initialization is not finished yet", "warn"); + return; + } + if (this.toolboxInstance.opened && this.toolboxInstance.close(), this.Editor.BlockSettings.opened && this.Editor.BlockSettings.close(), !e) + return; + this.hoveredBlock = e; + const t = e.holder, { isMobile: o } = this.Editor.UI; + let i; + const s = 20, r = e.firstInput, a = t.getBoundingClientRect(), l = r !== void 0 ? r.getBoundingClientRect() : null, c = l !== null ? l.top - a.top : null, d = c !== null ? c > s : void 0; + if (o) + i = t.offsetTop + t.offsetHeight; + else if (r === void 0 || d) { + const h = parseInt(window.getComputedStyle(e.pluginsContent).paddingTop); + i = t.offsetTop + h; + } else { + const h = li(r), p = parseInt(window.getComputedStyle(this.nodes.plusButton).height, 10), g = 8; + i = t.offsetTop + h - p + g + c; + } + this.nodes.wrapper.style.top = `${Math.floor(i)}px`, this.Editor.BlockManager.blocks.length === 1 && e.isEmpty ? this.blockTunesToggler.hide() : this.blockTunesToggler.show(), this.open(); + } + /** + * Close the Toolbar + */ + close() { + var e, t; + this.Editor.ReadOnly.isEnabled || ((e = this.nodes.wrapper) == null || e.classList.remove(this.CSS.toolbarOpened), this.blockActions.hide(), (t = this.toolboxInstance) == null || t.close(), this.Editor.BlockSettings.close(), this.reset()); + } + /** + * Reset the Toolbar position to prevent DOM height growth, for example after blocks deletion + */ + reset() { + this.nodes.wrapper.style.top = "unset"; + } + /** + * Open Toolbar with Plus Button and Actions + * + * @param {boolean} withBlockActions - by default, Toolbar opens with Block Actions. + * This flag allows to open Toolbar without Actions. + */ + open(e = !0) { + this.nodes.wrapper.classList.add(this.CSS.toolbarOpened), e ? this.blockActions.show() : this.blockActions.hide(); + } + /** + * Draws Toolbar elements + */ + async make() { + this.nodes.wrapper = u.make("div", this.CSS.toolbar), ["content", "actions"].forEach((s) => { + this.nodes[s] = u.make("div", this.CSS[s]); + }), u.append(this.nodes.wrapper, this.nodes.content), u.append(this.nodes.content, this.nodes.actions), this.nodes.plusButton = u.make("div", this.CSS.plusButton, { + innerHTML: ts + }), u.append(this.nodes.actions, this.nodes.plusButton), this.readOnlyMutableListeners.on(this.nodes.plusButton, "click", () => { + $e(!0), this.plusButtonClicked(); + }, !1); + const e = u.make("div"); + e.appendChild(document.createTextNode(z.ui(K.ui.toolbar.toolbox, "Add"))), e.appendChild(u.make("div", this.CSS.plusButtonShortcut, { + textContent: "/" + })), ze(this.nodes.plusButton, e, { + hidingDelay: 400 + }), this.nodes.settingsToggler = u.make("span", this.CSS.settingsToggler, { + innerHTML: es + }), u.append(this.nodes.actions, this.nodes.settingsToggler); + const t = u.make("div"), o = u.text(z.ui(K.ui.blockTunes.toggler, "Click to tune")), i = await xs("Slash", "/"); + t.appendChild(o), t.appendChild(u.make("div", this.CSS.plusButtonShortcut, { + textContent: vt(`CMD + ${i}`) + })), ze(this.nodes.settingsToggler, t, { + hidingDelay: 400 + }), u.append(this.nodes.actions, this.makeToolbox()), u.append(this.nodes.actions, this.Editor.BlockSettings.getElement()), u.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper); + } + /** + * Creates the Toolbox instance and return it's rendered element + */ + makeToolbox() { + return this.toolboxInstance = new Es({ + api: this.Editor.API.methods, + tools: this.Editor.Tools.blockTools, + i18nLabels: { + filter: z.ui(K.ui.popover, "Filter"), + nothingFound: z.ui(K.ui.popover, "Nothing found") + } + }), this.toolboxInstance.on(Le.Opened, () => { + this.Editor.UI.nodes.wrapper.classList.add(this.CSS.openedToolboxHolderModifier); + }), this.toolboxInstance.on(Le.Closed, () => { + this.Editor.UI.nodes.wrapper.classList.remove(this.CSS.openedToolboxHolderModifier); + }), this.toolboxInstance.on(Le.BlockAdded, ({ block: e }) => { + const { BlockManager: t, Caret: o } = this.Editor, i = t.getBlockById(e.id); + i.inputs.length === 0 && (i === t.lastBlock ? (t.insertAtEnd(), o.setToBlock(t.lastBlock)) : o.setToBlock(t.nextBlock)); + }), this.toolboxInstance.getElement(); + } + /** + * Handler for Plus Button + */ + plusButtonClicked() { + var e; + this.Editor.BlockManager.currentBlock = this.hoveredBlock, (e = this.toolboxInstance) == null || e.toggle(); + } + /** + * Enable bindings + */ + enableModuleBindings() { + this.readOnlyMutableListeners.on(this.nodes.settingsToggler, "mousedown", (e) => { + var t; + e.stopPropagation(), this.settingsTogglerClicked(), (t = this.toolboxInstance) != null && t.opened && this.toolboxInstance.close(), $e(!0); + }, !0), be() || this.eventsDispatcher.on(ln, (e) => { + var t; + this.Editor.BlockSettings.opened || (t = this.toolboxInstance) != null && t.opened || this.moveAndOpen(e.block); + }); + } + /** + * Disable bindings + */ + disableModuleBindings() { + this.readOnlyMutableListeners.clearAll(); + } + /** + * Clicks on the Block Settings toggler + */ + settingsTogglerClicked() { + this.Editor.BlockManager.currentBlock = this.hoveredBlock, this.Editor.BlockSettings.opened ? this.Editor.BlockSettings.close() : this.Editor.BlockSettings.open(this.hoveredBlock); + } + /** + * Draws Toolbar UI + * + * Toolbar contains BlockSettings and Toolbox. + * That's why at first we draw its components and then Toolbar itself + * + * Steps: + * - Make Toolbar dependent components like BlockSettings, Toolbox and so on + * - Make itself and append dependent nodes to itself + * + */ + drawUI() { + this.Editor.BlockSettings.make(), this.make(); + } + /** + * Removes all created and saved HTMLElements + * It is used in Read-Only mode + */ + destroy() { + this.removeAllNodes(), this.toolboxInstance && this.toolboxInstance.destroy(); + } +} +var ae = /* @__PURE__ */ ((n) => (n[n.Block = 0] = "Block", n[n.Inline = 1] = "Inline", n[n.Tune = 2] = "Tune", n))(ae || {}), Pe = /* @__PURE__ */ ((n) => (n.Shortcut = "shortcut", n.Toolbox = "toolbox", n.EnabledInlineTools = "inlineToolbar", n.EnabledBlockTunes = "tunes", n.Config = "config", n))(Pe || {}), cn = /* @__PURE__ */ ((n) => (n.Shortcut = "shortcut", n.SanitizeConfig = "sanitize", n))(cn || {}), pe = /* @__PURE__ */ ((n) => (n.IsEnabledLineBreaks = "enableLineBreaks", n.Toolbox = "toolbox", n.ConversionConfig = "conversionConfig", n.IsReadOnlySupported = "isReadOnlySupported", n.PasteConfig = "pasteConfig", n))(pe || {}), We = /* @__PURE__ */ ((n) => (n.IsInline = "isInline", n.Title = "title", n.IsReadOnlySupported = "isReadOnlySupported", n))(We || {}), mt = /* @__PURE__ */ ((n) => (n.IsTune = "isTune", n))(mt || {}); +class Tt { + /** + * @class + * @param {ConstructorOptions} options - Constructor options + */ + constructor({ + name: e, + constructable: t, + config: o, + api: i, + isDefault: s, + isInternal: r = !1, + defaultPlaceholder: a + }) { + this.api = i, this.name = e, this.constructable = t, this.config = o, this.isDefault = s, this.isInternal = r, this.defaultPlaceholder = a; + } + /** + * Returns Tool user configuration + */ + get settings() { + const e = this.config.config || {}; + return this.isDefault && !("placeholder" in e) && this.defaultPlaceholder && (e.placeholder = this.defaultPlaceholder), e; + } + /** + * Calls Tool's reset method + */ + reset() { + if (A(this.constructable.reset)) + return this.constructable.reset(); + } + /** + * Calls Tool's prepare method + */ + prepare() { + if (A(this.constructable.prepare)) + return this.constructable.prepare({ + toolName: this.name, + config: this.settings + }); + } + /** + * Returns shortcut for Tool (internal or specified by user) + */ + get shortcut() { + const e = this.constructable.shortcut; + return this.config.shortcut || e; + } + /** + * Returns Tool's sanitizer configuration + */ + get sanitizeConfig() { + return this.constructable.sanitize || {}; + } + /** + * Returns true if Tools is inline + */ + isInline() { + return this.type === ae.Inline; + } + /** + * Returns true if Tools is block + */ + isBlock() { + return this.type === ae.Block; + } + /** + * Returns true if Tools is tune + */ + isTune() { + return this.type === ae.Tune; + } +} +class Cs extends E { + /** + * @param moduleConfiguration - Module Configuration + * @param moduleConfiguration.config - Editor's config + * @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher + */ + constructor({ config: e, eventsDispatcher: t }) { + super({ + config: e, + eventsDispatcher: t + }), this.CSS = { + inlineToolbar: "ce-inline-toolbar" + }, this.opened = !1, this.popover = null, this.toolbarVerticalMargin = be() ? 20 : 6, this.tools = /* @__PURE__ */ new Map(), window.requestIdleCallback(() => { + this.make(); + }, { timeout: 2e3 }); + } + /** + * Moving / appearance + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + /** + * Shows Inline Toolbar if something is selected + * + * @param [needToClose] - pass true to close toolbar if it is not allowed. + * Avoid to use it just for closing IT, better call .close() clearly. + */ + async tryToShow(e = !1) { + e && this.close(), this.allowedToShow() && (await this.open(), this.Editor.Toolbar.close()); + } + /** + * Hides Inline Toolbar + */ + close() { + var e, t; + if (this.opened) { + for (const [o, i] of this.tools) { + const s = this.getToolShortcut(o.name); + s !== void 0 && ge.remove(this.Editor.UI.nodes.redactor, s), A(i.clear) && i.clear(); + } + this.tools = /* @__PURE__ */ new Map(), this.reset(), this.opened = !1, (e = this.popover) == null || e.hide(), (t = this.popover) == null || t.destroy(), this.popover = null; + } + } + /** + * Check if node is contained by Inline Toolbar + * + * @param {Node} node — node to check + */ + containsNode(e) { + return this.nodes.wrapper === void 0 ? !1 : this.nodes.wrapper.contains(e); + } + /** + * Removes UI and its components + */ + destroy() { + var e; + this.removeAllNodes(), (e = this.popover) == null || e.destroy(), this.popover = null; + } + /** + * Making DOM + */ + make() { + this.nodes.wrapper = u.make("div", [ + this.CSS.inlineToolbar, + ...this.isRtl ? [this.Editor.UI.CSS.editorRtlFix] : [] + ]), u.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper); + } + /** + * Shows Inline Toolbar + */ + async open() { + var t; + if (this.opened) + return; + this.opened = !0, this.popover !== null && this.popover.destroy(), this.createToolsInstances(); + const e = await this.getPopoverItems(); + this.popover = new hs({ + items: e, + scopeElement: this.Editor.API.methods.ui.nodes.redactor, + messages: { + nothingFound: z.ui(K.ui.popover, "Nothing found"), + search: z.ui(K.ui.popover, "Filter") + } + }), this.move(this.popover.size.width), (t = this.nodes.wrapper) == null || t.append(this.popover.getElement()), this.popover.show(); + } + /** + * Move Toolbar to the selected text + * + * @param popoverWidth - width of the toolbar popover + */ + move(e) { + const t = b.rect, o = this.Editor.UI.nodes.wrapper.getBoundingClientRect(), i = { + x: t.x - o.x, + y: t.y + t.height - // + window.scrollY + o.top + this.toolbarVerticalMargin + }; + i.x + e + o.x > this.Editor.UI.contentRect.right && (i.x = this.Editor.UI.contentRect.right - e - o.x), this.nodes.wrapper.style.left = Math.floor(i.x) + "px", this.nodes.wrapper.style.top = Math.floor(i.y) + "px"; + } + /** + * Clear orientation classes and reset position + */ + reset() { + this.nodes.wrapper.style.left = "0", this.nodes.wrapper.style.top = "0"; + } + /** + * Need to show Inline Toolbar or not + */ + allowedToShow() { + const e = ["IMG", "INPUT"], t = b.get(), o = b.text; + if (!t || !t.anchorNode || t.isCollapsed || o.length < 1) + return !1; + const i = u.isElement(t.anchorNode) ? t.anchorNode : t.anchorNode.parentElement; + if (i === null || t !== null && e.includes(i.tagName)) + return !1; + const s = this.Editor.BlockManager.getBlock(t.anchorNode); + return !s || this.getTools().some((c) => s.tool.inlineTools.has(c.name)) === !1 ? !1 : i.closest("[contenteditable]") !== null; + } + /** + * Working with Tools + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + /** + * Returns tools that are available for current block + * + * Used to check if Inline Toolbar could be shown + * and to render tools in the Inline Toolbar + */ + getTools() { + const e = this.Editor.BlockManager.currentBlock; + return e ? Array.from(e.tool.inlineTools.values()).filter((o) => !(this.Editor.ReadOnly.isEnabled && o.isReadOnlySupported !== !0)) : []; + } + /** + * Constructs tools instances and saves them to this.tools + */ + createToolsInstances() { + this.tools = /* @__PURE__ */ new Map(), this.getTools().forEach((t) => { + const o = t.create(); + this.tools.set(t, o); + }); + } + /** + * Returns Popover Items for tools segregated by their appearance type: regular items and custom html elements. + */ + async getPopoverItems() { + const e = []; + let t = 0; + for (const [o, i] of this.tools) { + const s = await i.render(), r = this.getToolShortcut(o.name); + if (r !== void 0) + try { + this.enableShortcuts(o.name, r); + } catch { + } + const a = r !== void 0 ? vt(r) : void 0, l = z.t( + K.toolNames, + o.title || je(o.name) + ); + [s].flat().forEach((c) => { + var h, p; + const d = { + name: o.name, + onActivate: () => { + this.toolClicked(i); + }, + hint: { + title: l, + description: a + } + }; + if (u.isElement(c)) { + const g = { + ...d, + element: c, + type: _.Html + }; + if (A(i.renderActions)) { + const f = i.renderActions(); + g.children = { + isOpen: (h = i.checkState) == null ? void 0 : h.call(i, b.get()), + /** Disable keyboard navigation in actions, as it might conflict with enter press handling */ + isFlippable: !1, + items: [ + { + type: _.Html, + element: f + } + ] + }; + } else + (p = i.checkState) == null || p.call(i, b.get()); + e.push(g); + } else if (c.type === _.Html) + e.push({ + ...d, + ...c, + type: _.Html + }); + else if (c.type === _.Separator) + e.push({ + type: _.Separator + }); + else { + const g = { + ...d, + ...c, + type: _.Default + }; + "children" in g && t !== 0 && e.push({ + type: _.Separator + }), e.push(g), "children" in g && t < this.tools.size - 1 && e.push({ + type: _.Separator + }); + } + }), t++; + } + return e; + } + /** + * Get shortcut name for tool + * + * @param toolName — Tool name + */ + getToolShortcut(e) { + const { Tools: t } = this.Editor, o = t.inlineTools.get(e), i = t.internal.inlineTools; + return Array.from(i.keys()).includes(e) ? this.inlineTools[e][cn.Shortcut] : o == null ? void 0 : o.shortcut; + } + /** + * Enable Tool shortcut with Editor Shortcuts Module + * + * @param toolName - tool name + * @param shortcut - shortcut according to the ShortcutData Module format + */ + enableShortcuts(e, t) { + ge.add({ + name: t, + handler: (o) => { + var s; + const { currentBlock: i } = this.Editor.BlockManager; + i && i.tool.enabledInlineTools && (o.preventDefault(), (s = this.popover) == null || s.activateItemByName(e)); + }, + /** + * We need to bind shortcut to the document to make it work in read-only mode + */ + on: document + }); + } + /** + * Inline Tool button clicks + * + * @param tool - Tool's instance + */ + toolClicked(e) { + var o; + const t = b.range; + (o = e.surround) == null || o.call(e, t), this.checkToolsState(); + } + /** + * Check Tools` state by selection + */ + checkToolsState() { + var e; + (e = this.tools) == null || e.forEach((t) => { + var o; + (o = t.checkState) == null || o.call(t, b.get()); + }); + } + /** + * Get inline tools tools + * Tools that has isInline is true + */ + get inlineTools() { + const e = {}; + return Array.from(this.Editor.Tools.inlineTools.entries()).forEach(([t, o]) => { + e[t] = o.create(); + }), e; + } +} +function dn() { + const n = window.getSelection(); + if (n === null) + return [null, 0]; + let e = n.focusNode, t = n.focusOffset; + return e === null ? [null, 0] : (e.nodeType !== Node.TEXT_NODE && e.childNodes.length > 0 && (e.childNodes[t] ? (e = e.childNodes[t], t = 0) : (e = e.childNodes[t - 1], t = e.textContent.length)), [e, t]); +} +function un(n, e, t, o) { + const i = document.createRange(); + o === "left" ? (i.setStart(n, 0), i.setEnd(e, t)) : (i.setStart(e, t), i.setEnd(n, n.childNodes.length)); + const s = i.cloneContents(), r = document.createElement("div"); + r.appendChild(s); + const a = r.textContent || ""; + return ai(a); +} +function Ne(n) { + const e = u.getDeepestNode(n); + if (e === null || u.isEmpty(n)) + return !0; + if (u.isNativeInput(e)) + return e.selectionEnd === 0; + if (u.isEmpty(n)) + return !0; + const [t, o] = dn(); + return t === null ? !1 : un(n, t, o, "left"); +} +function Re(n) { + const e = u.getDeepestNode(n, !0); + if (e === null) + return !0; + if (u.isNativeInput(e)) + return e.selectionEnd === e.value.length; + const [t, o] = dn(); + return t === null ? !1 : un(n, t, o, "right"); +} +var hn = {}, St = {}, Xe = {}, de = {}, It = {}, Ot = {}; +Object.defineProperty(Ot, "__esModule", { value: !0 }); +Ot.allInputsSelector = Ts; +function Ts() { + var n = ["text", "password", "email", "number", "search", "tel", "url"]; + return "[contenteditable=true], textarea, input:not([type]), " + n.map(function(e) { + return 'input[type="'.concat(e, '"]'); + }).join(", "); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.allInputsSelector = void 0; + var e = Ot; + Object.defineProperty(n, "allInputsSelector", { enumerable: !0, get: function() { + return e.allInputsSelector; + } }); +})(It); +var ue = {}, _t = {}; +Object.defineProperty(_t, "__esModule", { value: !0 }); +_t.isNativeInput = Ss; +function Ss(n) { + var e = [ + "INPUT", + "TEXTAREA" + ]; + return n && n.tagName ? e.includes(n.tagName) : !1; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isNativeInput = void 0; + var e = _t; + Object.defineProperty(n, "isNativeInput", { enumerable: !0, get: function() { + return e.isNativeInput; + } }); +})(ue); +var pn = {}, Mt = {}; +Object.defineProperty(Mt, "__esModule", { value: !0 }); +Mt.append = Is; +function Is(n, e) { + Array.isArray(e) ? e.forEach(function(t) { + n.appendChild(t); + }) : n.appendChild(e); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.append = void 0; + var e = Mt; + Object.defineProperty(n, "append", { enumerable: !0, get: function() { + return e.append; + } }); +})(pn); +var At = {}, Lt = {}; +Object.defineProperty(Lt, "__esModule", { value: !0 }); +Lt.blockElements = Os; +function Os() { + return [ + "address", + "article", + "aside", + "blockquote", + "canvas", + "div", + "dl", + "dt", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "header", + "hgroup", + "hr", + "li", + "main", + "nav", + "noscript", + "ol", + "output", + "p", + "pre", + "ruby", + "section", + "table", + "tbody", + "thead", + "tr", + "tfoot", + "ul", + "video" + ]; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.blockElements = void 0; + var e = Lt; + Object.defineProperty(n, "blockElements", { enumerable: !0, get: function() { + return e.blockElements; + } }); +})(At); +var fn = {}, Pt = {}; +Object.defineProperty(Pt, "__esModule", { value: !0 }); +Pt.calculateBaseline = _s; +function _s(n) { + var e = window.getComputedStyle(n), t = parseFloat(e.fontSize), o = parseFloat(e.lineHeight) || t * 1.2, i = parseFloat(e.paddingTop), s = parseFloat(e.borderTopWidth), r = parseFloat(e.marginTop), a = t * 0.8, l = (o - t) / 2, c = r + s + i + l + a; + return c; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.calculateBaseline = void 0; + var e = Pt; + Object.defineProperty(n, "calculateBaseline", { enumerable: !0, get: function() { + return e.calculateBaseline; + } }); +})(fn); +var gn = {}, Nt = {}, Rt = {}, Dt = {}; +Object.defineProperty(Dt, "__esModule", { value: !0 }); +Dt.isContentEditable = Ms; +function Ms(n) { + return n.contentEditable === "true"; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isContentEditable = void 0; + var e = Dt; + Object.defineProperty(n, "isContentEditable", { enumerable: !0, get: function() { + return e.isContentEditable; + } }); +})(Rt); +Object.defineProperty(Nt, "__esModule", { value: !0 }); +Nt.canSetCaret = Ps; +var As = ue, Ls = Rt; +function Ps(n) { + var e = !0; + if ((0, As.isNativeInput)(n)) + switch (n.type) { + case "file": + case "checkbox": + case "radio": + case "hidden": + case "submit": + case "button": + case "image": + case "reset": + e = !1; + break; + } + else + e = (0, Ls.isContentEditable)(n); + return e; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.canSetCaret = void 0; + var e = Nt; + Object.defineProperty(n, "canSetCaret", { enumerable: !0, get: function() { + return e.canSetCaret; + } }); +})(gn); +var Ve = {}, Ft = {}; +function Ns(n, e, t) { + const o = t.value !== void 0 ? "value" : "get", i = t[o], s = `#${e}Cache`; + if (t[o] = function(...r) { + return this[s] === void 0 && (this[s] = i.apply(this, r)), this[s]; + }, o === "get" && t.set) { + const r = t.set; + t.set = function(a) { + delete n[s], r.apply(this, a); + }; + } + return t; +} +function mn() { + const n = { + win: !1, + mac: !1, + x11: !1, + linux: !1 + }, e = Object.keys(n).find((t) => window.navigator.appVersion.toLowerCase().indexOf(t) !== -1); + return e !== void 0 && (n[e] = !0), n; +} +function jt(n) { + return n != null && n !== "" && (typeof n != "object" || Object.keys(n).length > 0); +} +function Rs(n) { + return !jt(n); +} +const Ds = () => typeof window < "u" && window.navigator !== null && jt(window.navigator.platform) && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === "MacIntel" && window.navigator.maxTouchPoints > 1); +function Fs(n) { + const e = mn(); + return n = n.replace(/shift/gi, "⇧").replace(/backspace/gi, "⌫").replace(/enter/gi, "⏎").replace(/up/gi, "↑").replace(/left/gi, "→").replace(/down/gi, "↓").replace(/right/gi, "←").replace(/escape/gi, "⎋").replace(/insert/gi, "Ins").replace(/delete/gi, "␡").replace(/\+/gi, "+"), e.mac ? n = n.replace(/ctrl|cmd/gi, "⌘").replace(/alt/gi, "⌥") : n = n.replace(/cmd/gi, "Ctrl").replace(/windows/gi, "WIN"), n; +} +function js(n) { + return n[0].toUpperCase() + n.slice(1); +} +function Hs(n) { + const e = document.createElement("div"); + e.style.position = "absolute", e.style.left = "-999px", e.style.bottom = "-999px", e.innerHTML = n, document.body.appendChild(e); + const t = window.getSelection(), o = document.createRange(); + if (o.selectNode(e), t === null) + throw new Error("Cannot copy text to clipboard"); + t.removeAllRanges(), t.addRange(o), document.execCommand("copy"), document.body.removeChild(e); +} +function $s(n, e, t) { + let o; + return (...i) => { + const s = this, r = () => { + o = void 0, t !== !0 && n.apply(s, i); + }, a = t === !0 && o !== void 0; + window.clearTimeout(o), o = window.setTimeout(r, e), a && n.apply(s, i); + }; +} +function oe(n) { + return Object.prototype.toString.call(n).match(/\s([a-zA-Z]+)/)[1].toLowerCase(); +} +function zs(n) { + return oe(n) === "boolean"; +} +function bn(n) { + return oe(n) === "function" || oe(n) === "asyncfunction"; +} +function Us(n) { + return bn(n) && /^\s*class\s+/.test(n.toString()); +} +function Ws(n) { + return oe(n) === "number"; +} +function De(n) { + return oe(n) === "object"; +} +function Ys(n) { + return Promise.resolve(n) === n; +} +function Ks(n) { + return oe(n) === "string"; +} +function Xs(n) { + return oe(n) === "undefined"; +} +function bt(n, ...e) { + if (!e.length) + return n; + const t = e.shift(); + if (De(n) && De(t)) + for (const o in t) + De(t[o]) ? (n[o] === void 0 && Object.assign(n, { [o]: {} }), bt(n[o], t[o])) : Object.assign(n, { [o]: t[o] }); + return bt(n, ...e); +} +function Vs(n, e, t) { + const o = `«${e}» is deprecated and will be removed in the next major release. Please use the «${t}» instead.`; + n && console.warn(o); +} +function qs(n) { + try { + return new URL(n).href; + } catch { + } + return n.substring(0, 2) === "//" ? window.location.protocol + n : window.location.origin + n; +} +function Zs(n) { + return n > 47 && n < 58 || n === 32 || n === 13 || n === 229 || n > 64 && n < 91 || n > 95 && n < 112 || n > 185 && n < 193 || n > 218 && n < 223; +} +const Gs = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + SHIFT: 16, + CTRL: 17, + ALT: 18, + ESC: 27, + SPACE: 32, + LEFT: 37, + UP: 38, + DOWN: 40, + RIGHT: 39, + DELETE: 46, + META: 91, + SLASH: 191 +}, Qs = { + LEFT: 0, + WHEEL: 1, + RIGHT: 2, + BACKWARD: 3, + FORWARD: 4 +}; +let Js = class { + constructor() { + this.completed = Promise.resolve(); + } + /** + * Add new promise to queue + * @param operation - promise should be added to queue + */ + add(e) { + return new Promise((t, o) => { + this.completed = this.completed.then(e).then(t).catch(o); + }); + } +}; +function er(n, e, t = void 0) { + let o, i, s, r = null, a = 0; + t || (t = {}); + const l = function() { + a = t.leading === !1 ? 0 : Date.now(), r = null, s = n.apply(o, i), r === null && (o = i = null); + }; + return function() { + const c = Date.now(); + !a && t.leading === !1 && (a = c); + const d = e - (c - a); + return o = this, i = arguments, d <= 0 || d > e ? (r && (clearTimeout(r), r = null), a = c, s = n.apply(o, i), r === null && (o = i = null)) : !r && t.trailing !== !1 && (r = setTimeout(l, d)), s; + }; +} +const tr = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ + __proto__: null, + PromiseQueue: Js, + beautifyShortcut: Fs, + cacheable: Ns, + capitalize: js, + copyTextToClipboard: Hs, + debounce: $s, + deepMerge: bt, + deprecationAssert: Vs, + getUserOS: mn, + getValidUrl: qs, + isBoolean: zs, + isClass: Us, + isEmpty: Rs, + isFunction: bn, + isIosDevice: Ds, + isNumber: Ws, + isObject: De, + isPrintableKey: Zs, + isPromise: Ys, + isString: Ks, + isUndefined: Xs, + keyCodes: Gs, + mouseButtons: Qs, + notEmpty: jt, + throttle: er, + typeOf: oe +}, Symbol.toStringTag, { value: "Module" })), Ht = /* @__PURE__ */ Xn(tr); +Object.defineProperty(Ft, "__esModule", { value: !0 }); +Ft.containsOnlyInlineElements = ir; +var or = Ht, nr = At; +function ir(n) { + var e; + (0, or.isString)(n) ? (e = document.createElement("div"), e.innerHTML = n) : e = n; + var t = function(o) { + return !(0, nr.blockElements)().includes(o.tagName.toLowerCase()) && Array.from(o.children).every(t); + }; + return Array.from(e.children).every(t); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.containsOnlyInlineElements = void 0; + var e = Ft; + Object.defineProperty(n, "containsOnlyInlineElements", { enumerable: !0, get: function() { + return e.containsOnlyInlineElements; + } }); +})(Ve); +var vn = {}, $t = {}, qe = {}, zt = {}; +Object.defineProperty(zt, "__esModule", { value: !0 }); +zt.make = sr; +function sr(n, e, t) { + var o; + e === void 0 && (e = null), t === void 0 && (t = {}); + var i = document.createElement(n); + if (Array.isArray(e)) { + var s = e.filter(function(a) { + return a !== void 0; + }); + (o = i.classList).add.apply(o, s); + } else + e !== null && i.classList.add(e); + for (var r in t) + Object.prototype.hasOwnProperty.call(t, r) && (i[r] = t[r]); + return i; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.make = void 0; + var e = zt; + Object.defineProperty(n, "make", { enumerable: !0, get: function() { + return e.make; + } }); +})(qe); +Object.defineProperty($t, "__esModule", { value: !0 }); +$t.fragmentToString = ar; +var rr = qe; +function ar(n) { + var e = (0, rr.make)("div"); + return e.appendChild(n), e.innerHTML; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.fragmentToString = void 0; + var e = $t; + Object.defineProperty(n, "fragmentToString", { enumerable: !0, get: function() { + return e.fragmentToString; + } }); +})(vn); +var kn = {}, Ut = {}; +Object.defineProperty(Ut, "__esModule", { value: !0 }); +Ut.getContentLength = cr; +var lr = ue; +function cr(n) { + var e, t; + return (0, lr.isNativeInput)(n) ? n.value.length : n.nodeType === Node.TEXT_NODE ? n.length : (t = (e = n.textContent) === null || e === void 0 ? void 0 : e.length) !== null && t !== void 0 ? t : 0; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.getContentLength = void 0; + var e = Ut; + Object.defineProperty(n, "getContentLength", { enumerable: !0, get: function() { + return e.getContentLength; + } }); +})(kn); +var Wt = {}, Yt = {}, Io = Ce && Ce.__spreadArray || function(n, e, t) { + if (t || arguments.length === 2) + for (var o = 0, i = e.length, s; o < i; o++) + (s || !(o in e)) && (s || (s = Array.prototype.slice.call(e, 0, o)), s[o] = e[o]); + return n.concat(s || Array.prototype.slice.call(e)); +}; +Object.defineProperty(Yt, "__esModule", { value: !0 }); +Yt.getDeepestBlockElements = yn; +var dr = Ve; +function yn(n) { + return (0, dr.containsOnlyInlineElements)(n) ? [n] : Array.from(n.children).reduce(function(e, t) { + return Io(Io([], e, !0), yn(t), !0); + }, []); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.getDeepestBlockElements = void 0; + var e = Yt; + Object.defineProperty(n, "getDeepestBlockElements", { enumerable: !0, get: function() { + return e.getDeepestBlockElements; + } }); +})(Wt); +var wn = {}, Kt = {}, Ze = {}, Xt = {}; +Object.defineProperty(Xt, "__esModule", { value: !0 }); +Xt.isLineBreakTag = ur; +function ur(n) { + return [ + "BR", + "WBR" + ].includes(n.tagName); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isLineBreakTag = void 0; + var e = Xt; + Object.defineProperty(n, "isLineBreakTag", { enumerable: !0, get: function() { + return e.isLineBreakTag; + } }); +})(Ze); +var Ge = {}, Vt = {}; +Object.defineProperty(Vt, "__esModule", { value: !0 }); +Vt.isSingleTag = hr; +function hr(n) { + return [ + "AREA", + "BASE", + "BR", + "COL", + "COMMAND", + "EMBED", + "HR", + "IMG", + "INPUT", + "KEYGEN", + "LINK", + "META", + "PARAM", + "SOURCE", + "TRACK", + "WBR" + ].includes(n.tagName); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isSingleTag = void 0; + var e = Vt; + Object.defineProperty(n, "isSingleTag", { enumerable: !0, get: function() { + return e.isSingleTag; + } }); +})(Ge); +Object.defineProperty(Kt, "__esModule", { value: !0 }); +Kt.getDeepestNode = En; +var pr = ue, fr = Ze, gr = Ge; +function En(n, e) { + e === void 0 && (e = !1); + var t = e ? "lastChild" : "firstChild", o = e ? "previousSibling" : "nextSibling"; + if (n.nodeType === Node.ELEMENT_NODE && n[t]) { + var i = n[t]; + if ((0, gr.isSingleTag)(i) && !(0, pr.isNativeInput)(i) && !(0, fr.isLineBreakTag)(i)) + if (i[o]) + i = i[o]; + else if (i.parentNode !== null && i.parentNode[o]) + i = i.parentNode[o]; + else + return i.parentNode; + return En(i, e); + } + return n; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.getDeepestNode = void 0; + var e = Kt; + Object.defineProperty(n, "getDeepestNode", { enumerable: !0, get: function() { + return e.getDeepestNode; + } }); +})(wn); +var xn = {}, qt = {}, Me = Ce && Ce.__spreadArray || function(n, e, t) { + if (t || arguments.length === 2) + for (var o = 0, i = e.length, s; o < i; o++) + (s || !(o in e)) && (s || (s = Array.prototype.slice.call(e, 0, o)), s[o] = e[o]); + return n.concat(s || Array.prototype.slice.call(e)); +}; +Object.defineProperty(qt, "__esModule", { value: !0 }); +qt.findAllInputs = yr; +var mr = Ve, br = Wt, vr = It, kr = ue; +function yr(n) { + return Array.from(n.querySelectorAll((0, vr.allInputsSelector)())).reduce(function(e, t) { + return (0, kr.isNativeInput)(t) || (0, mr.containsOnlyInlineElements)(t) ? Me(Me([], e, !0), [t], !1) : Me(Me([], e, !0), (0, br.getDeepestBlockElements)(t), !0); + }, []); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.findAllInputs = void 0; + var e = qt; + Object.defineProperty(n, "findAllInputs", { enumerable: !0, get: function() { + return e.findAllInputs; + } }); +})(xn); +var Bn = {}, Zt = {}; +Object.defineProperty(Zt, "__esModule", { value: !0 }); +Zt.isCollapsedWhitespaces = wr; +function wr(n) { + return !/[^\t\n\r ]/.test(n); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isCollapsedWhitespaces = void 0; + var e = Zt; + Object.defineProperty(n, "isCollapsedWhitespaces", { enumerable: !0, get: function() { + return e.isCollapsedWhitespaces; + } }); +})(Bn); +var Gt = {}, Qt = {}; +Object.defineProperty(Qt, "__esModule", { value: !0 }); +Qt.isElement = xr; +var Er = Ht; +function xr(n) { + return (0, Er.isNumber)(n) ? !1 : !!n && !!n.nodeType && n.nodeType === Node.ELEMENT_NODE; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isElement = void 0; + var e = Qt; + Object.defineProperty(n, "isElement", { enumerable: !0, get: function() { + return e.isElement; + } }); +})(Gt); +var Cn = {}, Jt = {}, eo = {}, to = {}; +Object.defineProperty(to, "__esModule", { value: !0 }); +to.isLeaf = Br; +function Br(n) { + return n === null ? !1 : n.childNodes.length === 0; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isLeaf = void 0; + var e = to; + Object.defineProperty(n, "isLeaf", { enumerable: !0, get: function() { + return e.isLeaf; + } }); +})(eo); +var oo = {}, no = {}; +Object.defineProperty(no, "__esModule", { value: !0 }); +no.isNodeEmpty = Or; +var Cr = Ze, Tr = Gt, Sr = ue, Ir = Ge; +function Or(n, e) { + var t = ""; + return (0, Ir.isSingleTag)(n) && !(0, Cr.isLineBreakTag)(n) ? !1 : ((0, Tr.isElement)(n) && (0, Sr.isNativeInput)(n) ? t = n.value : n.textContent !== null && (t = n.textContent.replace("​", "")), e !== void 0 && (t = t.replace(new RegExp(e, "g"), "")), t.trim().length === 0); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isNodeEmpty = void 0; + var e = no; + Object.defineProperty(n, "isNodeEmpty", { enumerable: !0, get: function() { + return e.isNodeEmpty; + } }); +})(oo); +Object.defineProperty(Jt, "__esModule", { value: !0 }); +Jt.isEmpty = Ar; +var _r = eo, Mr = oo; +function Ar(n, e) { + n.normalize(); + for (var t = [n]; t.length > 0; ) { + var o = t.shift(); + if (o) { + if (n = o, (0, _r.isLeaf)(n) && !(0, Mr.isNodeEmpty)(n, e)) + return !1; + t.push.apply(t, Array.from(n.childNodes)); + } + } + return !0; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isEmpty = void 0; + var e = Jt; + Object.defineProperty(n, "isEmpty", { enumerable: !0, get: function() { + return e.isEmpty; + } }); +})(Cn); +var Tn = {}, io = {}; +Object.defineProperty(io, "__esModule", { value: !0 }); +io.isFragment = Pr; +var Lr = Ht; +function Pr(n) { + return (0, Lr.isNumber)(n) ? !1 : !!n && !!n.nodeType && n.nodeType === Node.DOCUMENT_FRAGMENT_NODE; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isFragment = void 0; + var e = io; + Object.defineProperty(n, "isFragment", { enumerable: !0, get: function() { + return e.isFragment; + } }); +})(Tn); +var Sn = {}, so = {}; +Object.defineProperty(so, "__esModule", { value: !0 }); +so.isHTMLString = Rr; +var Nr = qe; +function Rr(n) { + var e = (0, Nr.make)("div"); + return e.innerHTML = n, e.childElementCount > 0; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isHTMLString = void 0; + var e = so; + Object.defineProperty(n, "isHTMLString", { enumerable: !0, get: function() { + return e.isHTMLString; + } }); +})(Sn); +var In = {}, ro = {}; +Object.defineProperty(ro, "__esModule", { value: !0 }); +ro.offset = Dr; +function Dr(n) { + var e = n.getBoundingClientRect(), t = window.pageXOffset || document.documentElement.scrollLeft, o = window.pageYOffset || document.documentElement.scrollTop, i = e.top + o, s = e.left + t; + return { + top: i, + left: s, + bottom: i + e.height, + right: s + e.width + }; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.offset = void 0; + var e = ro; + Object.defineProperty(n, "offset", { enumerable: !0, get: function() { + return e.offset; + } }); +})(In); +var On = {}, ao = {}; +Object.defineProperty(ao, "__esModule", { value: !0 }); +ao.prepend = Fr; +function Fr(n, e) { + Array.isArray(e) ? (e = e.reverse(), e.forEach(function(t) { + return n.prepend(t); + })) : n.prepend(e); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.prepend = void 0; + var e = ao; + Object.defineProperty(n, "prepend", { enumerable: !0, get: function() { + return e.prepend; + } }); +})(On); +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.prepend = n.offset = n.make = n.isLineBreakTag = n.isSingleTag = n.isNodeEmpty = n.isLeaf = n.isHTMLString = n.isFragment = n.isEmpty = n.isElement = n.isContentEditable = n.isCollapsedWhitespaces = n.findAllInputs = n.isNativeInput = n.allInputsSelector = n.getDeepestNode = n.getDeepestBlockElements = n.getContentLength = n.fragmentToString = n.containsOnlyInlineElements = n.canSetCaret = n.calculateBaseline = n.blockElements = n.append = void 0; + var e = It; + Object.defineProperty(n, "allInputsSelector", { enumerable: !0, get: function() { + return e.allInputsSelector; + } }); + var t = ue; + Object.defineProperty(n, "isNativeInput", { enumerable: !0, get: function() { + return t.isNativeInput; + } }); + var o = pn; + Object.defineProperty(n, "append", { enumerable: !0, get: function() { + return o.append; + } }); + var i = At; + Object.defineProperty(n, "blockElements", { enumerable: !0, get: function() { + return i.blockElements; + } }); + var s = fn; + Object.defineProperty(n, "calculateBaseline", { enumerable: !0, get: function() { + return s.calculateBaseline; + } }); + var r = gn; + Object.defineProperty(n, "canSetCaret", { enumerable: !0, get: function() { + return r.canSetCaret; + } }); + var a = Ve; + Object.defineProperty(n, "containsOnlyInlineElements", { enumerable: !0, get: function() { + return a.containsOnlyInlineElements; + } }); + var l = vn; + Object.defineProperty(n, "fragmentToString", { enumerable: !0, get: function() { + return l.fragmentToString; + } }); + var c = kn; + Object.defineProperty(n, "getContentLength", { enumerable: !0, get: function() { + return c.getContentLength; + } }); + var d = Wt; + Object.defineProperty(n, "getDeepestBlockElements", { enumerable: !0, get: function() { + return d.getDeepestBlockElements; + } }); + var h = wn; + Object.defineProperty(n, "getDeepestNode", { enumerable: !0, get: function() { + return h.getDeepestNode; + } }); + var p = xn; + Object.defineProperty(n, "findAllInputs", { enumerable: !0, get: function() { + return p.findAllInputs; + } }); + var g = Bn; + Object.defineProperty(n, "isCollapsedWhitespaces", { enumerable: !0, get: function() { + return g.isCollapsedWhitespaces; + } }); + var f = Rt; + Object.defineProperty(n, "isContentEditable", { enumerable: !0, get: function() { + return f.isContentEditable; + } }); + var v = Gt; + Object.defineProperty(n, "isElement", { enumerable: !0, get: function() { + return v.isElement; + } }); + var O = Cn; + Object.defineProperty(n, "isEmpty", { enumerable: !0, get: function() { + return O.isEmpty; + } }); + var T = Tn; + Object.defineProperty(n, "isFragment", { enumerable: !0, get: function() { + return T.isFragment; + } }); + var M = Sn; + Object.defineProperty(n, "isHTMLString", { enumerable: !0, get: function() { + return M.isHTMLString; + } }); + var q = eo; + Object.defineProperty(n, "isLeaf", { enumerable: !0, get: function() { + return q.isLeaf; + } }); + var F = oo; + Object.defineProperty(n, "isNodeEmpty", { enumerable: !0, get: function() { + return F.isNodeEmpty; + } }); + var H = Ze; + Object.defineProperty(n, "isLineBreakTag", { enumerable: !0, get: function() { + return H.isLineBreakTag; + } }); + var Q = Ge; + Object.defineProperty(n, "isSingleTag", { enumerable: !0, get: function() { + return Q.isSingleTag; + } }); + var ie = qe; + Object.defineProperty(n, "make", { enumerable: !0, get: function() { + return ie.make; + } }); + var k = In; + Object.defineProperty(n, "offset", { enumerable: !0, get: function() { + return k.offset; + } }); + var m = On; + Object.defineProperty(n, "prepend", { enumerable: !0, get: function() { + return m.prepend; + } }); +})(de); +var Qe = {}; +Object.defineProperty(Qe, "__esModule", { value: !0 }); +Qe.getContenteditableSlice = Hr; +var jr = de; +function Hr(n, e, t, o, i) { + var s; + i === void 0 && (i = !1); + var r = document.createRange(); + if (o === "left" ? (r.setStart(n, 0), r.setEnd(e, t)) : (r.setStart(e, t), r.setEnd(n, n.childNodes.length)), i === !0) { + var a = r.extractContents(); + return (0, jr.fragmentToString)(a); + } + var l = r.cloneContents(), c = document.createElement("div"); + c.appendChild(l); + var d = (s = c.textContent) !== null && s !== void 0 ? s : ""; + return d; +} +Object.defineProperty(Xe, "__esModule", { value: !0 }); +Xe.checkContenteditableSliceForEmptiness = Ur; +var $r = de, zr = Qe; +function Ur(n, e, t, o) { + var i = (0, zr.getContenteditableSlice)(n, e, t, o); + return (0, $r.isCollapsedWhitespaces)(i); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.checkContenteditableSliceForEmptiness = void 0; + var e = Xe; + Object.defineProperty(n, "checkContenteditableSliceForEmptiness", { enumerable: !0, get: function() { + return e.checkContenteditableSliceForEmptiness; + } }); +})(St); +var _n = {}; +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.getContenteditableSlice = void 0; + var e = Qe; + Object.defineProperty(n, "getContenteditableSlice", { enumerable: !0, get: function() { + return e.getContenteditableSlice; + } }); +})(_n); +var Mn = {}, lo = {}; +Object.defineProperty(lo, "__esModule", { value: !0 }); +lo.focus = Yr; +var Wr = de; +function Yr(n, e) { + var t, o; + if (e === void 0 && (e = !0), (0, Wr.isNativeInput)(n)) { + n.focus(); + var i = e ? 0 : n.value.length; + n.setSelectionRange(i, i); + } else { + var s = document.createRange(), r = window.getSelection(); + if (!r) + return; + var a = function(p) { + var g = document.createTextNode(""); + p.appendChild(g), s.setStart(g, 0), s.setEnd(g, 0); + }, l = function(p) { + return p != null; + }, c = n.childNodes, d = e ? c[0] : c[c.length - 1]; + if (l(d)) { + for (; l(d) && d.nodeType !== Node.TEXT_NODE; ) + d = e ? d.firstChild : d.lastChild; + if (l(d) && d.nodeType === Node.TEXT_NODE) { + var h = (o = (t = d.textContent) === null || t === void 0 ? void 0 : t.length) !== null && o !== void 0 ? o : 0, i = e ? 0 : h; + s.setStart(d, i), s.setEnd(d, i); + } else + a(n); + } else + a(n); + r.removeAllRanges(), r.addRange(s); + } +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.focus = void 0; + var e = lo; + Object.defineProperty(n, "focus", { enumerable: !0, get: function() { + return e.focus; + } }); +})(Mn); +var co = {}, Je = {}; +Object.defineProperty(Je, "__esModule", { value: !0 }); +Je.getCaretNodeAndOffset = Kr; +function Kr() { + var n = window.getSelection(); + if (n === null) + return [null, 0]; + var e = n.focusNode, t = n.focusOffset; + return e === null ? [null, 0] : (e.nodeType !== Node.TEXT_NODE && e.childNodes.length > 0 && (e.childNodes[t] !== void 0 ? (e = e.childNodes[t], t = 0) : (e = e.childNodes[t - 1], e.textContent !== null && (t = e.textContent.length))), [e, t]); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.getCaretNodeAndOffset = void 0; + var e = Je; + Object.defineProperty(n, "getCaretNodeAndOffset", { enumerable: !0, get: function() { + return e.getCaretNodeAndOffset; + } }); +})(co); +var An = {}, et = {}; +Object.defineProperty(et, "__esModule", { value: !0 }); +et.getRange = Xr; +function Xr() { + var n = window.getSelection(); + return n && n.rangeCount ? n.getRangeAt(0) : null; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.getRange = void 0; + var e = et; + Object.defineProperty(n, "getRange", { enumerable: !0, get: function() { + return e.getRange; + } }); +})(An); +var Ln = {}, uo = {}; +Object.defineProperty(uo, "__esModule", { value: !0 }); +uo.isCaretAtEndOfInput = Zr; +var Oo = de, Vr = co, qr = St; +function Zr(n) { + var e = (0, Oo.getDeepestNode)(n, !0); + if (e === null) + return !0; + if ((0, Oo.isNativeInput)(e)) + return e.selectionEnd === e.value.length; + var t = (0, Vr.getCaretNodeAndOffset)(), o = t[0], i = t[1]; + return o === null ? !1 : (0, qr.checkContenteditableSliceForEmptiness)(n, o, i, "right"); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isCaretAtEndOfInput = void 0; + var e = uo; + Object.defineProperty(n, "isCaretAtEndOfInput", { enumerable: !0, get: function() { + return e.isCaretAtEndOfInput; + } }); +})(Ln); +var Pn = {}, ho = {}; +Object.defineProperty(ho, "__esModule", { value: !0 }); +ho.isCaretAtStartOfInput = Jr; +var Ae = de, Gr = Je, Qr = Xe; +function Jr(n) { + var e = (0, Ae.getDeepestNode)(n); + if (e === null || (0, Ae.isEmpty)(n)) + return !0; + if ((0, Ae.isNativeInput)(e)) + return e.selectionEnd === 0; + if ((0, Ae.isEmpty)(n)) + return !0; + var t = (0, Gr.getCaretNodeAndOffset)(), o = t[0], i = t[1]; + return o === null ? !1 : (0, Qr.checkContenteditableSliceForEmptiness)(n, o, i, "left"); +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.isCaretAtStartOfInput = void 0; + var e = ho; + Object.defineProperty(n, "isCaretAtStartOfInput", { enumerable: !0, get: function() { + return e.isCaretAtStartOfInput; + } }); +})(Pn); +var Nn = {}, po = {}; +Object.defineProperty(po, "__esModule", { value: !0 }); +po.save = oa; +var ea = de, ta = et; +function oa() { + var n = (0, ta.getRange)(), e = (0, ea.make)("span"); + if (e.id = "cursor", e.hidden = !0, !!n) + return n.insertNode(e), function() { + var o = window.getSelection(); + o && (n.setStartAfter(e), n.setEndAfter(e), o.removeAllRanges(), o.addRange(n), setTimeout(function() { + e.remove(); + }, 150)); + }; +} +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.save = void 0; + var e = po; + Object.defineProperty(n, "save", { enumerable: !0, get: function() { + return e.save; + } }); +})(Nn); +(function(n) { + Object.defineProperty(n, "__esModule", { value: !0 }), n.save = n.isCaretAtStartOfInput = n.isCaretAtEndOfInput = n.getRange = n.getCaretNodeAndOffset = n.focus = n.getContenteditableSlice = n.checkContenteditableSliceForEmptiness = void 0; + var e = St; + Object.defineProperty(n, "checkContenteditableSliceForEmptiness", { enumerable: !0, get: function() { + return e.checkContenteditableSliceForEmptiness; + } }); + var t = _n; + Object.defineProperty(n, "getContenteditableSlice", { enumerable: !0, get: function() { + return t.getContenteditableSlice; + } }); + var o = Mn; + Object.defineProperty(n, "focus", { enumerable: !0, get: function() { + return o.focus; + } }); + var i = co; + Object.defineProperty(n, "getCaretNodeAndOffset", { enumerable: !0, get: function() { + return i.getCaretNodeAndOffset; + } }); + var s = An; + Object.defineProperty(n, "getRange", { enumerable: !0, get: function() { + return s.getRange; + } }); + var r = Ln; + Object.defineProperty(n, "isCaretAtEndOfInput", { enumerable: !0, get: function() { + return r.isCaretAtEndOfInput; + } }); + var a = Pn; + Object.defineProperty(n, "isCaretAtStartOfInput", { enumerable: !0, get: function() { + return a.isCaretAtStartOfInput; + } }); + var l = Nn; + Object.defineProperty(n, "save", { enumerable: !0, get: function() { + return l.save; + } }); +})(hn); +class na extends E { + /** + * All keydowns on Block + * + * @param {KeyboardEvent} event - keydown + */ + keydown(e) { + switch (this.beforeKeydownProcessing(e), e.keyCode) { + case y.BACKSPACE: + this.backspace(e); + break; + case y.DELETE: + this.delete(e); + break; + case y.ENTER: + this.enter(e); + break; + case y.DOWN: + case y.RIGHT: + this.arrowRightAndDown(e); + break; + case y.UP: + case y.LEFT: + this.arrowLeftAndUp(e); + break; + case y.TAB: + this.tabPressed(e); + break; + } + e.key === "/" && !e.ctrlKey && !e.metaKey && this.slashPressed(e), e.code === "Slash" && (e.ctrlKey || e.metaKey) && (e.preventDefault(), this.commandSlashPressed()); + } + /** + * Fires on keydown before event processing + * + * @param {KeyboardEvent} event - keydown + */ + beforeKeydownProcessing(e) { + this.needToolbarClosing(e) && Po(e.keyCode) && (this.Editor.Toolbar.close(), e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || this.Editor.BlockSelection.clearSelection(e)); + } + /** + * Key up on Block: + * - shows Inline Toolbar if something selected + * - shows conversion toolbar with 85% of block selection + * + * @param {KeyboardEvent} event - keyup event + */ + keyup(e) { + e.shiftKey || this.Editor.UI.checkEmptiness(); + } + /** + * Add drop target styles + * + * @param {DragEvent} event - drag over event + */ + dragOver(e) { + const t = this.Editor.BlockManager.getBlockByChildNode(e.target); + t.dropTarget = !0; + } + /** + * Remove drop target style + * + * @param {DragEvent} event - drag leave event + */ + dragLeave(e) { + const t = this.Editor.BlockManager.getBlockByChildNode(e.target); + t.dropTarget = !1; + } + /** + * Copying selected blocks + * Before putting to the clipboard we sanitize all blocks and then copy to the clipboard + * + * @param {ClipboardEvent} event - clipboard event + */ + handleCommandC(e) { + const { BlockSelection: t } = this.Editor; + t.anyBlockSelected && t.copySelectedBlocks(e); + } + /** + * Copy and Delete selected Blocks + * + * @param {ClipboardEvent} event - clipboard event + */ + handleCommandX(e) { + const { BlockSelection: t, BlockManager: o, Caret: i } = this.Editor; + t.anyBlockSelected && t.copySelectedBlocks(e).then(() => { + const s = o.removeSelectedBlocks(), r = o.insertDefaultBlockAtIndex(s, !0); + i.setToBlock(r, i.positions.START), t.clearSelection(e); + }); + } + /** + * Tab pressed inside a Block. + * + * @param {KeyboardEvent} event - keydown + */ + tabPressed(e) { + const { InlineToolbar: t, Caret: o } = this.Editor; + if (t.opened) + return; + (e.shiftKey ? o.navigatePrevious(!0) : o.navigateNext(!0)) && e.preventDefault(); + } + /** + * '/' + 'command' keydown inside a Block + */ + commandSlashPressed() { + this.Editor.BlockSelection.selectedBlocks.length > 1 || this.activateBlockSettings(); + } + /** + * '/' keydown inside a Block + * + * @param event - keydown + */ + slashPressed(e) { + !this.Editor.UI.nodes.wrapper.contains(e.target) || !this.Editor.BlockManager.currentBlock.isEmpty || (e.preventDefault(), this.Editor.Caret.insertContentAtCaretPosition("/"), this.activateToolbox()); + } + /** + * ENTER pressed on block + * + * @param {KeyboardEvent} event - keydown + */ + enter(e) { + const { BlockManager: t, UI: o } = this.Editor, i = t.currentBlock; + if (i === void 0 || i.tool.isLineBreaksEnabled || o.someToolbarOpened && o.someFlipperButtonFocused || e.shiftKey && !pt) + return; + let s = i; + i.currentInput !== void 0 && Ne(i.currentInput) && !i.hasMedia ? this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex) : i.currentInput && Re(i.currentInput) ? s = this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex + 1) : s = this.Editor.BlockManager.split(), this.Editor.Caret.setToBlock(s), this.Editor.Toolbar.moveAndOpen(s), e.preventDefault(); + } + /** + * Handle backspace keydown on Block + * + * @param {KeyboardEvent} event - keydown + */ + backspace(e) { + const { BlockManager: t, Caret: o } = this.Editor, { currentBlock: i, previousBlock: s } = t; + if (i === void 0 || !b.isCollapsed || !i.currentInput || !Ne(i.currentInput)) + return; + if (e.preventDefault(), this.Editor.Toolbar.close(), !(i.currentInput === i.firstInput)) { + o.navigatePrevious(); + return; + } + if (s === null) + return; + if (s.isEmpty) { + t.removeBlock(s); + return; + } + if (i.isEmpty) { + t.removeBlock(i); + const l = t.currentBlock; + o.setToBlock(l, o.positions.END); + return; + } + xo(s, i) ? this.mergeBlocks(s, i) : o.setToBlock(s, o.positions.END); + } + /** + * Handles delete keydown on Block + * Removes char after the caret. + * If caret is at the end of the block, merge next block with current + * + * @param {KeyboardEvent} event - keydown + */ + delete(e) { + const { BlockManager: t, Caret: o } = this.Editor, { currentBlock: i, nextBlock: s } = t; + if (!b.isCollapsed || !Re(i.currentInput)) + return; + if (e.preventDefault(), this.Editor.Toolbar.close(), !(i.currentInput === i.lastInput)) { + o.navigateNext(); + return; + } + if (s === null) + return; + if (s.isEmpty) { + t.removeBlock(s); + return; + } + if (i.isEmpty) { + t.removeBlock(i), o.setToBlock(s, o.positions.START); + return; + } + xo(i, s) ? this.mergeBlocks(i, s) : o.setToBlock(s, o.positions.START); + } + /** + * Merge passed Blocks + * + * @param targetBlock - to which Block we want to merge + * @param blockToMerge - what Block we want to merge + */ + mergeBlocks(e, t) { + const { BlockManager: o, Toolbar: i } = this.Editor; + e.lastInput !== void 0 && (hn.focus(e.lastInput, !1), o.mergeBlocks(e, t).then(() => { + i.close(); + })); + } + /** + * Handle right and down keyboard keys + * + * @param {KeyboardEvent} event - keyboard event + */ + arrowRightAndDown(e) { + const t = ce.usedKeys.includes(e.keyCode) && (!e.shiftKey || e.keyCode === y.TAB); + if (this.Editor.UI.someToolbarOpened && t) + return; + this.Editor.Toolbar.close(); + const { currentBlock: o } = this.Editor.BlockManager, s = ((o == null ? void 0 : o.currentInput) !== void 0 ? Re(o.currentInput) : void 0) || this.Editor.BlockSelection.anyBlockSelected; + if (e.shiftKey && e.keyCode === y.DOWN && s) { + this.Editor.CrossBlockSelection.toggleBlockSelectedState(); + return; + } + if (e.keyCode === y.DOWN || e.keyCode === y.RIGHT && !this.isRtl ? this.Editor.Caret.navigateNext() : this.Editor.Caret.navigatePrevious()) { + e.preventDefault(); + return; + } + Fe(() => { + this.Editor.BlockManager.currentBlock && this.Editor.BlockManager.currentBlock.updateCurrentInput(); + }, 20)(), this.Editor.BlockSelection.clearSelection(e); + } + /** + * Handle left and up keyboard keys + * + * @param {KeyboardEvent} event - keyboard event + */ + arrowLeftAndUp(e) { + if (this.Editor.UI.someToolbarOpened) { + if (ce.usedKeys.includes(e.keyCode) && (!e.shiftKey || e.keyCode === y.TAB)) + return; + this.Editor.UI.closeAllToolbars(); + } + this.Editor.Toolbar.close(); + const { currentBlock: t } = this.Editor.BlockManager, i = ((t == null ? void 0 : t.currentInput) !== void 0 ? Ne(t.currentInput) : void 0) || this.Editor.BlockSelection.anyBlockSelected; + if (e.shiftKey && e.keyCode === y.UP && i) { + this.Editor.CrossBlockSelection.toggleBlockSelectedState(!1); + return; + } + if (e.keyCode === y.UP || e.keyCode === y.LEFT && !this.isRtl ? this.Editor.Caret.navigatePrevious() : this.Editor.Caret.navigateNext()) { + e.preventDefault(); + return; + } + Fe(() => { + this.Editor.BlockManager.currentBlock && this.Editor.BlockManager.currentBlock.updateCurrentInput(); + }, 20)(), this.Editor.BlockSelection.clearSelection(e); + } + /** + * Cases when we need to close Toolbar + * + * @param {KeyboardEvent} event - keyboard event + */ + needToolbarClosing(e) { + const t = e.keyCode === y.ENTER && this.Editor.Toolbar.toolbox.opened, o = e.keyCode === y.ENTER && this.Editor.BlockSettings.opened, i = e.keyCode === y.ENTER && this.Editor.InlineToolbar.opened, s = e.keyCode === y.TAB; + return !(e.shiftKey || s || t || o || i); + } + /** + * If Toolbox is not open, then just open it and show plus button + */ + activateToolbox() { + this.Editor.Toolbar.opened || this.Editor.Toolbar.moveAndOpen(), this.Editor.Toolbar.toolbox.open(); + } + /** + * Open Toolbar and show BlockSettings before flipping Tools + */ + activateBlockSettings() { + this.Editor.Toolbar.opened || this.Editor.Toolbar.moveAndOpen(), this.Editor.BlockSettings.opened || this.Editor.BlockSettings.open(); + } +} +class ct { + /** + * @class + * @param {HTMLElement} workingArea — editor`s working node + */ + constructor(e) { + this.blocks = [], this.workingArea = e; + } + /** + * Get length of Block instances array + * + * @returns {number} + */ + get length() { + return this.blocks.length; + } + /** + * Get Block instances array + * + * @returns {Block[]} + */ + get array() { + return this.blocks; + } + /** + * Get blocks html elements array + * + * @returns {HTMLElement[]} + */ + get nodes() { + return No(this.workingArea.children); + } + /** + * Proxy trap to implement array-like setter + * + * @example + * blocks[0] = new Block(...) + * @param {Blocks} instance — Blocks instance + * @param {PropertyKey} property — block index or any Blocks class property key to set + * @param {Block} value — value to set + * @returns {boolean} + */ + static set(e, t, o) { + return isNaN(Number(t)) ? (Reflect.set(e, t, o), !0) : (e.insert(+t, o), !0); + } + /** + * Proxy trap to implement array-like getter + * + * @param {Blocks} instance — Blocks instance + * @param {PropertyKey} property — Blocks class property key + * @returns {Block|*} + */ + static get(e, t) { + return isNaN(Number(t)) ? Reflect.get(e, t) : e.get(+t); + } + /** + * Push new Block to the blocks array and append it to working area + * + * @param {Block} block - Block to add + */ + push(e) { + this.blocks.push(e), this.insertToDOM(e); + } + /** + * Swaps blocks with indexes first and second + * + * @param {number} first - first block index + * @param {number} second - second block index + * @deprecated — use 'move' instead + */ + swap(e, t) { + const o = this.blocks[t]; + u.swap(this.blocks[e].holder, o.holder), this.blocks[t] = this.blocks[e], this.blocks[e] = o; + } + /** + * Move a block from one to another index + * + * @param {number} toIndex - new index of the block + * @param {number} fromIndex - block to move + */ + move(e, t) { + const o = this.blocks.splice(t, 1)[0], i = e - 1, s = Math.max(0, i), r = this.blocks[s]; + e > 0 ? this.insertToDOM(o, "afterend", r) : this.insertToDOM(o, "beforebegin", r), this.blocks.splice(e, 0, o); + const a = this.composeBlockEvent("move", { + fromIndex: t, + toIndex: e + }); + o.call(ee.MOVED, a); + } + /** + * Insert new Block at passed index + * + * @param {number} index — index to insert Block + * @param {Block} block — Block to insert + * @param {boolean} replace — it true, replace block on given index + */ + insert(e, t, o = !1) { + if (!this.length) { + this.push(t); + return; + } + e > this.length && (e = this.length), o && (this.blocks[e].holder.remove(), this.blocks[e].call(ee.REMOVED)); + const i = o ? 1 : 0; + if (this.blocks.splice(e, i, t), e > 0) { + const s = this.blocks[e - 1]; + this.insertToDOM(t, "afterend", s); + } else { + const s = this.blocks[e + 1]; + s ? this.insertToDOM(t, "beforebegin", s) : this.insertToDOM(t); + } + } + /** + * Replaces block under passed index with passed block + * + * @param index - index of existed block + * @param block - new block + */ + replace(e, t) { + if (this.blocks[e] === void 0) + throw Error("Incorrect index"); + this.blocks[e].holder.replaceWith(t.holder), this.blocks[e] = t; + } + /** + * Inserts several blocks at once + * + * @param blocks - blocks to insert + * @param index - index to insert blocks at + */ + insertMany(e, t) { + const o = new DocumentFragment(); + for (const i of e) + o.appendChild(i.holder); + if (this.length > 0) { + if (t > 0) { + const i = Math.min(t - 1, this.length - 1); + this.blocks[i].holder.after(o); + } else + t === 0 && this.workingArea.prepend(o); + this.blocks.splice(t, 0, ...e); + } else + this.blocks.push(...e), this.workingArea.appendChild(o); + e.forEach((i) => i.call(ee.RENDERED)); + } + /** + * Remove block + * + * @param {number} index - index of Block to remove + */ + remove(e) { + isNaN(e) && (e = this.length - 1), this.blocks[e].holder.remove(), this.blocks[e].call(ee.REMOVED), this.blocks.splice(e, 1); + } + /** + * Remove all blocks + */ + removeAll() { + this.workingArea.innerHTML = "", this.blocks.forEach((e) => e.call(ee.REMOVED)), this.blocks.length = 0; + } + /** + * Insert Block after passed target + * + * @todo decide if this method is necessary + * @param {Block} targetBlock — target after which Block should be inserted + * @param {Block} newBlock — Block to insert + */ + insertAfter(e, t) { + const o = this.blocks.indexOf(e); + this.insert(o + 1, t); + } + /** + * Get Block by index + * + * @param {number} index — Block index + * @returns {Block} + */ + get(e) { + return this.blocks[e]; + } + /** + * Return index of passed Block + * + * @param {Block} block - Block to find + * @returns {number} + */ + indexOf(e) { + return this.blocks.indexOf(e); + } + /** + * Insert new Block into DOM + * + * @param {Block} block - Block to insert + * @param {InsertPosition} position — insert position (if set, will use insertAdjacentElement) + * @param {Block} target — Block related to position + */ + insertToDOM(e, t, o) { + t ? o.holder.insertAdjacentElement(t, e.holder) : this.workingArea.appendChild(e.holder), e.call(ee.RENDERED); + } + /** + * Composes Block event with passed type and details + * + * @param {string} type - event type + * @param {object} detail - event detail + */ + composeBlockEvent(e, t) { + return new CustomEvent(e, { + detail: t + }); + } +} +const _o = "block-removed", Mo = "block-added", ia = "block-moved", Ao = "block-changed"; +class sa { + constructor() { + this.completed = Promise.resolve(); + } + /** + * Add new promise to queue + * + * @param operation - promise should be added to queue + */ + add(e) { + return new Promise((t, o) => { + this.completed = this.completed.then(e).then(t).catch(o); + }); + } +} +class ra extends E { + constructor() { + super(...arguments), this._currentBlockIndex = -1, this._blocks = null; + } + /** + * Returns current Block index + * + * @returns {number} + */ + get currentBlockIndex() { + return this._currentBlockIndex; + } + /** + * Set current Block index and fire Block lifecycle callbacks + * + * @param {number} newIndex - index of Block to set as current + */ + set currentBlockIndex(e) { + this._currentBlockIndex = e; + } + /** + * returns first Block + * + * @returns {Block} + */ + get firstBlock() { + return this._blocks[0]; + } + /** + * returns last Block + * + * @returns {Block} + */ + get lastBlock() { + return this._blocks[this._blocks.length - 1]; + } + /** + * Get current Block instance + * + * @returns {Block} + */ + get currentBlock() { + return this._blocks[this.currentBlockIndex]; + } + /** + * Set passed Block as a current + * + * @param block - block to set as a current + */ + set currentBlock(e) { + this.currentBlockIndex = this.getBlockIndex(e); + } + /** + * Returns next Block instance + * + * @returns {Block|null} + */ + get nextBlock() { + return this.currentBlockIndex === this._blocks.length - 1 ? null : this._blocks[this.currentBlockIndex + 1]; + } + /** + * Return first Block with inputs after current Block + * + * @returns {Block | undefined} + */ + get nextContentfulBlock() { + return this.blocks.slice(this.currentBlockIndex + 1).find((t) => !!t.inputs.length); + } + /** + * Return first Block with inputs before current Block + * + * @returns {Block | undefined} + */ + get previousContentfulBlock() { + return this.blocks.slice(0, this.currentBlockIndex).reverse().find((t) => !!t.inputs.length); + } + /** + * Returns previous Block instance + * + * @returns {Block|null} + */ + get previousBlock() { + return this.currentBlockIndex === 0 ? null : this._blocks[this.currentBlockIndex - 1]; + } + /** + * Get array of Block instances + * + * @returns {Block[]} {@link Blocks#array} + */ + get blocks() { + return this._blocks.array; + } + /** + * Check if each Block is empty + * + * @returns {boolean} + */ + get isEditorEmpty() { + return this.blocks.every((e) => e.isEmpty); + } + /** + * Should be called after Editor.UI preparation + * Define this._blocks property + */ + prepare() { + const e = new ct(this.Editor.UI.nodes.redactor); + this._blocks = new Proxy(e, { + set: ct.set, + get: ct.get + }), this.listeners.on( + document, + "copy", + (t) => this.Editor.BlockEvents.handleCommandC(t) + ); + } + /** + * Toggle read-only state + * + * If readOnly is true: + * - Unbind event handlers from created Blocks + * + * if readOnly is false: + * - Bind event handlers to all existing Blocks + * + * @param {boolean} readOnlyEnabled - "read only" state + */ + toggleReadOnly(e) { + e ? this.disableModuleBindings() : this.enableModuleBindings(); + } + /** + * Creates Block instance by tool name + * + * @param {object} options - block creation options + * @param {string} options.tool - tools passed in editor config {@link EditorConfig#tools} + * @param {string} [options.id] - unique id for this block + * @param {BlockToolData} [options.data] - constructor params + * @returns {Block} + */ + composeBlock({ + tool: e, + data: t = {}, + id: o = void 0, + tunes: i = {} + }) { + const s = this.Editor.ReadOnly.isEnabled, r = this.Editor.Tools.blockTools.get(e), a = new R({ + id: o, + data: t, + tool: r, + api: this.Editor.API, + readOnly: s, + tunesData: i + }, this.eventsDispatcher); + return s || window.requestIdleCallback(() => { + this.bindBlockEvents(a); + }, { timeout: 2e3 }), a; + } + /** + * Insert new block into _blocks + * + * @param {object} options - insert options + * @param {string} [options.id] - block's unique id + * @param {string} [options.tool] - plugin name, by default method inserts the default block type + * @param {object} [options.data] - plugin data + * @param {number} [options.index] - index where to insert new Block + * @param {boolean} [options.needToFocus] - flag shows if needed to update current Block index + * @param {boolean} [options.replace] - flag shows if block by passed index should be replaced with inserted one + * @returns {Block} + */ + insert({ + id: e = void 0, + tool: t = this.config.defaultBlock, + data: o = {}, + index: i, + needToFocus: s = !0, + replace: r = !1, + tunes: a = {} + } = {}) { + let l = i; + l === void 0 && (l = this.currentBlockIndex + (r ? 0 : 1)); + const c = this.composeBlock({ + id: e, + tool: t, + data: o, + tunes: a + }); + return r && this.blockDidMutated(_o, this.getBlockByIndex(l), { + index: l + }), this._blocks.insert(l, c, r), this.blockDidMutated(Mo, c, { + index: l + }), s ? this.currentBlockIndex = l : l <= this.currentBlockIndex && this.currentBlockIndex++, c; + } + /** + * Inserts several blocks at once + * + * @param blocks - blocks to insert + * @param index - index where to insert + */ + insertMany(e, t = 0) { + this._blocks.insertMany(e, t); + } + /** + * Update Block data. + * + * Currently we don't have an 'update' method in the Tools API, so we just create a new block with the same id and type + * Should not trigger 'block-removed' or 'block-added' events. + * + * If neither data nor tunes is provided, return the provided block instead. + * + * @param block - block to update + * @param data - (optional) new data + * @param tunes - (optional) tune data + */ + async update(e, t, o) { + if (!t && !o) + return e; + const i = await e.data, s = this.composeBlock({ + id: e.id, + tool: e.name, + data: Object.assign({}, i, t ?? {}), + tunes: o ?? e.tunes + }), r = this.getBlockIndex(e); + return this._blocks.replace(r, s), this.blockDidMutated(Ao, s, { + index: r + }), s; + } + /** + * Replace passed Block with the new one with specified Tool and data + * + * @param block - block to replace + * @param newTool - new Tool name + * @param data - new Tool data + */ + replace(e, t, o) { + const i = this.getBlockIndex(e); + return this.insert({ + tool: t, + data: o, + index: i, + replace: !0 + }); + } + /** + * Insert pasted content. Call onPaste callback after insert. + * + * @param {string} toolName - name of Tool to insert + * @param {PasteEvent} pasteEvent - pasted data + * @param {boolean} replace - should replace current block + */ + paste(e, t, o = !1) { + const i = this.insert({ + tool: e, + replace: o + }); + try { + window.requestIdleCallback(() => { + i.call(ee.ON_PASTE, t); + }); + } catch (s) { + S(`${e}: onPaste callback call is failed`, "error", s); + } + return i; + } + /** + * Insert new default block at passed index + * + * @param {number} index - index where Block should be inserted + * @param {boolean} needToFocus - if true, updates current Block index + * + * TODO: Remove method and use insert() with index instead (?) + * @returns {Block} inserted Block + */ + insertDefaultBlockAtIndex(e, t = !1) { + const o = this.composeBlock({ tool: this.config.defaultBlock }); + return this._blocks[e] = o, this.blockDidMutated(Mo, o, { + index: e + }), t ? this.currentBlockIndex = e : e <= this.currentBlockIndex && this.currentBlockIndex++, o; + } + /** + * Always inserts at the end + * + * @returns {Block} + */ + insertAtEnd() { + return this.currentBlockIndex = this.blocks.length - 1, this.insert(); + } + /** + * Merge two blocks + * + * @param {Block} targetBlock - previous block will be append to this block + * @param {Block} blockToMerge - block that will be merged with target block + * @returns {Promise} - the sequence that can be continued + */ + async mergeBlocks(e, t) { + let o; + if (e.name === t.name && e.mergeable) { + const i = await t.data; + if (V(i)) { + console.error("Could not merge Block. Failed to extract original Block data."); + return; + } + const [s] = yt([i], e.tool.sanitizeConfig); + o = s; + } else if (e.mergeable && He(t, "export") && He(e, "import")) { + const i = await t.exportDataAsString(), s = Z(i, e.tool.sanitizeConfig); + o = Bo(s, e.tool.conversionConfig); + } + o !== void 0 && (await e.mergeWith(o), this.removeBlock(t), this.currentBlockIndex = this._blocks.indexOf(e)); + } + /** + * Remove passed Block + * + * @param block - Block to remove + * @param addLastBlock - if true, adds new default block at the end. @todo remove this logic and use event-bus instead + */ + removeBlock(e, t = !0) { + return new Promise((o) => { + const i = this._blocks.indexOf(e); + if (!this.validateIndex(i)) + throw new Error("Can't find a Block to remove"); + this._blocks.remove(i), e.destroy(), this.blockDidMutated(_o, e, { + index: i + }), this.currentBlockIndex >= i && this.currentBlockIndex--, this.blocks.length ? i === 0 && (this.currentBlockIndex = 0) : (this.unsetCurrentBlock(), t && this.insert()), o(); + }); + } + /** + * Remove only selected Blocks + * and returns first Block index where started removing... + * + * @returns {number|undefined} + */ + removeSelectedBlocks() { + let e; + for (let t = this.blocks.length - 1; t >= 0; t--) + this.blocks[t].selected && (this.removeBlock(this.blocks[t]), e = t); + return e; + } + /** + * Attention! + * After removing insert the new default typed Block and focus on it + * Removes all blocks + */ + removeAllBlocks() { + for (let e = this.blocks.length - 1; e >= 0; e--) + this._blocks.remove(e); + this.unsetCurrentBlock(), this.insert(), this.currentBlock.firstInput.focus(); + } + /** + * Split current Block + * 1. Extract content from Caret position to the Block`s end + * 2. Insert a new Block below current one with extracted content + * + * @returns {Block} + */ + split() { + const e = this.Editor.Caret.extractFragmentFromCaretPosition(), t = u.make("div"); + t.appendChild(e); + const o = { + text: u.isEmpty(t) ? "" : t.innerHTML + }; + return this.insert({ data: o }); + } + /** + * Returns Block by passed index + * + * @param {number} index - index to get. -1 to get last + * @returns {Block} + */ + getBlockByIndex(e) { + return e === -1 && (e = this._blocks.length - 1), this._blocks[e]; + } + /** + * Returns an index for passed Block + * + * @param block - block to find index + */ + getBlockIndex(e) { + return this._blocks.indexOf(e); + } + /** + * Returns the Block by passed id + * + * @param id - id of block to get + * @returns {Block} + */ + getBlockById(e) { + return this._blocks.array.find((t) => t.id === e); + } + /** + * Get Block instance by html element + * + * @param {Node} element - html element to get Block by + */ + getBlock(e) { + u.isElement(e) || (e = e.parentNode); + const t = this._blocks.nodes, o = e.closest(`.${R.CSS.wrapper}`), i = t.indexOf(o); + if (i >= 0) + return this._blocks[i]; + } + /** + * 1) Find first-level Block from passed child Node + * 2) Mark it as current + * + * @param {Node} childNode - look ahead from this node. + * @returns {Block | undefined} can return undefined in case when the passed child note is not a part of the current editor instance + */ + setCurrentBlockByChildNode(e) { + u.isElement(e) || (e = e.parentNode); + const t = e.closest(`.${R.CSS.wrapper}`); + if (!t) + return; + const o = t.closest(`.${this.Editor.UI.CSS.editorWrapper}`); + if (o != null && o.isEqualNode(this.Editor.UI.nodes.wrapper)) + return this.currentBlockIndex = this._blocks.nodes.indexOf(t), this.currentBlock.updateCurrentInput(), this.currentBlock; + } + /** + * Return block which contents passed node + * + * @param {Node} childNode - node to get Block by + * @returns {Block} + */ + getBlockByChildNode(e) { + if (!e || !(e instanceof Node)) + return; + u.isElement(e) || (e = e.parentNode); + const t = e.closest(`.${R.CSS.wrapper}`); + return this.blocks.find((o) => o.holder === t); + } + /** + * Swap Blocks Position + * + * @param {number} fromIndex - index of first block + * @param {number} toIndex - index of second block + * @deprecated — use 'move' instead + */ + swap(e, t) { + this._blocks.swap(e, t), this.currentBlockIndex = t; + } + /** + * Move a block to a new index + * + * @param {number} toIndex - index where to move Block + * @param {number} fromIndex - index of Block to move + */ + move(e, t = this.currentBlockIndex) { + if (isNaN(e) || isNaN(t)) { + S("Warning during 'move' call: incorrect indices provided.", "warn"); + return; + } + if (!this.validateIndex(e) || !this.validateIndex(t)) { + S("Warning during 'move' call: indices cannot be lower than 0 or greater than the amount of blocks.", "warn"); + return; + } + this._blocks.move(e, t), this.currentBlockIndex = e, this.blockDidMutated(ia, this.currentBlock, { + fromIndex: t, + toIndex: e + }); + } + /** + * Converts passed Block to the new Tool + * Uses Conversion Config + * + * @param blockToConvert - Block that should be converted + * @param targetToolName - name of the Tool to convert to + * @param blockDataOverrides - optional new Block data overrides + */ + async convert(e, t, o) { + if (!await e.save()) + throw new Error("Could not convert Block. Failed to extract original Block data."); + const s = this.Editor.Tools.blockTools.get(t); + if (!s) + throw new Error(`Could not convert Block. Tool «${t}» not found.`); + const r = await e.exportDataAsString(), a = Z( + r, + s.sanitizeConfig + ); + let l = Bo(a, s.conversionConfig, s.settings); + return o && (l = Object.assign(l, o)), this.replace(e, s.name, l); + } + /** + * Sets current Block Index -1 which means unknown + * and clear highlights + */ + unsetCurrentBlock() { + this.currentBlockIndex = -1; + } + /** + * Clears Editor + * + * @param {boolean} needToAddDefaultBlock - 1) in internal calls (for example, in api.blocks.render) + * we don't need to add an empty default block + * 2) in api.blocks.clear we should add empty block + */ + async clear(e = !1) { + const t = new sa(); + [...this.blocks].forEach((i) => { + t.add(async () => { + await this.removeBlock(i, !1); + }); + }), await t.completed, this.unsetCurrentBlock(), e && this.insert(), this.Editor.UI.checkEmptiness(); + } + /** + * Cleans up all the block tools' resources + * This is called when editor is destroyed + */ + async destroy() { + await Promise.all(this.blocks.map((e) => e.destroy())); + } + /** + * Bind Block events + * + * @param {Block} block - Block to which event should be bound + */ + bindBlockEvents(e) { + const { BlockEvents: t } = this.Editor; + this.readOnlyMutableListeners.on(e.holder, "keydown", (o) => { + t.keydown(o); + }), this.readOnlyMutableListeners.on(e.holder, "keyup", (o) => { + t.keyup(o); + }), this.readOnlyMutableListeners.on(e.holder, "dragover", (o) => { + t.dragOver(o); + }), this.readOnlyMutableListeners.on(e.holder, "dragleave", (o) => { + t.dragLeave(o); + }), e.on("didMutated", (o) => this.blockDidMutated(Ao, o, { + index: this.getBlockIndex(o) + })); + } + /** + * Disable mutable handlers and bindings + */ + disableModuleBindings() { + this.readOnlyMutableListeners.clearAll(); + } + /** + * Enables all module handlers and bindings for all Blocks + */ + enableModuleBindings() { + this.readOnlyMutableListeners.on( + document, + "cut", + (e) => this.Editor.BlockEvents.handleCommandX(e) + ), this.blocks.forEach((e) => { + this.bindBlockEvents(e); + }); + } + /** + * Validates that the given index is not lower than 0 or higher than the amount of blocks + * + * @param {number} index - index of blocks array to validate + * @returns {boolean} + */ + validateIndex(e) { + return !(e < 0 || e >= this._blocks.length); + } + /** + * Block mutation callback + * + * @param mutationType - what happened with block + * @param block - mutated block + * @param detailData - additional data to pass with change event + */ + blockDidMutated(e, t, o) { + const i = new CustomEvent(e, { + detail: { + target: new J(t), + ...o + } + }); + return this.eventsDispatcher.emit($o, { + event: i + }), t; + } +} +class aa extends E { + constructor() { + super(...arguments), this.anyBlockSelectedCache = null, this.needToSelectAll = !1, this.nativeInputSelected = !1, this.readyToBlockSelection = !1; + } + /** + * Sanitizer Config + * + * @returns {SanitizerConfig} + */ + get sanitizerConfig() { + return { + p: {}, + h1: {}, + h2: {}, + h3: {}, + h4: {}, + h5: {}, + h6: {}, + ol: {}, + ul: {}, + li: {}, + br: !0, + img: { + src: !0, + width: !0, + height: !0 + }, + a: { + href: !0 + }, + b: {}, + i: {}, + u: {} + }; + } + /** + * Flag that identifies all Blocks selection + * + * @returns {boolean} + */ + get allBlocksSelected() { + const { BlockManager: e } = this.Editor; + return e.blocks.every((t) => t.selected === !0); + } + /** + * Set selected all blocks + * + * @param {boolean} state - state to set + */ + set allBlocksSelected(e) { + const { BlockManager: t } = this.Editor; + t.blocks.forEach((o) => { + o.selected = e; + }), this.clearCache(); + } + /** + * Flag that identifies any Block selection + * + * @returns {boolean} + */ + get anyBlockSelected() { + const { BlockManager: e } = this.Editor; + return this.anyBlockSelectedCache === null && (this.anyBlockSelectedCache = e.blocks.some((t) => t.selected === !0)), this.anyBlockSelectedCache; + } + /** + * Return selected Blocks array + * + * @returns {Block[]} + */ + get selectedBlocks() { + return this.Editor.BlockManager.blocks.filter((e) => e.selected); + } + /** + * Module Preparation + * Registers Shortcuts CMD+A and CMD+C + * to select all and copy them + */ + prepare() { + this.selection = new b(), ge.add({ + name: "CMD+A", + handler: (e) => { + const { BlockManager: t, ReadOnly: o } = this.Editor; + if (o.isEnabled) { + e.preventDefault(), this.selectAllBlocks(); + return; + } + t.currentBlock && this.handleCommandA(e); + }, + on: this.Editor.UI.nodes.redactor + }); + } + /** + * Toggle read-only state + * + * - Remove all ranges + * - Unselect all Blocks + */ + toggleReadOnly() { + b.get().removeAllRanges(), this.allBlocksSelected = !1; + } + /** + * Remove selection of Block + * + * @param {number?} index - Block index according to the BlockManager's indexes + */ + unSelectBlockByIndex(e) { + const { BlockManager: t } = this.Editor; + let o; + isNaN(e) ? o = t.currentBlock : o = t.getBlockByIndex(e), o.selected = !1, this.clearCache(); + } + /** + * Clear selection from Blocks + * + * @param {Event} reason - event caused clear of selection + * @param {boolean} restoreSelection - if true, restore saved selection + */ + clearSelection(e, t = !1) { + const { BlockManager: o, Caret: i, RectangleSelection: s } = this.Editor; + this.needToSelectAll = !1, this.nativeInputSelected = !1, this.readyToBlockSelection = !1; + const r = e && e instanceof KeyboardEvent, a = r && Po(e.keyCode); + if (this.anyBlockSelected && r && a && !b.isSelectionExists) { + const l = o.removeSelectedBlocks(); + o.insertDefaultBlockAtIndex(l, !0), i.setToBlock(o.currentBlock), Fe(() => { + const c = e.key; + i.insertContentAtCaretPosition(c.length > 1 ? "" : c); + }, 20)(); + } + if (this.Editor.CrossBlockSelection.clear(e), !this.anyBlockSelected || s.isRectActivated()) { + this.Editor.RectangleSelection.clearSelection(); + return; + } + t && this.selection.restore(), this.allBlocksSelected = !1; + } + /** + * Reduce each Block and copy its content + * + * @param {ClipboardEvent} e - copy/cut event + * @returns {Promise} + */ + copySelectedBlocks(e) { + e.preventDefault(); + const t = u.make("div"); + this.selectedBlocks.forEach((s) => { + const r = Z(s.holder.innerHTML, this.sanitizerConfig), a = u.make("p"); + a.innerHTML = r, t.appendChild(a); + }); + const o = Array.from(t.childNodes).map((s) => s.textContent).join(` + +`), i = t.innerHTML; + return e.clipboardData.setData("text/plain", o), e.clipboardData.setData("text/html", i), Promise.all(this.selectedBlocks.map((s) => s.save())).then((s) => { + try { + e.clipboardData.setData(this.Editor.Paste.MIME_TYPE, JSON.stringify(s)); + } catch { + } + }); + } + /** + * Select Block by its index + * + * @param {number?} index - Block index according to the BlockManager's indexes + */ + selectBlockByIndex(e) { + const { BlockManager: t } = this.Editor, o = t.getBlockByIndex(e); + o !== void 0 && this.selectBlock(o); + } + /** + * Select passed Block + * + * @param {Block} block - Block to select + */ + selectBlock(e) { + this.selection.save(), b.get().removeAllRanges(), e.selected = !0, this.clearCache(), this.Editor.InlineToolbar.close(); + } + /** + * Remove selection from passed Block + * + * @param {Block} block - Block to unselect + */ + unselectBlock(e) { + e.selected = !1, this.clearCache(); + } + /** + * Clear anyBlockSelected cache + */ + clearCache() { + this.anyBlockSelectedCache = null; + } + /** + * Module destruction + * De-registers Shortcut CMD+A + */ + destroy() { + ge.remove(this.Editor.UI.nodes.redactor, "CMD+A"); + } + /** + * First CMD+A selects all input content by native behaviour, + * next CMD+A keypress selects all blocks + * + * @param {KeyboardEvent} event - keyboard event + */ + handleCommandA(e) { + if (this.Editor.RectangleSelection.clearSelection(), u.isNativeInput(e.target) && !this.readyToBlockSelection) { + this.readyToBlockSelection = !0; + return; + } + const t = this.Editor.BlockManager.getBlock(e.target), o = t.inputs; + if (o.length > 1 && !this.readyToBlockSelection) { + this.readyToBlockSelection = !0; + return; + } + if (o.length === 1 && !this.needToSelectAll) { + this.needToSelectAll = !0; + return; + } + this.needToSelectAll ? (e.preventDefault(), this.selectAllBlocks(), this.needToSelectAll = !1, this.readyToBlockSelection = !1) : this.readyToBlockSelection && (e.preventDefault(), this.selectBlock(t), this.needToSelectAll = !0); + } + /** + * Select All Blocks + * Each Block has selected setter that makes Block copyable + */ + selectAllBlocks() { + this.selection.save(), b.get().removeAllRanges(), this.allBlocksSelected = !0, this.Editor.InlineToolbar.close(); + } +} +class Ye extends E { + /** + * Allowed caret positions in input + * + * @static + * @returns {{START: string, END: string, DEFAULT: string}} + */ + get positions() { + return { + START: "start", + END: "end", + DEFAULT: "default" + }; + } + /** + * Elements styles that can be useful for Caret Module + */ + static get CSS() { + return { + shadowCaret: "cdx-shadow-caret" + }; + } + /** + * Method gets Block instance and puts caret to the text node with offset + * There two ways that method applies caret position: + * - first found text node: sets at the beginning, but you can pass an offset + * - last found text node: sets at the end of the node. Also, you can customize the behaviour + * + * @param {Block} block - Block class + * @param {string} position - position where to set caret. + * If default - leave default behaviour and apply offset if it's passed + * @param {number} offset - caret offset regarding to the block content + */ + setToBlock(e, t = this.positions.DEFAULT, o = 0) { + var c; + const { BlockManager: i, BlockSelection: s } = this.Editor; + if (s.clearSelection(), !e.focusable) { + (c = window.getSelection()) == null || c.removeAllRanges(), s.selectBlock(e), i.currentBlock = e; + return; + } + let r; + switch (t) { + case this.positions.START: + r = e.firstInput; + break; + case this.positions.END: + r = e.lastInput; + break; + default: + r = e.currentInput; + } + if (!r) + return; + let a, l = o; + if (t === this.positions.START) + a = u.getDeepestNode(r, !1), l = 0; + else if (t === this.positions.END) + a = u.getDeepestNode(r, !0), l = u.getContentLength(a); + else { + const { node: d, offset: h } = u.getNodeByOffset(r, o); + d ? (a = d, l = h) : (a = u.getDeepestNode(r, !1), l = 0); + } + this.set(a, l), i.setCurrentBlockByChildNode(e.holder), i.currentBlock.currentInput = r; + } + /** + * Set caret to the current input of current Block. + * + * @param {HTMLElement} input - input where caret should be set + * @param {string} position - position of the caret. + * If default - leave default behaviour and apply offset if it's passed + * @param {number} offset - caret offset regarding to the text node + */ + setToInput(e, t = this.positions.DEFAULT, o = 0) { + const { currentBlock: i } = this.Editor.BlockManager, s = u.getDeepestNode(e); + switch (t) { + case this.positions.START: + this.set(s, 0); + break; + case this.positions.END: + this.set(s, u.getContentLength(s)); + break; + default: + o && this.set(s, o); + } + i.currentInput = e; + } + /** + * Creates Document Range and sets caret to the element with offset + * + * @param {HTMLElement} element - target node. + * @param {number} offset - offset + */ + set(e, t = 0) { + const { top: i, bottom: s } = b.setCursor(e, t), { innerHeight: r } = window; + i < 0 ? window.scrollBy(0, i - 30) : s > r && window.scrollBy(0, s - r + 30); + } + /** + * Set Caret to the last Block + * If last block is not empty, append another empty block + */ + setToTheLastBlock() { + const e = this.Editor.BlockManager.lastBlock; + if (e) + if (e.tool.isDefault && e.isEmpty) + this.setToBlock(e); + else { + const t = this.Editor.BlockManager.insertAtEnd(); + this.setToBlock(t); + } + } + /** + * Extract content fragment of current Block from Caret position to the end of the Block + */ + extractFragmentFromCaretPosition() { + const e = b.get(); + if (e.rangeCount) { + const t = e.getRangeAt(0), o = this.Editor.BlockManager.currentBlock.currentInput; + if (t.deleteContents(), o) + if (u.isNativeInput(o)) { + const i = o, s = document.createDocumentFragment(), r = i.value.substring(0, i.selectionStart), a = i.value.substring(i.selectionStart); + return s.textContent = a, i.value = r, s; + } else { + const i = t.cloneRange(); + return i.selectNodeContents(o), i.setStart(t.endContainer, t.endOffset), i.extractContents(); + } + } + } + /** + * Set's caret to the next Block or Tool`s input + * Before moving caret, we should check if caret position is at the end of Plugins node + * Using {@link Dom#getDeepestNode} to get a last node and match with current selection + * + * @param {boolean} force - pass true to skip check for caret position + */ + navigateNext(e = !1) { + const { BlockManager: t } = this.Editor, { currentBlock: o, nextBlock: i } = t; + if (o === void 0) + return !1; + const { nextInput: s, currentInput: r } = o, a = r !== void 0 ? Re(r) : void 0; + let l = i; + const c = e || a || !o.focusable; + if (s && c) + return this.setToInput(s, this.positions.START), !0; + if (l === null) { + if (o.tool.isDefault || !c) + return !1; + l = t.insertAtEnd(); + } + return c ? (this.setToBlock(l, this.positions.START), !0) : !1; + } + /** + * Set's caret to the previous Tool`s input or Block + * Before moving caret, we should check if caret position is start of the Plugins node + * Using {@link Dom#getDeepestNode} to get a last node and match with current selection + * + * @param {boolean} force - pass true to skip check for caret position + */ + navigatePrevious(e = !1) { + const { currentBlock: t, previousBlock: o } = this.Editor.BlockManager; + if (!t) + return !1; + const { previousInput: i, currentInput: s } = t, r = s !== void 0 ? Ne(s) : void 0, a = e || r || !t.focusable; + return i && a ? (this.setToInput(i, this.positions.END), !0) : o !== null && a ? (this.setToBlock(o, this.positions.END), !0) : !1; + } + /** + * Inserts shadow element after passed element where caret can be placed + * + * @param {Element} element - element after which shadow caret should be inserted + */ + createShadow(e) { + const t = document.createElement("span"); + t.classList.add(Ye.CSS.shadowCaret), e.insertAdjacentElement("beforeend", t); + } + /** + * Restores caret position + * + * @param {HTMLElement} element - element where caret should be restored + */ + restoreCaret(e) { + const t = e.querySelector(`.${Ye.CSS.shadowCaret}`); + if (!t) + return; + new b().expandToTag(t); + const i = document.createRange(); + i.selectNode(t), i.extractContents(); + } + /** + * Inserts passed content at caret position + * + * @param {string} content - content to insert + */ + insertContentAtCaretPosition(e) { + const t = document.createDocumentFragment(), o = document.createElement("div"), i = b.get(), s = b.range; + o.innerHTML = e, Array.from(o.childNodes).forEach((c) => t.appendChild(c)), t.childNodes.length === 0 && t.appendChild(new Text()); + const r = t.lastChild; + s.deleteContents(), s.insertNode(t); + const a = document.createRange(), l = r.nodeType === Node.TEXT_NODE ? r : r.firstChild; + l !== null && l.textContent !== null && a.setStart(l, l.textContent.length), i.removeAllRanges(), i.addRange(a); + } +} +class la extends E { + constructor() { + super(...arguments), this.onMouseUp = () => { + this.listeners.off(document, "mouseover", this.onMouseOver), this.listeners.off(document, "mouseup", this.onMouseUp); + }, this.onMouseOver = (e) => { + const { BlockManager: t, BlockSelection: o } = this.Editor; + if (e.relatedTarget === null && e.target === null) + return; + const i = t.getBlockByChildNode(e.relatedTarget) || this.lastSelectedBlock, s = t.getBlockByChildNode(e.target); + if (!(!i || !s) && s !== i) { + if (i === this.firstSelectedBlock) { + b.get().removeAllRanges(), i.selected = !0, s.selected = !0, o.clearCache(); + return; + } + if (s === this.firstSelectedBlock) { + i.selected = !1, s.selected = !1, o.clearCache(); + return; + } + this.Editor.InlineToolbar.close(), this.toggleBlocksSelectedState(i, s), this.lastSelectedBlock = s; + } + }; + } + /** + * Module preparation + * + * @returns {Promise} + */ + async prepare() { + this.listeners.on(document, "mousedown", (e) => { + this.enableCrossBlockSelection(e); + }); + } + /** + * Sets up listeners + * + * @param {MouseEvent} event - mouse down event + */ + watchSelection(e) { + if (e.button !== qn.LEFT) + return; + const { BlockManager: t } = this.Editor; + this.firstSelectedBlock = t.getBlock(e.target), this.lastSelectedBlock = this.firstSelectedBlock, this.listeners.on(document, "mouseover", this.onMouseOver), this.listeners.on(document, "mouseup", this.onMouseUp); + } + /** + * Return boolean is cross block selection started: + * there should be at least 2 selected blocks + */ + get isCrossBlockSelectionStarted() { + return !!this.firstSelectedBlock && !!this.lastSelectedBlock && this.firstSelectedBlock !== this.lastSelectedBlock; + } + /** + * Change selection state of the next Block + * Used for CBS via Shift + arrow keys + * + * @param {boolean} next - if true, toggle next block. Previous otherwise + */ + toggleBlockSelectedState(e = !0) { + const { BlockManager: t, BlockSelection: o } = this.Editor; + this.lastSelectedBlock || (this.lastSelectedBlock = this.firstSelectedBlock = t.currentBlock), this.firstSelectedBlock === this.lastSelectedBlock && (this.firstSelectedBlock.selected = !0, o.clearCache(), b.get().removeAllRanges()); + const i = t.blocks.indexOf(this.lastSelectedBlock) + (e ? 1 : -1), s = t.blocks[i]; + s && (this.lastSelectedBlock.selected !== s.selected ? (s.selected = !0, o.clearCache()) : (this.lastSelectedBlock.selected = !1, o.clearCache()), this.lastSelectedBlock = s, this.Editor.InlineToolbar.close(), s.holder.scrollIntoView({ + block: "nearest" + })); + } + /** + * Clear saved state + * + * @param {Event} reason - event caused clear of selection + */ + clear(e) { + const { BlockManager: t, BlockSelection: o, Caret: i } = this.Editor, s = t.blocks.indexOf(this.firstSelectedBlock), r = t.blocks.indexOf(this.lastSelectedBlock); + if (o.anyBlockSelected && s > -1 && r > -1 && e && e instanceof KeyboardEvent) + switch (e.keyCode) { + case y.DOWN: + case y.RIGHT: + i.setToBlock(t.blocks[Math.max(s, r)], i.positions.END); + break; + case y.UP: + case y.LEFT: + i.setToBlock(t.blocks[Math.min(s, r)], i.positions.START); + break; + default: + i.setToBlock(t.blocks[Math.max(s, r)], i.positions.END); + } + this.firstSelectedBlock = this.lastSelectedBlock = null; + } + /** + * Enables Cross Block Selection + * + * @param {MouseEvent} event - mouse down event + */ + enableCrossBlockSelection(e) { + const { UI: t } = this.Editor; + b.isCollapsed || this.Editor.BlockSelection.clearSelection(e), t.nodes.redactor.contains(e.target) ? this.watchSelection(e) : this.Editor.BlockSelection.clearSelection(e); + } + /** + * Change blocks selection state between passed two blocks. + * + * @param {Block} firstBlock - first block in range + * @param {Block} lastBlock - last block in range + */ + toggleBlocksSelectedState(e, t) { + const { BlockManager: o, BlockSelection: i } = this.Editor, s = o.blocks.indexOf(e), r = o.blocks.indexOf(t), a = e.selected !== t.selected; + for (let l = Math.min(s, r); l <= Math.max(s, r); l++) { + const c = o.blocks[l]; + c !== this.firstSelectedBlock && c !== (a ? e : t) && (o.blocks[l].selected = !o.blocks[l].selected, i.clearCache()); + } + } +} +class ca extends E { + constructor() { + super(...arguments), this.isStartedAtEditor = !1; + } + /** + * Toggle read-only state + * + * if state is true: + * - disable all drag-n-drop event handlers + * + * if state is false: + * - restore drag-n-drop event handlers + * + * @param {boolean} readOnlyEnabled - "read only" state + */ + toggleReadOnly(e) { + e ? this.disableModuleBindings() : this.enableModuleBindings(); + } + /** + * Add drag events listeners to editor zone + */ + enableModuleBindings() { + const { UI: e } = this.Editor; + this.readOnlyMutableListeners.on(e.nodes.holder, "drop", async (t) => { + await this.processDrop(t); + }, !0), this.readOnlyMutableListeners.on(e.nodes.holder, "dragstart", () => { + this.processDragStart(); + }), this.readOnlyMutableListeners.on(e.nodes.holder, "dragover", (t) => { + this.processDragOver(t); + }, !0); + } + /** + * Unbind drag-n-drop event handlers + */ + disableModuleBindings() { + this.readOnlyMutableListeners.clearAll(); + } + /** + * Handle drop event + * + * @param {DragEvent} dropEvent - drop event + */ + async processDrop(e) { + const { + BlockManager: t, + Paste: o, + Caret: i + } = this.Editor; + e.preventDefault(), t.blocks.forEach((r) => { + r.dropTarget = !1; + }), b.isAtEditor && !b.isCollapsed && this.isStartedAtEditor && document.execCommand("delete"), this.isStartedAtEditor = !1; + const s = t.setCurrentBlockByChildNode(e.target); + if (s) + this.Editor.Caret.setToBlock(s, i.positions.END); + else { + const r = t.setCurrentBlockByChildNode(t.lastBlock.holder); + this.Editor.Caret.setToBlock(r, i.positions.END); + } + await o.processDataTransfer(e.dataTransfer, !0); + } + /** + * Handle drag start event + */ + processDragStart() { + b.isAtEditor && !b.isCollapsed && (this.isStartedAtEditor = !0), this.Editor.InlineToolbar.close(); + } + /** + * @param {DragEvent} dragEvent - drag event + */ + processDragOver(e) { + e.preventDefault(); + } +} +const da = 180, ua = 400; +class ha extends E { + /** + * Prepare the module + * + * @param options - options used by the modification observer module + * @param options.config - Editor configuration object + * @param options.eventsDispatcher - common Editor event bus + */ + constructor({ config: e, eventsDispatcher: t }) { + super({ + config: e, + eventsDispatcher: t + }), this.disabled = !1, this.batchingTimeout = null, this.batchingOnChangeQueue = /* @__PURE__ */ new Map(), this.batchTime = ua, this.mutationObserver = new MutationObserver((o) => { + this.redactorChanged(o); + }), this.eventsDispatcher.on($o, (o) => { + this.particularBlockChanged(o.event); + }), this.eventsDispatcher.on(zo, () => { + this.disable(); + }), this.eventsDispatcher.on(Uo, () => { + this.enable(); + }); + } + /** + * Enables onChange event + */ + enable() { + this.mutationObserver.observe( + this.Editor.UI.nodes.redactor, + { + childList: !0, + subtree: !0, + characterData: !0, + attributes: !0 + } + ), this.disabled = !1; + } + /** + * Disables onChange event + */ + disable() { + this.mutationObserver.disconnect(), this.disabled = !0; + } + /** + * Call onChange event passed to Editor.js configuration + * + * @param event - some of our custom change events + */ + particularBlockChanged(e) { + this.disabled || !A(this.config.onChange) || (this.batchingOnChangeQueue.set(`block:${e.detail.target.id}:event:${e.type}`, e), this.batchingTimeout && clearTimeout(this.batchingTimeout), this.batchingTimeout = setTimeout(() => { + let t; + this.batchingOnChangeQueue.size === 1 ? t = this.batchingOnChangeQueue.values().next().value : t = Array.from(this.batchingOnChangeQueue.values()), this.config.onChange && this.config.onChange(this.Editor.API.methods, t), this.batchingOnChangeQueue.clear(); + }, this.batchTime)); + } + /** + * Fired on every blocks wrapper dom change + * + * @param mutations - mutations happened + */ + redactorChanged(e) { + this.eventsDispatcher.emit(ft, { + mutations: e + }); + } +} +const Rn = class Dn extends E { + constructor() { + super(...arguments), this.MIME_TYPE = "application/x-editor-js", this.toolsTags = {}, this.tagsByTool = {}, this.toolsPatterns = [], this.toolsFiles = {}, this.exceptionList = [], this.processTool = (e) => { + try { + const t = e.create({}, {}, !1); + if (e.pasteConfig === !1) { + this.exceptionList.push(e.name); + return; + } + if (!A(t.onPaste)) + return; + this.getTagsConfig(e), this.getFilesConfig(e), this.getPatternsConfig(e); + } catch (t) { + S( + `Paste handling for «${e.name}» Tool hasn't been set up because of the error`, + "warn", + t + ); + } + }, this.handlePasteEvent = async (e) => { + const { BlockManager: t, Toolbar: o } = this.Editor, i = t.setCurrentBlockByChildNode(e.target); + !i || this.isNativeBehaviour(e.target) && !e.clipboardData.types.includes("Files") || i && this.exceptionList.includes(i.name) || (e.preventDefault(), this.processDataTransfer(e.clipboardData), o.close()); + }; + } + /** + * Set onPaste callback and collect tools` paste configurations + */ + async prepare() { + this.processTools(); + } + /** + * Set read-only state + * + * @param {boolean} readOnlyEnabled - read only flag value + */ + toggleReadOnly(e) { + e ? this.unsetCallback() : this.setCallback(); + } + /** + * Handle pasted or dropped data transfer object + * + * @param {DataTransfer} dataTransfer - pasted or dropped data transfer object + * @param {boolean} isDragNDrop - true if data transfer comes from drag'n'drop events + */ + async processDataTransfer(e, t = !1) { + const { Tools: o } = this.Editor, i = e.types; + if ((i.includes ? i.includes("Files") : i.contains("Files")) && !V(this.toolsFiles)) { + await this.processFiles(e.files); + return; + } + const r = e.getData(this.MIME_TYPE), a = e.getData("text/plain"); + let l = e.getData("text/html"); + if (r) + try { + this.insertEditorJSData(JSON.parse(r)); + return; + } catch { + } + t && a.trim() && l.trim() && (l = "

    " + (l.trim() ? l : a) + "

    "); + const c = Object.keys(this.toolsTags).reduce((p, g) => (p[g.toLowerCase()] = this.toolsTags[g].sanitizationConfig ?? {}, p), {}), d = Object.assign({}, c, o.getAllInlineToolsSanitizeConfig(), { br: {} }), h = Z(l, d); + !h.trim() || h.trim() === a || !u.isHTMLString(h) ? await this.processText(a) : await this.processText(h, !0); + } + /** + * Process pasted text and divide them into Blocks + * + * @param {string} data - text to process. Can be HTML or plain. + * @param {boolean} isHTML - if passed string is HTML, this parameter should be true + */ + async processText(e, t = !1) { + const { Caret: o, BlockManager: i } = this.Editor, s = t ? this.processHTML(e) : this.processPlain(e); + if (!s.length) + return; + if (s.length === 1) { + s[0].isBlock ? this.processSingleBlock(s.pop()) : this.processInlinePaste(s.pop()); + return; + } + const a = i.currentBlock && i.currentBlock.tool.isDefault && i.currentBlock.isEmpty; + s.map( + async (l, c) => this.insertBlock(l, c === 0 && a) + ), i.currentBlock && o.setToBlock(i.currentBlock, o.positions.END); + } + /** + * Set onPaste callback handler + */ + setCallback() { + this.listeners.on(this.Editor.UI.nodes.holder, "paste", this.handlePasteEvent); + } + /** + * Unset onPaste callback handler + */ + unsetCallback() { + this.listeners.off(this.Editor.UI.nodes.holder, "paste", this.handlePasteEvent); + } + /** + * Get and process tool`s paste configs + */ + processTools() { + const e = this.Editor.Tools.blockTools; + Array.from(e.values()).forEach(this.processTool); + } + /** + * Get tags name list from either tag name or sanitization config. + * + * @param {string | object} tagOrSanitizeConfig - tag name or sanitize config object. + * @returns {string[]} array of tags. + */ + collectTagNames(e) { + return te(e) ? [e] : D(e) ? Object.keys(e) : []; + } + /** + * Get tags to substitute by Tool + * + * @param tool - BlockTool object + */ + getTagsConfig(e) { + if (e.pasteConfig === !1) + return; + const t = e.pasteConfig.tags || [], o = []; + t.forEach((i) => { + const s = this.collectTagNames(i); + o.push(...s), s.forEach((r) => { + if (Object.prototype.hasOwnProperty.call(this.toolsTags, r)) { + S( + `Paste handler for «${e.name}» Tool on «${r}» tag is skipped because it is already used by «${this.toolsTags[r].tool.name}» Tool.`, + "warn" + ); + return; + } + const a = D(i) ? i[r] : null; + this.toolsTags[r.toUpperCase()] = { + tool: e, + sanitizationConfig: a + }; + }); + }), this.tagsByTool[e.name] = o.map((i) => i.toUpperCase()); + } + /** + * Get files` types and extensions to substitute by Tool + * + * @param tool - BlockTool object + */ + getFilesConfig(e) { + if (e.pasteConfig === !1) + return; + const { files: t = {} } = e.pasteConfig; + let { extensions: o, mimeTypes: i } = t; + !o && !i || (o && !Array.isArray(o) && (S(`«extensions» property of the onDrop config for «${e.name}» Tool should be an array`), o = []), i && !Array.isArray(i) && (S(`«mimeTypes» property of the onDrop config for «${e.name}» Tool should be an array`), i = []), i && (i = i.filter((s) => ei(s) ? !0 : (S(`MIME type value «${s}» for the «${e.name}» Tool is not a valid MIME type`, "warn"), !1))), this.toolsFiles[e.name] = { + extensions: o || [], + mimeTypes: i || [] + }); + } + /** + * Get RegExp patterns to substitute by Tool + * + * @param tool - BlockTool object + */ + getPatternsConfig(e) { + e.pasteConfig === !1 || !e.pasteConfig.patterns || V(e.pasteConfig.patterns) || Object.entries(e.pasteConfig.patterns).forEach(([t, o]) => { + o instanceof RegExp || S( + `Pattern ${o} for «${e.name}» Tool is skipped because it should be a Regexp instance.`, + "warn" + ), this.toolsPatterns.push({ + key: t, + pattern: o, + tool: e + }); + }); + } + /** + * Check if browser behavior suits better + * + * @param {EventTarget} element - element where content has been pasted + * @returns {boolean} + */ + isNativeBehaviour(e) { + return u.isNativeInput(e); + } + /** + * Get files from data transfer object and insert related Tools + * + * @param {FileList} items - pasted or dropped items + */ + async processFiles(e) { + const { BlockManager: t } = this.Editor; + let o; + o = await Promise.all( + Array.from(e).map((r) => this.processFile(r)) + ), o = o.filter((r) => !!r); + const s = t.currentBlock.tool.isDefault && t.currentBlock.isEmpty; + o.forEach( + (r, a) => { + t.paste(r.type, r.event, a === 0 && s); + } + ); + } + /** + * Get information about file and find Tool to handle it + * + * @param {File} file - file to process + */ + async processFile(e) { + const t = Jn(e), o = Object.entries(this.toolsFiles).find(([r, { mimeTypes: a, extensions: l }]) => { + const [c, d] = e.type.split("/"), h = l.find((g) => g.toLowerCase() === t.toLowerCase()), p = a.find((g) => { + const [f, v] = g.split("/"); + return f === c && (v === d || v === "*"); + }); + return !!h || !!p; + }); + if (!o) + return; + const [i] = o; + return { + event: this.composePasteEvent("file", { + file: e + }), + type: i + }; + } + /** + * Split HTML string to blocks and return it as array of Block data + * + * @param {string} innerHTML - html string to process + * @returns {PasteData[]} + */ + processHTML(e) { + const { Tools: t } = this.Editor, o = u.make("DIV"); + return o.innerHTML = e, this.getNodes(o).map((s) => { + let r, a = t.defaultTool, l = !1; + switch (s.nodeType) { + case Node.DOCUMENT_FRAGMENT_NODE: + r = u.make("div"), r.appendChild(s); + break; + case Node.ELEMENT_NODE: + r = s, l = !0, this.toolsTags[r.tagName] && (a = this.toolsTags[r.tagName].tool); + break; + } + const { tags: c } = a.pasteConfig || { tags: [] }, d = c.reduce((g, f) => (this.collectTagNames(f).forEach((O) => { + const T = D(f) ? f[O] : null; + g[O.toLowerCase()] = T || {}; + }), g), {}), h = Object.assign({}, d, a.baseSanitizeConfig); + if (r.tagName.toLowerCase() === "table") { + const g = Z(r.outerHTML, h); + r = u.make("div", void 0, { + innerHTML: g + }).firstChild; + } else + r.innerHTML = Z(r.innerHTML, h); + const p = this.composePasteEvent("tag", { + data: r + }); + return { + content: r, + isBlock: l, + tool: a.name, + event: p + }; + }).filter((s) => { + const r = u.isEmpty(s.content), a = u.isSingleTag(s.content); + return !r || a; + }); + } + /** + * Split plain text by new line symbols and return it as array of Block data + * + * @param {string} plain - string to process + * @returns {PasteData[]} + */ + processPlain(e) { + const { defaultBlock: t } = this.config; + if (!e) + return []; + const o = t; + return e.split(/\r?\n/).filter((i) => i.trim()).map((i) => { + const s = u.make("div"); + s.textContent = i; + const r = this.composePasteEvent("tag", { + data: s + }); + return { + content: s, + tool: o, + isBlock: !1, + event: r + }; + }); + } + /** + * Process paste of single Block tool content + * + * @param {PasteData} dataToInsert - data of Block to insert + */ + async processSingleBlock(e) { + const { Caret: t, BlockManager: o } = this.Editor, { currentBlock: i } = o; + if (!i || e.tool !== i.name || !u.containsOnlyInlineElements(e.content.innerHTML)) { + this.insertBlock(e, (i == null ? void 0 : i.tool.isDefault) && i.isEmpty); + return; + } + t.insertContentAtCaretPosition(e.content.innerHTML); + } + /** + * Process paste to single Block: + * 1. Find patterns` matches + * 2. Insert new block if it is not the same type as current one + * 3. Just insert text if there is no substitutions + * + * @param {PasteData} dataToInsert - data of Block to insert + */ + async processInlinePaste(e) { + const { BlockManager: t, Caret: o } = this.Editor, { content: i } = e; + if (t.currentBlock && t.currentBlock.tool.isDefault && i.textContent.length < Dn.PATTERN_PROCESSING_MAX_LENGTH) { + const r = await this.processPattern(i.textContent); + if (r) { + const a = t.currentBlock && t.currentBlock.tool.isDefault && t.currentBlock.isEmpty, l = t.paste(r.tool, r.event, a); + o.setToBlock(l, o.positions.END); + return; + } + } + if (t.currentBlock && t.currentBlock.currentInput) { + const r = t.currentBlock.tool.baseSanitizeConfig; + document.execCommand( + "insertHTML", + !1, + Z(i.innerHTML, r) + ); + } else + this.insertBlock(e); + } + /** + * Get patterns` matches + * + * @param {string} text - text to process + * @returns {Promise<{event: PasteEvent, tool: string}>} + */ + async processPattern(e) { + const t = this.toolsPatterns.find((i) => { + const s = i.pattern.exec(e); + return s ? e === s.shift() : !1; + }); + return t ? { + event: this.composePasteEvent("pattern", { + key: t.key, + data: e + }), + tool: t.tool.name + } : void 0; + } + /** + * Insert pasted Block content to Editor + * + * @param {PasteData} data - data to insert + * @param {boolean} canReplaceCurrentBlock - if true and is current Block is empty, will replace current Block + * @returns {void} + */ + insertBlock(e, t = !1) { + const { BlockManager: o, Caret: i } = this.Editor, { currentBlock: s } = o; + let r; + if (t && s && s.isEmpty) { + r = o.paste(e.tool, e.event, !0), i.setToBlock(r, i.positions.END); + return; + } + r = o.paste(e.tool, e.event), i.setToBlock(r, i.positions.END); + } + /** + * Insert data passed as application/x-editor-js JSON + * + * @param {Array} blocks — Blocks' data to insert + * @returns {void} + */ + insertEditorJSData(e) { + const { BlockManager: t, Caret: o, Tools: i } = this.Editor; + yt( + e, + (r) => i.blockTools.get(r).sanitizeConfig + ).forEach(({ tool: r, data: a }, l) => { + let c = !1; + l === 0 && (c = t.currentBlock && t.currentBlock.tool.isDefault && t.currentBlock.isEmpty); + const d = t.insert({ + tool: r, + data: a, + replace: c + }); + o.setToBlock(d, o.positions.END); + }); + } + /** + * Fetch nodes from Element node + * + * @param {Node} node - current node + * @param {Node[]} nodes - processed nodes + * @param {Node} destNode - destination node + */ + processElementNode(e, t, o) { + const i = Object.keys(this.toolsTags), s = e, { tool: r } = this.toolsTags[s.tagName] || {}, a = this.tagsByTool[r == null ? void 0 : r.name] || [], l = i.includes(s.tagName), c = u.blockElements.includes(s.tagName.toLowerCase()), d = Array.from(s.children).some( + ({ tagName: p }) => i.includes(p) && !a.includes(p) + ), h = Array.from(s.children).some( + ({ tagName: p }) => u.blockElements.includes(p.toLowerCase()) + ); + if (!c && !l && !d) + return o.appendChild(s), [...t, o]; + if (l && !d || c && !h && !d) + return [...t, o, s]; + } + /** + * Recursively divide HTML string to two types of nodes: + * 1. Block element + * 2. Document Fragments contained text and markup tags like a, b, i etc. + * + * @param {Node} wrapper - wrapper of paster HTML content + * @returns {Node[]} + */ + getNodes(e) { + const t = Array.from(e.childNodes); + let o; + const i = (s, r) => { + if (u.isEmpty(r) && !u.isSingleTag(r)) + return s; + const a = s[s.length - 1]; + let l = new DocumentFragment(); + switch (a && u.isFragment(a) && (l = s.pop()), r.nodeType) { + case Node.ELEMENT_NODE: + if (o = this.processElementNode(r, s, l), o) + return o; + break; + case Node.TEXT_NODE: + return l.appendChild(r), [...s, l]; + default: + return [...s, l]; + } + return [...s, ...Array.from(r.childNodes).reduce(i, [])]; + }; + return t.reduce(i, []); + } + /** + * Compose paste event with passed type and detail + * + * @param {string} type - event type + * @param {PasteEventDetail} detail - event detail + */ + composePasteEvent(e, t) { + return new CustomEvent(e, { + detail: t + }); + } +}; +Rn.PATTERN_PROCESSING_MAX_LENGTH = 450; +let pa = Rn; +class fa extends E { + constructor() { + super(...arguments), this.toolsDontSupportReadOnly = [], this.readOnlyEnabled = !1; + } + /** + * Returns state of read only mode + */ + get isEnabled() { + return this.readOnlyEnabled; + } + /** + * Set initial state + */ + async prepare() { + const { Tools: e } = this.Editor, { blockTools: t } = e, o = []; + Array.from(t.entries()).forEach(([i, s]) => { + s.isReadOnlySupported || o.push(i); + }), this.toolsDontSupportReadOnly = o, this.config.readOnly && o.length > 0 && this.throwCriticalError(), this.toggle(this.config.readOnly, !0); + } + /** + * Set read-only mode or toggle current state + * Call all Modules `toggleReadOnly` method and re-render Editor + * + * @param state - (optional) read-only state or toggle + * @param isInitial - (optional) true when editor is initializing + */ + async toggle(e = !this.readOnlyEnabled, t = !1) { + e && this.toolsDontSupportReadOnly.length > 0 && this.throwCriticalError(); + const o = this.readOnlyEnabled; + this.readOnlyEnabled = e; + for (const s in this.Editor) + this.Editor[s].toggleReadOnly && this.Editor[s].toggleReadOnly(e); + if (o === e) + return this.readOnlyEnabled; + if (t) + return this.readOnlyEnabled; + this.Editor.ModificationsObserver.disable(); + const i = await this.Editor.Saver.save(); + return await this.Editor.BlockManager.clear(), await this.Editor.Renderer.render(i.blocks), this.Editor.ModificationsObserver.enable(), this.readOnlyEnabled; + } + /** + * Throws an error about tools which don't support read-only mode + */ + throwCriticalError() { + throw new Ho( + `To enable read-only mode all connected tools should support it. Tools ${this.toolsDontSupportReadOnly.join(", ")} don't support read-only mode.` + ); + } +} +class Be extends E { + constructor() { + super(...arguments), this.isRectSelectionActivated = !1, this.SCROLL_SPEED = 3, this.HEIGHT_OF_SCROLL_ZONE = 40, this.BOTTOM_SCROLL_ZONE = 1, this.TOP_SCROLL_ZONE = 2, this.MAIN_MOUSE_BUTTON = 0, this.mousedown = !1, this.isScrolling = !1, this.inScrollZone = null, this.startX = 0, this.startY = 0, this.mouseX = 0, this.mouseY = 0, this.stackOfSelected = [], this.listenerIds = []; + } + /** + * CSS classes for the Block + * + * @returns {{wrapper: string, content: string}} + */ + static get CSS() { + return { + overlay: "codex-editor-overlay", + overlayContainer: "codex-editor-overlay__container", + rect: "codex-editor-overlay__rectangle", + topScrollZone: "codex-editor-overlay__scroll-zone--top", + bottomScrollZone: "codex-editor-overlay__scroll-zone--bottom" + }; + } + /** + * Module Preparation + * Creating rect and hang handlers + */ + prepare() { + this.enableModuleBindings(); + } + /** + * Init rect params + * + * @param {number} pageX - X coord of mouse + * @param {number} pageY - Y coord of mouse + */ + startSelection(e, t) { + const o = document.elementFromPoint(e - window.pageXOffset, t - window.pageYOffset); + o.closest(`.${this.Editor.Toolbar.CSS.toolbar}`) || (this.Editor.BlockSelection.allBlocksSelected = !1, this.clearSelection(), this.stackOfSelected = []); + const s = [ + `.${R.CSS.content}`, + `.${this.Editor.Toolbar.CSS.toolbar}`, + `.${this.Editor.InlineToolbar.CSS.inlineToolbar}` + ], r = o.closest("." + this.Editor.UI.CSS.editorWrapper), a = s.some((l) => !!o.closest(l)); + !r || a || (this.mousedown = !0, this.startX = e, this.startY = t); + } + /** + * Clear all params to end selection + */ + endSelection() { + this.mousedown = !1, this.startX = 0, this.startY = 0, this.overlayRectangle.style.display = "none"; + } + /** + * is RectSelection Activated + */ + isRectActivated() { + return this.isRectSelectionActivated; + } + /** + * Mark that selection is end + */ + clearSelection() { + this.isRectSelectionActivated = !1; + } + /** + * Sets Module necessary event handlers + */ + enableModuleBindings() { + const { container: e } = this.genHTML(); + this.listeners.on(e, "mousedown", (t) => { + this.processMouseDown(t); + }, !1), this.listeners.on(document.body, "mousemove", dt((t) => { + this.processMouseMove(t); + }, 10), { + passive: !0 + }), this.listeners.on(document.body, "mouseleave", () => { + this.processMouseLeave(); + }), this.listeners.on(window, "scroll", dt((t) => { + this.processScroll(t); + }, 10), { + passive: !0 + }), this.listeners.on(document.body, "mouseup", () => { + this.processMouseUp(); + }, !1); + } + /** + * Handle mouse down events + * + * @param {MouseEvent} mouseEvent - mouse event payload + */ + processMouseDown(e) { + if (e.button !== this.MAIN_MOUSE_BUTTON) + return; + e.target.closest(u.allInputsSelector) !== null || this.startSelection(e.pageX, e.pageY); + } + /** + * Handle mouse move events + * + * @param {MouseEvent} mouseEvent - mouse event payload + */ + processMouseMove(e) { + this.changingRectangle(e), this.scrollByZones(e.clientY); + } + /** + * Handle mouse leave + */ + processMouseLeave() { + this.clearSelection(), this.endSelection(); + } + /** + * @param {MouseEvent} mouseEvent - mouse event payload + */ + processScroll(e) { + this.changingRectangle(e); + } + /** + * Handle mouse up + */ + processMouseUp() { + this.clearSelection(), this.endSelection(); + } + /** + * Scroll If mouse in scroll zone + * + * @param {number} clientY - Y coord of mouse + */ + scrollByZones(e) { + if (this.inScrollZone = null, e <= this.HEIGHT_OF_SCROLL_ZONE && (this.inScrollZone = this.TOP_SCROLL_ZONE), document.documentElement.clientHeight - e <= this.HEIGHT_OF_SCROLL_ZONE && (this.inScrollZone = this.BOTTOM_SCROLL_ZONE), !this.inScrollZone) { + this.isScrolling = !1; + return; + } + this.isScrolling || (this.scrollVertical(this.inScrollZone === this.TOP_SCROLL_ZONE ? -this.SCROLL_SPEED : this.SCROLL_SPEED), this.isScrolling = !0); + } + /** + * Generates required HTML elements + * + * @returns {Object} + */ + genHTML() { + const { UI: e } = this.Editor, t = e.nodes.holder.querySelector("." + e.CSS.editorWrapper), o = u.make("div", Be.CSS.overlay, {}), i = u.make("div", Be.CSS.overlayContainer, {}), s = u.make("div", Be.CSS.rect, {}); + return i.appendChild(s), o.appendChild(i), t.appendChild(o), this.overlayRectangle = s, { + container: t, + overlay: o + }; + } + /** + * Activates scrolling if blockSelection is active and mouse is in scroll zone + * + * @param {number} speed - speed of scrolling + */ + scrollVertical(e) { + if (!(this.inScrollZone && this.mousedown)) + return; + const t = window.pageYOffset; + window.scrollBy(0, e), this.mouseY += window.pageYOffset - t, setTimeout(() => { + this.scrollVertical(e); + }, 0); + } + /** + * Handles the change in the rectangle and its effect + * + * @param {MouseEvent} event - mouse event + */ + changingRectangle(e) { + if (!this.mousedown) + return; + e.pageY !== void 0 && (this.mouseX = e.pageX, this.mouseY = e.pageY); + const { rightPos: t, leftPos: o, index: i } = this.genInfoForMouseSelection(), s = this.startX > t && this.mouseX > t, r = this.startX < o && this.mouseX < o; + this.rectCrossesBlocks = !(s || r), this.isRectSelectionActivated || (this.rectCrossesBlocks = !1, this.isRectSelectionActivated = !0, this.shrinkRectangleToPoint(), this.overlayRectangle.style.display = "block"), this.updateRectangleSize(), this.Editor.Toolbar.close(), i !== void 0 && (this.trySelectNextBlock(i), this.inverseSelection(), b.get().removeAllRanges()); + } + /** + * Shrink rect to singular point + */ + shrinkRectangleToPoint() { + this.overlayRectangle.style.left = `${this.startX - window.pageXOffset}px`, this.overlayRectangle.style.top = `${this.startY - window.pageYOffset}px`, this.overlayRectangle.style.bottom = `calc(100% - ${this.startY - window.pageYOffset}px`, this.overlayRectangle.style.right = `calc(100% - ${this.startX - window.pageXOffset}px`; + } + /** + * Select or unselect all of blocks in array if rect is out or in selectable area + */ + inverseSelection() { + const t = this.Editor.BlockManager.getBlockByIndex(this.stackOfSelected[0]).selected; + if (this.rectCrossesBlocks && !t) + for (const o of this.stackOfSelected) + this.Editor.BlockSelection.selectBlockByIndex(o); + if (!this.rectCrossesBlocks && t) + for (const o of this.stackOfSelected) + this.Editor.BlockSelection.unSelectBlockByIndex(o); + } + /** + * Updates size of rectangle + */ + updateRectangleSize() { + this.mouseY >= this.startY ? (this.overlayRectangle.style.top = `${this.startY - window.pageYOffset}px`, this.overlayRectangle.style.bottom = `calc(100% - ${this.mouseY - window.pageYOffset}px`) : (this.overlayRectangle.style.bottom = `calc(100% - ${this.startY - window.pageYOffset}px`, this.overlayRectangle.style.top = `${this.mouseY - window.pageYOffset}px`), this.mouseX >= this.startX ? (this.overlayRectangle.style.left = `${this.startX - window.pageXOffset}px`, this.overlayRectangle.style.right = `calc(100% - ${this.mouseX - window.pageXOffset}px`) : (this.overlayRectangle.style.right = `calc(100% - ${this.startX - window.pageXOffset}px`, this.overlayRectangle.style.left = `${this.mouseX - window.pageXOffset}px`); + } + /** + * Collects information needed to determine the behavior of the rectangle + * + * @returns {object} index - index next Block, leftPos - start of left border of Block, rightPos - right border + */ + genInfoForMouseSelection() { + const t = document.body.offsetWidth / 2, o = this.mouseY - window.pageYOffset, i = document.elementFromPoint(t, o), s = this.Editor.BlockManager.getBlockByChildNode(i); + let r; + s !== void 0 && (r = this.Editor.BlockManager.blocks.findIndex((h) => h.holder === s.holder)); + const a = this.Editor.BlockManager.lastBlock.holder.querySelector("." + R.CSS.content), l = Number.parseInt(window.getComputedStyle(a).width, 10) / 2, c = t - l, d = t + l; + return { + index: r, + leftPos: c, + rightPos: d + }; + } + /** + * Select block with index index + * + * @param index - index of block in redactor + */ + addBlockInSelection(e) { + this.rectCrossesBlocks && this.Editor.BlockSelection.selectBlockByIndex(e), this.stackOfSelected.push(e); + } + /** + * Adds a block to the selection and determines which blocks should be selected + * + * @param {object} index - index of new block in the reactor + */ + trySelectNextBlock(e) { + const t = this.stackOfSelected[this.stackOfSelected.length - 1] === e, o = this.stackOfSelected.length, i = 1, s = -1, r = 0; + if (t) + return; + const a = this.stackOfSelected[o - 1] - this.stackOfSelected[o - 2] > 0; + let l = r; + o > 1 && (l = a ? i : s); + const c = e > this.stackOfSelected[o - 1] && l === i, d = e < this.stackOfSelected[o - 1] && l === s, p = !(c || d || l === r); + if (!p && (e > this.stackOfSelected[o - 1] || this.stackOfSelected[o - 1] === void 0)) { + let v = this.stackOfSelected[o - 1] + 1 || e; + for (v; v <= e; v++) + this.addBlockInSelection(v); + return; + } + if (!p && e < this.stackOfSelected[o - 1]) { + for (let v = this.stackOfSelected[o - 1] - 1; v >= e; v--) + this.addBlockInSelection(v); + return; + } + if (!p) + return; + let g = o - 1, f; + for (e > this.stackOfSelected[o - 1] ? f = () => e > this.stackOfSelected[g] : f = () => e < this.stackOfSelected[g]; f(); ) + this.rectCrossesBlocks && this.Editor.BlockSelection.unSelectBlockByIndex(this.stackOfSelected[g]), this.stackOfSelected.pop(), g--; + } +} +class ga extends E { + /** + * Renders passed blocks as one batch + * + * @param blocksData - blocks to render + */ + async render(e) { + return new Promise((t) => { + const { Tools: o, BlockManager: i } = this.Editor; + if (e.length === 0) + i.insert(); + else { + const s = e.map(({ type: r, data: a, tunes: l, id: c }) => { + o.available.has(r) === !1 && (X(`Tool «${r}» is not found. Check 'tools' property at the Editor.js config.`, "warn"), a = this.composeStubDataForTool(r, a, c), r = o.stubTool); + let d; + try { + d = i.composeBlock({ + id: c, + tool: r, + data: a, + tunes: l + }); + } catch (h) { + S(`Block «${r}» skipped because of plugins error`, "error", { + data: a, + error: h + }), a = this.composeStubDataForTool(r, a, c), r = o.stubTool, d = i.composeBlock({ + id: c, + tool: r, + data: a, + tunes: l + }); + } + return d; + }); + i.insertMany(s); + } + window.requestIdleCallback(() => { + t(); + }, { timeout: 2e3 }); + }); + } + /** + * Create data for the Stub Tool that will be used instead of unavailable tool + * + * @param tool - unavailable tool name to stub + * @param data - data of unavailable block + * @param [id] - id of unavailable block + */ + composeStubDataForTool(e, t, o) { + const { Tools: i } = this.Editor; + let s = e; + if (i.unavailable.has(e)) { + const r = i.unavailable.get(e).toolbox; + r !== void 0 && r[0].title !== void 0 && (s = r[0].title); + } + return { + savedData: { + id: o, + type: e, + data: t + }, + title: s + }; + } +} +class ma extends E { + /** + * Composes new chain of Promises to fire them alternatelly + * + * @returns {OutputData} + */ + async save() { + const { BlockManager: e, Tools: t } = this.Editor, o = e.blocks, i = []; + try { + o.forEach((a) => { + i.push(this.getSavedData(a)); + }); + const s = await Promise.all(i), r = await yt(s, (a) => t.blockTools.get(a).sanitizeConfig); + return this.makeOutput(r); + } catch (s) { + X("Saving failed due to the Error %o", "error", s); + } + } + /** + * Saves and validates + * + * @param {Block} block - Editor's Tool + * @returns {ValidatedData} - Tool's validated data + */ + async getSavedData(e) { + const t = await e.save(), o = t && await e.validate(t.data); + return { + ...t, + isValid: o + }; + } + /** + * Creates output object with saved data, time and version of editor + * + * @param {ValidatedData} allExtractedData - data extracted from Blocks + * @returns {OutputData} + */ + makeOutput(e) { + const t = []; + return e.forEach(({ id: o, tool: i, data: s, tunes: r, isValid: a }) => { + if (!a) { + S(`Block «${i}» skipped because saved data is invalid`); + return; + } + if (i === this.Editor.Tools.stubTool) { + t.push(s); + return; + } + const l = { + id: o, + type: i, + data: s, + ...!V(r) && { + tunes: r + } + }; + t.push(l); + }), { + time: +/* @__PURE__ */ new Date(), + blocks: t, + version: "2.31.1" + }; + } +} +(function() { + try { + if (typeof document < "u") { + var n = document.createElement("style"); + n.appendChild(document.createTextNode(".ce-paragraph{line-height:1.6em;outline:none}.ce-block:only-of-type .ce-paragraph[data-placeholder-active]:empty:before,.ce-block:only-of-type .ce-paragraph[data-placeholder-active][data-empty=true]:before{content:attr(data-placeholder-active)}.ce-paragraph p:first-of-type{margin-top:0}.ce-paragraph p:last-of-type{margin-bottom:0}")), document.head.appendChild(n); + } + } catch (e) { + console.error("vite-plugin-css-injected-by-js", e); + } +})(); +const ba = ''; +function va(n) { + const e = document.createElement("div"); + e.innerHTML = n.trim(); + const t = document.createDocumentFragment(); + return t.append(...Array.from(e.childNodes)), t; +} +/** + * Base Paragraph Block for the Editor.js. + * Represents a regular text block + * + * @author CodeX (team@codex.so) + * @copyright CodeX 2018 + * @license The MIT License (MIT) + */ +class fo { + /** + * Default placeholder for Paragraph Tool + * + * @returns {string} + * @class + */ + static get DEFAULT_PLACEHOLDER() { + return ""; + } + /** + * Render plugin`s main Element and fill it with saved data + * + * @param {object} params - constructor params + * @param {ParagraphData} params.data - previously saved data + * @param {ParagraphConfig} params.config - user config for Tool + * @param {object} params.api - editor.js api + * @param {boolean} readOnly - read only mode flag + */ + constructor({ data: e, config: t, api: o, readOnly: i }) { + this.api = o, this.readOnly = i, this._CSS = { + block: this.api.styles.block, + wrapper: "ce-paragraph" + }, this.readOnly || (this.onKeyUp = this.onKeyUp.bind(this)), this._placeholder = t.placeholder ? t.placeholder : fo.DEFAULT_PLACEHOLDER, this._data = e ?? {}, this._element = null, this._preserveBlank = t.preserveBlank ?? !1; + } + /** + * Check if text content is empty and set empty string to inner html. + * We need this because some browsers (e.g. Safari) insert
    into empty contenteditanle elements + * + * @param {KeyboardEvent} e - key up event + */ + onKeyUp(e) { + if (e.code !== "Backspace" && e.code !== "Delete" || !this._element) + return; + const { textContent: t } = this._element; + t === "" && (this._element.innerHTML = ""); + } + /** + * Create Tool's view + * + * @returns {HTMLDivElement} + * @private + */ + drawView() { + const e = document.createElement("DIV"); + return e.classList.add(this._CSS.wrapper, this._CSS.block), e.contentEditable = "false", e.dataset.placeholderActive = this.api.i18n.t(this._placeholder), this._data.text && (e.innerHTML = this._data.text), this.readOnly || (e.contentEditable = "true", e.addEventListener("keyup", this.onKeyUp)), e; + } + /** + * Return Tool's view + * + * @returns {HTMLDivElement} + */ + render() { + return this._element = this.drawView(), this._element; + } + /** + * Method that specified how to merge two Text blocks. + * Called by Editor.js by backspace at the beginning of the Block + * + * @param {ParagraphData} data + * @public + */ + merge(e) { + if (!this._element) + return; + this._data.text += e.text; + const t = va(e.text); + this._element.appendChild(t), this._element.normalize(); + } + /** + * Validate Paragraph block data: + * - check for emptiness + * + * @param {ParagraphData} savedData — data received after saving + * @returns {boolean} false if saved data is not correct, otherwise true + * @public + */ + validate(e) { + return !(e.text.trim() === "" && !this._preserveBlank); + } + /** + * Extract Tool's data from the view + * + * @param {HTMLDivElement} toolsContent - Paragraph tools rendered view + * @returns {ParagraphData} - saved data + * @public + */ + save(e) { + return { + text: e.innerHTML + }; + } + /** + * On paste callback fired from Editor. + * + * @param {HTMLPasteEvent} event - event with pasted data + */ + onPaste(e) { + const t = { + text: e.detail.data.innerHTML + }; + this._data = t, window.requestAnimationFrame(() => { + this._element && (this._element.innerHTML = this._data.text || ""); + }); + } + /** + * Enable Conversion Toolbar. Paragraph can be converted to/from other tools + * @returns {ConversionConfig} + */ + static get conversionConfig() { + return { + export: "text", + // to convert Paragraph to other block, use 'text' property of saved data + import: "text" + // to covert other block's exported string to Paragraph, fill 'text' property of tool data + }; + } + /** + * Sanitizer rules + * @returns {SanitizerConfig} - Edtior.js sanitizer config + */ + static get sanitize() { + return { + text: { + br: !0 + } + }; + } + /** + * Returns true to notify the core that read-only mode is supported + * + * @returns {boolean} + */ + static get isReadOnlySupported() { + return !0; + } + /** + * Used by Editor paste handling API. + * Provides configuration to handle P tags. + * + * @returns {PasteConfig} - Paragraph Paste Setting + */ + static get pasteConfig() { + return { + tags: ["P"] + }; + } + /** + * Icon and title for displaying at the Toolbox + * + * @returns {ToolboxConfig} - Paragraph Toolbox Setting + */ + static get toolbox() { + return { + icon: ba, + title: "Text" + }; + } +} +class go { + constructor() { + this.commandName = "bold"; + } + /** + * Sanitizer Rule + * Leave tags + * + * @returns {object} + */ + static get sanitize() { + return { + b: {} + }; + } + /** + * Create button for Inline Toolbar + */ + render() { + return { + icon: Ki, + name: "bold", + onActivate: () => { + document.execCommand(this.commandName); + }, + isActive: () => document.queryCommandState(this.commandName) + }; + } + /** + * Set a shortcut + * + * @returns {boolean} + */ + get shortcut() { + return "CMD+B"; + } +} +go.isInline = !0; +go.title = "Bold"; +class mo { + constructor() { + this.commandName = "italic", this.CSS = { + button: "ce-inline-tool", + buttonActive: "ce-inline-tool--active", + buttonModifier: "ce-inline-tool--italic" + }, this.nodes = { + button: null + }; + } + /** + * Sanitizer Rule + * Leave tags + * + * @returns {object} + */ + static get sanitize() { + return { + i: {} + }; + } + /** + * Create button for Inline Toolbar + */ + render() { + return this.nodes.button = document.createElement("button"), this.nodes.button.type = "button", this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier), this.nodes.button.innerHTML = Ji, this.nodes.button; + } + /** + * Wrap range with tag + */ + surround() { + document.execCommand(this.commandName); + } + /** + * Check selection and set activated state to button if there are tag + */ + checkState() { + const e = document.queryCommandState(this.commandName); + return this.nodes.button.classList.toggle(this.CSS.buttonActive, e), e; + } + /** + * Set a shortcut + */ + get shortcut() { + return "CMD+I"; + } +} +mo.isInline = !0; +mo.title = "Italic"; +class bo { + /** + * @param api - Editor.js API + */ + constructor({ api: e }) { + this.commandLink = "createLink", this.commandUnlink = "unlink", this.ENTER_KEY = 13, this.CSS = { + button: "ce-inline-tool", + buttonActive: "ce-inline-tool--active", + buttonModifier: "ce-inline-tool--link", + buttonUnlink: "ce-inline-tool--unlink", + input: "ce-inline-tool-input", + inputShowed: "ce-inline-tool-input--showed" + }, this.nodes = { + button: null, + input: null + }, this.inputOpened = !1, this.toolbar = e.toolbar, this.inlineToolbar = e.inlineToolbar, this.notifier = e.notifier, this.i18n = e.i18n, this.selection = new b(); + } + /** + * Sanitizer Rule + * Leave tags + * + * @returns {object} + */ + static get sanitize() { + return { + a: { + href: !0, + target: "_blank", + rel: "nofollow" + } + }; + } + /** + * Create button for Inline Toolbar + */ + render() { + return this.nodes.button = document.createElement("button"), this.nodes.button.type = "button", this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier), this.nodes.button.innerHTML = Co, this.nodes.button; + } + /** + * Input for the link + */ + renderActions() { + return this.nodes.input = document.createElement("input"), this.nodes.input.placeholder = this.i18n.t("Add a link"), this.nodes.input.enterKeyHint = "done", this.nodes.input.classList.add(this.CSS.input), this.nodes.input.addEventListener("keydown", (e) => { + e.keyCode === this.ENTER_KEY && this.enterPressed(e); + }), this.nodes.input; + } + /** + * Handle clicks on the Inline Toolbar icon + * + * @param {Range} range - range to wrap with link + */ + surround(e) { + if (e) { + this.inputOpened ? (this.selection.restore(), this.selection.removeFakeBackground()) : (this.selection.setFakeBackground(), this.selection.save()); + const t = this.selection.findParentTag("A"); + if (t) { + this.selection.expandToTag(t), this.unlink(), this.closeActions(), this.checkState(), this.toolbar.close(); + return; + } + } + this.toggleActions(); + } + /** + * Check selection and set activated state to button if there are tag + */ + checkState() { + const e = this.selection.findParentTag("A"); + if (e) { + this.nodes.button.innerHTML = ns, this.nodes.button.classList.add(this.CSS.buttonUnlink), this.nodes.button.classList.add(this.CSS.buttonActive), this.openActions(); + const t = e.getAttribute("href"); + this.nodes.input.value = t !== "null" ? t : "", this.selection.save(); + } else + this.nodes.button.innerHTML = Co, this.nodes.button.classList.remove(this.CSS.buttonUnlink), this.nodes.button.classList.remove(this.CSS.buttonActive); + return !!e; + } + /** + * Function called with Inline Toolbar closing + */ + clear() { + this.closeActions(); + } + /** + * Set a shortcut + */ + get shortcut() { + return "CMD+K"; + } + /** + * Show/close link input + */ + toggleActions() { + this.inputOpened ? this.closeActions(!1) : this.openActions(!0); + } + /** + * @param {boolean} needFocus - on link creation we need to focus input. On editing - nope. + */ + openActions(e = !1) { + this.nodes.input.classList.add(this.CSS.inputShowed), e && this.nodes.input.focus(), this.inputOpened = !0; + } + /** + * Close input + * + * @param {boolean} clearSavedSelection — we don't need to clear saved selection + * on toggle-clicks on the icon of opened Toolbar + */ + closeActions(e = !0) { + if (this.selection.isFakeBackgroundEnabled) { + const t = new b(); + t.save(), this.selection.restore(), this.selection.removeFakeBackground(), t.restore(); + } + this.nodes.input.classList.remove(this.CSS.inputShowed), this.nodes.input.value = "", e && this.selection.clearSaved(), this.inputOpened = !1; + } + /** + * Enter pressed on input + * + * @param {KeyboardEvent} event - enter keydown event + */ + enterPressed(e) { + let t = this.nodes.input.value || ""; + if (!t.trim()) { + this.selection.restore(), this.unlink(), e.preventDefault(), this.closeActions(); + return; + } + if (!this.validateURL(t)) { + this.notifier.show({ + message: "Pasted link is not valid.", + style: "error" + }), S("Incorrect Link pasted", "warn", t); + return; + } + t = this.prepareLink(t), this.selection.restore(), this.selection.removeFakeBackground(), this.insertLink(t), e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), this.selection.collapseToEnd(), this.inlineToolbar.close(); + } + /** + * Detects if passed string is URL + * + * @param {string} str - string to validate + * @returns {boolean} + */ + validateURL(e) { + return !/\s/.test(e); + } + /** + * Process link before injection + * - sanitize + * - add protocol for links like 'google.com' + * + * @param {string} link - raw user input + */ + prepareLink(e) { + return e = e.trim(), e = this.addProtocol(e), e; + } + /** + * Add 'http' protocol to the links like 'vc.ru', 'google.com' + * + * @param {string} link - string to process + */ + addProtocol(e) { + if (/^(\w+):(\/\/)?/.test(e)) + return e; + const t = /^\/[^/\s]/.test(e), o = e.substring(0, 1) === "#", i = /^\/\/[^/\s]/.test(e); + return !t && !o && !i && (e = "http://" + e), e; + } + /** + * Inserts tag with "href" + * + * @param {string} link - "href" value + */ + insertLink(e) { + const t = this.selection.findParentTag("A"); + t && this.selection.expandToTag(t), document.execCommand(this.commandLink, !1, e); + } + /** + * Removes tag + */ + unlink() { + document.execCommand(this.commandUnlink); + } +} +bo.isInline = !0; +bo.title = "Link"; +class Fn { + /** + * @param api - Editor.js API + */ + constructor({ api: e }) { + this.i18nAPI = e.i18n, this.blocksAPI = e.blocks, this.selectionAPI = e.selection, this.toolsAPI = e.tools, this.caretAPI = e.caret; + } + /** + * Returns tool's UI config + */ + async render() { + const e = b.get(), t = this.blocksAPI.getBlockByElement(e.anchorNode); + if (t === void 0) + return []; + const o = this.toolsAPI.getBlockTools(), i = await Yo(t, o); + if (i.length === 0) + return []; + const s = i.reduce((c, d) => { + var h; + return (h = d.toolbox) == null || h.forEach((p) => { + c.push({ + icon: p.icon, + title: z.t(K.toolNames, p.title), + name: d.name, + closeOnActivate: !0, + onActivate: async () => { + const g = await this.blocksAPI.convert(t.id, d.name, p.data); + this.caretAPI.setToBlock(g, "end"); + } + }); + }), c; + }, []), r = await t.getActiveToolboxEntry(), a = r !== void 0 ? r.icon : Go, l = !be(); + return { + icon: a, + name: "convert-to", + hint: { + title: this.i18nAPI.t("Convert to") + }, + children: { + searchable: l, + items: s, + onOpen: () => { + l && (this.selectionAPI.setFakeBackground(), this.selectionAPI.save()); + }, + onClose: () => { + l && (this.selectionAPI.restore(), this.selectionAPI.removeFakeBackground()); + } + } + }; + } +} +Fn.isInline = !0; +class jn { + /** + * @param options - constructor options + * @param options.data - stub tool data + * @param options.api - Editor.js API + */ + constructor({ data: e, api: t }) { + this.CSS = { + wrapper: "ce-stub", + info: "ce-stub__info", + title: "ce-stub__title", + subtitle: "ce-stub__subtitle" + }, this.api = t, this.title = e.title || this.api.i18n.t("Error"), this.subtitle = this.api.i18n.t("The block can not be displayed correctly."), this.savedData = e.savedData, this.wrapper = this.make(); + } + /** + * Returns stub holder + * + * @returns {HTMLElement} + */ + render() { + return this.wrapper; + } + /** + * Return original Tool data + * + * @returns {BlockToolData} + */ + save() { + return this.savedData; + } + /** + * Create Tool html markup + * + * @returns {HTMLElement} + */ + make() { + const e = u.make("div", this.CSS.wrapper), t = is, o = u.make("div", this.CSS.info), i = u.make("div", this.CSS.title, { + textContent: this.title + }), s = u.make("div", this.CSS.subtitle, { + textContent: this.subtitle + }); + return e.innerHTML = t, o.appendChild(i), o.appendChild(s), e.appendChild(o), e; + } +} +jn.isReadOnlySupported = !0; +class ka extends Tt { + constructor() { + super(...arguments), this.type = ae.Inline; + } + /** + * Returns title for Inline Tool if specified by user + */ + get title() { + return this.constructable[We.Title]; + } + /** + * Constructs new InlineTool instance from constructable + */ + create() { + return new this.constructable({ + api: this.api, + config: this.settings + }); + } + /** + * Allows inline tool to be available in read-only mode + * Can be used, for example, by comments tool + */ + get isReadOnlySupported() { + return this.constructable[We.IsReadOnlySupported] ?? !1; + } +} +class ya extends Tt { + constructor() { + super(...arguments), this.type = ae.Tune; + } + /** + * Constructs new BlockTune instance from constructable + * + * @param data - Tune data + * @param block - Block API object + */ + create(e, t) { + return new this.constructable({ + api: this.api, + config: this.settings, + block: t, + data: e + }); + } +} +class j extends Map { + /** + * Returns Block Tools collection + */ + get blockTools() { + const e = Array.from(this.entries()).filter(([, t]) => t.isBlock()); + return new j(e); + } + /** + * Returns Inline Tools collection + */ + get inlineTools() { + const e = Array.from(this.entries()).filter(([, t]) => t.isInline()); + return new j(e); + } + /** + * Returns Block Tunes collection + */ + get blockTunes() { + const e = Array.from(this.entries()).filter(([, t]) => t.isTune()); + return new j(e); + } + /** + * Returns internal Tools collection + */ + get internalTools() { + const e = Array.from(this.entries()).filter(([, t]) => t.isInternal); + return new j(e); + } + /** + * Returns Tools collection provided by user + */ + get externalTools() { + const e = Array.from(this.entries()).filter(([, t]) => !t.isInternal); + return new j(e); + } +} +var wa = Object.defineProperty, Ea = Object.getOwnPropertyDescriptor, Hn = (n, e, t, o) => { + for (var i = o > 1 ? void 0 : o ? Ea(e, t) : e, s = n.length - 1, r; s >= 0; s--) + (r = n[s]) && (i = (o ? r(e, t, i) : r(i)) || i); + return o && i && wa(e, t, i), i; +}; +class vo extends Tt { + constructor() { + super(...arguments), this.type = ae.Block, this.inlineTools = new j(), this.tunes = new j(); + } + /** + * Creates new Tool instance + * + * @param data - Tool data + * @param block - BlockAPI for current Block + * @param readOnly - True if Editor is in read-only mode + */ + create(e, t, o) { + return new this.constructable({ + data: e, + block: t, + readOnly: o, + api: this.api, + config: this.settings + }); + } + /** + * Returns true if read-only mode is supported by Tool + */ + get isReadOnlySupported() { + return this.constructable[pe.IsReadOnlySupported] === !0; + } + /** + * Returns true if Tool supports linebreaks + */ + get isLineBreaksEnabled() { + return this.constructable[pe.IsEnabledLineBreaks]; + } + /** + * Returns Tool toolbox configuration (internal or user-specified). + * + * Merges internal and user-defined toolbox configs based on the following rules: + * + * - If both internal and user-defined toolbox configs are arrays their items are merged. + * Length of the second one is kept. + * + * - If both are objects their properties are merged. + * + * - If one is an object and another is an array than internal config is replaced with user-defined + * config. This is made to allow user to override default tool's toolbox representation (single/multiple entries) + */ + get toolbox() { + const e = this.constructable[pe.Toolbox], t = this.config[Pe.Toolbox]; + if (!V(e) && t !== !1) + return t ? Array.isArray(e) ? Array.isArray(t) ? t.map((o, i) => { + const s = e[i]; + return s ? { + ...s, + ...o + } : o; + }) : [t] : Array.isArray(t) ? t : [ + { + ...e, + ...t + } + ] : Array.isArray(e) ? e : [e]; + } + /** + * Returns Tool conversion configuration + */ + get conversionConfig() { + return this.constructable[pe.ConversionConfig]; + } + /** + * Returns enabled inline tools for Tool + */ + get enabledInlineTools() { + return this.config[Pe.EnabledInlineTools] || !1; + } + /** + * Returns enabled tunes for Tool + */ + get enabledBlockTunes() { + return this.config[Pe.EnabledBlockTunes]; + } + /** + * Returns Tool paste configuration + */ + get pasteConfig() { + return this.constructable[pe.PasteConfig] ?? {}; + } + get sanitizeConfig() { + const e = super.sanitizeConfig, t = this.baseSanitizeConfig; + if (V(e)) + return t; + const o = {}; + for (const i in e) + if (Object.prototype.hasOwnProperty.call(e, i)) { + const s = e[i]; + D(s) ? o[i] = Object.assign({}, t, s) : o[i] = s; + } + return o; + } + get baseSanitizeConfig() { + const e = {}; + return Array.from(this.inlineTools.values()).forEach((t) => Object.assign(e, t.sanitizeConfig)), Array.from(this.tunes.values()).forEach((t) => Object.assign(e, t.sanitizeConfig)), e; + } +} +Hn([ + me +], vo.prototype, "sanitizeConfig", 1); +Hn([ + me +], vo.prototype, "baseSanitizeConfig", 1); +class xa { + /** + * @class + * @param config - tools config + * @param editorConfig - EditorJS config + * @param api - EditorJS API module + */ + constructor(e, t, o) { + this.api = o, this.config = e, this.editorConfig = t; + } + /** + * Returns Tool object based on it's type + * + * @param name - tool name + */ + get(e) { + const { class: t, isInternal: o = !1, ...i } = this.config[e], s = this.getConstructor(t), r = t[mt.IsTune]; + return new s({ + name: e, + constructable: t, + config: i, + api: this.api.getMethodsForTool(e, r), + isDefault: e === this.editorConfig.defaultBlock, + defaultPlaceholder: this.editorConfig.placeholder, + isInternal: o + }); + } + /** + * Find appropriate Tool object constructor for Tool constructable + * + * @param constructable - Tools constructable + */ + getConstructor(e) { + switch (!0) { + case e[We.IsInline]: + return ka; + case e[mt.IsTune]: + return ya; + default: + return vo; + } + } +} +class $n { + /** + * MoveDownTune constructor + * + * @param {API} api — Editor's API + */ + constructor({ api: e }) { + this.CSS = { + animation: "wobble" + }, this.api = e; + } + /** + * Tune's appearance in block settings menu + */ + render() { + return { + icon: Xi, + title: this.api.i18n.t("Move down"), + onActivate: () => this.handleClick(), + name: "move-down" + }; + } + /** + * Handle clicks on 'move down' button + */ + handleClick() { + const e = this.api.blocks.getCurrentBlockIndex(), t = this.api.blocks.getBlockByIndex(e + 1); + if (!t) + throw new Error("Unable to move Block down since it is already the last"); + const o = t.holder, i = o.getBoundingClientRect(); + let s = Math.abs(window.innerHeight - o.offsetHeight); + i.top < window.innerHeight && (s = window.scrollY + o.offsetHeight), window.scrollTo(0, s), this.api.blocks.move(e + 1), this.api.toolbar.toggleBlockSettings(!0); + } +} +$n.isTune = !0; +class zn { + /** + * DeleteTune constructor + * + * @param {API} api - Editor's API + */ + constructor({ api: e }) { + this.api = e; + } + /** + * Tune's appearance in block settings menu + */ + render() { + return { + icon: Gi, + title: this.api.i18n.t("Delete"), + name: "delete", + confirmation: { + title: this.api.i18n.t("Click to delete"), + onActivate: () => this.handleClick() + } + }; + } + /** + * Delete block conditions passed + */ + handleClick() { + this.api.blocks.delete(); + } +} +zn.isTune = !0; +class Un { + /** + * MoveUpTune constructor + * + * @param {API} api - Editor's API + */ + constructor({ api: e }) { + this.CSS = { + animation: "wobble" + }, this.api = e; + } + /** + * Tune's appearance in block settings menu + */ + render() { + return { + icon: Zi, + title: this.api.i18n.t("Move up"), + onActivate: () => this.handleClick(), + name: "move-up" + }; + } + /** + * Move current block up + */ + handleClick() { + const e = this.api.blocks.getCurrentBlockIndex(), t = this.api.blocks.getBlockByIndex(e), o = this.api.blocks.getBlockByIndex(e - 1); + if (e === 0 || !t || !o) + throw new Error("Unable to move Block up since it is already the first"); + const i = t.holder, s = o.holder, r = i.getBoundingClientRect(), a = s.getBoundingClientRect(); + let l; + a.top > 0 ? l = Math.abs(r.top) - Math.abs(a.top) : l = Math.abs(r.top) + a.height, window.scrollBy(0, -1 * l), this.api.blocks.move(e - 1), this.api.toolbar.toggleBlockSettings(!0); + } +} +Un.isTune = !0; +var Ba = Object.defineProperty, Ca = Object.getOwnPropertyDescriptor, Ta = (n, e, t, o) => { + for (var i = o > 1 ? void 0 : o ? Ca(e, t) : e, s = n.length - 1, r; s >= 0; s--) + (r = n[s]) && (i = (o ? r(e, t, i) : r(i)) || i); + return o && i && Ba(e, t, i), i; +}; +class Wn extends E { + constructor() { + super(...arguments), this.stubTool = "stub", this.toolsAvailable = new j(), this.toolsUnavailable = new j(); + } + /** + * Returns available Tools + */ + get available() { + return this.toolsAvailable; + } + /** + * Returns unavailable Tools + */ + get unavailable() { + return this.toolsUnavailable; + } + /** + * Return Tools for the Inline Toolbar + */ + get inlineTools() { + return this.available.inlineTools; + } + /** + * Return editor block tools + */ + get blockTools() { + return this.available.blockTools; + } + /** + * Return available Block Tunes + * + * @returns {object} - object of Inline Tool's classes + */ + get blockTunes() { + return this.available.blockTunes; + } + /** + * Returns default Tool object + */ + get defaultTool() { + return this.blockTools.get(this.config.defaultBlock); + } + /** + * Returns internal tools + */ + get internal() { + return this.available.internalTools; + } + /** + * Creates instances via passed or default configuration + * + * @returns {Promise} + */ + async prepare() { + if (this.validateTools(), this.config.tools = ut({}, this.internalTools, this.config.tools), !Object.prototype.hasOwnProperty.call(this.config, "tools") || Object.keys(this.config.tools).length === 0) + throw Error("Can't start without tools"); + const e = this.prepareConfig(); + this.factory = new xa(e, this.config, this.Editor.API); + const t = this.getListOfPrepareFunctions(e); + if (t.length === 0) + return Promise.resolve(); + await Qn(t, (o) => { + this.toolPrepareMethodSuccess(o); + }, (o) => { + this.toolPrepareMethodFallback(o); + }), this.prepareBlockTools(); + } + getAllInlineToolsSanitizeConfig() { + const e = {}; + return Array.from(this.inlineTools.values()).forEach((t) => { + Object.assign(e, t.sanitizeConfig); + }), e; + } + /** + * Calls each Tool reset method to clean up anything set by Tool + */ + destroy() { + Object.values(this.available).forEach(async (e) => { + A(e.reset) && await e.reset(); + }); + } + /** + * Returns internal tools + * Includes Bold, Italic, Link and Paragraph + */ + get internalTools() { + return { + convertTo: { + class: Fn, + isInternal: !0 + }, + link: { + class: bo, + isInternal: !0 + }, + bold: { + class: go, + isInternal: !0 + }, + italic: { + class: mo, + isInternal: !0 + }, + paragraph: { + class: fo, + inlineToolbar: !0, + isInternal: !0 + }, + stub: { + class: jn, + isInternal: !0 + }, + moveUp: { + class: Un, + isInternal: !0 + }, + delete: { + class: zn, + isInternal: !0 + }, + moveDown: { + class: $n, + isInternal: !0 + } + }; + } + /** + * Tool prepare method success callback + * + * @param {object} data - append tool to available list + */ + toolPrepareMethodSuccess(e) { + const t = this.factory.get(e.toolName); + if (t.isInline()) { + const i = ["render"].filter((s) => !t.create()[s]); + if (i.length) { + S( + `Incorrect Inline Tool: ${t.name}. Some of required methods is not implemented %o`, + "warn", + i + ), this.toolsUnavailable.set(t.name, t); + return; + } + } + this.toolsAvailable.set(t.name, t); + } + /** + * Tool prepare method fail callback + * + * @param {object} data - append tool to unavailable list + */ + toolPrepareMethodFallback(e) { + this.toolsUnavailable.set(e.toolName, this.factory.get(e.toolName)); + } + /** + * Binds prepare function of plugins with user or default config + * + * @returns {Array} list of functions that needs to be fired sequentially + * @param config - tools config + */ + getListOfPrepareFunctions(e) { + const t = []; + return Object.entries(e).forEach(([o, i]) => { + t.push({ + // eslint-disable-next-line @typescript-eslint/no-empty-function + function: A(i.class.prepare) ? i.class.prepare : () => { + }, + data: { + toolName: o, + config: i.config + } + }); + }), t; + } + /** + * Assign enabled Inline Tools and Block Tunes for Block Tool + */ + prepareBlockTools() { + Array.from(this.blockTools.values()).forEach((e) => { + this.assignInlineToolsToBlockTool(e), this.assignBlockTunesToBlockTool(e); + }); + } + /** + * Assign enabled Inline Tools for Block Tool + * + * @param tool - Block Tool + */ + assignInlineToolsToBlockTool(e) { + if (this.config.inlineToolbar !== !1) { + if (e.enabledInlineTools === !0) { + e.inlineTools = new j( + Array.isArray(this.config.inlineToolbar) ? this.config.inlineToolbar.map((t) => [t, this.inlineTools.get(t)]) : Array.from(this.inlineTools.entries()) + ); + return; + } + Array.isArray(e.enabledInlineTools) && (e.inlineTools = new j( + /** Prepend ConvertTo Inline Tool */ + ["convertTo", ...e.enabledInlineTools].map((t) => [t, this.inlineTools.get(t)]) + )); + } + } + /** + * Assign enabled Block Tunes for Block Tool + * + * @param tool — Block Tool + */ + assignBlockTunesToBlockTool(e) { + if (e.enabledBlockTunes !== !1) { + if (Array.isArray(e.enabledBlockTunes)) { + const t = new j( + e.enabledBlockTunes.map((o) => [o, this.blockTunes.get(o)]) + ); + e.tunes = new j([...t, ...this.blockTunes.internalTools]); + return; + } + if (Array.isArray(this.config.tunes)) { + const t = new j( + this.config.tunes.map((o) => [o, this.blockTunes.get(o)]) + ); + e.tunes = new j([...t, ...this.blockTunes.internalTools]); + return; + } + e.tunes = this.blockTunes.internalTools; + } + } + /** + * Validate Tools configuration objects and throw Error for user if it is invalid + */ + validateTools() { + for (const e in this.config.tools) + if (Object.prototype.hasOwnProperty.call(this.config.tools, e)) { + if (e in this.internalTools) + return; + const t = this.config.tools[e]; + if (!A(t) && !A(t.class)) + throw Error( + `Tool «${e}» must be a constructor function or an object with function in the «class» property` + ); + } + } + /** + * Unify tools config + */ + prepareConfig() { + const e = {}; + for (const t in this.config.tools) + D(this.config.tools[t]) ? e[t] = this.config.tools[t] : e[t] = { class: this.config.tools[t] }; + return e; + } +} +Ta([ + me +], Wn.prototype, "getAllInlineToolsSanitizeConfig", 1); +const Sa = `:root{--selectionColor: #e1f2ff;--inlineSelectionColor: #d4ecff;--bg-light: #eff2f5;--grayText: #707684;--color-dark: #1D202B;--color-active-icon: #388AE5;--color-gray-border: rgba(201, 201, 204, .48);--content-width: 650px;--narrow-mode-right-padding: 50px;--toolbox-buttons-size: 26px;--toolbox-buttons-size--mobile: 36px;--icon-size: 20px;--icon-size--mobile: 28px;--block-padding-vertical: .4em;--color-line-gray: #EFF0F1 }.codex-editor{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;z-index:1}.codex-editor .hide{display:none}.codex-editor__redactor [contenteditable]:empty:after{content:"\\feff"}@media (min-width: 651px){.codex-editor--narrow .codex-editor__redactor{margin-right:50px}}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .codex-editor__redactor{margin-left:50px;margin-right:0}}@media (min-width: 651px){.codex-editor--narrow .ce-toolbar__actions{right:-5px}}.codex-editor-copyable{position:absolute;height:1px;width:1px;top:-400%;opacity:.001}.codex-editor-overlay{position:fixed;top:0;left:0;right:0;bottom:0;z-index:999;pointer-events:none;overflow:hidden}.codex-editor-overlay__container{position:relative;pointer-events:auto;z-index:0}.codex-editor-overlay__rectangle{position:absolute;pointer-events:none;background-color:#2eaadc33;border:1px solid transparent}.codex-editor svg{max-height:100%}.codex-editor path{stroke:currentColor}.codex-editor ::-moz-selection{background-color:#d4ecff}.codex-editor ::selection{background-color:#d4ecff}.codex-editor--toolbox-opened [contentEditable=true][data-placeholder]:focus:before{opacity:0!important}.ce-scroll-locked{overflow:hidden}.ce-scroll-locked--hard{overflow:hidden;top:calc(-1 * var(--window-scroll-offset));position:fixed;width:100%}.ce-toolbar{position:absolute;left:0;right:0;top:0;-webkit-transition:opacity .1s ease;transition:opacity .1s ease;will-change:opacity,top;display:none}.ce-toolbar--opened{display:block}.ce-toolbar__content{max-width:650px;margin:0 auto;position:relative}.ce-toolbar__plus{color:#1d202b;cursor:pointer;width:26px;height:26px;border-radius:7px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-ms-flex-negative:0;flex-shrink:0}@media (max-width: 650px){.ce-toolbar__plus{width:36px;height:36px}}@media (hover: hover){.ce-toolbar__plus:hover{background-color:#eff2f5}}.ce-toolbar__plus--active{background-color:#eff2f5;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.ce-toolbar__plus-shortcut{opacity:.6;word-spacing:-2px;margin-top:5px}@media (max-width: 650px){.ce-toolbar__plus{position:absolute;background-color:#fff;border:1px solid #E8E8EB;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px #0d142121;border-radius:6px;z-index:2;position:static}.ce-toolbar__plus--left-oriented:before{left:15px;margin-left:0}.ce-toolbar__plus--right-oriented:before{left:auto;right:15px;margin-left:0}}.ce-toolbar__actions{position:absolute;right:100%;opacity:0;display:-webkit-box;display:-ms-flexbox;display:flex;padding-right:5px}.ce-toolbar__actions--opened{opacity:1}@media (max-width: 650px){.ce-toolbar__actions{right:auto}}.ce-toolbar__settings-btn{color:#1d202b;width:26px;height:26px;border-radius:7px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;margin-left:3px;cursor:pointer;user-select:none}@media (max-width: 650px){.ce-toolbar__settings-btn{width:36px;height:36px}}@media (hover: hover){.ce-toolbar__settings-btn:hover{background-color:#eff2f5}}.ce-toolbar__settings-btn--active{background-color:#eff2f5;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@media (min-width: 651px){.ce-toolbar__settings-btn{width:24px}}.ce-toolbar__settings-btn--hidden{display:none}@media (max-width: 650px){.ce-toolbar__settings-btn{position:absolute;background-color:#fff;border:1px solid #E8E8EB;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px #0d142121;border-radius:6px;z-index:2;position:static}.ce-toolbar__settings-btn--left-oriented:before{left:15px;margin-left:0}.ce-toolbar__settings-btn--right-oriented:before{left:auto;right:15px;margin-left:0}}.ce-toolbar__plus svg,.ce-toolbar__settings-btn svg{width:24px;height:24px}@media (min-width: 651px){.codex-editor--narrow .ce-toolbar__plus{left:5px}}@media (min-width: 651px){.codex-editor--narrow .ce-toolbox .ce-popover{right:0;left:auto;left:initial}}.ce-inline-toolbar{--y-offset: 8px;--color-background-icon-active: rgba(56, 138, 229, .1);--color-text-icon-active: #388AE5;--color-text-primary: black;position:absolute;visibility:hidden;-webkit-transition:opacity .25s ease;transition:opacity .25s ease;will-change:opacity,left,top;top:0;left:0;z-index:3;opacity:1;visibility:visible}.ce-inline-toolbar [hidden]{display:none!important}.ce-inline-toolbar__toggler-and-button-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;padding:0 6px}.ce-inline-toolbar__buttons{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-inline-toolbar__dropdown{display:-webkit-box;display:-ms-flexbox;display:flex;padding:6px;margin:0 6px 0 -6px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;cursor:pointer;border-right:1px solid rgba(201,201,204,.48);-webkit-box-sizing:border-box;box-sizing:border-box}@media (hover: hover){.ce-inline-toolbar__dropdown:hover{background:#eff2f5}}.ce-inline-toolbar__dropdown--hidden{display:none}.ce-inline-toolbar__dropdown-content,.ce-inline-toolbar__dropdown-arrow{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-inline-toolbar__dropdown-content svg,.ce-inline-toolbar__dropdown-arrow svg{width:20px;height:20px}.ce-inline-toolbar__shortcut{opacity:.6;word-spacing:-3px;margin-top:3px}.ce-inline-tool{color:var(--color-text-primary);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:0;border-radius:4px;line-height:normal;height:100%;padding:0;width:28px;background-color:transparent;cursor:pointer}@media (max-width: 650px){.ce-inline-tool{width:36px;height:36px}}@media (hover: hover){.ce-inline-tool:hover{background-color:#f8f8f8}}.ce-inline-tool svg{display:block;width:20px;height:20px}@media (max-width: 650px){.ce-inline-tool svg{width:28px;height:28px}}.ce-inline-tool--link .icon--unlink,.ce-inline-tool--unlink .icon--link{display:none}.ce-inline-tool--unlink .icon--unlink{display:inline-block;margin-bottom:-1px}.ce-inline-tool-input{background:#F8F8F8;border:1px solid rgba(226,226,229,.2);border-radius:6px;padding:4px 8px;font-size:14px;line-height:22px;outline:none;margin:0;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box;display:none;font-weight:500;-webkit-appearance:none;font-family:inherit}@media (max-width: 650px){.ce-inline-tool-input{font-size:15px;font-weight:500}}.ce-inline-tool-input::-webkit-input-placeholder{color:#707684}.ce-inline-tool-input::-moz-placeholder{color:#707684}.ce-inline-tool-input:-ms-input-placeholder{color:#707684}.ce-inline-tool-input::-ms-input-placeholder{color:#707684}.ce-inline-tool-input::placeholder{color:#707684}.ce-inline-tool-input--showed{display:block}.ce-inline-tool--active{background:var(--color-background-icon-active);color:var(--color-text-icon-active)}@-webkit-keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.ce-block{-webkit-animation:fade-in .3s ease;animation:fade-in .3s ease;-webkit-animation-fill-mode:none;animation-fill-mode:none;-webkit-animation-fill-mode:initial;animation-fill-mode:initial}.ce-block:first-of-type{margin-top:0}.ce-block--selected .ce-block__content{background:#e1f2ff}.ce-block--selected .ce-block__content [contenteditable]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ce-block--selected .ce-block__content img,.ce-block--selected .ce-block__content .ce-stub{opacity:.55}.ce-block--stretched .ce-block__content{max-width:none}.ce-block__content{position:relative;max-width:650px;margin:0 auto;-webkit-transition:background-color .15s ease;transition:background-color .15s ease}.ce-block--drop-target .ce-block__content:before{content:"";position:absolute;top:100%;left:-20px;margin-top:-1px;height:8px;width:8px;border:solid #388AE5;border-width:1px 1px 0 0;-webkit-transform-origin:right;transform-origin:right;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ce-block--drop-target .ce-block__content:after{content:"";position:absolute;top:100%;height:1px;width:100%;color:#388ae5;background:repeating-linear-gradient(90deg,#388AE5,#388AE5 1px,#fff 1px,#fff 6px)}.ce-block a{cursor:pointer;-webkit-text-decoration:underline;text-decoration:underline}.ce-block b{font-weight:700}.ce-block i{font-style:italic}@-webkit-keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}20%{-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}60%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}20%{-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}60%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@-webkit-keyframes selectionBounce{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}50%{-webkit-transform:scale3d(1.01,1.01,1.01);transform:scale3d(1.01,1.01,1.01)}70%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes selectionBounce{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}50%{-webkit-transform:scale3d(1.01,1.01,1.01);transform:scale3d(1.01,1.01,1.01)}70%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@-webkit-keyframes buttonClicked{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.95,.95,.95);transform:scale3d(.95,.95,.95)}60%{-webkit-transform:scale3d(1.02,1.02,1.02);transform:scale3d(1.02,1.02,1.02)}80%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes buttonClicked{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.95,.95,.95);transform:scale3d(.95,.95,.95)}60%{-webkit-transform:scale3d(1.02,1.02,1.02);transform:scale3d(1.02,1.02,1.02)}80%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}.cdx-block{padding:.4em 0}.cdx-block::-webkit-input-placeholder{line-height:normal!important}.cdx-input{border:1px solid rgba(201,201,204,.48);-webkit-box-shadow:inset 0 1px 2px 0 rgba(35,44,72,.06);box-shadow:inset 0 1px 2px #232c480f;border-radius:3px;padding:10px 12px;outline:none;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.cdx-input[data-placeholder]:before{position:static!important}.cdx-input[data-placeholder]:before{display:inline-block;width:0;white-space:nowrap;pointer-events:none}.cdx-settings-button{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:3px;cursor:pointer;border:0;outline:none;background-color:transparent;vertical-align:bottom;color:inherit;margin:0;min-width:26px;min-height:26px}.cdx-settings-button--focused{background:rgba(34,186,255,.08)!important}.cdx-settings-button--focused{-webkit-box-shadow:inset 0 0 0px 1px rgba(7,161,227,.08);box-shadow:inset 0 0 0 1px #07a1e314}.cdx-settings-button--focused-animated{-webkit-animation-name:buttonClicked;animation-name:buttonClicked;-webkit-animation-duration:.25s;animation-duration:.25s}.cdx-settings-button--active{color:#388ae5}.cdx-settings-button svg{width:auto;height:auto}@media (max-width: 650px){.cdx-settings-button svg{width:28px;height:28px}}@media (max-width: 650px){.cdx-settings-button{width:36px;height:36px;border-radius:8px}}@media (hover: hover){.cdx-settings-button:hover{background-color:#eff2f5}}.cdx-loader{position:relative;border:1px solid rgba(201,201,204,.48)}.cdx-loader:before{content:"";position:absolute;left:50%;top:50%;width:18px;height:18px;margin:-11px 0 0 -11px;border:2px solid rgba(201,201,204,.48);border-left-color:#388ae5;border-radius:50%;-webkit-animation:cdxRotation 1.2s infinite linear;animation:cdxRotation 1.2s infinite linear}@-webkit-keyframes cdxRotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes cdxRotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.cdx-button{padding:13px;border-radius:3px;border:1px solid rgba(201,201,204,.48);font-size:14.9px;background:#fff;-webkit-box-shadow:0 2px 2px 0 rgba(18,30,57,.04);box-shadow:0 2px 2px #121e390a;color:#707684;text-align:center;cursor:pointer}@media (hover: hover){.cdx-button:hover{background:#FBFCFE;-webkit-box-shadow:0 1px 3px 0 rgba(18,30,57,.08);box-shadow:0 1px 3px #121e3914}}.cdx-button svg{height:20px;margin-right:.2em;margin-top:-2px}.ce-stub{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:12px 18px;margin:10px 0;border-radius:10px;background:#eff2f5;border:1px solid #EFF0F1;color:#707684;font-size:14px}.ce-stub svg{width:20px;height:20px}.ce-stub__info{margin-left:14px}.ce-stub__title{font-weight:500;text-transform:capitalize}.codex-editor.codex-editor--rtl{direction:rtl}.codex-editor.codex-editor--rtl .cdx-list{padding-left:0;padding-right:40px}.codex-editor.codex-editor--rtl .ce-toolbar__plus{right:-26px;left:auto}.codex-editor.codex-editor--rtl .ce-toolbar__actions{right:auto;left:-26px}@media (max-width: 650px){.codex-editor.codex-editor--rtl .ce-toolbar__actions{margin-left:0;margin-right:auto;padding-right:0;padding-left:10px}}.codex-editor.codex-editor--rtl .ce-settings{left:5px;right:auto}.codex-editor.codex-editor--rtl .ce-settings:before{right:auto;left:25px}.codex-editor.codex-editor--rtl .ce-settings__button:not(:nth-child(3n+3)){margin-left:3px;margin-right:0}.codex-editor.codex-editor--rtl .ce-conversion-tool__icon{margin-right:0;margin-left:10px}.codex-editor.codex-editor--rtl .ce-inline-toolbar__dropdown{border-right:0px solid transparent;border-left:1px solid rgba(201,201,204,.48);margin:0 -6px 0 6px}.codex-editor.codex-editor--rtl .ce-inline-toolbar__dropdown .icon--toggler-down{margin-left:0;margin-right:4px}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .ce-toolbar__plus{left:0;right:5px}}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .ce-toolbar__actions{left:-5px}}.cdx-search-field{--icon-margin-right: 10px;background:#F8F8F8;border:1px solid rgba(226,226,229,.2);border-radius:6px;padding:2px;display:grid;grid-template-columns:auto auto 1fr;grid-template-rows:auto}.cdx-search-field__icon{width:26px;height:26px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-right:var(--icon-margin-right)}.cdx-search-field__icon svg{width:20px;height:20px;color:#707684}.cdx-search-field__input{font-size:14px;outline:none;font-weight:500;font-family:inherit;border:0;background:transparent;margin:0;padding:0;line-height:22px;min-width:calc(100% - 26px - var(--icon-margin-right))}.cdx-search-field__input::-webkit-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::-moz-placeholder{color:#707684;font-weight:500}.cdx-search-field__input:-ms-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::-ms-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::placeholder{color:#707684;font-weight:500}.ce-popover{--border-radius: 6px;--width: 200px;--max-height: 270px;--padding: 6px;--offset-from-target: 8px;--color-border: #EFF0F1;--color-shadow: rgba(13, 20, 33, .1);--color-background: white;--color-text-primary: black;--color-text-secondary: #707684;--color-border-icon: rgba(201, 201, 204, .48);--color-border-icon-disabled: #EFF0F1;--color-text-icon-active: #388AE5;--color-background-icon-active: rgba(56, 138, 229, .1);--color-background-item-focus: rgba(34, 186, 255, .08);--color-shadow-item-focus: rgba(7, 161, 227, .08);--color-background-item-hover: #F8F8F8;--color-background-item-confirm: #E24A4A;--color-background-item-confirm-hover: #CE4343;--popover-top: calc(100% + var(--offset-from-target));--popover-left: 0;--nested-popover-overlap: 4px;--icon-size: 20px;--item-padding: 3px;--item-height: calc(var(--icon-size) + 2 * var(--item-padding))}.ce-popover__container{min-width:var(--width);width:var(--width);max-height:var(--max-height);border-radius:var(--border-radius);overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:0px 3px 15px -3px var(--color-shadow);box-shadow:0 3px 15px -3px var(--color-shadow);position:absolute;left:var(--popover-left);top:var(--popover-top);background:var(--color-background);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;z-index:4;opacity:0;max-height:0;pointer-events:none;padding:0;border:none}.ce-popover--opened>.ce-popover__container{opacity:1;padding:var(--padding);max-height:var(--max-height);pointer-events:auto;-webkit-animation:panelShowing .1s ease;animation:panelShowing .1s ease;border:1px solid var(--color-border)}@media (max-width: 650px){.ce-popover--opened>.ce-popover__container{-webkit-animation:panelShowingMobile .25s ease;animation:panelShowingMobile .25s ease}}.ce-popover--open-top .ce-popover__container{--popover-top: calc(-1 * (var(--offset-from-target) + var(--popover-height)))}.ce-popover--open-left .ce-popover__container{--popover-left: calc(-1 * var(--width) + 100%)}.ce-popover__items{overflow-y:auto;-ms-scroll-chaining:none;overscroll-behavior:contain}@media (max-width: 650px){.ce-popover__overlay{position:fixed;top:0;bottom:0;left:0;right:0;background:#1D202B;z-index:3;opacity:.5;-webkit-transition:opacity .12s ease-in;transition:opacity .12s ease-in;will-change:opacity;visibility:visible}}.ce-popover__overlay--hidden{display:none}@media (max-width: 650px){.ce-popover .ce-popover__container{--offset: 5px;position:fixed;max-width:none;min-width:calc(100% - var(--offset) * 2);left:var(--offset);right:var(--offset);bottom:calc(var(--offset) + env(safe-area-inset-bottom));top:auto;border-radius:10px}}.ce-popover__search{margin-bottom:5px}.ce-popover__nothing-found-message{color:#707684;display:none;cursor:default;padding:3px;font-size:14px;line-height:20px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ce-popover__nothing-found-message--displayed{display:block}.ce-popover--nested .ce-popover__container{--popover-left: calc(var(--nesting-level) * (var(--width) - var(--nested-popover-overlap)));top:calc(var(--trigger-item-top) - var(--nested-popover-overlap));position:absolute}.ce-popover--open-top.ce-popover--nested .ce-popover__container{top:calc(var(--trigger-item-top) - var(--popover-height) + var(--item-height) + var(--offset-from-target) + var(--nested-popover-overlap))}.ce-popover--open-left .ce-popover--nested .ce-popover__container{--popover-left: calc(-1 * (var(--nesting-level) + 1) * var(--width) + 100%)}.ce-popover-item-separator{padding:4px 3px}.ce-popover-item-separator--hidden{display:none}.ce-popover-item-separator__line{height:1px;background:var(--color-border);width:100%}.ce-popover-item-html--hidden{display:none}.ce-popover-item{--border-radius: 6px;border-radius:var(--border-radius);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:var(--item-padding);color:var(--color-text-primary);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:none;background:transparent}@media (max-width: 650px){.ce-popover-item{padding:4px}}.ce-popover-item:not(:last-of-type){margin-bottom:1px}.ce-popover-item__icon{width:26px;height:26px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ce-popover-item__icon svg{width:20px;height:20px}@media (max-width: 650px){.ce-popover-item__icon{width:36px;height:36px;border-radius:8px}.ce-popover-item__icon svg{width:28px;height:28px}}.ce-popover-item__icon--tool{margin-right:4px}.ce-popover-item__title{font-size:14px;line-height:20px;font-weight:500;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-right:auto}@media (max-width: 650px){.ce-popover-item__title{font-size:16px}}.ce-popover-item__secondary-title{color:var(--color-text-secondary);font-size:12px;white-space:nowrap;letter-spacing:-.1em;padding-right:5px;opacity:.6}@media (max-width: 650px){.ce-popover-item__secondary-title{display:none}}.ce-popover-item--active{background:var(--color-background-icon-active);color:var(--color-text-icon-active)}.ce-popover-item--disabled{color:var(--color-text-secondary);cursor:default;pointer-events:none}.ce-popover-item--focused:not(.ce-popover-item--no-focus){background:var(--color-background-item-focus)!important}.ce-popover-item--hidden{display:none}@media (hover: hover){.ce-popover-item:hover{cursor:pointer}.ce-popover-item:hover:not(.ce-popover-item--no-hover){background-color:var(--color-background-item-hover)}}.ce-popover-item--confirmation{background:var(--color-background-item-confirm)}.ce-popover-item--confirmation .ce-popover-item__title,.ce-popover-item--confirmation .ce-popover-item__icon{color:#fff}@media (hover: hover){.ce-popover-item--confirmation:not(.ce-popover-item--no-hover):hover{background:var(--color-background-item-confirm-hover)}}.ce-popover-item--confirmation:not(.ce-popover-item--no-focus).ce-popover-item--focused{background:var(--color-background-item-confirm-hover)!important}@-webkit-keyframes panelShowing{0%{opacity:0;-webkit-transform:translateY(-8px) scale(.9);transform:translateY(-8px) scale(.9)}70%{opacity:1;-webkit-transform:translateY(2px);transform:translateY(2px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes panelShowing{0%{opacity:0;-webkit-transform:translateY(-8px) scale(.9);transform:translateY(-8px) scale(.9)}70%{opacity:1;-webkit-transform:translateY(2px);transform:translateY(2px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes panelShowingMobile{0%{opacity:0;-webkit-transform:translateY(14px) scale(.98);transform:translateY(14px) scale(.98)}70%{opacity:1;-webkit-transform:translateY(-4px);transform:translateY(-4px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes panelShowingMobile{0%{opacity:0;-webkit-transform:translateY(14px) scale(.98);transform:translateY(14px) scale(.98)}70%{opacity:1;-webkit-transform:translateY(-4px);transform:translateY(-4px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.wobble{-webkit-animation-name:wobble;animation-name:wobble;-webkit-animation-duration:.4s;animation-duration:.4s}@-webkit-keyframes wobble{0%{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-9%,0,0);transform:translate3d(-9%,0,0)}30%{-webkit-transform:translate3d(9%,0,0);transform:translate3d(9%,0,0)}45%{-webkit-transform:translate3d(-4%,0,0);transform:translate3d(-4%,0,0)}60%{-webkit-transform:translate3d(4%,0,0);transform:translate3d(4%,0,0)}75%{-webkit-transform:translate3d(-1%,0,0);transform:translate3d(-1%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}@keyframes wobble{0%{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-9%,0,0);transform:translate3d(-9%,0,0)}30%{-webkit-transform:translate3d(9%,0,0);transform:translate3d(9%,0,0)}45%{-webkit-transform:translate3d(-4%,0,0);transform:translate3d(-4%,0,0)}60%{-webkit-transform:translate3d(4%,0,0);transform:translate3d(4%,0,0)}75%{-webkit-transform:translate3d(-1%,0,0);transform:translate3d(-1%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}.ce-popover-header{margin-bottom:8px;margin-top:4px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ce-popover-header__text{font-size:18px;font-weight:600}.ce-popover-header__back-button{border:0;background:transparent;width:36px;height:36px;color:var(--color-text-primary)}.ce-popover-header__back-button svg{display:block;width:28px;height:28px}.ce-popover--inline{--height: 38px;--height-mobile: 46px;--container-padding: 4px;position:relative}.ce-popover--inline .ce-popover__custom-content{margin-bottom:0}.ce-popover--inline .ce-popover__items{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-popover--inline .ce-popover__container{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;padding:var(--container-padding);height:var(--height);top:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;width:-webkit-max-content;width:-moz-max-content;width:max-content;-webkit-animation:none;animation:none}@media (max-width: 650px){.ce-popover--inline .ce-popover__container{height:var(--height-mobile);position:absolute}}.ce-popover--inline .ce-popover-item-separator{padding:0 4px}.ce-popover--inline .ce-popover-item-separator__line{height:100%;width:1px}.ce-popover--inline .ce-popover-item{border-radius:4px;padding:4px}.ce-popover--inline .ce-popover-item__icon--tool{-webkit-box-shadow:none;box-shadow:none;background:transparent;margin-right:0}.ce-popover--inline .ce-popover-item__icon{width:auto;width:initial;height:auto;height:initial}.ce-popover--inline .ce-popover-item__icon svg{width:20px;height:20px}@media (max-width: 650px){.ce-popover--inline .ce-popover-item__icon svg{width:28px;height:28px}}.ce-popover--inline .ce-popover-item:not(:last-of-type){margin-bottom:0;margin-bottom:initial}.ce-popover--inline .ce-popover-item-html{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ce-popover--inline .ce-popover-item__icon--chevron-right{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.ce-popover--inline .ce-popover--nested-level-1 .ce-popover__container{--offset: 3px;left:0;top:calc(var(--height) + var(--offset))}@media (max-width: 650px){.ce-popover--inline .ce-popover--nested-level-1 .ce-popover__container{top:calc(var(--height-mobile) + var(--offset))}}.ce-popover--inline .ce-popover--nested .ce-popover__container{min-width:var(--width);width:var(--width);height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;padding:6px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ce-popover--inline .ce-popover--nested .ce-popover__items{display:block;width:100%}.ce-popover--inline .ce-popover--nested .ce-popover-item{border-radius:6px;padding:3px}@media (max-width: 650px){.ce-popover--inline .ce-popover--nested .ce-popover-item{padding:4px}}.ce-popover--inline .ce-popover--nested .ce-popover-item__icon--tool{margin-right:4px}.ce-popover--inline .ce-popover--nested .ce-popover-item__icon{width:26px;height:26px}.ce-popover--inline .ce-popover--nested .ce-popover-item-separator{padding:4px 3px}.ce-popover--inline .ce-popover--nested .ce-popover-item-separator__line{width:100%;height:1px}.codex-editor [data-placeholder]:empty:before,.codex-editor [data-placeholder][data-empty=true]:before{pointer-events:none;color:#707684;cursor:text;content:attr(data-placeholder)}.codex-editor [data-placeholder-active]:empty:before,.codex-editor [data-placeholder-active][data-empty=true]:before{pointer-events:none;color:#707684;cursor:text}.codex-editor [data-placeholder-active]:empty:focus:before,.codex-editor [data-placeholder-active][data-empty=true]:focus:before{content:attr(data-placeholder-active)} +`; +class Ia extends E { + constructor() { + super(...arguments), this.isMobile = !1, this.contentRectCache = null, this.resizeDebouncer = Eo(() => { + this.windowResize(); + }, 200), this.selectionChangeDebounced = Eo(() => { + this.selectionChanged(); + }, da), this.documentTouchedListener = (e) => { + this.documentTouched(e); + }; + } + /** + * Editor.js UI CSS class names + * + * @returns {{editorWrapper: string, editorZone: string}} + */ + get CSS() { + return { + editorWrapper: "codex-editor", + editorWrapperNarrow: "codex-editor--narrow", + editorZone: "codex-editor__redactor", + editorZoneHidden: "codex-editor__redactor--hidden", + editorEmpty: "codex-editor--empty", + editorRtlFix: "codex-editor--rtl" + }; + } + /** + * Return Width of center column of Editor + * + * @returns {DOMRect} + */ + get contentRect() { + if (this.contentRectCache !== null) + return this.contentRectCache; + const e = this.nodes.wrapper.querySelector(`.${R.CSS.content}`); + return e ? (this.contentRectCache = e.getBoundingClientRect(), this.contentRectCache) : { + width: 650, + left: 0, + right: 0 + }; + } + /** + * Making main interface + */ + async prepare() { + this.setIsMobile(), this.make(), this.loadStyles(); + } + /** + * Toggle read-only state + * + * If readOnly is true: + * - removes all listeners from main UI module elements + * + * if readOnly is false: + * - enables all listeners to UI module elements + * + * @param {boolean} readOnlyEnabled - "read only" state + */ + toggleReadOnly(e) { + e ? this.unbindReadOnlySensitiveListeners() : window.requestIdleCallback(() => { + this.bindReadOnlySensitiveListeners(); + }, { + timeout: 2e3 + }); + } + /** + * Check if Editor is empty and set CSS class to wrapper + */ + checkEmptiness() { + const { BlockManager: e } = this.Editor; + this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty, e.isEditorEmpty); + } + /** + * Check if one of Toolbar is opened + * Used to prevent global keydowns (for example, Enter) conflicts with Enter-on-toolbar + * + * @returns {boolean} + */ + get someToolbarOpened() { + const { Toolbar: e, BlockSettings: t, InlineToolbar: o } = this.Editor; + return !!(t.opened || o.opened || e.toolbox.opened); + } + /** + * Check for some Flipper-buttons is under focus + */ + get someFlipperButtonFocused() { + return this.Editor.Toolbar.toolbox.hasFocus() ? !0 : Object.entries(this.Editor).filter(([e, t]) => t.flipper instanceof ce).some(([e, t]) => t.flipper.hasFocus()); + } + /** + * Clean editor`s UI + */ + destroy() { + this.nodes.holder.innerHTML = "", this.unbindReadOnlyInsensitiveListeners(); + } + /** + * Close all Editor's toolbars + */ + closeAllToolbars() { + const { Toolbar: e, BlockSettings: t, InlineToolbar: o } = this.Editor; + t.close(), o.close(), e.toolbox.close(); + } + /** + * Check for mobile mode and save the result + */ + setIsMobile() { + const e = window.innerWidth < Ro; + e !== this.isMobile && this.eventsDispatcher.emit(Te, { + isEnabled: this.isMobile + }), this.isMobile = e; + } + /** + * Makes Editor.js interface + */ + make() { + this.nodes.holder = u.getHolder(this.config.holder), this.nodes.wrapper = u.make("div", [ + this.CSS.editorWrapper, + ...this.isRtl ? [this.CSS.editorRtlFix] : [] + ]), this.nodes.redactor = u.make("div", this.CSS.editorZone), this.nodes.holder.offsetWidth < this.contentRect.width && this.nodes.wrapper.classList.add(this.CSS.editorWrapperNarrow), this.nodes.redactor.style.paddingBottom = this.config.minHeight + "px", this.nodes.wrapper.appendChild(this.nodes.redactor), this.nodes.holder.appendChild(this.nodes.wrapper), this.bindReadOnlyInsensitiveListeners(); + } + /** + * Appends CSS + */ + loadStyles() { + const e = "editor-js-styles"; + if (u.get(e)) + return; + const t = u.make("style", null, { + id: e, + textContent: Sa.toString() + }); + this.config.style && !V(this.config.style) && this.config.style.nonce && t.setAttribute("nonce", this.config.style.nonce), u.prepend(document.head, t); + } + /** + * Adds listeners that should work both in read-only and read-write modes + */ + bindReadOnlyInsensitiveListeners() { + this.listeners.on(document, "selectionchange", this.selectionChangeDebounced), this.listeners.on(window, "resize", this.resizeDebouncer, { + passive: !0 + }), this.listeners.on(this.nodes.redactor, "mousedown", this.documentTouchedListener, { + capture: !0, + passive: !0 + }), this.listeners.on(this.nodes.redactor, "touchstart", this.documentTouchedListener, { + capture: !0, + passive: !0 + }); + } + /** + * Removes listeners that should work both in read-only and read-write modes + */ + unbindReadOnlyInsensitiveListeners() { + this.listeners.off(document, "selectionchange", this.selectionChangeDebounced), this.listeners.off(window, "resize", this.resizeDebouncer), this.listeners.off(this.nodes.redactor, "mousedown", this.documentTouchedListener), this.listeners.off(this.nodes.redactor, "touchstart", this.documentTouchedListener); + } + /** + * Adds listeners that should work only in read-only mode + */ + bindReadOnlySensitiveListeners() { + this.readOnlyMutableListeners.on(this.nodes.redactor, "click", (e) => { + this.redactorClicked(e); + }, !1), this.readOnlyMutableListeners.on(document, "keydown", (e) => { + this.documentKeydown(e); + }, !0), this.readOnlyMutableListeners.on(document, "mousedown", (e) => { + this.documentClicked(e); + }, !0), this.watchBlockHoveredEvents(), this.enableInputsEmptyMark(); + } + /** + * Listen redactor mousemove to emit 'block-hovered' event + */ + watchBlockHoveredEvents() { + let e; + this.readOnlyMutableListeners.on(this.nodes.redactor, "mousemove", dt((t) => { + const o = t.target.closest(".ce-block"); + this.Editor.BlockSelection.anyBlockSelected || o && e !== o && (e = o, this.eventsDispatcher.emit(ln, { + block: this.Editor.BlockManager.getBlockByChildNode(o) + })); + }, 20), { + passive: !0 + }); + } + /** + * Unbind events that should work only in read-only mode + */ + unbindReadOnlySensitiveListeners() { + this.readOnlyMutableListeners.clearAll(); + } + /** + * Resize window handler + */ + windowResize() { + this.contentRectCache = null, this.setIsMobile(); + } + /** + * All keydowns on document + * + * @param {KeyboardEvent} event - keyboard event + */ + documentKeydown(e) { + switch (e.keyCode) { + case y.ENTER: + this.enterPressed(e); + break; + case y.BACKSPACE: + case y.DELETE: + this.backspacePressed(e); + break; + case y.ESC: + this.escapePressed(e); + break; + default: + this.defaultBehaviour(e); + break; + } + } + /** + * Ignore all other document's keydown events + * + * @param {KeyboardEvent} event - keyboard event + */ + defaultBehaviour(e) { + const { currentBlock: t } = this.Editor.BlockManager, o = e.target.closest(`.${this.CSS.editorWrapper}`), i = e.altKey || e.ctrlKey || e.metaKey || e.shiftKey; + if (t !== void 0 && o === null) { + this.Editor.BlockEvents.keydown(e); + return; + } + o || t && i || (this.Editor.BlockManager.unsetCurrentBlock(), this.Editor.Toolbar.close()); + } + /** + * @param {KeyboardEvent} event - keyboard event + */ + backspacePressed(e) { + const { BlockManager: t, BlockSelection: o, Caret: i } = this.Editor; + if (o.anyBlockSelected && !b.isSelectionExists) { + const s = t.removeSelectedBlocks(), r = t.insertDefaultBlockAtIndex(s, !0); + i.setToBlock(r, i.positions.START), o.clearSelection(e), e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(); + } + } + /** + * Escape pressed + * If some of Toolbar components are opened, then close it otherwise close Toolbar + * + * @param {Event} event - escape keydown event + */ + escapePressed(e) { + this.Editor.BlockSelection.clearSelection(e), this.Editor.Toolbar.toolbox.opened ? (this.Editor.Toolbar.toolbox.close(), this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock, this.Editor.Caret.positions.END)) : this.Editor.BlockSettings.opened ? this.Editor.BlockSettings.close() : this.Editor.InlineToolbar.opened ? this.Editor.InlineToolbar.close() : this.Editor.Toolbar.close(); + } + /** + * Enter pressed on document + * + * @param {KeyboardEvent} event - keyboard event + */ + enterPressed(e) { + const { BlockManager: t, BlockSelection: o } = this.Editor; + if (this.someToolbarOpened) + return; + const i = t.currentBlockIndex >= 0; + if (o.anyBlockSelected && !b.isSelectionExists) { + o.clearSelection(e), e.preventDefault(), e.stopImmediatePropagation(), e.stopPropagation(); + return; + } + if (!this.someToolbarOpened && i && e.target.tagName === "BODY") { + const s = this.Editor.BlockManager.insert(); + e.preventDefault(), this.Editor.Caret.setToBlock(s), this.Editor.Toolbar.moveAndOpen(s); + } + this.Editor.BlockSelection.clearSelection(e); + } + /** + * All clicks on document + * + * @param {MouseEvent} event - Click event + */ + documentClicked(e) { + var a, l; + if (!e.isTrusted) + return; + const t = e.target; + this.nodes.holder.contains(t) || b.isAtEditor || (this.Editor.BlockManager.unsetCurrentBlock(), this.Editor.Toolbar.close()); + const i = (a = this.Editor.BlockSettings.nodes.wrapper) == null ? void 0 : a.contains(t), s = (l = this.Editor.Toolbar.nodes.settingsToggler) == null ? void 0 : l.contains(t), r = i || s; + if (this.Editor.BlockSettings.opened && !r) { + this.Editor.BlockSettings.close(); + const c = this.Editor.BlockManager.getBlockByChildNode(t); + this.Editor.Toolbar.moveAndOpen(c); + } + this.Editor.BlockSelection.clearSelection(e); + } + /** + * First touch on editor + * Fired before click + * + * Used to change current block — we need to do it before 'selectionChange' event. + * Also: + * - Move and show the Toolbar + * - Set a Caret + * + * @param event - touch or mouse event + */ + documentTouched(e) { + let t = e.target; + if (t === this.nodes.redactor) { + const o = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX, i = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY; + t = document.elementFromPoint(o, i); + } + try { + this.Editor.BlockManager.setCurrentBlockByChildNode(t); + } catch { + this.Editor.RectangleSelection.isRectActivated() || this.Editor.Caret.setToTheLastBlock(); + } + this.Editor.ReadOnly.isEnabled || this.Editor.Toolbar.moveAndOpen(); + } + /** + * All clicks on the redactor zone + * + * @param {MouseEvent} event - click event + * @description + * - By clicks on the Editor's bottom zone: + * - if last Block is empty, set a Caret to this + * - otherwise, add a new empty Block and set a Caret to that + */ + redactorClicked(e) { + if (!b.isCollapsed) + return; + const t = e.target, o = e.metaKey || e.ctrlKey; + if (u.isAnchor(t) && o) { + e.stopImmediatePropagation(), e.stopPropagation(); + const i = t.getAttribute("href"), s = oi(i); + ii(s); + return; + } + this.processBottomZoneClick(e); + } + /** + * Check if user clicks on the Editor's bottom zone: + * - set caret to the last block + * - or add new empty block + * + * @param event - click event + */ + processBottomZoneClick(e) { + const t = this.Editor.BlockManager.getBlockByIndex(-1), o = u.offset(t.holder).bottom, i = e.pageY, { BlockSelection: s } = this.Editor; + if (e.target instanceof Element && e.target.isEqualNode(this.nodes.redactor) && /** + * If there is cross block selection started, target will be equal to redactor so we need additional check + */ + !s.anyBlockSelected && /** + * Prevent caret jumping (to last block) when clicking between blocks + */ + o < i) { + e.stopImmediatePropagation(), e.stopPropagation(); + const { BlockManager: a, Caret: l, Toolbar: c } = this.Editor; + (!a.lastBlock.tool.isDefault || !a.lastBlock.isEmpty) && a.insertAtEnd(), l.setToTheLastBlock(), c.moveAndOpen(a.lastBlock); + } + } + /** + * Handle selection changes on mobile devices + * Uses for showing the Inline Toolbar + */ + selectionChanged() { + const { CrossBlockSelection: e, BlockSelection: t } = this.Editor, o = b.anchorElement; + if (e.isCrossBlockSelectionStarted && t.anyBlockSelected && b.get().removeAllRanges(), !o) { + b.range || this.Editor.InlineToolbar.close(); + return; + } + const i = o.closest(`.${R.CSS.content}`); + (i === null || i.closest(`.${b.CSS.editorWrapper}`) !== this.nodes.wrapper) && (this.Editor.InlineToolbar.containsNode(o) || this.Editor.InlineToolbar.close(), !(o.dataset.inlineToolbar === "true")) || (this.Editor.BlockManager.currentBlock || this.Editor.BlockManager.setCurrentBlockByChildNode(o), this.Editor.InlineToolbar.tryToShow(!0)); + } + /** + * Editor.js provides and ability to show placeholders for empty contenteditable elements + * + * This method watches for input and focus events and toggles 'data-empty' attribute + * to workaroud the case, when inputs contains only
    s and has no visible content + * Then, CSS could rely on this attribute to show placeholders + */ + enableInputsEmptyMark() { + function e(t) { + const o = t.target; + Do(o); + } + this.readOnlyMutableListeners.on(this.nodes.wrapper, "input", e), this.readOnlyMutableListeners.on(this.nodes.wrapper, "focusin", e), this.readOnlyMutableListeners.on(this.nodes.wrapper, "focusout", e); + } +} +const Oa = { + // API Modules + BlocksAPI: gi, + CaretAPI: bi, + EventsAPI: vi, + I18nAPI: kt, + API: ki, + InlineToolbarAPI: yi, + ListenersAPI: wi, + NotifierAPI: Ci, + ReadOnlyAPI: Ti, + SanitizerAPI: Li, + SaverAPI: Pi, + SelectionAPI: Ni, + ToolsAPI: Ri, + StylesAPI: Di, + ToolbarAPI: Fi, + TooltipAPI: Ui, + UiAPI: Wi, + // Toolbar Modules + BlockSettings: ms, + Toolbar: Bs, + InlineToolbar: Cs, + // Modules + BlockEvents: na, + BlockManager: ra, + BlockSelection: aa, + Caret: Ye, + CrossBlockSelection: la, + DragNDrop: ca, + ModificationsObserver: ha, + Paste: pa, + ReadOnly: fa, + RectangleSelection: Be, + Renderer: ga, + Saver: ma, + Tools: Wn, + UI: Ia +}; +class _a { + /** + * @param {EditorConfig} config - user configuration + */ + constructor(e) { + this.moduleInstances = {}, this.eventsDispatcher = new Oe(); + let t, o; + this.isReady = new Promise((i, s) => { + t = i, o = s; + }), Promise.resolve().then(async () => { + this.configuration = e, this.validate(), this.init(), await this.start(), await this.render(); + const { BlockManager: i, Caret: s, UI: r, ModificationsObserver: a } = this.moduleInstances; + r.checkEmptiness(), a.enable(), this.configuration.autofocus === !0 && this.configuration.readOnly !== !0 && s.setToBlock(i.blocks[0], s.positions.START), t(); + }).catch((i) => { + S(`Editor.js is not ready because of ${i}`, "error"), o(i); + }); + } + /** + * Setting for configuration + * + * @param {EditorConfig|string} config - Editor's config to set + */ + set configuration(e) { + var o, i; + D(e) ? this.config = { + ...e + } : this.config = { + holder: e + }, ht(!!this.config.holderId, "config.holderId", "config.holder"), this.config.holderId && !this.config.holder && (this.config.holder = this.config.holderId, this.config.holderId = null), this.config.holder == null && (this.config.holder = "editorjs"), this.config.logLevel || (this.config.logLevel = Lo.VERBOSE), Zn(this.config.logLevel), ht(!!this.config.initialBlock, "config.initialBlock", "config.defaultBlock"), this.config.defaultBlock = this.config.defaultBlock || this.config.initialBlock || "paragraph", this.config.minHeight = this.config.minHeight !== void 0 ? this.config.minHeight : 300; + const t = { + type: this.config.defaultBlock, + data: {} + }; + this.config.placeholder = this.config.placeholder || !1, this.config.sanitizer = this.config.sanitizer || { + p: !0, + b: !0, + a: !0 + }, this.config.hideToolbar = this.config.hideToolbar ? this.config.hideToolbar : !1, this.config.tools = this.config.tools || {}, this.config.i18n = this.config.i18n || {}, this.config.data = this.config.data || { blocks: [] }, this.config.onReady = this.config.onReady || (() => { + }), this.config.onChange = this.config.onChange || (() => { + }), this.config.inlineToolbar = this.config.inlineToolbar !== void 0 ? this.config.inlineToolbar : !0, (V(this.config.data) || !this.config.data.blocks || this.config.data.blocks.length === 0) && (this.config.data = { blocks: [t] }), this.config.readOnly = this.config.readOnly || !1, (o = this.config.i18n) != null && o.messages && z.setDictionary(this.config.i18n.messages), this.config.i18n.direction = ((i = this.config.i18n) == null ? void 0 : i.direction) || "ltr"; + } + /** + * Returns private property + * + * @returns {EditorConfig} + */ + get configuration() { + return this.config; + } + /** + * Checks for required fields in Editor's config + */ + validate() { + const { holderId: e, holder: t } = this.config; + if (e && t) + throw Error("«holderId» and «holder» param can't assign at the same time."); + if (te(t) && !u.get(t)) + throw Error(`element with ID «${t}» is missing. Pass correct holder's ID.`); + if (t && D(t) && !u.isElement(t)) + throw Error("«holder» value must be an Element node"); + } + /** + * Initializes modules: + * - make and save instances + * - configure + */ + init() { + this.constructModules(), this.configureModules(); + } + /** + * Start Editor! + * + * Get list of modules that needs to be prepared and return a sequence (Promise) + * + * @returns {Promise} + */ + async start() { + await [ + "Tools", + "UI", + "BlockManager", + "Paste", + "BlockSelection", + "RectangleSelection", + "CrossBlockSelection", + "ReadOnly" + ].reduce( + (t, o) => t.then(async () => { + try { + await this.moduleInstances[o].prepare(); + } catch (i) { + if (i instanceof Ho) + throw new Error(i.message); + S(`Module ${o} was skipped because of %o`, "warn", i); + } + }), + Promise.resolve() + ); + } + /** + * Render initial data + */ + render() { + return this.moduleInstances.Renderer.render(this.config.data.blocks); + } + /** + * Make modules instances and save it to the @property this.moduleInstances + */ + constructModules() { + Object.entries(Oa).forEach(([e, t]) => { + try { + this.moduleInstances[e] = new t({ + config: this.configuration, + eventsDispatcher: this.eventsDispatcher + }); + } catch (o) { + S("[constructModules]", `Module ${e} skipped because`, "error", o); + } + }); + } + /** + * Modules instances configuration: + * - pass other modules to the 'state' property + * - ... + */ + configureModules() { + for (const e in this.moduleInstances) + Object.prototype.hasOwnProperty.call(this.moduleInstances, e) && (this.moduleInstances[e].state = this.getModulesDiff(e)); + } + /** + * Return modules without passed name + * + * @param {string} name - module for witch modules difference should be calculated + */ + getModulesDiff(e) { + const t = {}; + for (const o in this.moduleInstances) + o !== e && (t[o] = this.moduleInstances[o]); + return t; + } +} +/** + * Editor.js + * + * @license Apache-2.0 + * @see Editor.js + * @author CodeX Team + */ +class Aa { + /** Editor version */ + static get version() { + return "2.31.1"; + } + /** + * @param {EditorConfig|string|undefined} [configuration] - user configuration + */ + constructor(e) { + let t = () => { + }; + D(e) && A(e.onReady) && (t = e.onReady); + const o = new _a(e); + this.isReady = o.isReady.then(() => { + this.exportAPI(o), t(); + }); + } + /** + * Export external API methods + * + * @param {Core} editor — Editor's instance + */ + exportAPI(e) { + const t = ["configuration"], o = () => { + Object.values(e.moduleInstances).forEach((s) => { + A(s.destroy) && s.destroy(), s.listeners.removeAll(); + }), zi(), e = null; + for (const s in this) + Object.prototype.hasOwnProperty.call(this, s) && delete this[s]; + Object.setPrototypeOf(this, null); + }; + t.forEach((s) => { + this[s] = e[s]; + }), this.destroy = o, Object.setPrototypeOf(this, e.moduleInstances.API.methods), delete this.exportAPI, Object.entries({ + blocks: { + clear: "clear", + render: "render" + }, + caret: { + focus: "focus" + }, + events: { + on: "on", + off: "off", + emit: "emit" + }, + saver: { + save: "save" + } + }).forEach(([s, r]) => { + Object.entries(r).forEach(([a, l]) => { + this[l] = e.moduleInstances.API.methods[s][a]; + }); + }); + } +} +export { + Aa as default +}; diff --git a/web/vendor/editorjs/editorjs.umd.js b/web/vendor/editorjs/editorjs.umd.js new file mode 100644 index 0000000..48468c9 --- /dev/null +++ b/web/vendor/editorjs/editorjs.umd.js @@ -0,0 +1,51 @@ +(function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(".ce-hint--align-start{text-align:left}.ce-hint--align-center{text-align:center}.ce-hint__description{opacity:.6;margin-top:3px}")),document.head.appendChild(e)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})(); +(function(J,ne){typeof exports=="object"&&typeof module<"u"?module.exports=ne():typeof define=="function"&&define.amd?define(ne):(J=typeof globalThis<"u"?globalThis:J||self,J.EditorJS=ne())})(this,function(){"use strict";var J=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function ne(n){return n&&n.__esModule&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n}function Yn(n){if(n.__esModule)return n;var e=n.default;if(typeof e=="function"){var t=function o(){return this instanceof o?Reflect.construct(e,arguments,this.constructor):e.apply(this,arguments)};t.prototype=e.prototype}else t={};return Object.defineProperty(t,"__esModule",{value:!0}),Object.keys(n).forEach(function(o){var i=Object.getOwnPropertyDescriptor(n,o);Object.defineProperty(t,o,i.get?i:{enumerable:!0,get:function(){return n[o]}})}),t}function tt(){}Object.assign(tt,{default:tt,register:tt,revert:function(){},__esModule:!0}),Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(n){const e=(this.document||this.ownerDocument).querySelectorAll(n);let t=e.length;for(;--t>=0&&e.item(t)!==this;);return t>-1}),Element.prototype.closest||(Element.prototype.closest=function(n){let e=this;if(!document.documentElement.contains(e))return null;do{if(e.matches(n))return e;e=e.parentElement||e.parentNode}while(e!==null);return null}),Element.prototype.prepend||(Element.prototype.prepend=function(e){const t=document.createDocumentFragment();Array.isArray(e)||(e=[e]),e.forEach(o=>{const i=o instanceof Node;t.appendChild(i?o:document.createTextNode(o))}),this.insertBefore(t,this.firstChild)}),Element.prototype.scrollIntoViewIfNeeded||(Element.prototype.scrollIntoViewIfNeeded=function(n){n=arguments.length===0?!0:!!n;const e=this.parentNode,t=window.getComputedStyle(e,null),o=parseInt(t.getPropertyValue("border-top-width")),i=parseInt(t.getPropertyValue("border-left-width")),s=this.offsetTop-e.offsetTope.scrollTop+e.clientHeight,a=this.offsetLeft-e.offsetLefte.scrollLeft+e.clientWidth,c=s&&!r;(s||r)&&n&&(e.scrollTop=this.offsetTop-e.offsetTop-e.clientHeight/2-o+this.clientHeight/2),(a||l)&&n&&(e.scrollLeft=this.offsetLeft-e.offsetLeft-e.clientWidth/2-i+this.clientWidth/2),(s||r||a||l)&&!n&&this.scrollIntoView(c)}),window.requestIdleCallback=window.requestIdleCallback||function(n){const e=Date.now();return setTimeout(function(){n({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-e))}})},1)},window.cancelIdleCallback=window.cancelIdleCallback||function(n){clearTimeout(n)};let Kn=(n=21)=>crypto.getRandomValues(new Uint8Array(n)).reduce((e,t)=>(t&=63,t<36?e+=t.toString(36):t<62?e+=(t-26).toString(36).toUpperCase():t>62?e+="-":e+="_",e),"");var ko=(n=>(n.VERBOSE="VERBOSE",n.INFO="INFO",n.WARN="WARN",n.ERROR="ERROR",n))(ko||{});const y={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,LEFT:37,UP:38,DOWN:40,RIGHT:39,DELETE:46,META:91,SLASH:191},Xn={LEFT:0,WHEEL:1,RIGHT:2,BACKWARD:3,FORWARD:4};function ye(n,e,t="log",o,i="color: inherit"){if(!("console"in window)||!window.console[t])return;const s=["info","log","warn","error"].includes(t),r=[];switch(ye.logLevel){case"ERROR":if(t!=="error")return;break;case"WARN":if(!["error","warn"].includes(t))return;break;case"INFO":if(!s||n)return;break}o&&r.push(o);const a="Editor.js 2.31.1",l=`line-height: 1em; + color: #006FEA; + display: inline-block; + font-size: 11px; + line-height: 1em; + background-color: #fff; + padding: 4px 9px; + border-radius: 30px; + border: 1px solid rgba(56, 138, 229, 0.16); + margin: 4px 5px 4px 0;`;n&&(s?(r.unshift(l,i),e=`%c${a}%c ${e}`):e=`( ${a} )${e}`);try{s?o?console[t](`${e} %o`,...r):console[t](e,...r):console[t](e)}catch{}}ye.logLevel="VERBOSE";function Vn(n){ye.logLevel=n}const T=ye.bind(window,!1),X=ye.bind(window,!0);function re(n){return Object.prototype.toString.call(n).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function M(n){return re(n)==="function"||re(n)==="asyncfunction"}function N(n){return re(n)==="object"}function Q(n){return re(n)==="string"}function qn(n){return re(n)==="boolean"}function yo(n){return re(n)==="number"}function wo(n){return re(n)==="undefined"}function V(n){return n?Object.keys(n).length===0&&n.constructor===Object:!0}function Eo(n){return n>47&&n<58||n===32||n===13||n===229||n>64&&n<91||n>95&&n<112||n>185&&n<193||n>218&&n<223}async function Zn(n,e=()=>{},t=()=>{}){async function o(i,s,r){try{await i.function(i.data),await s(wo(i.data)?{}:i.data)}catch{r(wo(i.data)?{}:i.data)}}return n.reduce(async(i,s)=>(await i,o(s,e,t)),Promise.resolve())}function xo(n){return Array.prototype.slice.call(n)}function Ae(n,e){return function(){const t=this,o=arguments;window.setTimeout(()=>n.apply(t,o),e)}}function Gn(n){return n.name.split(".").pop()}function Jn(n){return/^[-\w]+\/([-+\w]+|\*)$/.test(n)}function Bo(n,e,t){let o;return(...i)=>{const s=this,r=()=>{o=null,t||n.apply(s,i)},a=t&&!o;window.clearTimeout(o),o=window.setTimeout(r,e),a&&n.apply(s,i)}}function ot(n,e,t=void 0){let o,i,s,r=null,a=0;t||(t={});const l=function(){a=t.leading===!1?0:Date.now(),r=null,s=n.apply(o,i),r||(o=i=null)};return function(){const c=Date.now();!a&&t.leading===!1&&(a=c);const d=e-(c-a);return o=this,i=arguments,d<=0||d>e?(r&&(clearTimeout(r),r=null),a=c,s=n.apply(o,i),r||(o=i=null)):!r&&t.trailing!==!1&&(r=setTimeout(l,d)),s}}function Qn(){const n={win:!1,mac:!1,x11:!1,linux:!1},e=Object.keys(n).find(t=>window.navigator.appVersion.toLowerCase().indexOf(t)!==-1);return e&&(n[e]=!0),n}function Le(n){return n[0].toUpperCase()+n.slice(1)}function nt(n,...e){if(!e.length)return n;const t=e.shift();if(N(n)&&N(t))for(const o in t)N(t[o])?(n[o]||Object.assign(n,{[o]:{}}),nt(n[o],t[o])):Object.assign(n,{[o]:t[o]});return nt(n,...e)}function it(n){const e=Qn();return n=n.replace(/shift/gi,"⇧").replace(/backspace/gi,"⌫").replace(/enter/gi,"⏎").replace(/up/gi,"↑").replace(/left/gi,"→").replace(/down/gi,"↓").replace(/right/gi,"←").replace(/escape/gi,"⎋").replace(/insert/gi,"Ins").replace(/delete/gi,"␡").replace(/\+/gi," + "),e.mac?n=n.replace(/ctrl|cmd/gi,"⌘").replace(/alt/gi,"⌥"):n=n.replace(/cmd/gi,"Ctrl").replace(/windows/gi,"WIN"),n}function ei(n){try{return new URL(n).href}catch{}return n.substring(0,2)==="//"?window.location.protocol+n:window.location.origin+n}function ti(){return Kn(10)}function oi(n){window.open(n,"_blank")}function ni(n=""){return`${n}${Math.floor(Math.random()*1e8).toString(16)}`}function st(n,e,t){const o=`«${e}» is deprecated and will be removed in the next major release. Please use the «${t}» instead.`;n&&X(o,"warn")}function fe(n,e,t){const o=t.value?"value":"get",i=t[o],s=`#${e}Cache`;if(t[o]=function(...r){return this[s]===void 0&&(this[s]=i.apply(this,...r)),this[s]},o==="get"&&t.set){const r=t.set;t.set=function(a){delete n[s],r.apply(this,a)}}return t}const Co=650;function ge(){return window.matchMedia(`(max-width: ${Co}px)`).matches}const rt=typeof window<"u"&&window.navigator&&window.navigator.platform&&(/iP(ad|hone|od)/.test(window.navigator.platform)||window.navigator.platform==="MacIntel"&&window.navigator.maxTouchPoints>1);function ii(n,e){const t=Array.isArray(n)||N(n),o=Array.isArray(e)||N(e);return t||o?JSON.stringify(n)===JSON.stringify(e):n===e}class u{static isSingleTag(e){return e.tagName&&["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"].includes(e.tagName)}static isLineBreakTag(e){return e&&e.tagName&&["BR","WBR"].includes(e.tagName)}static make(e,t=null,o={}){const i=document.createElement(e);if(Array.isArray(t)){const s=t.filter(r=>r!==void 0);i.classList.add(...s)}else t&&i.classList.add(t);for(const s in o)Object.prototype.hasOwnProperty.call(o,s)&&(i[s]=o[s]);return i}static text(e){return document.createTextNode(e)}static append(e,t){Array.isArray(t)?t.forEach(o=>e.appendChild(o)):e.appendChild(t)}static prepend(e,t){Array.isArray(t)?(t=t.reverse(),t.forEach(o=>e.prepend(o))):e.prepend(t)}static swap(e,t){const o=document.createElement("div"),i=e.parentNode;i.insertBefore(o,e),i.insertBefore(e,t),i.insertBefore(t,o),i.removeChild(o)}static find(e=document,t){return e.querySelector(t)}static get(e){return document.getElementById(e)}static findAll(e=document,t){return e.querySelectorAll(t)}static get allInputsSelector(){return"[contenteditable=true], textarea, input:not([type]), "+["text","password","email","number","search","tel","url"].map(t=>`input[type="${t}"]`).join(", ")}static findAllInputs(e){return xo(e.querySelectorAll(u.allInputsSelector)).reduce((t,o)=>u.isNativeInput(o)||u.containsOnlyInlineElements(o)?[...t,o]:[...t,...u.getDeepestBlockElements(o)],[])}static getDeepestNode(e,t=!1){const o=t?"lastChild":"firstChild",i=t?"previousSibling":"nextSibling";if(e&&e.nodeType===Node.ELEMENT_NODE&&e[o]){let s=e[o];if(u.isSingleTag(s)&&!u.isNativeInput(s)&&!u.isLineBreakTag(s))if(s[i])s=s[i];else if(s.parentNode[i])s=s.parentNode[i];else return s.parentNode;return this.getDeepestNode(s,t)}return e}static isElement(e){return yo(e)?!1:e&&e.nodeType&&e.nodeType===Node.ELEMENT_NODE}static isFragment(e){return yo(e)?!1:e&&e.nodeType&&e.nodeType===Node.DOCUMENT_FRAGMENT_NODE}static isContentEditable(e){return e.contentEditable==="true"}static isNativeInput(e){const t=["INPUT","TEXTAREA"];return e&&e.tagName?t.includes(e.tagName):!1}static canSetCaret(e){let t=!0;if(u.isNativeInput(e))switch(e.type){case"file":case"checkbox":case"radio":case"hidden":case"submit":case"button":case"image":case"reset":t=!1;break}else t=u.isContentEditable(e);return t}static isNodeEmpty(e,t){let o;return this.isSingleTag(e)&&!this.isLineBreakTag(e)?!1:(this.isElement(e)&&this.isNativeInput(e)?o=e.value:o=e.textContent.replace("​",""),t&&(o=o.replace(new RegExp(t,"g"),"")),o.length===0)}static isLeaf(e){return e?e.childNodes.length===0:!1}static isEmpty(e,t){const o=[e];for(;o.length>0;)if(e=o.shift(),!!e){if(this.isLeaf(e)&&!this.isNodeEmpty(e,t))return!1;e.childNodes&&o.push(...Array.from(e.childNodes))}return!0}static isHTMLString(e){const t=u.make("div");return t.innerHTML=e,t.childElementCount>0}static getContentLength(e){return u.isNativeInput(e)?e.value.length:e.nodeType===Node.TEXT_NODE?e.length:e.textContent.length}static get blockElements(){return["address","article","aside","blockquote","canvas","div","dl","dt","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","hr","li","main","nav","noscript","ol","output","p","pre","ruby","section","table","tbody","thead","tr","tfoot","ul","video"]}static containsOnlyInlineElements(e){let t;Q(e)?(t=document.createElement("div"),t.innerHTML=e):t=e;const o=i=>!u.blockElements.includes(i.tagName.toLowerCase())&&Array.from(i.children).every(o);return Array.from(t.children).every(o)}static getDeepestBlockElements(e){return u.containsOnlyInlineElements(e)?[e]:Array.from(e.children).reduce((t,o)=>[...t,...u.getDeepestBlockElements(o)],[])}static getHolder(e){return Q(e)?document.getElementById(e):e}static isAnchor(e){return e.tagName.toLowerCase()==="a"}static offset(e){const t=e.getBoundingClientRect(),o=window.pageXOffset||document.documentElement.scrollLeft,i=window.pageYOffset||document.documentElement.scrollTop,s=t.top+i,r=t.left+o;return{top:s,left:r,bottom:s+t.height,right:r+t.width}}static getNodeByOffset(e,t){let o=0,i=null;const s=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,null);let r=s.nextNode();for(;r;){const c=r.textContent,d=c===null?0:c.length;if(i=r,o+d>=t)break;o+=d,r=s.nextNode()}if(!i)return{node:null,offset:0};const a=i.textContent;if(a===null||a.length===0)return{node:null,offset:0};const l=Math.min(t-o,a.length);return{node:i,offset:l}}}function si(n){return!/[^\t\n\r ]/.test(n)}function ri(n){const e=window.getComputedStyle(n),t=parseFloat(e.fontSize),o=parseFloat(e.lineHeight)||t*1.2,i=parseFloat(e.paddingTop),s=parseFloat(e.borderTopWidth),r=parseFloat(e.marginTop),a=t*.8,l=(o-t)/2;return r+s+i+l+a}function To(n){n.dataset.empty=u.isEmpty(n)?"true":"false"}const So={ui:{blockTunes:{toggler:{"Click to tune":"","or drag to move":""}},inlineToolbar:{converter:{"Convert to":""}},toolbar:{toolbox:{Add:""}},popover:{Filter:"","Nothing found":"","Convert to":""}},toolNames:{Text:"",Link:"",Bold:"",Italic:""},tools:{link:{"Add a link":""},stub:{"The block can not be displayed correctly.":""}},blockTunes:{delete:{Delete:"","Click to delete":""},moveUp:{"Move up":""},moveDown:{"Move down":""}}},Io=class ke{static ui(e,t){return ke._t(e,t)}static t(e,t){return ke._t(e,t)}static setDictionary(e){ke.currentDictionary=e}static _t(e,t){const o=ke.getNamespace(e);return!o||!o[t]?t:o[t]}static getNamespace(e){return e.split(".").reduce((o,i)=>!o||!Object.keys(o).length?{}:o[i],ke.currentDictionary)}};Io.currentDictionary=So;let H=Io;class Oo extends Error{}class we{constructor(){this.subscribers={}}on(e,t){e in this.subscribers||(this.subscribers[e]=[]),this.subscribers[e].push(t)}once(e,t){e in this.subscribers||(this.subscribers[e]=[]);const o=i=>{const s=t(i),r=this.subscribers[e].indexOf(o);return r!==-1&&this.subscribers[e].splice(r,1),s};this.subscribers[e].push(o)}emit(e,t){V(this.subscribers)||!this.subscribers[e]||this.subscribers[e].reduce((o,i)=>{const s=i(o);return s!==void 0?s:o},t)}off(e,t){if(this.subscribers[e]===void 0){console.warn(`EventDispatcher .off(): there is no subscribers for event "${e.toString()}". Probably, .off() called before .on()`);return}for(let o=0;o{const l=this.allListeners.indexOf(s[a]);l>-1&&(this.allListeners.splice(l,1),r.element.removeEventListener(r.eventType,r.handler,r.options))})}offById(e){const t=this.findById(e);t&&t.element.removeEventListener(t.eventType,t.handler,t.options)}findOne(e,t,o){const i=this.findAll(e,t,o);return i.length>0?i[0]:null}findAll(e,t,o){let i;const s=e?this.findByEventTarget(e):[];return e&&t&&o?i=s.filter(r=>r.eventType===t&&r.handler===o):e&&t?i=s.filter(r=>r.eventType===t):i=s,i}removeAll(){this.allListeners.map(e=>{e.element.removeEventListener(e.eventType,e.handler,e.options)}),this.allListeners=[]}destroy(){this.removeAll()}findByEventTarget(e){return this.allListeners.filter(t=>{if(t.element===e)return t})}findByType(e){return this.allListeners.filter(t=>{if(t.eventType===e)return t})}findByHandler(e){return this.allListeners.filter(t=>{if(t.handler===e)return t})}findById(e){return this.allListeners.find(t=>t.id===e)}}class E{constructor({config:e,eventsDispatcher:t}){if(this.nodes={},this.listeners=new Ee,this.readOnlyMutableListeners={on:(o,i,s,r=!1)=>{this.mutableListenerIds.push(this.listeners.on(o,i,s,r))},clearAll:()=>{for(const o of this.mutableListenerIds)this.listeners.offById(o);this.mutableListenerIds=[]}},this.mutableListenerIds=[],new.target===E)throw new TypeError("Constructors for abstract class Module are not allowed.");this.config=e,this.eventsDispatcher=t}set state(e){this.Editor=e}removeAllNodes(){for(const e in this.nodes){const t=this.nodes[e];t instanceof HTMLElement&&t.remove()}}get isRtl(){return this.config.i18n.direction==="rtl"}}class b{constructor(){this.instance=null,this.selection=null,this.savedSelectionRange=null,this.isFakeBackgroundEnabled=!1,this.commandBackground="backColor",this.commandRemoveFormat="removeFormat"}static get CSS(){return{editorWrapper:"codex-editor",editorZone:"codex-editor__redactor"}}static get anchorNode(){const e=window.getSelection();return e?e.anchorNode:null}static get anchorElement(){const e=window.getSelection();if(!e)return null;const t=e.anchorNode;return t?u.isElement(t)?t:t.parentElement:null}static get anchorOffset(){const e=window.getSelection();return e?e.anchorOffset:null}static get isCollapsed(){const e=window.getSelection();return e?e.isCollapsed:null}static get isAtEditor(){return this.isSelectionAtEditor(b.get())}static isSelectionAtEditor(e){if(!e)return!1;let t=e.anchorNode||e.focusNode;t&&t.nodeType===Node.TEXT_NODE&&(t=t.parentNode);let o=null;return t&&t instanceof Element&&(o=t.closest(`.${b.CSS.editorZone}`)),o?o.nodeType===Node.ELEMENT_NODE:!1}static isRangeAtEditor(e){if(!e)return;let t=e.startContainer;t&&t.nodeType===Node.TEXT_NODE&&(t=t.parentNode);let o=null;return t&&t instanceof Element&&(o=t.closest(`.${b.CSS.editorZone}`)),o?o.nodeType===Node.ELEMENT_NODE:!1}static get isSelectionExists(){return!!b.get().anchorNode}static get range(){return this.getRangeFromSelection(this.get())}static getRangeFromSelection(e){return e&&e.rangeCount?e.getRangeAt(0):null}static get rect(){let e=document.selection,t,o={x:0,y:0,width:0,height:0};if(e&&e.type!=="Control")return e=e,t=e.createRange(),o.x=t.boundingLeft,o.y=t.boundingTop,o.width=t.boundingWidth,o.height=t.boundingHeight,o;if(!window.getSelection)return T("Method window.getSelection is not supported","warn"),o;if(e=window.getSelection(),e.rangeCount===null||isNaN(e.rangeCount))return T("Method SelectionUtils.rangeCount is not supported","warn"),o;if(e.rangeCount===0)return o;if(t=e.getRangeAt(0).cloneRange(),t.getBoundingClientRect&&(o=t.getBoundingClientRect()),o.x===0&&o.y===0){const i=document.createElement("span");if(i.getBoundingClientRect){i.appendChild(document.createTextNode("​")),t.insertNode(i),o=i.getBoundingClientRect();const s=i.parentNode;s.removeChild(i),s.normalize()}}return o}static get text(){return window.getSelection?window.getSelection().toString():""}static get(){return window.getSelection()}static setCursor(e,t=0){const o=document.createRange(),i=window.getSelection();return u.isNativeInput(e)?u.canSetCaret(e)?(e.focus(),e.selectionStart=e.selectionEnd=t,e.getBoundingClientRect()):void 0:(o.setStart(e,t),o.setEnd(e,t),i.removeAllRanges(),i.addRange(o),o.getBoundingClientRect())}static isRangeInsideContainer(e){const t=b.range;return t===null?!1:e.contains(t.startContainer)}static addFakeCursor(){const e=b.range;if(e===null)return;const t=u.make("span","codex-editor__fake-cursor");t.dataset.mutationFree="true",e.collapse(),e.insertNode(t)}static isFakeCursorInsideContainer(e){return u.find(e,".codex-editor__fake-cursor")!==null}static removeFakeCursor(e=document.body){const t=u.find(e,".codex-editor__fake-cursor");t&&t.remove()}removeFakeBackground(){this.isFakeBackgroundEnabled&&(this.isFakeBackgroundEnabled=!1,document.execCommand(this.commandRemoveFormat))}setFakeBackground(){document.execCommand(this.commandBackground,!1,"#a8d6ff"),this.isFakeBackgroundEnabled=!0}save(){this.savedSelectionRange=b.range}restore(){if(!this.savedSelectionRange)return;const e=window.getSelection();e.removeAllRanges(),e.addRange(this.savedSelectionRange)}clearSaved(){this.savedSelectionRange=null}collapseToEnd(){const e=window.getSelection(),t=document.createRange();t.selectNodeContents(e.focusNode),t.collapse(!1),e.removeAllRanges(),e.addRange(t)}findParentTag(e,t,o=10){const i=window.getSelection();let s=null;return!i||!i.anchorNode||!i.focusNode?null:([i.anchorNode,i.focusNode].forEach(a=>{let l=o;for(;l>0&&a.parentNode&&!(a.tagName===e&&(s=a,t&&a.classList&&!a.classList.contains(t)&&(s=null),s));)a=a.parentNode,l--}),s)}expandToTag(e){const t=window.getSelection();t.removeAllRanges();const o=document.createRange();o.selectNodeContents(e),t.addRange(o)}}function ai(n,e){const{type:t,target:o,addedNodes:i,removedNodes:s}=n;return n.type==="attributes"&&n.attributeName==="data-empty"?!1:!!(e.contains(o)||t==="childList"&&(Array.from(i).some(l=>l===e)||Array.from(s).some(l=>l===e)))}const at="redactor dom changed",_o="block changed",Mo="fake cursor is about to be toggled",Ao="fake cursor have been set",xe="editor mobile layout toggled";function lt(n,e){if(!n.conversionConfig)return!1;const t=n.conversionConfig[e];return M(t)||Q(t)}function Pe(n,e){return lt(n.tool,e)}function Lo(n,e){return Object.entries(n).some(([t,o])=>e[t]&&ii(e[t],o))}async function Po(n,e){const o=(await n.save()).data,i=e.find(s=>s.name===n.name);return i!==void 0&&!lt(i,"export")?[]:e.reduce((s,r)=>{if(!lt(r,"import")||r.toolbox===void 0)return s;const a=r.toolbox.filter(l=>{if(V(l)||l.icon===void 0)return!1;if(l.data!==void 0){if(Lo(l.data,o))return!1}else if(r.name===n.name)return!1;return!0});return s.push({...r,toolbox:a}),s},[])}function No(n,e){return n.mergeable?n.name===e.name?!0:Pe(e,"export")&&Pe(n,"import"):!1}function li(n,e){const t=e==null?void 0:e.export;return M(t)?t(n):Q(t)?n[t]:(t!==void 0&&T("Conversion «export» property must be a string or function. String means key of saved data object to export. Function should export processed string to export."),"")}function Ro(n,e,t){const o=e==null?void 0:e.import;return M(o)?o(n,t):Q(o)?{[o]:n}:(o!==void 0&&T("Conversion «import» property must be a string or function. String means key of tool data to import. Function accepts a imported string and return composed tool data."),{})}var _=(n=>(n.Default="default",n.Separator="separator",n.Html="html",n))(_||{}),te=(n=>(n.APPEND_CALLBACK="appendCallback",n.RENDERED="rendered",n.MOVED="moved",n.UPDATED="updated",n.REMOVED="removed",n.ON_PASTE="onPaste",n))(te||{});class R extends we{constructor({id:e=ti(),data:t,tool:o,readOnly:i,tunesData:s},r){super(),this.cachedInputs=[],this.toolRenderedElement=null,this.tunesInstances=new Map,this.defaultTunesInstances=new Map,this.unavailableTunesData={},this.inputIndex=0,this.editorEventBus=null,this.handleFocus=()=>{this.dropInputsCache(),this.updateCurrentInput()},this.didMutated=(a=void 0)=>{const l=a===void 0,c=a instanceof InputEvent;!l&&!c&&this.detectToolRootChange(a);let d;l||c?d=!0:d=!(a.length>0&&a.every(p=>{const{addedNodes:g,removedNodes:f,target:v}=p;return[...Array.from(g),...Array.from(f),v].some(S=>(u.isElement(S)||(S=S.parentElement),S&&S.closest('[data-mutation-free="true"]')!==null))})),d&&(this.dropInputsCache(),this.updateCurrentInput(),this.toggleInputsEmptyMark(),this.call("updated"),this.emit("didMutated",this))},this.name=o.name,this.id=e,this.settings=o.settings,this.config=o.settings.config||{},this.editorEventBus=r||null,this.blockAPI=new ee(this),this.tool=o,this.toolInstance=o.create(t,this.blockAPI,i),this.tunes=o.tunes,this.composeTunes(s),this.holder=this.compose(),window.requestIdleCallback(()=>{this.watchBlockMutations(),this.addInputEvents(),this.toggleInputsEmptyMark()})}static get CSS(){return{wrapper:"ce-block",wrapperStretched:"ce-block--stretched",content:"ce-block__content",selected:"ce-block--selected",dropTarget:"ce-block--drop-target"}}get inputs(){if(this.cachedInputs.length!==0)return this.cachedInputs;const e=u.findAllInputs(this.holder);return this.inputIndex>e.length-1&&(this.inputIndex=e.length-1),this.cachedInputs=e,e}get currentInput(){return this.inputs[this.inputIndex]}set currentInput(e){const t=this.inputs.findIndex(o=>o===e||o.contains(e));t!==-1&&(this.inputIndex=t)}get firstInput(){return this.inputs[0]}get lastInput(){const e=this.inputs;return e[e.length-1]}get nextInput(){return this.inputs[this.inputIndex+1]}get previousInput(){return this.inputs[this.inputIndex-1]}get data(){return this.save().then(e=>e&&!V(e.data)?e.data:{})}get sanitize(){return this.tool.sanitizeConfig}get mergeable(){return M(this.toolInstance.merge)}get focusable(){return this.inputs.length!==0}get isEmpty(){const e=u.isEmpty(this.pluginsContent,"/"),t=!this.hasMedia;return e&&t}get hasMedia(){const e=["img","iframe","video","audio","source","input","textarea","twitterwidget"];return!!this.holder.querySelector(e.join(","))}set selected(e){var i,s;this.holder.classList.toggle(R.CSS.selected,e);const t=e===!0&&b.isRangeInsideContainer(this.holder),o=e===!1&&b.isFakeCursorInsideContainer(this.holder);(t||o)&&((i=this.editorEventBus)==null||i.emit(Mo,{state:e}),t?b.addFakeCursor():b.removeFakeCursor(this.holder),(s=this.editorEventBus)==null||s.emit(Ao,{state:e}))}get selected(){return this.holder.classList.contains(R.CSS.selected)}set stretched(e){this.holder.classList.toggle(R.CSS.wrapperStretched,e)}get stretched(){return this.holder.classList.contains(R.CSS.wrapperStretched)}set dropTarget(e){this.holder.classList.toggle(R.CSS.dropTarget,e)}get pluginsContent(){return this.toolRenderedElement}call(e,t){if(M(this.toolInstance[e])){e==="appendCallback"&&T("`appendCallback` hook is deprecated and will be removed in the next major release. Use `rendered` hook instead","warn");try{this.toolInstance[e].call(this.toolInstance,t)}catch(o){T(`Error during '${e}' call: ${o.message}`,"error")}}}async mergeWith(e){await this.toolInstance.merge(e)}async save(){const e=await this.toolInstance.save(this.pluginsContent),t=this.unavailableTunesData;[...this.tunesInstances.entries(),...this.defaultTunesInstances.entries()].forEach(([s,r])=>{if(M(r.save))try{t[s]=r.save()}catch(a){T(`Tune ${r.constructor.name} save method throws an Error %o`,"warn",a)}});const o=window.performance.now();let i;return Promise.resolve(e).then(s=>(i=window.performance.now(),{id:this.id,tool:this.name,data:s,tunes:t,time:i-o})).catch(s=>{T(`Saving process for ${this.name} tool failed due to the ${s}`,"log","red")})}async validate(e){let t=!0;return this.toolInstance.validate instanceof Function&&(t=await this.toolInstance.validate(e)),t}getTunes(){const e=[],t=[],o=typeof this.toolInstance.renderSettings=="function"?this.toolInstance.renderSettings():[];return u.isElement(o)?e.push({type:_.Html,element:o}):Array.isArray(o)?e.push(...o):e.push(o),[...this.tunesInstances.values(),...this.defaultTunesInstances.values()].map(s=>s.render()).forEach(s=>{u.isElement(s)?t.push({type:_.Html,element:s}):Array.isArray(s)?t.push(...s):t.push(s)}),{toolTunes:e,commonTunes:t}}updateCurrentInput(){this.currentInput=u.isNativeInput(document.activeElement)||!b.anchorNode?document.activeElement:b.anchorNode}dispatchChange(){this.didMutated()}destroy(){this.unwatchBlockMutations(),this.removeInputEvents(),super.destroy(),M(this.toolInstance.destroy)&&this.toolInstance.destroy()}async getActiveToolboxEntry(){const e=this.tool.toolbox;if(e.length===1)return Promise.resolve(this.tool.toolbox[0]);const t=await this.data,o=e;return o==null?void 0:o.find(i=>Lo(i.data,t))}async exportDataAsString(){const e=await this.data;return li(e,this.tool.conversionConfig)}compose(){const e=u.make("div",R.CSS.wrapper),t=u.make("div",R.CSS.content),o=this.toolInstance.render();e.dataset.id=this.id,this.toolRenderedElement=o,t.appendChild(this.toolRenderedElement);let i=t;return[...this.tunesInstances.values(),...this.defaultTunesInstances.values()].forEach(s=>{if(M(s.wrap))try{i=s.wrap(i)}catch(r){T(`Tune ${s.constructor.name} wrap method throws an Error %o`,"warn",r)}}),e.appendChild(i),e}composeTunes(e){Array.from(this.tunes.values()).forEach(t=>{(t.isInternal?this.defaultTunesInstances:this.tunesInstances).set(t.name,t.create(e[t.name],this.blockAPI))}),Object.entries(e).forEach(([t,o])=>{this.tunesInstances.has(t)||(this.unavailableTunesData[t]=o)})}addInputEvents(){this.inputs.forEach(e=>{e.addEventListener("focus",this.handleFocus),u.isNativeInput(e)&&e.addEventListener("input",this.didMutated)})}removeInputEvents(){this.inputs.forEach(e=>{e.removeEventListener("focus",this.handleFocus),u.isNativeInput(e)&&e.removeEventListener("input",this.didMutated)})}watchBlockMutations(){var e;this.redactorDomChangedCallback=t=>{const{mutations:o}=t;o.some(s=>ai(s,this.toolRenderedElement))&&this.didMutated(o)},(e=this.editorEventBus)==null||e.on(at,this.redactorDomChangedCallback)}unwatchBlockMutations(){var e;(e=this.editorEventBus)==null||e.off(at,this.redactorDomChangedCallback)}detectToolRootChange(e){e.forEach(t=>{if(Array.from(t.removedNodes).includes(this.toolRenderedElement)){const i=t.addedNodes[t.addedNodes.length-1];this.toolRenderedElement=i}})}dropInputsCache(){this.cachedInputs=[]}toggleInputsEmptyMark(){this.inputs.forEach(To)}}class ci extends E{constructor(){super(...arguments),this.insert=(e=this.config.defaultBlock,t={},o={},i,s,r,a)=>{const l=this.Editor.BlockManager.insert({id:a,tool:e,data:t,index:i,needToFocus:s,replace:r});return new ee(l)},this.composeBlockData=async e=>{const t=this.Editor.Tools.blockTools.get(e);return new R({tool:t,api:this.Editor.API,readOnly:!0,data:{},tunesData:{}}).data},this.update=async(e,t,o)=>{const{BlockManager:i}=this.Editor,s=i.getBlockById(e);if(s===void 0)throw new Error(`Block with id "${e}" not found`);const r=await i.update(s,t,o);return new ee(r)},this.convert=async(e,t,o)=>{var h,p;const{BlockManager:i,Tools:s}=this.Editor,r=i.getBlockById(e);if(!r)throw new Error(`Block with id "${e}" not found`);const a=s.blockTools.get(r.name),l=s.blockTools.get(t);if(!l)throw new Error(`Block Tool with type "${t}" not found`);const c=((h=a==null?void 0:a.conversionConfig)==null?void 0:h.export)!==void 0,d=((p=l.conversionConfig)==null?void 0:p.import)!==void 0;if(c&&d){const g=await i.convert(r,t,o);return new ee(g)}else{const g=[c?!1:Le(r.name),d?!1:Le(t)].filter(Boolean).join(" and ");throw new Error(`Conversion from "${r.name}" to "${t}" is not possible. ${g} tool(s) should provide a "conversionConfig"`)}},this.insertMany=(e,t=this.Editor.BlockManager.blocks.length-1)=>{this.validateIndex(t);const o=e.map(({id:i,type:s,data:r})=>this.Editor.BlockManager.composeBlock({id:i,tool:s||this.config.defaultBlock,data:r}));return this.Editor.BlockManager.insertMany(o,t),o.map(i=>new ee(i))}}get methods(){return{clear:()=>this.clear(),render:e=>this.render(e),renderFromHTML:e=>this.renderFromHTML(e),delete:e=>this.delete(e),swap:(e,t)=>this.swap(e,t),move:(e,t)=>this.move(e,t),getBlockByIndex:e=>this.getBlockByIndex(e),getById:e=>this.getById(e),getCurrentBlockIndex:()=>this.getCurrentBlockIndex(),getBlockIndex:e=>this.getBlockIndex(e),getBlocksCount:()=>this.getBlocksCount(),getBlockByElement:e=>this.getBlockByElement(e),stretchBlock:(e,t=!0)=>this.stretchBlock(e,t),insertNewBlock:()=>this.insertNewBlock(),insert:this.insert,insertMany:this.insertMany,update:this.update,composeBlockData:this.composeBlockData,convert:this.convert}}getBlocksCount(){return this.Editor.BlockManager.blocks.length}getCurrentBlockIndex(){return this.Editor.BlockManager.currentBlockIndex}getBlockIndex(e){const t=this.Editor.BlockManager.getBlockById(e);if(!t){X("There is no block with id `"+e+"`","warn");return}return this.Editor.BlockManager.getBlockIndex(t)}getBlockByIndex(e){const t=this.Editor.BlockManager.getBlockByIndex(e);if(t===void 0){X("There is no block at index `"+e+"`","warn");return}return new ee(t)}getById(e){const t=this.Editor.BlockManager.getBlockById(e);return t===void 0?(X("There is no block with id `"+e+"`","warn"),null):new ee(t)}getBlockByElement(e){const t=this.Editor.BlockManager.getBlock(e);if(t===void 0){X("There is no block corresponding to element `"+e+"`","warn");return}return new ee(t)}swap(e,t){T("`blocks.swap()` method is deprecated and will be removed in the next major release. Use `block.move()` method instead","info"),this.Editor.BlockManager.swap(e,t)}move(e,t){this.Editor.BlockManager.move(e,t)}delete(e=this.Editor.BlockManager.currentBlockIndex){try{const t=this.Editor.BlockManager.getBlockByIndex(e);this.Editor.BlockManager.removeBlock(t)}catch(t){X(t,"warn");return}this.Editor.BlockManager.blocks.length===0&&this.Editor.BlockManager.insert(),this.Editor.BlockManager.currentBlock&&this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock,this.Editor.Caret.positions.END),this.Editor.Toolbar.close()}async clear(){await this.Editor.BlockManager.clear(!0),this.Editor.InlineToolbar.close()}async render(e){if(e===void 0||e.blocks===void 0)throw new Error("Incorrect data passed to the render() method");this.Editor.ModificationsObserver.disable(),await this.Editor.BlockManager.clear(),await this.Editor.Renderer.render(e.blocks),this.Editor.ModificationsObserver.enable()}async renderFromHTML(e){return await this.Editor.BlockManager.clear(),this.Editor.Paste.processText(e,!0)}stretchBlock(e,t=!0){st(!0,"blocks.stretchBlock()","BlockAPI");const o=this.Editor.BlockManager.getBlockByIndex(e);o&&(o.stretched=t)}insertNewBlock(){T("Method blocks.insertNewBlock() is deprecated and it will be removed in the next major release. Use blocks.insert() instead.","warn"),this.insert()}validateIndex(e){if(typeof e!="number")throw new Error("Index should be a number");if(e<0)throw new Error("Index should be greater than or equal to 0");if(e===null)throw new Error("Index should be greater than or equal to 0")}}function di(n,e){return typeof n=="number"?e.BlockManager.getBlockByIndex(n):typeof n=="string"?e.BlockManager.getBlockById(n):e.BlockManager.getBlockById(n.id)}class ui extends E{constructor(){super(...arguments),this.setToFirstBlock=(e=this.Editor.Caret.positions.DEFAULT,t=0)=>this.Editor.BlockManager.firstBlock?(this.Editor.Caret.setToBlock(this.Editor.BlockManager.firstBlock,e,t),!0):!1,this.setToLastBlock=(e=this.Editor.Caret.positions.DEFAULT,t=0)=>this.Editor.BlockManager.lastBlock?(this.Editor.Caret.setToBlock(this.Editor.BlockManager.lastBlock,e,t),!0):!1,this.setToPreviousBlock=(e=this.Editor.Caret.positions.DEFAULT,t=0)=>this.Editor.BlockManager.previousBlock?(this.Editor.Caret.setToBlock(this.Editor.BlockManager.previousBlock,e,t),!0):!1,this.setToNextBlock=(e=this.Editor.Caret.positions.DEFAULT,t=0)=>this.Editor.BlockManager.nextBlock?(this.Editor.Caret.setToBlock(this.Editor.BlockManager.nextBlock,e,t),!0):!1,this.setToBlock=(e,t=this.Editor.Caret.positions.DEFAULT,o=0)=>{const i=di(e,this.Editor);return i===void 0?!1:(this.Editor.Caret.setToBlock(i,t,o),!0)},this.focus=(e=!1)=>e?this.setToLastBlock(this.Editor.Caret.positions.END):this.setToFirstBlock(this.Editor.Caret.positions.START)}get methods(){return{setToFirstBlock:this.setToFirstBlock,setToLastBlock:this.setToLastBlock,setToPreviousBlock:this.setToPreviousBlock,setToNextBlock:this.setToNextBlock,setToBlock:this.setToBlock,focus:this.focus}}}class hi extends E{get methods(){return{emit:(e,t)=>this.emit(e,t),off:(e,t)=>this.off(e,t),on:(e,t)=>this.on(e,t)}}on(e,t){this.eventsDispatcher.on(e,t)}emit(e,t){this.eventsDispatcher.emit(e,t)}off(e,t){this.eventsDispatcher.off(e,t)}}class ct extends E{static getNamespace(e,t){return t?`blockTunes.${e}`:`tools.${e}`}get methods(){return{t:()=>{X("I18n.t() method can be accessed only from Tools","warn")}}}getMethodsForTool(e,t){return Object.assign(this.methods,{t:o=>H.t(ct.getNamespace(e,t),o)})}}class pi extends E{get methods(){return{blocks:this.Editor.BlocksAPI.methods,caret:this.Editor.CaretAPI.methods,tools:this.Editor.ToolsAPI.methods,events:this.Editor.EventsAPI.methods,listeners:this.Editor.ListenersAPI.methods,notifier:this.Editor.NotifierAPI.methods,sanitizer:this.Editor.SanitizerAPI.methods,saver:this.Editor.SaverAPI.methods,selection:this.Editor.SelectionAPI.methods,styles:this.Editor.StylesAPI.classes,toolbar:this.Editor.ToolbarAPI.methods,inlineToolbar:this.Editor.InlineToolbarAPI.methods,tooltip:this.Editor.TooltipAPI.methods,i18n:this.Editor.I18nAPI.methods,readOnly:this.Editor.ReadOnlyAPI.methods,ui:this.Editor.UiAPI.methods}}getMethodsForTool(e,t){return Object.assign(this.methods,{i18n:this.Editor.I18nAPI.getMethodsForTool(e,t)})}}class fi extends E{get methods(){return{close:()=>this.close(),open:()=>this.open()}}open(){this.Editor.InlineToolbar.tryToShow()}close(){this.Editor.InlineToolbar.close()}}class gi extends E{get methods(){return{on:(e,t,o,i)=>this.on(e,t,o,i),off:(e,t,o,i)=>this.off(e,t,o,i),offById:e=>this.offById(e)}}on(e,t,o,i){return this.listeners.on(e,t,o,i)}off(e,t,o,i){this.listeners.off(e,t,o,i)}offById(e){this.listeners.offById(e)}}var Do={exports:{}};(function(n,e){(function(t,o){n.exports=o()})(window,function(){return function(t){var o={};function i(s){if(o[s])return o[s].exports;var r=o[s]={i:s,l:!1,exports:{}};return t[s].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=t,i.c=o,i.d=function(s,r,a){i.o(s,r)||Object.defineProperty(s,r,{enumerable:!0,get:a})},i.r=function(s){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})},i.t=function(s,r){if(1&r&&(s=i(s)),8&r||4&r&&typeof s=="object"&&s&&s.__esModule)return s;var a=Object.create(null);if(i.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:s}),2&r&&typeof s!="string")for(var l in s)i.d(a,l,(function(c){return s[c]}).bind(null,l));return a},i.n=function(s){var r=s&&s.__esModule?function(){return s.default}:function(){return s};return i.d(r,"a",r),r},i.o=function(s,r){return Object.prototype.hasOwnProperty.call(s,r)},i.p="/",i(i.s=0)}([function(t,o,i){i(1),t.exports=function(){var s=i(6),r="cdx-notify--bounce-in",a=null;return{show:function(l){if(l.message){(function(){if(a)return!0;a=s.getWrapper(),document.body.appendChild(a)})();var c=null,d=l.time||8e3;switch(l.type){case"confirm":c=s.confirm(l);break;case"prompt":c=s.prompt(l);break;default:c=s.alert(l),window.setTimeout(function(){c.remove()},d)}a.appendChild(c),c.classList.add(r)}}}}()},function(t,o,i){var s=i(2);typeof s=="string"&&(s=[[t.i,s,""]]);var r={hmr:!0,transform:void 0,insertInto:void 0};i(4)(s,r),s.locals&&(t.exports=s.locals)},function(t,o,i){(t.exports=i(3)(!1)).push([t.i,`.cdx-notify--error{background:#fffbfb!important}.cdx-notify--error::before{background:#fb5d5d!important}.cdx-notify__input{max-width:130px;padding:5px 10px;background:#f7f7f7;border:0;border-radius:3px;font-size:13px;color:#656b7c;outline:0}.cdx-notify__input:-ms-input-placeholder{color:#656b7c}.cdx-notify__input::placeholder{color:#656b7c}.cdx-notify__input:focus:-ms-input-placeholder{color:rgba(101,107,124,.3)}.cdx-notify__input:focus::placeholder{color:rgba(101,107,124,.3)}.cdx-notify__button{border:none;border-radius:3px;font-size:13px;padding:5px 10px;cursor:pointer}.cdx-notify__button:last-child{margin-left:10px}.cdx-notify__button--cancel{background:#f2f5f7;box-shadow:0 2px 1px 0 rgba(16,19,29,0);color:#656b7c}.cdx-notify__button--cancel:hover{background:#eee}.cdx-notify__button--confirm{background:#34c992;box-shadow:0 1px 1px 0 rgba(18,49,35,.05);color:#fff}.cdx-notify__button--confirm:hover{background:#33b082}.cdx-notify__btns-wrapper{display:-ms-flexbox;display:flex;-ms-flex-flow:row nowrap;flex-flow:row nowrap;margin-top:5px}.cdx-notify__cross{position:absolute;top:5px;right:5px;width:10px;height:10px;padding:5px;opacity:.54;cursor:pointer}.cdx-notify__cross::after,.cdx-notify__cross::before{content:'';position:absolute;left:9px;top:5px;height:12px;width:2px;background:#575d67}.cdx-notify__cross::before{transform:rotate(-45deg)}.cdx-notify__cross::after{transform:rotate(45deg)}.cdx-notify__cross:hover{opacity:1}.cdx-notifies{position:fixed;z-index:2;bottom:20px;left:20px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif}.cdx-notify{position:relative;width:220px;margin-top:15px;padding:13px 16px;background:#fff;box-shadow:0 11px 17px 0 rgba(23,32,61,.13);border-radius:5px;font-size:14px;line-height:1.4em;word-wrap:break-word}.cdx-notify::before{content:'';position:absolute;display:block;top:0;left:0;width:3px;height:calc(100% - 6px);margin:3px;border-radius:5px;background:0 0}@keyframes bounceIn{0%{opacity:0;transform:scale(.3)}50%{opacity:1;transform:scale(1.05)}70%{transform:scale(.9)}100%{transform:scale(1)}}.cdx-notify--bounce-in{animation-name:bounceIn;animation-duration:.6s;animation-iteration-count:1}.cdx-notify--success{background:#fafffe!important}.cdx-notify--success::before{background:#41ffb1!important}`,""])},function(t,o){t.exports=function(i){var s=[];return s.toString=function(){return this.map(function(r){var a=function(l,c){var d=l[1]||"",h=l[3];if(!h)return d;if(c&&typeof btoa=="function"){var p=(f=h,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(f))))+" */"),g=h.sources.map(function(v){return"/*# sourceURL="+h.sourceRoot+v+" */"});return[d].concat(g).concat([p]).join(` +`)}var f;return[d].join(` +`)}(r,i);return r[2]?"@media "+r[2]+"{"+a+"}":a}).join("")},s.i=function(r,a){typeof r=="string"&&(r=[[null,r,""]]);for(var l={},c=0;c=0&&p.splice(m,1)}function A(k){var m=document.createElement("style");return k.attrs.type===void 0&&(k.attrs.type="text/css"),G(m,k.attrs),O(k,m),m}function G(k,m){Object.keys(m).forEach(function(w){k.setAttribute(w,m[w])})}function j(k,m){var w,x,I,C;if(m.transform&&k.css){if(!(C=m.transform(k.css)))return function(){};k.css=C}if(m.singleton){var D=h++;w=d||(d=A(m)),x=he.bind(null,w,D,!1),I=he.bind(null,w,D,!0)}else k.sourceMap&&typeof URL=="function"&&typeof URL.createObjectURL=="function"&&typeof URL.revokeObjectURL=="function"&&typeof Blob=="function"&&typeof btoa=="function"?(w=function(B){var K=document.createElement("link");return B.attrs.type===void 0&&(B.attrs.type="text/css"),B.attrs.rel="stylesheet",G(K,B.attrs),O(B,K),K}(m),x=(function(B,K,Oe){var pe=Oe.css,vo=Oe.sourceMap,Ca=K.convertToAbsoluteUrls===void 0&&vo;(K.convertToAbsoluteUrls||Ca)&&(pe=g(pe)),vo&&(pe+=` +/*# sourceMappingURL=data:application/json;base64,`+btoa(unescape(encodeURIComponent(JSON.stringify(vo))))+" */");var Ta=new Blob([pe],{type:"text/css"}),$n=B.href;B.href=URL.createObjectURL(Ta),$n&&URL.revokeObjectURL($n)}).bind(null,w,m),I=function(){S(w),w.href&&URL.revokeObjectURL(w.href)}):(w=A(m),x=(function(B,K){var Oe=K.css,pe=K.media;if(pe&&B.setAttribute("media",pe),B.styleSheet)B.styleSheet.cssText=Oe;else{for(;B.firstChild;)B.removeChild(B.firstChild);B.appendChild(document.createTextNode(Oe))}}).bind(null,w),I=function(){S(w)});return x(k),function(B){if(B){if(B.css===k.css&&B.media===k.media&&B.sourceMap===k.sourceMap)return;x(k=B)}else I()}}t.exports=function(k,m){if(typeof DEBUG<"u"&&DEBUG&&typeof document!="object")throw new Error("The style-loader cannot be used in a non-browser environment");(m=m||{}).attrs=typeof m.attrs=="object"?m.attrs:{},m.singleton||typeof m.singleton=="boolean"||(m.singleton=l()),m.insertInto||(m.insertInto="head"),m.insertAt||(m.insertAt="bottom");var w=v(k,m);return f(w,m),function(x){for(var I=[],C=0;Cthis.show(e)}}show(e){return this.notifier.show(e)}}class yi extends E{get methods(){const e=()=>this.isEnabled;return{toggle:t=>this.toggle(t),get isEnabled(){return e()}}}toggle(e){return this.Editor.ReadOnly.toggle(e)}get isEnabled(){return this.Editor.ReadOnly.isEnabled}}var Fo={exports:{}};(function(n,e){(function(t,o){n.exports=o()})(J,function(){function t(h){var p=h.tags,g=Object.keys(p),f=g.map(function(v){return typeof p[v]}).every(function(v){return v==="object"||v==="boolean"||v==="function"});if(!f)throw new Error("The configuration was invalid");this.config=h}var o=["P","LI","TD","TH","DIV","H1","H2","H3","H4","H5","H6","PRE"];function i(h){return o.indexOf(h.nodeName)!==-1}var s=["A","B","STRONG","I","EM","SUB","SUP","U","STRIKE"];function r(h){return s.indexOf(h.nodeName)!==-1}t.prototype.clean=function(h){const p=document.implementation.createHTMLDocument(),g=p.createElement("div");return g.innerHTML=h,this._sanitize(p,g),g.innerHTML},t.prototype._sanitize=function(h,p){var g=a(h,p),f=g.firstChild();if(f)do{if(f.nodeType===Node.TEXT_NODE)if(f.data.trim()===""&&(f.previousElementSibling&&i(f.previousElementSibling)||f.nextElementSibling&&i(f.nextElementSibling))){p.removeChild(f),this._sanitize(h,p);break}else continue;if(f.nodeType===Node.COMMENT_NODE){p.removeChild(f),this._sanitize(h,p);break}var v=r(f),O;v&&(O=Array.prototype.some.call(f.childNodes,i));var S=!!p.parentNode,A=i(p)&&i(f)&&S,G=f.nodeName.toLowerCase(),j=l(this.config,G,f),U=v&&O;if(U||c(f,j)||!this.config.keepNestedBlockElements&&A){if(!(f.nodeName==="SCRIPT"||f.nodeName==="STYLE"))for(;f.childNodes.length>0;)p.insertBefore(f.childNodes[0],f);p.removeChild(f),this._sanitize(h,p);break}for(var oe=0;oe"u"?!0:typeof p=="boolean"?!p:!1}function d(h,p,g){var f=h.name.toLowerCase();return p===!0?!1:typeof p[f]=="function"?!p[f](h.value,g):typeof p[f]>"u"||p[f]===!1?!0:typeof p[f]=="string"?p[f]!==h.value:!1}return t})})(Fo);var wi=Fo.exports;const Ei=ne(wi);function dt(n,e){return n.map(t=>{const o=M(e)?e(t.tool):e;return V(o)||(t.data=ut(t.data,o)),t})}function q(n,e={}){const t={tags:e};return new Ei(t).clean(n)}function ut(n,e){return Array.isArray(n)?xi(n,e):N(n)?Bi(n,e):Q(n)?Ci(n,e):n}function xi(n,e){return n.map(t=>ut(t,e))}function Bi(n,e){const t={};for(const o in n){if(!Object.prototype.hasOwnProperty.call(n,o))continue;const i=n[o],s=Ti(e[o])?e[o]:e;t[o]=ut(i,s)}return t}function Ci(n,e){return N(e)?q(n,e):e===!1?q(n,{}):n}function Ti(n){return N(n)||qn(n)||M(n)}class Si extends E{get methods(){return{clean:(e,t)=>this.clean(e,t)}}clean(e,t){return q(e,t)}}class Ii extends E{get methods(){return{save:()=>this.save()}}save(){const e="Editor's content can not be saved in read-only mode";return this.Editor.ReadOnly.isEnabled?(X(e,"warn"),Promise.reject(new Error(e))):this.Editor.Saver.save()}}class Oi extends E{constructor(){super(...arguments),this.selectionUtils=new b}get methods(){return{findParentTag:(e,t)=>this.findParentTag(e,t),expandToTag:e=>this.expandToTag(e),save:()=>this.selectionUtils.save(),restore:()=>this.selectionUtils.restore(),setFakeBackground:()=>this.selectionUtils.setFakeBackground(),removeFakeBackground:()=>this.selectionUtils.removeFakeBackground()}}findParentTag(e,t){return this.selectionUtils.findParentTag(e,t)}expandToTag(e){this.selectionUtils.expandToTag(e)}}class _i extends E{get methods(){return{getBlockTools:()=>Array.from(this.Editor.Tools.blockTools.values())}}}class Mi extends E{get classes(){return{block:"cdx-block",inlineToolButton:"ce-inline-tool",inlineToolButtonActive:"ce-inline-tool--active",input:"cdx-input",loader:"cdx-loader",button:"cdx-button",settingsButton:"cdx-settings-button",settingsButtonActive:"cdx-settings-button--active"}}}class Ai extends E{get methods(){return{close:()=>this.close(),open:()=>this.open(),toggleBlockSettings:e=>this.toggleBlockSettings(e),toggleToolbox:e=>this.toggleToolbox(e)}}open(){this.Editor.Toolbar.moveAndOpen()}close(){this.Editor.Toolbar.close()}toggleBlockSettings(e){if(this.Editor.BlockManager.currentBlockIndex===-1){X("Could't toggle the Toolbar because there is no block selected ","warn");return}e??!this.Editor.BlockSettings.opened?(this.Editor.Toolbar.moveAndOpen(),this.Editor.BlockSettings.open()):this.Editor.BlockSettings.close()}toggleToolbox(e){if(this.Editor.BlockManager.currentBlockIndex===-1){X("Could't toggle the Toolbox because there is no block selected ","warn");return}e??!this.Editor.Toolbar.toolbox.opened?(this.Editor.Toolbar.moveAndOpen(),this.Editor.Toolbar.toolbox.open()):this.Editor.Toolbar.toolbox.close()}}var jo={exports:{}};/*! + * CodeX.Tooltips + * + * @version 1.0.5 + * + * @licence MIT + * @author CodeX + * + * + */(function(n,e){(function(t,o){n.exports=o()})(window,function(){return function(t){var o={};function i(s){if(o[s])return o[s].exports;var r=o[s]={i:s,l:!1,exports:{}};return t[s].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=t,i.c=o,i.d=function(s,r,a){i.o(s,r)||Object.defineProperty(s,r,{enumerable:!0,get:a})},i.r=function(s){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})},i.t=function(s,r){if(1&r&&(s=i(s)),8&r||4&r&&typeof s=="object"&&s&&s.__esModule)return s;var a=Object.create(null);if(i.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:s}),2&r&&typeof s!="string")for(var l in s)i.d(a,l,(function(c){return s[c]}).bind(null,l));return a},i.n=function(s){var r=s&&s.__esModule?function(){return s.default}:function(){return s};return i.d(r,"a",r),r},i.o=function(s,r){return Object.prototype.hasOwnProperty.call(s,r)},i.p="",i(i.s=0)}([function(t,o,i){t.exports=i(1)},function(t,o,i){i.r(o),i.d(o,"default",function(){return s});class s{constructor(){this.nodes={wrapper:null,content:null},this.showed=!1,this.offsetTop=10,this.offsetLeft=10,this.offsetRight=10,this.hidingDelay=0,this.handleWindowScroll=()=>{this.showed&&this.hide(!0)},this.loadStyles(),this.prepare(),window.addEventListener("scroll",this.handleWindowScroll,{passive:!0})}get CSS(){return{tooltip:"ct",tooltipContent:"ct__content",tooltipShown:"ct--shown",placement:{left:"ct--left",bottom:"ct--bottom",right:"ct--right",top:"ct--top"}}}show(a,l,c){this.nodes.wrapper||this.prepare(),this.hidingTimeout&&clearTimeout(this.hidingTimeout);const d=Object.assign({placement:"bottom",marginTop:0,marginLeft:0,marginRight:0,marginBottom:0,delay:70,hidingDelay:0},c);if(d.hidingDelay&&(this.hidingDelay=d.hidingDelay),this.nodes.content.innerHTML="",typeof l=="string")this.nodes.content.appendChild(document.createTextNode(l));else{if(!(l instanceof Node))throw Error("[CodeX Tooltip] Wrong type of «content» passed. It should be an instance of Node or String. But "+typeof l+" given.");this.nodes.content.appendChild(l)}switch(this.nodes.wrapper.classList.remove(...Object.values(this.CSS.placement)),d.placement){case"top":this.placeTop(a,d);break;case"left":this.placeLeft(a,d);break;case"right":this.placeRight(a,d);break;case"bottom":default:this.placeBottom(a,d)}d&&d.delay?this.showingTimeout=setTimeout(()=>{this.nodes.wrapper.classList.add(this.CSS.tooltipShown),this.showed=!0},d.delay):(this.nodes.wrapper.classList.add(this.CSS.tooltipShown),this.showed=!0)}hide(a=!1){if(this.hidingDelay&&!a)return this.hidingTimeout&&clearTimeout(this.hidingTimeout),void(this.hidingTimeout=setTimeout(()=>{this.hide(!0)},this.hidingDelay));this.nodes.wrapper.classList.remove(this.CSS.tooltipShown),this.showed=!1,this.showingTimeout&&clearTimeout(this.showingTimeout)}onHover(a,l,c){a.addEventListener("mouseenter",()=>{this.show(a,l,c)}),a.addEventListener("mouseleave",()=>{this.hide()})}destroy(){this.nodes.wrapper.remove(),window.removeEventListener("scroll",this.handleWindowScroll)}prepare(){this.nodes.wrapper=this.make("div",this.CSS.tooltip),this.nodes.content=this.make("div",this.CSS.tooltipContent),this.append(this.nodes.wrapper,this.nodes.content),this.append(document.body,this.nodes.wrapper)}loadStyles(){const a="codex-tooltips-style";if(document.getElementById(a))return;const l=i(2),c=this.make("style",null,{textContent:l.toString(),id:a});this.prepend(document.head,c)}placeBottom(a,l){const c=a.getBoundingClientRect(),d=c.left+a.clientWidth/2-this.nodes.wrapper.offsetWidth/2,h=c.bottom+window.pageYOffset+this.offsetTop+l.marginTop;this.applyPlacement("bottom",d,h)}placeTop(a,l){const c=a.getBoundingClientRect(),d=c.left+a.clientWidth/2-this.nodes.wrapper.offsetWidth/2,h=c.top+window.pageYOffset-this.nodes.wrapper.clientHeight-this.offsetTop;this.applyPlacement("top",d,h)}placeLeft(a,l){const c=a.getBoundingClientRect(),d=c.left-this.nodes.wrapper.offsetWidth-this.offsetLeft-l.marginLeft,h=c.top+window.pageYOffset+a.clientHeight/2-this.nodes.wrapper.offsetHeight/2;this.applyPlacement("left",d,h)}placeRight(a,l){const c=a.getBoundingClientRect(),d=c.right+this.offsetRight+l.marginRight,h=c.top+window.pageYOffset+a.clientHeight/2-this.nodes.wrapper.offsetHeight/2;this.applyPlacement("right",d,h)}applyPlacement(a,l,c){this.nodes.wrapper.classList.add(this.CSS.placement[a]),this.nodes.wrapper.style.left=l+"px",this.nodes.wrapper.style.top=c+"px"}make(a,l=null,c={}){const d=document.createElement(a);Array.isArray(l)?d.classList.add(...l):l&&d.classList.add(l);for(const h in c)c.hasOwnProperty(h)&&(d[h]=c[h]);return d}append(a,l){Array.isArray(l)?l.forEach(c=>a.appendChild(c)):a.appendChild(l)}prepend(a,l){Array.isArray(l)?(l=l.reverse()).forEach(c=>a.prepend(c)):a.prepend(l)}}},function(t,o){t.exports=`.ct{z-index:999;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-transition:opacity 50ms ease-in,-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,transform 70ms cubic-bezier(.215,.61,.355,1),-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);will-change:opacity,top,left;-webkit-box-shadow:0 8px 12px 0 rgba(29,32,43,.17),0 4px 5px -3px rgba(5,6,12,.49);box-shadow:0 8px 12px 0 rgba(29,32,43,.17),0 4px 5px -3px rgba(5,6,12,.49);border-radius:9px}.ct,.ct:before{position:absolute;top:0;left:0}.ct:before{content:"";bottom:0;right:0;background-color:#1d202b;z-index:-1;border-radius:4px}@supports(-webkit-mask-box-image:url("")){.ct:before{border-radius:0;-webkit-mask-box-image:url('data:image/svg+xml;charset=utf-8,') 48% 41% 37.9% 53.3%}}@media (--mobile){.ct{display:none}}.ct__content{padding:6px 10px;color:#cdd1e0;font-size:12px;text-align:center;letter-spacing:.02em;line-height:1em}.ct:after{content:"";width:8px;height:8px;position:absolute;background-color:#1d202b;z-index:-1}.ct--bottom{-webkit-transform:translateY(5px);transform:translateY(5px)}.ct--bottom:after{top:-3px;left:50%;-webkit-transform:translateX(-50%) rotate(-45deg);transform:translateX(-50%) rotate(-45deg)}.ct--top{-webkit-transform:translateY(-5px);transform:translateY(-5px)}.ct--top:after{top:auto;bottom:-3px;left:50%;-webkit-transform:translateX(-50%) rotate(-45deg);transform:translateX(-50%) rotate(-45deg)}.ct--left{-webkit-transform:translateX(-5px);transform:translateX(-5px)}.ct--left:after{top:50%;left:auto;right:0;-webkit-transform:translate(41.6%,-50%) rotate(-45deg);transform:translate(41.6%,-50%) rotate(-45deg)}.ct--right{-webkit-transform:translateX(5px);transform:translateX(5px)}.ct--right:after{top:50%;left:0;-webkit-transform:translate(-41.6%,-50%) rotate(-45deg);transform:translate(-41.6%,-50%) rotate(-45deg)}.ct--shown{opacity:1;-webkit-transform:none;transform:none}`}]).default})})(jo);var Li=jo.exports;const Pi=ne(Li);let $=null;function ht(){$||($=new Pi)}function Ni(n,e,t){ht(),$==null||$.show(n,e,t)}function Ne(n=!1){ht(),$==null||$.hide(n)}function Re(n,e,t){ht(),$==null||$.onHover(n,e,t)}function Ri(){$==null||$.destroy(),$=null}class Di extends E{constructor({config:e,eventsDispatcher:t}){super({config:e,eventsDispatcher:t})}get methods(){return{show:(e,t,o)=>this.show(e,t,o),hide:()=>this.hide(),onHover:(e,t,o)=>this.onHover(e,t,o)}}show(e,t,o){Ni(e,t,o)}hide(){Ne()}onHover(e,t,o){Re(e,t,o)}}class Fi extends E{get methods(){return{nodes:this.editorNodes}}get editorNodes(){return{wrapper:this.Editor.UI.nodes.wrapper,redactor:this.Editor.UI.nodes.redactor}}}function Ho(n,e){const t={};return Object.entries(n).forEach(([o,i])=>{if(N(i)){const s=e?`${e}.${o}`:o;Object.values(i).every(a=>Q(a))?t[o]=s:t[o]=Ho(i,s);return}t[o]=i}),t}const W=Ho(So);function ji(n,e){const t={};return Object.keys(n).forEach(o=>{const i=e[o];i!==void 0?t[i]=n[o]:t[o]=n[o]}),t}const $o=class _e{constructor(e,t){this.cursor=-1,this.items=[],this.items=e||[],this.focusedCssClass=t}get currentItem(){return this.cursor===-1?null:this.items[this.cursor]}setCursor(e){e=-1&&(this.dropCursor(),this.cursor=e,this.items[this.cursor].classList.add(this.focusedCssClass))}setItems(e){this.items=e}next(){this.cursor=this.leafNodesAndReturnIndex(_e.directions.RIGHT)}previous(){this.cursor=this.leafNodesAndReturnIndex(_e.directions.LEFT)}dropCursor(){this.cursor!==-1&&(this.items[this.cursor].classList.remove(this.focusedCssClass),this.cursor=-1)}leafNodesAndReturnIndex(e){if(this.items.length===0)return this.cursor;let t=this.cursor;return t===-1?t=e===_e.directions.RIGHT?-1:0:this.items[t].classList.remove(this.focusedCssClass),e===_e.directions.RIGHT?t=(t+1)%this.items.length:t=(this.items.length+t-1)%this.items.length,u.canSetCaret(this.items[t])&&Ae(()=>b.setCursor(this.items[t]),50)(),this.items[t].classList.add(this.focusedCssClass),t}};$o.directions={RIGHT:"right",LEFT:"left"};let Be=$o;class ae{constructor(e){this.iterator=null,this.activated=!1,this.flipCallbacks=[],this.onKeyDown=t=>{if(!(!this.isEventReadyForHandling(t)||t.shiftKey===!0))switch(ae.usedKeys.includes(t.keyCode)&&t.preventDefault(),t.keyCode){case y.TAB:this.handleTabPress(t);break;case y.LEFT:case y.UP:this.flipLeft();break;case y.RIGHT:case y.DOWN:this.flipRight();break;case y.ENTER:this.handleEnterPress(t);break}},this.iterator=new Be(e.items,e.focusedItemClass),this.activateCallback=e.activateCallback,this.allowedKeys=e.allowedKeys||ae.usedKeys}get isActivated(){return this.activated}static get usedKeys(){return[y.TAB,y.LEFT,y.RIGHT,y.ENTER,y.UP,y.DOWN]}activate(e,t){this.activated=!0,e&&this.iterator.setItems(e),t!==void 0&&this.iterator.setCursor(t),document.addEventListener("keydown",this.onKeyDown,!0)}deactivate(){this.activated=!1,this.dropCursor(),document.removeEventListener("keydown",this.onKeyDown)}focusFirst(){this.dropCursor(),this.flipRight()}flipLeft(){this.iterator.previous(),this.flipCallback()}flipRight(){this.iterator.next(),this.flipCallback()}hasFocus(){return!!this.iterator.currentItem}onFlip(e){this.flipCallbacks.push(e)}removeOnFlip(e){this.flipCallbacks=this.flipCallbacks.filter(t=>t!==e)}dropCursor(){this.iterator.dropCursor()}isEventReadyForHandling(e){return this.activated&&this.allowedKeys.includes(e.keyCode)}handleTabPress(e){switch(e.shiftKey?Be.directions.LEFT:Be.directions.RIGHT){case Be.directions.RIGHT:this.flipRight();break;case Be.directions.LEFT:this.flipLeft();break}}handleEnterPress(e){this.activated&&(this.iterator.currentItem&&(e.stopPropagation(),e.preventDefault(),this.iterator.currentItem.click()),M(this.activateCallback)&&this.activateCallback(this.iterator.currentItem))}flipCallback(){this.iterator.currentItem&&this.iterator.currentItem.scrollIntoViewIfNeeded(),this.flipCallbacks.forEach(e=>e())}}const Hi='',$i='',zi='',Ui='',Wi='',Yi='',Ki='',Xi='',zo='',Vi='',qi='',Uo='',Zi='',Gi='',Ji='',Qi="__",es="--";function ie(n){return(e,t)=>[[n,e].filter(i=>!!i).join(Qi),t].filter(i=>!!i).join(es)}const Ce=ie("ce-hint"),Te={root:Ce(),alignedStart:Ce(null,"align-left"),alignedCenter:Ce(null,"align-center"),title:Ce("title"),description:Ce("description")},Ma="";class ts{constructor(e){this.nodes={root:u.make("div",[Te.root,e.alignment==="center"?Te.alignedCenter:Te.alignedStart]),title:u.make("div",Te.title,{textContent:e.title})},this.nodes.root.appendChild(this.nodes.title),e.description!==void 0&&(this.nodes.description=u.make("div",Te.description,{textContent:e.description}),this.nodes.root.appendChild(this.nodes.description))}getElement(){return this.nodes.root}}class pt{constructor(e){this.params=e}get name(){if(this.params!==void 0&&"name"in this.params)return this.params.name}destroy(){Ne()}onChildrenOpen(){var e;this.params!==void 0&&"children"in this.params&&typeof((e=this.params.children)==null?void 0:e.onOpen)=="function"&&this.params.children.onOpen()}onChildrenClose(){var e;this.params!==void 0&&"children"in this.params&&typeof((e=this.params.children)==null?void 0:e.onClose)=="function"&&this.params.children.onClose()}handleClick(){var e,t;this.params!==void 0&&"onActivate"in this.params&&((t=(e=this.params).onActivate)==null||t.call(e,this.params))}addHint(e,t){const o=new ts(t);Re(e,o.getElement(),{placement:t.position,hidingDelay:100})}get children(){var e;return this.params!==void 0&&"children"in this.params&&((e=this.params.children)==null?void 0:e.items)!==void 0?this.params.children.items:[]}get hasChildren(){return this.children.length>0}get isChildrenOpen(){var e;return this.params!==void 0&&"children"in this.params&&((e=this.params.children)==null?void 0:e.isOpen)===!0}get isChildrenFlippable(){var e;return!(this.params===void 0||!("children"in this.params)||((e=this.params.children)==null?void 0:e.isFlippable)===!1)}get isChildrenSearchable(){var e;return this.params!==void 0&&"children"in this.params&&((e=this.params.children)==null?void 0:e.searchable)===!0}get closeOnActivate(){return this.params!==void 0&&"closeOnActivate"in this.params&&this.params.closeOnActivate}get isActive(){return this.params===void 0||!("isActive"in this.params)?!1:typeof this.params.isActive=="function"?this.params.isActive():this.params.isActive===!0}}const Y=ie("ce-popover-item"),L={container:Y(),active:Y(null,"active"),disabled:Y(null,"disabled"),focused:Y(null,"focused"),hidden:Y(null,"hidden"),confirmationState:Y(null,"confirmation"),noHover:Y(null,"no-hover"),noFocus:Y(null,"no-focus"),title:Y("title"),secondaryTitle:Y("secondary-title"),icon:Y("icon"),iconTool:Y("icon","tool"),iconChevronRight:Y("icon","chevron-right"),wobbleAnimation:ie("wobble")()};class le extends pt{constructor(e,t){super(e),this.params=e,this.nodes={root:null,icon:null},this.confirmationState=null,this.removeSpecialFocusBehavior=()=>{var o;(o=this.nodes.root)==null||o.classList.remove(L.noFocus)},this.removeSpecialHoverBehavior=()=>{var o;(o=this.nodes.root)==null||o.classList.remove(L.noHover)},this.onErrorAnimationEnd=()=>{var o,i;(o=this.nodes.icon)==null||o.classList.remove(L.wobbleAnimation),(i=this.nodes.icon)==null||i.removeEventListener("animationend",this.onErrorAnimationEnd)},this.nodes.root=this.make(e,t)}get isDisabled(){return this.params.isDisabled===!0}get toggle(){return this.params.toggle}get title(){return this.params.title}get isConfirmationStateEnabled(){return this.confirmationState!==null}get isFocused(){return this.nodes.root===null?!1:this.nodes.root.classList.contains(L.focused)}getElement(){return this.nodes.root}handleClick(){if(this.isConfirmationStateEnabled&&this.confirmationState!==null){this.activateOrEnableConfirmationMode(this.confirmationState);return}this.activateOrEnableConfirmationMode(this.params)}toggleActive(e){var t;(t=this.nodes.root)==null||t.classList.toggle(L.active,e)}toggleHidden(e){var t;(t=this.nodes.root)==null||t.classList.toggle(L.hidden,e)}reset(){this.isConfirmationStateEnabled&&this.disableConfirmationMode()}onFocus(){this.disableSpecialHoverAndFocusBehavior()}make(e,t){var s,r;const o=(t==null?void 0:t.wrapperTag)||"div",i=u.make(o,L.container,{type:o==="button"?"button":void 0});return e.name&&(i.dataset.itemName=e.name),this.nodes.icon=u.make("div",[L.icon,L.iconTool],{innerHTML:e.icon||Ki}),i.appendChild(this.nodes.icon),e.title!==void 0&&i.appendChild(u.make("div",L.title,{innerHTML:e.title||""})),e.secondaryLabel&&i.appendChild(u.make("div",L.secondaryTitle,{textContent:e.secondaryLabel})),this.hasChildren&&i.appendChild(u.make("div",[L.icon,L.iconChevronRight],{innerHTML:Ui})),this.isActive&&i.classList.add(L.active),e.isDisabled&&i.classList.add(L.disabled),e.hint!==void 0&&((s=t==null?void 0:t.hint)==null?void 0:s.enabled)!==!1&&this.addHint(i,{...e.hint,position:((r=t==null?void 0:t.hint)==null?void 0:r.position)||"right"}),i}enableConfirmationMode(e){if(this.nodes.root===null)return;const t={...this.params,...e,confirmation:"confirmation"in e?e.confirmation:void 0},o=this.make(t);this.nodes.root.innerHTML=o.innerHTML,this.nodes.root.classList.add(L.confirmationState),this.confirmationState=e,this.enableSpecialHoverAndFocusBehavior()}disableConfirmationMode(){if(this.nodes.root===null)return;const e=this.make(this.params);this.nodes.root.innerHTML=e.innerHTML,this.nodes.root.classList.remove(L.confirmationState),this.confirmationState=null,this.disableSpecialHoverAndFocusBehavior()}enableSpecialHoverAndFocusBehavior(){var e,t,o;(e=this.nodes.root)==null||e.classList.add(L.noHover),(t=this.nodes.root)==null||t.classList.add(L.noFocus),(o=this.nodes.root)==null||o.addEventListener("mouseleave",this.removeSpecialHoverBehavior,{once:!0})}disableSpecialHoverAndFocusBehavior(){var e;this.removeSpecialFocusBehavior(),this.removeSpecialHoverBehavior(),(e=this.nodes.root)==null||e.removeEventListener("mouseleave",this.removeSpecialHoverBehavior)}activateOrEnableConfirmationMode(e){var t;if(!("confirmation"in e)||e.confirmation===void 0)try{(t=e.onActivate)==null||t.call(e,e),this.disableConfirmationMode()}catch{this.animateError()}else this.enableConfirmationMode(e.confirmation)}animateError(){var e,t,o;(e=this.nodes.icon)!=null&&e.classList.contains(L.wobbleAnimation)||((t=this.nodes.icon)==null||t.classList.add(L.wobbleAnimation),(o=this.nodes.icon)==null||o.addEventListener("animationend",this.onErrorAnimationEnd))}}const ft=ie("ce-popover-item-separator"),gt={container:ft(),line:ft("line"),hidden:ft(null,"hidden")};class Wo extends pt{constructor(){super(),this.nodes={root:u.make("div",gt.container),line:u.make("div",gt.line)},this.nodes.root.appendChild(this.nodes.line)}getElement(){return this.nodes.root}toggleHidden(e){var t;(t=this.nodes.root)==null||t.classList.toggle(gt.hidden,e)}}var Z=(n=>(n.Closed="closed",n.ClosedOnActivate="closed-on-activate",n))(Z||{});const z=ie("ce-popover"),P={popover:z(),popoverContainer:z("container"),popoverOpenTop:z(null,"open-top"),popoverOpenLeft:z(null,"open-left"),popoverOpened:z(null,"opened"),search:z("search"),nothingFoundMessage:z("nothing-found-message"),nothingFoundMessageDisplayed:z("nothing-found-message","displayed"),items:z("items"),overlay:z("overlay"),overlayHidden:z("overlay","hidden"),popoverNested:z(null,"nested"),getPopoverNestedClass:n=>z(null,`nested-level-${n.toString()}`),popoverInline:z(null,"inline"),popoverHeader:z("header")};var me=(n=>(n.NestingLevel="--nesting-level",n.PopoverHeight="--popover-height",n.InlinePopoverWidth="--inline-popover-width",n.TriggerItemLeft="--trigger-item-left",n.TriggerItemTop="--trigger-item-top",n))(me||{});const Yo=ie("ce-popover-item-html"),Ko={root:Yo(),hidden:Yo(null,"hidden")};class Se extends pt{constructor(e,t){var o,i;super(e),this.nodes={root:u.make("div",Ko.root)},this.nodes.root.appendChild(e.element),e.name&&(this.nodes.root.dataset.itemName=e.name),e.hint!==void 0&&((o=t==null?void 0:t.hint)==null?void 0:o.enabled)!==!1&&this.addHint(this.nodes.root,{...e.hint,position:((i=t==null?void 0:t.hint)==null?void 0:i.position)||"right"})}getElement(){return this.nodes.root}toggleHidden(e){var t;(t=this.nodes.root)==null||t.classList.toggle(Ko.hidden,e)}getControls(){const e=this.nodes.root.querySelectorAll(`button, ${u.allInputsSelector}`);return Array.from(e)}}class Xo extends we{constructor(e,t={}){super(),this.params=e,this.itemsRenderParams=t,this.listeners=new Ee,this.messages={nothingFound:"Nothing found",search:"Search"},this.items=this.buildItems(e.items),e.messages&&(this.messages={...this.messages,...e.messages}),this.nodes={},this.nodes.popoverContainer=u.make("div",[P.popoverContainer]),this.nodes.nothingFoundMessage=u.make("div",[P.nothingFoundMessage],{textContent:this.messages.nothingFound}),this.nodes.popoverContainer.appendChild(this.nodes.nothingFoundMessage),this.nodes.items=u.make("div",[P.items]),this.items.forEach(o=>{const i=o.getElement();i!==null&&this.nodes.items.appendChild(i)}),this.nodes.popoverContainer.appendChild(this.nodes.items),this.listeners.on(this.nodes.popoverContainer,"click",o=>this.handleClick(o)),this.nodes.popover=u.make("div",[P.popover,this.params.class]),this.nodes.popover.appendChild(this.nodes.popoverContainer)}get itemsDefault(){return this.items.filter(e=>e instanceof le)}getElement(){return this.nodes.popover}show(){this.nodes.popover.classList.add(P.popoverOpened),this.search!==void 0&&this.search.focus()}hide(){this.nodes.popover.classList.remove(P.popoverOpened),this.nodes.popover.classList.remove(P.popoverOpenTop),this.itemsDefault.forEach(e=>e.reset()),this.search!==void 0&&this.search.clear(),this.emit(Z.Closed)}destroy(){var e;this.items.forEach(t=>t.destroy()),this.nodes.popover.remove(),this.listeners.removeAll(),(e=this.search)==null||e.destroy()}activateItemByName(e){const t=this.items.find(o=>o.name===e);this.handleItemClick(t)}buildItems(e){return e.map(t=>{switch(t.type){case _.Separator:return new Wo;case _.Html:return new Se(t,this.itemsRenderParams[_.Html]);default:return new le(t,this.itemsRenderParams[_.Default])}})}getTargetItem(e){return this.items.filter(t=>t instanceof le||t instanceof Se).find(t=>{const o=t.getElement();return o===null?!1:e.composedPath().includes(o)})}handleItemClick(e){if(!("isDisabled"in e&&e.isDisabled)){if(e.hasChildren){this.showNestedItems(e),"handleClick"in e&&typeof e.handleClick=="function"&&e.handleClick();return}this.itemsDefault.filter(t=>t!==e).forEach(t=>t.reset()),"handleClick"in e&&typeof e.handleClick=="function"&&e.handleClick(),this.toggleItemActivenessIfNeeded(e),e.closeOnActivate&&(this.hide(),this.emit(Z.ClosedOnActivate))}}handleClick(e){const t=this.getTargetItem(e);t!==void 0&&this.handleItemClick(t)}toggleItemActivenessIfNeeded(e){if(e instanceof le&&(e.toggle===!0&&e.toggleActive(),typeof e.toggle=="string")){const t=this.itemsDefault.filter(o=>o.toggle===e.toggle);if(t.length===1){e.toggleActive();return}t.forEach(o=>{o.toggleActive(o===e)})}}}var De=(n=>(n.Search="search",n))(De||{});const mt=ie("cdx-search-field"),bt={wrapper:mt(),icon:mt("icon"),input:mt("input")};class os extends we{constructor({items:e,placeholder:t}){super(),this.listeners=new Ee,this.items=e,this.wrapper=u.make("div",bt.wrapper);const o=u.make("div",bt.icon,{innerHTML:Zi});this.input=u.make("input",bt.input,{placeholder:t,tabIndex:-1}),this.wrapper.appendChild(o),this.wrapper.appendChild(this.input),this.listeners.on(this.input,"input",()=>{this.searchQuery=this.input.value,this.emit(De.Search,{query:this.searchQuery,items:this.foundItems})})}getElement(){return this.wrapper}focus(){this.input.focus()}clear(){this.input.value="",this.searchQuery="",this.emit(De.Search,{query:"",items:this.foundItems})}destroy(){this.listeners.removeAll()}get foundItems(){return this.items.filter(e=>this.checkItem(e))}checkItem(e){var i,s;const t=((i=e.title)==null?void 0:i.toLowerCase())||"",o=(s=this.searchQuery)==null?void 0:s.toLowerCase();return o!==void 0?t.includes(o):!1}}var ns=Object.defineProperty,is=Object.getOwnPropertyDescriptor,ss=(n,e,t,o)=>{for(var i=o>1?void 0:o?is(e,t):e,s=n.length-1,r;s>=0;s--)(r=n[s])&&(i=(o?r(e,t,i):r(i))||i);return o&&i&&ns(e,t,i),i};const Vo=class zn extends Xo{constructor(e,t){super(e,t),this.nestingLevel=0,this.nestedPopoverTriggerItem=null,this.previouslyHoveredItem=null,this.scopeElement=document.body,this.hide=()=>{var o;super.hide(),this.destroyNestedPopoverIfExists(),(o=this.flipper)==null||o.deactivate(),this.previouslyHoveredItem=null},this.onFlip=()=>{const o=this.itemsDefault.find(i=>i.isFocused);o==null||o.onFocus()},this.onSearch=o=>{var a;const i=o.query==="",s=o.items.length===0;this.items.forEach(l=>{let c=!1;l instanceof le?c=!o.items.includes(l):(l instanceof Wo||l instanceof Se)&&(c=s||!i),l.toggleHidden(c)}),this.toggleNothingFoundMessage(s);const r=o.query===""?this.flippableElements:o.items.map(l=>l.getElement());(a=this.flipper)!=null&&a.isActivated&&(this.flipper.deactivate(),this.flipper.activate(r))},e.nestingLevel!==void 0&&(this.nestingLevel=e.nestingLevel),this.nestingLevel>0&&this.nodes.popover.classList.add(P.popoverNested),e.scopeElement!==void 0&&(this.scopeElement=e.scopeElement),this.nodes.popoverContainer!==null&&this.listeners.on(this.nodes.popoverContainer,"mouseover",o=>this.handleHover(o)),e.searchable&&this.addSearch(),e.flippable!==!1&&(this.flipper=new ae({items:this.flippableElements,focusedItemClass:L.focused,allowedKeys:[y.TAB,y.UP,y.DOWN,y.ENTER]}),this.flipper.onFlip(this.onFlip))}hasFocus(){return this.flipper===void 0?!1:this.flipper.hasFocus()}get scrollTop(){return this.nodes.items===null?0:this.nodes.items.scrollTop}get offsetTop(){return this.nodes.popoverContainer===null?0:this.nodes.popoverContainer.offsetTop}show(){var e;this.nodes.popover.style.setProperty(me.PopoverHeight,this.size.height+"px"),this.shouldOpenBottom||this.nodes.popover.classList.add(P.popoverOpenTop),this.shouldOpenRight||this.nodes.popover.classList.add(P.popoverOpenLeft),super.show(),(e=this.flipper)==null||e.activate(this.flippableElements)}destroy(){this.hide(),super.destroy()}showNestedItems(e){this.nestedPopover!==null&&this.nestedPopover!==void 0||(this.nestedPopoverTriggerItem=e,this.showNestedPopoverForItem(e))}handleHover(e){const t=this.getTargetItem(e);t!==void 0&&this.previouslyHoveredItem!==t&&(this.destroyNestedPopoverIfExists(),this.previouslyHoveredItem=t,t.hasChildren&&this.showNestedPopoverForItem(t))}setTriggerItemPosition(e,t){const o=t.getElement(),i=(o?o.offsetTop:0)-this.scrollTop,s=this.offsetTop+i;e.style.setProperty(me.TriggerItemTop,s+"px")}destroyNestedPopoverIfExists(){var e,t;this.nestedPopover===void 0||this.nestedPopover===null||(this.nestedPopover.off(Z.ClosedOnActivate,this.hide),this.nestedPopover.hide(),this.nestedPopover.destroy(),this.nestedPopover.getElement().remove(),this.nestedPopover=null,(e=this.flipper)==null||e.activate(this.flippableElements),(t=this.nestedPopoverTriggerItem)==null||t.onChildrenClose())}showNestedPopoverForItem(e){var o;this.nestedPopover=new zn({searchable:e.isChildrenSearchable,items:e.children,nestingLevel:this.nestingLevel+1,flippable:e.isChildrenFlippable,messages:this.messages}),e.onChildrenOpen(),this.nestedPopover.on(Z.ClosedOnActivate,this.hide);const t=this.nestedPopover.getElement();return this.nodes.popover.appendChild(t),this.setTriggerItemPosition(t,e),t.style.setProperty(me.NestingLevel,this.nestedPopover.nestingLevel.toString()),this.nestedPopover.show(),(o=this.flipper)==null||o.deactivate(),this.nestedPopover}get shouldOpenBottom(){if(this.nodes.popover===void 0||this.nodes.popover===null)return!1;const e=this.nodes.popoverContainer.getBoundingClientRect(),t=this.scopeElement.getBoundingClientRect(),o=this.size.height,i=e.top+o,s=e.top-o,r=Math.min(window.innerHeight,t.bottom);return s{if(t instanceof le)return t.getElement();if(t instanceof Se)return t.getControls()}).flat().filter(t=>t!=null)}addSearch(){this.search=new os({items:this.itemsDefault,placeholder:this.messages.search}),this.search.on(De.Search,this.onSearch);const e=this.search.getElement();e.classList.add(P.search),this.nodes.popoverContainer.insertBefore(e,this.nodes.popoverContainer.firstChild)}toggleNothingFoundMessage(e){this.nodes.nothingFoundMessage.classList.toggle(P.nothingFoundMessageDisplayed,e)}};ss([fe],Vo.prototype,"size",1);let vt=Vo;class rs extends vt{constructor(e){const t=!ge();super({...e,class:P.popoverInline},{[_.Default]:{wrapperTag:"button",hint:{position:"top",alignment:"center",enabled:t}},[_.Html]:{hint:{position:"top",alignment:"center",enabled:t}}}),this.items.forEach(o=>{!(o instanceof le)&&!(o instanceof Se)||o.hasChildren&&o.isChildrenOpen&&this.showNestedItems(o)})}get offsetLeft(){return this.nodes.popoverContainer===null?0:this.nodes.popoverContainer.offsetLeft}show(){this.nestingLevel===0&&this.nodes.popover.style.setProperty(me.InlinePopoverWidth,this.size.width+"px"),super.show()}handleHover(){}setTriggerItemPosition(e,t){const o=t.getElement(),i=o?o.offsetLeft:0,s=this.offsetLeft+i;e.style.setProperty(me.TriggerItemLeft,s+"px")}showNestedItems(e){if(this.nestedPopoverTriggerItem===e){this.destroyNestedPopoverIfExists(),this.nestedPopoverTriggerItem=null;return}super.showNestedItems(e)}showNestedPopoverForItem(e){const t=super.showNestedPopoverForItem(e);return t.getElement().classList.add(P.getPopoverNestedClass(t.nestingLevel)),t}handleItemClick(e){var t;e!==this.nestedPopoverTriggerItem&&((t=this.nestedPopoverTriggerItem)==null||t.handleClick(),super.destroyNestedPopoverIfExists()),super.handleItemClick(e)}}const qo=class Me{constructor(){this.scrollPosition=null}lock(){rt?this.lockHard():document.body.classList.add(Me.CSS.scrollLocked)}unlock(){rt?this.unlockHard():document.body.classList.remove(Me.CSS.scrollLocked)}lockHard(){this.scrollPosition=window.pageYOffset,document.documentElement.style.setProperty("--window-scroll-offset",`${this.scrollPosition}px`),document.body.classList.add(Me.CSS.scrollLockedHard)}unlockHard(){document.body.classList.remove(Me.CSS.scrollLockedHard),this.scrollPosition!==null&&window.scrollTo(0,this.scrollPosition),this.scrollPosition=null}};qo.CSS={scrollLocked:"ce-scroll-locked",scrollLockedHard:"ce-scroll-locked--hard"};let as=qo;const kt=ie("ce-popover-header"),yt={root:kt(),text:kt("text"),backButton:kt("back-button")};class ls{constructor({text:e,onBackButtonClick:t}){this.listeners=new Ee,this.text=e,this.onBackButtonClick=t,this.nodes={root:u.make("div",[yt.root]),backButton:u.make("button",[yt.backButton]),text:u.make("div",[yt.text])},this.nodes.backButton.innerHTML=zi,this.nodes.root.appendChild(this.nodes.backButton),this.listeners.on(this.nodes.backButton,"click",this.onBackButtonClick),this.nodes.text.innerText=this.text,this.nodes.root.appendChild(this.nodes.text)}getElement(){return this.nodes.root}destroy(){this.nodes.root.remove(),this.listeners.destroy()}}class cs{constructor(){this.history=[]}push(e){this.history.push(e)}pop(){return this.history.pop()}get currentTitle(){return this.history.length===0?"":this.history[this.history.length-1].title}get currentItems(){return this.history.length===0?[]:this.history[this.history.length-1].items}reset(){for(;this.history.length>1;)this.pop()}}class Zo extends Xo{constructor(e){super(e,{[_.Default]:{hint:{enabled:!1}},[_.Html]:{hint:{enabled:!1}}}),this.scrollLocker=new as,this.history=new cs,this.isHidden=!0,this.nodes.overlay=u.make("div",[P.overlay,P.overlayHidden]),this.nodes.popover.insertBefore(this.nodes.overlay,this.nodes.popover.firstChild),this.listeners.on(this.nodes.overlay,"click",()=>{this.hide()}),this.history.push({items:e.items})}show(){this.nodes.overlay.classList.remove(P.overlayHidden),super.show(),this.scrollLocker.lock(),this.isHidden=!1}hide(){this.isHidden||(super.hide(),this.nodes.overlay.classList.add(P.overlayHidden),this.scrollLocker.unlock(),this.history.reset(),this.isHidden=!0)}destroy(){super.destroy(),this.scrollLocker.unlock()}showNestedItems(e){this.updateItemsAndHeader(e.children,e.title),this.history.push({title:e.title,items:e.children})}updateItemsAndHeader(e,t){if(this.header!==null&&this.header!==void 0&&(this.header.destroy(),this.header=null),t!==void 0){this.header=new ls({text:t,onBackButtonClick:()=>{this.history.pop(),this.updateItemsAndHeader(this.history.currentItems,this.history.currentTitle)}});const o=this.header.getElement();o!==null&&this.nodes.popoverContainer.insertBefore(o,this.nodes.popoverContainer.firstChild)}this.items.forEach(o=>{var i;return(i=o.getElement())==null?void 0:i.remove()}),this.items=this.buildItems(e),this.items.forEach(o=>{var s;const i=o.getElement();i!==null&&((s=this.nodes.items)==null||s.appendChild(i))})}}class ds extends E{constructor(){super(...arguments),this.opened=!1,this.hasMobileLayoutToggleListener=!1,this.selection=new b,this.popover=null,this.close=()=>{this.opened&&(this.opened=!1,b.isAtEditor||this.selection.restore(),this.selection.clearSaved(),!this.Editor.CrossBlockSelection.isCrossBlockSelectionStarted&&this.Editor.BlockManager.currentBlock&&this.Editor.BlockSelection.unselectBlock(this.Editor.BlockManager.currentBlock),this.eventsDispatcher.emit(this.events.closed),this.popover&&(this.popover.off(Z.Closed,this.onPopoverClose),this.popover.destroy(),this.popover.getElement().remove(),this.popover=null))},this.onPopoverClose=()=>{this.close()}}get events(){return{opened:"block-settings-opened",closed:"block-settings-closed"}}get CSS(){return{settings:"ce-settings"}}get flipper(){var e;if(this.popover!==null)return"flipper"in this.popover?(e=this.popover)==null?void 0:e.flipper:void 0}make(){this.nodes.wrapper=u.make("div",[this.CSS.settings]),this.eventsDispatcher.on(xe,this.close),this.hasMobileLayoutToggleListener=!0}destroy(){this.removeAllNodes(),this.listeners.destroy(),this.hasMobileLayoutToggleListener&&(this.eventsDispatcher.off(xe,this.close),this.hasMobileLayoutToggleListener=!1)}async open(e=this.Editor.BlockManager.currentBlock){var s;this.opened=!0,this.selection.save(),this.Editor.BlockSelection.selectBlock(e),this.Editor.BlockSelection.clearCache();const{toolTunes:t,commonTunes:o}=e.getTunes();this.eventsDispatcher.emit(this.events.opened);const i=ge()?Zo:vt;this.popover=new i({searchable:!0,items:await this.getTunesItems(e,o,t),scopeElement:this.Editor.API.methods.ui.nodes.redactor,messages:{nothingFound:H.ui(W.ui.popover,"Nothing found"),search:H.ui(W.ui.popover,"Filter")}}),this.popover.on(Z.Closed,this.onPopoverClose),(s=this.nodes.wrapper)==null||s.append(this.popover.getElement()),this.popover.show()}getElement(){return this.nodes.wrapper}async getTunesItems(e,t,o){const i=[];o!==void 0&&o.length>0&&(i.push(...o),i.push({type:_.Separator}));const s=Array.from(this.Editor.Tools.blockTools.values()),a=(await Po(e,s)).reduce((l,c)=>(c.toolbox.forEach(d=>{l.push({icon:d.icon,title:H.t(W.toolNames,d.title),name:c.name,closeOnActivate:!0,onActivate:async()=>{const{BlockManager:h,Caret:p,Toolbar:g}=this.Editor,f=await h.convert(e,c.name,d.data);g.close(),p.setToBlock(f,p.positions.END)}})}),l),[]);return a.length>0&&(i.push({icon:Uo,name:"convert-to",title:H.ui(W.ui.popover,"Convert to"),children:{searchable:!0,items:a}}),i.push({type:_.Separator})),i.push(...t),i.map(l=>this.resolveTuneAliases(l))}resolveTuneAliases(e){if(e.type===_.Separator||e.type===_.Html)return e;const t=ji(e,{label:"title"});return e.confirmation&&(t.confirmation=this.resolveTuneAliases(e.confirmation)),t}}var Go={exports:{}};/*! + * Library for handling keyboard shortcuts + * @copyright CodeX (https://codex.so) + * @license MIT + * @author CodeX (https://codex.so) + * @version 1.2.0 + */(function(n,e){(function(t,o){n.exports=o()})(window,function(){return function(t){var o={};function i(s){if(o[s])return o[s].exports;var r=o[s]={i:s,l:!1,exports:{}};return t[s].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=t,i.c=o,i.d=function(s,r,a){i.o(s,r)||Object.defineProperty(s,r,{enumerable:!0,get:a})},i.r=function(s){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})},i.t=function(s,r){if(1&r&&(s=i(s)),8&r||4&r&&typeof s=="object"&&s&&s.__esModule)return s;var a=Object.create(null);if(i.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:s}),2&r&&typeof s!="string")for(var l in s)i.d(a,l,(function(c){return s[c]}).bind(null,l));return a},i.n=function(s){var r=s&&s.__esModule?function(){return s.default}:function(){return s};return i.d(r,"a",r),r},i.o=function(s,r){return Object.prototype.hasOwnProperty.call(s,r)},i.p="",i(i.s=0)}([function(t,o,i){function s(l,c){for(var d=0;dr!==o);if(s.length===0){this.registeredShortcuts.delete(e);return}this.registeredShortcuts.set(e,s)}findShortcut(e,t){return(this.registeredShortcuts.get(e)||[]).find(({name:i})=>i===t)}}const be=new ps;var fs=Object.defineProperty,gs=Object.getOwnPropertyDescriptor,Jo=(n,e,t,o)=>{for(var i=o>1?void 0:o?gs(e,t):e,s=n.length-1,r;s>=0;s--)(r=n[s])&&(i=(o?r(e,t,i):r(i))||i);return o&&i&&fs(e,t,i),i},Fe=(n=>(n.Opened="toolbox-opened",n.Closed="toolbox-closed",n.BlockAdded="toolbox-block-added",n))(Fe||{});const wt=class Un extends we{constructor({api:e,tools:t,i18nLabels:o}){super(),this.opened=!1,this.listeners=new Ee,this.popover=null,this.handleMobileLayoutToggle=()=>{this.destroyPopover(),this.initPopover()},this.onPopoverClose=()=>{this.opened=!1,this.emit("toolbox-closed")},this.api=e,this.tools=t,this.i18nLabels=o,this.enableShortcuts(),this.nodes={toolbox:u.make("div",Un.CSS.toolbox)},this.initPopover(),this.api.events.on(xe,this.handleMobileLayoutToggle)}get isEmpty(){return this.toolsToBeDisplayed.length===0}static get CSS(){return{toolbox:"ce-toolbox"}}getElement(){return this.nodes.toolbox}hasFocus(){if(this.popover!==null)return"hasFocus"in this.popover?this.popover.hasFocus():void 0}destroy(){var e;super.destroy(),this.nodes&&this.nodes.toolbox&&this.nodes.toolbox.remove(),this.removeAllShortcuts(),(e=this.popover)==null||e.off(Z.Closed,this.onPopoverClose),this.listeners.destroy(),this.api.events.off(xe,this.handleMobileLayoutToggle)}toolButtonActivated(e,t){this.insertNewBlock(e,t)}open(){var e;this.isEmpty||((e=this.popover)==null||e.show(),this.opened=!0,this.emit("toolbox-opened"))}close(){var e;(e=this.popover)==null||e.hide(),this.opened=!1,this.emit("toolbox-closed")}toggle(){this.opened?this.close():this.open()}initPopover(){var t;const e=ge()?Zo:vt;this.popover=new e({scopeElement:this.api.ui.nodes.redactor,searchable:!0,messages:{nothingFound:this.i18nLabels.nothingFound,search:this.i18nLabels.filter},items:this.toolboxItemsToBeDisplayed}),this.popover.on(Z.Closed,this.onPopoverClose),(t=this.nodes.toolbox)==null||t.append(this.popover.getElement())}destroyPopover(){this.popover!==null&&(this.popover.hide(),this.popover.off(Z.Closed,this.onPopoverClose),this.popover.destroy(),this.popover=null),this.nodes.toolbox!==null&&(this.nodes.toolbox.innerHTML="")}get toolsToBeDisplayed(){const e=[];return this.tools.forEach(t=>{t.toolbox&&e.push(t)}),e}get toolboxItemsToBeDisplayed(){const e=(t,o,i=!0)=>({icon:t.icon,title:H.t(W.toolNames,t.title||Le(o.name)),name:o.name,onActivate:()=>{this.toolButtonActivated(o.name,t.data)},secondaryLabel:o.shortcut&&i?it(o.shortcut):""});return this.toolsToBeDisplayed.reduce((t,o)=>(Array.isArray(o.toolbox)?o.toolbox.forEach((i,s)=>{t.push(e(i,o,s===0))}):o.toolbox!==void 0&&t.push(e(o.toolbox,o)),t),[])}enableShortcuts(){this.toolsToBeDisplayed.forEach(e=>{const t=e.shortcut;t&&this.enableShortcutForTool(e.name,t)})}enableShortcutForTool(e,t){be.add({name:t,on:this.api.ui.nodes.redactor,handler:async o=>{o.preventDefault();const i=this.api.blocks.getCurrentBlockIndex(),s=this.api.blocks.getBlockByIndex(i);if(s)try{const r=await this.api.blocks.convert(s.id,e);this.api.caret.setToBlock(r,"end");return}catch{}this.insertNewBlock(e)}})}removeAllShortcuts(){this.toolsToBeDisplayed.forEach(e=>{const t=e.shortcut;t&&be.remove(this.api.ui.nodes.redactor,t)})}async insertNewBlock(e,t){const o=this.api.blocks.getCurrentBlockIndex(),i=this.api.blocks.getBlockByIndex(o);if(!i)return;const s=i.isEmpty?o:o+1;let r;if(t){const l=await this.api.blocks.composeBlockData(e);r=Object.assign(l,t)}const a=this.api.blocks.insert(e,r,void 0,s,void 0,i.isEmpty);a.call(te.APPEND_CALLBACK),this.api.caret.setToBlock(s),this.emit("toolbox-block-added",{block:a}),this.api.toolbar.close()}};Jo([fe],wt.prototype,"toolsToBeDisplayed",1),Jo([fe],wt.prototype,"toolboxItemsToBeDisplayed",1);let ms=wt;const Qo="block hovered";async function bs(n,e){const t=navigator.keyboard;if(!t)return e;try{return(await t.getLayoutMap()).get(n)||e}catch(o){return console.error(o),e}}class vs extends E{constructor({config:e,eventsDispatcher:t}){super({config:e,eventsDispatcher:t}),this.toolboxInstance=null}get CSS(){return{toolbar:"ce-toolbar",content:"ce-toolbar__content",actions:"ce-toolbar__actions",actionsOpened:"ce-toolbar__actions--opened",toolbarOpened:"ce-toolbar--opened",openedToolboxHolderModifier:"codex-editor--toolbox-opened",plusButton:"ce-toolbar__plus",plusButtonShortcut:"ce-toolbar__plus-shortcut",settingsToggler:"ce-toolbar__settings-btn",settingsTogglerHidden:"ce-toolbar__settings-btn--hidden"}}get opened(){return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened)}get toolbox(){var e;return{opened:(e=this.toolboxInstance)==null?void 0:e.opened,close:()=>{var t;(t=this.toolboxInstance)==null||t.close()},open:()=>{if(this.toolboxInstance===null){T("toolbox.open() called before initialization is finished","warn");return}this.Editor.BlockManager.currentBlock=this.hoveredBlock,this.toolboxInstance.open()},toggle:()=>{if(this.toolboxInstance===null){T("toolbox.toggle() called before initialization is finished","warn");return}this.toolboxInstance.toggle()},hasFocus:()=>{var t;return(t=this.toolboxInstance)==null?void 0:t.hasFocus()}}}get blockActions(){return{hide:()=>{this.nodes.actions.classList.remove(this.CSS.actionsOpened)},show:()=>{this.nodes.actions.classList.add(this.CSS.actionsOpened)}}}get blockTunesToggler(){return{hide:()=>this.nodes.settingsToggler.classList.add(this.CSS.settingsTogglerHidden),show:()=>this.nodes.settingsToggler.classList.remove(this.CSS.settingsTogglerHidden)}}toggleReadOnly(e){e?(this.destroy(),this.Editor.BlockSettings.destroy(),this.disableModuleBindings()):window.requestIdleCallback(()=>{this.drawUI(),this.enableModuleBindings()},{timeout:2e3})}moveAndOpen(e=this.Editor.BlockManager.currentBlock){if(this.toolboxInstance===null){T("Can't open Toolbar since Editor initialization is not finished yet","warn");return}if(this.toolboxInstance.opened&&this.toolboxInstance.close(),this.Editor.BlockSettings.opened&&this.Editor.BlockSettings.close(),!e)return;this.hoveredBlock=e;const t=e.holder,{isMobile:o}=this.Editor.UI;let i;const s=20,r=e.firstInput,a=t.getBoundingClientRect(),l=r!==void 0?r.getBoundingClientRect():null,c=l!==null?l.top-a.top:null,d=c!==null?c>s:void 0;if(o)i=t.offsetTop+t.offsetHeight;else if(r===void 0||d){const h=parseInt(window.getComputedStyle(e.pluginsContent).paddingTop);i=t.offsetTop+h}else{const h=ri(r),p=parseInt(window.getComputedStyle(this.nodes.plusButton).height,10),g=8;i=t.offsetTop+h-p+g+c}this.nodes.wrapper.style.top=`${Math.floor(i)}px`,this.Editor.BlockManager.blocks.length===1&&e.isEmpty?this.blockTunesToggler.hide():this.blockTunesToggler.show(),this.open()}close(){var e,t;this.Editor.ReadOnly.isEnabled||((e=this.nodes.wrapper)==null||e.classList.remove(this.CSS.toolbarOpened),this.blockActions.hide(),(t=this.toolboxInstance)==null||t.close(),this.Editor.BlockSettings.close(),this.reset())}reset(){this.nodes.wrapper.style.top="unset"}open(e=!0){this.nodes.wrapper.classList.add(this.CSS.toolbarOpened),e?this.blockActions.show():this.blockActions.hide()}async make(){this.nodes.wrapper=u.make("div",this.CSS.toolbar),["content","actions"].forEach(s=>{this.nodes[s]=u.make("div",this.CSS[s])}),u.append(this.nodes.wrapper,this.nodes.content),u.append(this.nodes.content,this.nodes.actions),this.nodes.plusButton=u.make("div",this.CSS.plusButton,{innerHTML:qi}),u.append(this.nodes.actions,this.nodes.plusButton),this.readOnlyMutableListeners.on(this.nodes.plusButton,"click",()=>{Ne(!0),this.plusButtonClicked()},!1);const e=u.make("div");e.appendChild(document.createTextNode(H.ui(W.ui.toolbar.toolbox,"Add"))),e.appendChild(u.make("div",this.CSS.plusButtonShortcut,{textContent:"/"})),Re(this.nodes.plusButton,e,{hidingDelay:400}),this.nodes.settingsToggler=u.make("span",this.CSS.settingsToggler,{innerHTML:Vi}),u.append(this.nodes.actions,this.nodes.settingsToggler);const t=u.make("div"),o=u.text(H.ui(W.ui.blockTunes.toggler,"Click to tune")),i=await bs("Slash","/");t.appendChild(o),t.appendChild(u.make("div",this.CSS.plusButtonShortcut,{textContent:it(`CMD + ${i}`)})),Re(this.nodes.settingsToggler,t,{hidingDelay:400}),u.append(this.nodes.actions,this.makeToolbox()),u.append(this.nodes.actions,this.Editor.BlockSettings.getElement()),u.append(this.Editor.UI.nodes.wrapper,this.nodes.wrapper)}makeToolbox(){return this.toolboxInstance=new ms({api:this.Editor.API.methods,tools:this.Editor.Tools.blockTools,i18nLabels:{filter:H.ui(W.ui.popover,"Filter"),nothingFound:H.ui(W.ui.popover,"Nothing found")}}),this.toolboxInstance.on(Fe.Opened,()=>{this.Editor.UI.nodes.wrapper.classList.add(this.CSS.openedToolboxHolderModifier)}),this.toolboxInstance.on(Fe.Closed,()=>{this.Editor.UI.nodes.wrapper.classList.remove(this.CSS.openedToolboxHolderModifier)}),this.toolboxInstance.on(Fe.BlockAdded,({block:e})=>{const{BlockManager:t,Caret:o}=this.Editor,i=t.getBlockById(e.id);i.inputs.length===0&&(i===t.lastBlock?(t.insertAtEnd(),o.setToBlock(t.lastBlock)):o.setToBlock(t.nextBlock))}),this.toolboxInstance.getElement()}plusButtonClicked(){var e;this.Editor.BlockManager.currentBlock=this.hoveredBlock,(e=this.toolboxInstance)==null||e.toggle()}enableModuleBindings(){this.readOnlyMutableListeners.on(this.nodes.settingsToggler,"mousedown",e=>{var t;e.stopPropagation(),this.settingsTogglerClicked(),(t=this.toolboxInstance)!=null&&t.opened&&this.toolboxInstance.close(),Ne(!0)},!0),ge()||this.eventsDispatcher.on(Qo,e=>{var t;this.Editor.BlockSettings.opened||(t=this.toolboxInstance)!=null&&t.opened||this.moveAndOpen(e.block)})}disableModuleBindings(){this.readOnlyMutableListeners.clearAll()}settingsTogglerClicked(){this.Editor.BlockManager.currentBlock=this.hoveredBlock,this.Editor.BlockSettings.opened?this.Editor.BlockSettings.close():this.Editor.BlockSettings.open(this.hoveredBlock)}drawUI(){this.Editor.BlockSettings.make(),this.make()}destroy(){this.removeAllNodes(),this.toolboxInstance&&this.toolboxInstance.destroy()}}var ce=(n=>(n[n.Block=0]="Block",n[n.Inline=1]="Inline",n[n.Tune=2]="Tune",n))(ce||{}),je=(n=>(n.Shortcut="shortcut",n.Toolbox="toolbox",n.EnabledInlineTools="inlineToolbar",n.EnabledBlockTunes="tunes",n.Config="config",n))(je||{}),en=(n=>(n.Shortcut="shortcut",n.SanitizeConfig="sanitize",n))(en||{}),ve=(n=>(n.IsEnabledLineBreaks="enableLineBreaks",n.Toolbox="toolbox",n.ConversionConfig="conversionConfig",n.IsReadOnlySupported="isReadOnlySupported",n.PasteConfig="pasteConfig",n))(ve||{}),He=(n=>(n.IsInline="isInline",n.Title="title",n.IsReadOnlySupported="isReadOnlySupported",n))(He||{}),Et=(n=>(n.IsTune="isTune",n))(Et||{});class xt{constructor({name:e,constructable:t,config:o,api:i,isDefault:s,isInternal:r=!1,defaultPlaceholder:a}){this.api=i,this.name=e,this.constructable=t,this.config=o,this.isDefault=s,this.isInternal=r,this.defaultPlaceholder=a}get settings(){const e=this.config.config||{};return this.isDefault&&!("placeholder"in e)&&this.defaultPlaceholder&&(e.placeholder=this.defaultPlaceholder),e}reset(){if(M(this.constructable.reset))return this.constructable.reset()}prepare(){if(M(this.constructable.prepare))return this.constructable.prepare({toolName:this.name,config:this.settings})}get shortcut(){const e=this.constructable.shortcut;return this.config.shortcut||e}get sanitizeConfig(){return this.constructable.sanitize||{}}isInline(){return this.type===ce.Inline}isBlock(){return this.type===ce.Block}isTune(){return this.type===ce.Tune}}class ks extends E{constructor({config:e,eventsDispatcher:t}){super({config:e,eventsDispatcher:t}),this.CSS={inlineToolbar:"ce-inline-toolbar"},this.opened=!1,this.popover=null,this.toolbarVerticalMargin=ge()?20:6,this.tools=new Map,window.requestIdleCallback(()=>{this.make()},{timeout:2e3})}async tryToShow(e=!1){e&&this.close(),this.allowedToShow()&&(await this.open(),this.Editor.Toolbar.close())}close(){var e,t;if(this.opened){for(const[o,i]of this.tools){const s=this.getToolShortcut(o.name);s!==void 0&&be.remove(this.Editor.UI.nodes.redactor,s),M(i.clear)&&i.clear()}this.tools=new Map,this.reset(),this.opened=!1,(e=this.popover)==null||e.hide(),(t=this.popover)==null||t.destroy(),this.popover=null}}containsNode(e){return this.nodes.wrapper===void 0?!1:this.nodes.wrapper.contains(e)}destroy(){var e;this.removeAllNodes(),(e=this.popover)==null||e.destroy(),this.popover=null}make(){this.nodes.wrapper=u.make("div",[this.CSS.inlineToolbar,...this.isRtl?[this.Editor.UI.CSS.editorRtlFix]:[]]),u.append(this.Editor.UI.nodes.wrapper,this.nodes.wrapper)}async open(){var t;if(this.opened)return;this.opened=!0,this.popover!==null&&this.popover.destroy(),this.createToolsInstances();const e=await this.getPopoverItems();this.popover=new rs({items:e,scopeElement:this.Editor.API.methods.ui.nodes.redactor,messages:{nothingFound:H.ui(W.ui.popover,"Nothing found"),search:H.ui(W.ui.popover,"Filter")}}),this.move(this.popover.size.width),(t=this.nodes.wrapper)==null||t.append(this.popover.getElement()),this.popover.show()}move(e){const t=b.rect,o=this.Editor.UI.nodes.wrapper.getBoundingClientRect(),i={x:t.x-o.x,y:t.y+t.height-o.top+this.toolbarVerticalMargin};i.x+e+o.x>this.Editor.UI.contentRect.right&&(i.x=this.Editor.UI.contentRect.right-e-o.x),this.nodes.wrapper.style.left=Math.floor(i.x)+"px",this.nodes.wrapper.style.top=Math.floor(i.y)+"px"}reset(){this.nodes.wrapper.style.left="0",this.nodes.wrapper.style.top="0"}allowedToShow(){const e=["IMG","INPUT"],t=b.get(),o=b.text;if(!t||!t.anchorNode||t.isCollapsed||o.length<1)return!1;const i=u.isElement(t.anchorNode)?t.anchorNode:t.anchorNode.parentElement;if(i===null||t!==null&&e.includes(i.tagName))return!1;const s=this.Editor.BlockManager.getBlock(t.anchorNode);return!s||this.getTools().some(c=>s.tool.inlineTools.has(c.name))===!1?!1:i.closest("[contenteditable]")!==null}getTools(){const e=this.Editor.BlockManager.currentBlock;return e?Array.from(e.tool.inlineTools.values()).filter(o=>!(this.Editor.ReadOnly.isEnabled&&o.isReadOnlySupported!==!0)):[]}createToolsInstances(){this.tools=new Map,this.getTools().forEach(t=>{const o=t.create();this.tools.set(t,o)})}async getPopoverItems(){const e=[];let t=0;for(const[o,i]of this.tools){const s=await i.render(),r=this.getToolShortcut(o.name);if(r!==void 0)try{this.enableShortcuts(o.name,r)}catch{}const a=r!==void 0?it(r):void 0,l=H.t(W.toolNames,o.title||Le(o.name));[s].flat().forEach(c=>{var h,p;const d={name:o.name,onActivate:()=>{this.toolClicked(i)},hint:{title:l,description:a}};if(u.isElement(c)){const g={...d,element:c,type:_.Html};if(M(i.renderActions)){const f=i.renderActions();g.children={isOpen:(h=i.checkState)==null?void 0:h.call(i,b.get()),isFlippable:!1,items:[{type:_.Html,element:f}]}}else(p=i.checkState)==null||p.call(i,b.get());e.push(g)}else if(c.type===_.Html)e.push({...d,...c,type:_.Html});else if(c.type===_.Separator)e.push({type:_.Separator});else{const g={...d,...c,type:_.Default};"children"in g&&t!==0&&e.push({type:_.Separator}),e.push(g),"children"in g&&t{var s;const{currentBlock:i}=this.Editor.BlockManager;i&&i.tool.enabledInlineTools&&(o.preventDefault(),(s=this.popover)==null||s.activateItemByName(e))},on:document})}toolClicked(e){var o;const t=b.range;(o=e.surround)==null||o.call(e,t),this.checkToolsState()}checkToolsState(){var e;(e=this.tools)==null||e.forEach(t=>{var o;(o=t.checkState)==null||o.call(t,b.get())})}get inlineTools(){const e={};return Array.from(this.Editor.Tools.inlineTools.entries()).forEach(([t,o])=>{e[t]=o.create()}),e}}function tn(){const n=window.getSelection();if(n===null)return[null,0];let e=n.focusNode,t=n.focusOffset;return e===null?[null,0]:(e.nodeType!==Node.TEXT_NODE&&e.childNodes.length>0&&(e.childNodes[t]?(e=e.childNodes[t],t=0):(e=e.childNodes[t-1],t=e.textContent.length)),[e,t])}function on(n,e,t,o){const i=document.createRange();o==="left"?(i.setStart(n,0),i.setEnd(e,t)):(i.setStart(e,t),i.setEnd(n,n.childNodes.length));const s=i.cloneContents(),r=document.createElement("div");r.appendChild(s);const a=r.textContent||"";return si(a)}function $e(n){const e=u.getDeepestNode(n);if(e===null||u.isEmpty(n))return!0;if(u.isNativeInput(e))return e.selectionEnd===0;if(u.isEmpty(n))return!0;const[t,o]=tn();return t===null?!1:on(n,t,o,"left")}function ze(n){const e=u.getDeepestNode(n,!0);if(e===null)return!0;if(u.isNativeInput(e))return e.selectionEnd===e.value.length;const[t,o]=tn();return t===null?!1:on(n,t,o,"right")}var nn={},Bt={},Ue={},de={},Ct={},Tt={};Object.defineProperty(Tt,"__esModule",{value:!0}),Tt.allInputsSelector=ys;function ys(){var n=["text","password","email","number","search","tel","url"];return"[contenteditable=true], textarea, input:not([type]), "+n.map(function(e){return'input[type="'.concat(e,'"]')}).join(", ")}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.allInputsSelector=void 0;var e=Tt;Object.defineProperty(n,"allInputsSelector",{enumerable:!0,get:function(){return e.allInputsSelector}})})(Ct);var ue={},St={};Object.defineProperty(St,"__esModule",{value:!0}),St.isNativeInput=ws;function ws(n){var e=["INPUT","TEXTAREA"];return n&&n.tagName?e.includes(n.tagName):!1}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.isNativeInput=void 0;var e=St;Object.defineProperty(n,"isNativeInput",{enumerable:!0,get:function(){return e.isNativeInput}})})(ue);var sn={},It={};Object.defineProperty(It,"__esModule",{value:!0}),It.append=Es;function Es(n,e){Array.isArray(e)?e.forEach(function(t){n.appendChild(t)}):n.appendChild(e)}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.append=void 0;var e=It;Object.defineProperty(n,"append",{enumerable:!0,get:function(){return e.append}})})(sn);var Ot={},_t={};Object.defineProperty(_t,"__esModule",{value:!0}),_t.blockElements=xs;function xs(){return["address","article","aside","blockquote","canvas","div","dl","dt","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","hr","li","main","nav","noscript","ol","output","p","pre","ruby","section","table","tbody","thead","tr","tfoot","ul","video"]}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.blockElements=void 0;var e=_t;Object.defineProperty(n,"blockElements",{enumerable:!0,get:function(){return e.blockElements}})})(Ot);var rn={},Mt={};Object.defineProperty(Mt,"__esModule",{value:!0}),Mt.calculateBaseline=Bs;function Bs(n){var e=window.getComputedStyle(n),t=parseFloat(e.fontSize),o=parseFloat(e.lineHeight)||t*1.2,i=parseFloat(e.paddingTop),s=parseFloat(e.borderTopWidth),r=parseFloat(e.marginTop),a=t*.8,l=(o-t)/2,c=r+s+i+l+a;return c}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.calculateBaseline=void 0;var e=Mt;Object.defineProperty(n,"calculateBaseline",{enumerable:!0,get:function(){return e.calculateBaseline}})})(rn);var an={},At={},Lt={},Pt={};Object.defineProperty(Pt,"__esModule",{value:!0}),Pt.isContentEditable=Cs;function Cs(n){return n.contentEditable==="true"}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.isContentEditable=void 0;var e=Pt;Object.defineProperty(n,"isContentEditable",{enumerable:!0,get:function(){return e.isContentEditable}})})(Lt),Object.defineProperty(At,"__esModule",{value:!0}),At.canSetCaret=Is;var Ts=ue,Ss=Lt;function Is(n){var e=!0;if((0,Ts.isNativeInput)(n))switch(n.type){case"file":case"checkbox":case"radio":case"hidden":case"submit":case"button":case"image":case"reset":e=!1;break}else e=(0,Ss.isContentEditable)(n);return e}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.canSetCaret=void 0;var e=At;Object.defineProperty(n,"canSetCaret",{enumerable:!0,get:function(){return e.canSetCaret}})})(an);var We={},Nt={};function Os(n,e,t){const o=t.value!==void 0?"value":"get",i=t[o],s=`#${e}Cache`;if(t[o]=function(...r){return this[s]===void 0&&(this[s]=i.apply(this,r)),this[s]},o==="get"&&t.set){const r=t.set;t.set=function(a){delete n[s],r.apply(this,a)}}return t}function ln(){const n={win:!1,mac:!1,x11:!1,linux:!1},e=Object.keys(n).find(t=>window.navigator.appVersion.toLowerCase().indexOf(t)!==-1);return e!==void 0&&(n[e]=!0),n}function Rt(n){return n!=null&&n!==""&&(typeof n!="object"||Object.keys(n).length>0)}function _s(n){return!Rt(n)}const Ms=()=>typeof window<"u"&&window.navigator!==null&&Rt(window.navigator.platform)&&(/iP(ad|hone|od)/.test(window.navigator.platform)||window.navigator.platform==="MacIntel"&&window.navigator.maxTouchPoints>1);function As(n){const e=ln();return n=n.replace(/shift/gi,"⇧").replace(/backspace/gi,"⌫").replace(/enter/gi,"⏎").replace(/up/gi,"↑").replace(/left/gi,"→").replace(/down/gi,"↓").replace(/right/gi,"←").replace(/escape/gi,"⎋").replace(/insert/gi,"Ins").replace(/delete/gi,"␡").replace(/\+/gi,"+"),e.mac?n=n.replace(/ctrl|cmd/gi,"⌘").replace(/alt/gi,"⌥"):n=n.replace(/cmd/gi,"Ctrl").replace(/windows/gi,"WIN"),n}function Ls(n){return n[0].toUpperCase()+n.slice(1)}function Ps(n){const e=document.createElement("div");e.style.position="absolute",e.style.left="-999px",e.style.bottom="-999px",e.innerHTML=n,document.body.appendChild(e);const t=window.getSelection(),o=document.createRange();if(o.selectNode(e),t===null)throw new Error("Cannot copy text to clipboard");t.removeAllRanges(),t.addRange(o),document.execCommand("copy"),document.body.removeChild(e)}function Ns(n,e,t){let o;return(...i)=>{const s=this,r=()=>{o=void 0,t!==!0&&n.apply(s,i)},a=t===!0&&o!==void 0;window.clearTimeout(o),o=window.setTimeout(r,e),a&&n.apply(s,i)}}function se(n){return Object.prototype.toString.call(n).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function Rs(n){return se(n)==="boolean"}function cn(n){return se(n)==="function"||se(n)==="asyncfunction"}function Ds(n){return cn(n)&&/^\s*class\s+/.test(n.toString())}function Fs(n){return se(n)==="number"}function Ye(n){return se(n)==="object"}function js(n){return Promise.resolve(n)===n}function Hs(n){return se(n)==="string"}function $s(n){return se(n)==="undefined"}function Dt(n,...e){if(!e.length)return n;const t=e.shift();if(Ye(n)&&Ye(t))for(const o in t)Ye(t[o])?(n[o]===void 0&&Object.assign(n,{[o]:{}}),Dt(n[o],t[o])):Object.assign(n,{[o]:t[o]});return Dt(n,...e)}function zs(n,e,t){const o=`«${e}» is deprecated and will be removed in the next major release. Please use the «${t}» instead.`;n&&console.warn(o)}function Us(n){try{return new URL(n).href}catch{}return n.substring(0,2)==="//"?window.location.protocol+n:window.location.origin+n}function Ws(n){return n>47&&n<58||n===32||n===13||n===229||n>64&&n<91||n>95&&n<112||n>185&&n<193||n>218&&n<223}const Ys={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,LEFT:37,UP:38,DOWN:40,RIGHT:39,DELETE:46,META:91,SLASH:191},Ks={LEFT:0,WHEEL:1,RIGHT:2,BACKWARD:3,FORWARD:4};let Xs=class{constructor(){this.completed=Promise.resolve()}add(e){return new Promise((t,o)=>{this.completed=this.completed.then(e).then(t).catch(o)})}};function Vs(n,e,t=void 0){let o,i,s,r=null,a=0;t||(t={});const l=function(){a=t.leading===!1?0:Date.now(),r=null,s=n.apply(o,i),r===null&&(o=i=null)};return function(){const c=Date.now();!a&&t.leading===!1&&(a=c);const d=e-(c-a);return o=this,i=arguments,d<=0||d>e?(r&&(clearTimeout(r),r=null),a=c,s=n.apply(o,i),r===null&&(o=i=null)):!r&&t.trailing!==!1&&(r=setTimeout(l,d)),s}}const Ft=Yn(Object.freeze(Object.defineProperty({__proto__:null,PromiseQueue:Xs,beautifyShortcut:As,cacheable:Os,capitalize:Ls,copyTextToClipboard:Ps,debounce:Ns,deepMerge:Dt,deprecationAssert:zs,getUserOS:ln,getValidUrl:Us,isBoolean:Rs,isClass:Ds,isEmpty:_s,isFunction:cn,isIosDevice:Ms,isNumber:Fs,isObject:Ye,isPrintableKey:Ws,isPromise:js,isString:Hs,isUndefined:$s,keyCodes:Ys,mouseButtons:Ks,notEmpty:Rt,throttle:Vs,typeOf:se},Symbol.toStringTag,{value:"Module"})));Object.defineProperty(Nt,"__esModule",{value:!0}),Nt.containsOnlyInlineElements=Gs;var qs=Ft,Zs=Ot;function Gs(n){var e;(0,qs.isString)(n)?(e=document.createElement("div"),e.innerHTML=n):e=n;var t=function(o){return!(0,Zs.blockElements)().includes(o.tagName.toLowerCase())&&Array.from(o.children).every(t)};return Array.from(e.children).every(t)}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.containsOnlyInlineElements=void 0;var e=Nt;Object.defineProperty(n,"containsOnlyInlineElements",{enumerable:!0,get:function(){return e.containsOnlyInlineElements}})})(We);var dn={},jt={},Ke={},Ht={};Object.defineProperty(Ht,"__esModule",{value:!0}),Ht.make=Js;function Js(n,e,t){var o;e===void 0&&(e=null),t===void 0&&(t={});var i=document.createElement(n);if(Array.isArray(e)){var s=e.filter(function(a){return a!==void 0});(o=i.classList).add.apply(o,s)}else e!==null&&i.classList.add(e);for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(i[r]=t[r]);return i}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.make=void 0;var e=Ht;Object.defineProperty(n,"make",{enumerable:!0,get:function(){return e.make}})})(Ke),Object.defineProperty(jt,"__esModule",{value:!0}),jt.fragmentToString=er;var Qs=Ke;function er(n){var e=(0,Qs.make)("div");return e.appendChild(n),e.innerHTML}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.fragmentToString=void 0;var e=jt;Object.defineProperty(n,"fragmentToString",{enumerable:!0,get:function(){return e.fragmentToString}})})(dn);var un={},$t={};Object.defineProperty($t,"__esModule",{value:!0}),$t.getContentLength=or;var tr=ue;function or(n){var e,t;return(0,tr.isNativeInput)(n)?n.value.length:n.nodeType===Node.TEXT_NODE?n.length:(t=(e=n.textContent)===null||e===void 0?void 0:e.length)!==null&&t!==void 0?t:0}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.getContentLength=void 0;var e=$t;Object.defineProperty(n,"getContentLength",{enumerable:!0,get:function(){return e.getContentLength}})})(un);var zt={},Ut={},hn=J&&J.__spreadArray||function(n,e,t){if(t||arguments.length===2)for(var o=0,i=e.length,s;o0;){var o=t.shift();if(o){if(n=o,(0,xr.isLeaf)(n)&&!(0,Br.isNodeEmpty)(n,e))return!1;t.push.apply(t,Array.from(n.childNodes))}}return!0}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.isEmpty=void 0;var e=Gt;Object.defineProperty(n,"isEmpty",{enumerable:!0,get:function(){return e.isEmpty}})})(vn);var kn={},oo={};Object.defineProperty(oo,"__esModule",{value:!0}),oo.isFragment=Sr;var Tr=Ft;function Sr(n){return(0,Tr.isNumber)(n)?!1:!!n&&!!n.nodeType&&n.nodeType===Node.DOCUMENT_FRAGMENT_NODE}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.isFragment=void 0;var e=oo;Object.defineProperty(n,"isFragment",{enumerable:!0,get:function(){return e.isFragment}})})(kn);var yn={},no={};Object.defineProperty(no,"__esModule",{value:!0}),no.isHTMLString=Or;var Ir=Ke;function Or(n){var e=(0,Ir.make)("div");return e.innerHTML=n,e.childElementCount>0}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.isHTMLString=void 0;var e=no;Object.defineProperty(n,"isHTMLString",{enumerable:!0,get:function(){return e.isHTMLString}})})(yn);var wn={},io={};Object.defineProperty(io,"__esModule",{value:!0}),io.offset=_r;function _r(n){var e=n.getBoundingClientRect(),t=window.pageXOffset||document.documentElement.scrollLeft,o=window.pageYOffset||document.documentElement.scrollTop,i=e.top+o,s=e.left+t;return{top:i,left:s,bottom:i+e.height,right:s+e.width}}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.offset=void 0;var e=io;Object.defineProperty(n,"offset",{enumerable:!0,get:function(){return e.offset}})})(wn);var En={},so={};Object.defineProperty(so,"__esModule",{value:!0}),so.prepend=Mr;function Mr(n,e){Array.isArray(e)?(e=e.reverse(),e.forEach(function(t){return n.prepend(t)})):n.prepend(e)}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.prepend=void 0;var e=so;Object.defineProperty(n,"prepend",{enumerable:!0,get:function(){return e.prepend}})})(En),function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.prepend=n.offset=n.make=n.isLineBreakTag=n.isSingleTag=n.isNodeEmpty=n.isLeaf=n.isHTMLString=n.isFragment=n.isEmpty=n.isElement=n.isContentEditable=n.isCollapsedWhitespaces=n.findAllInputs=n.isNativeInput=n.allInputsSelector=n.getDeepestNode=n.getDeepestBlockElements=n.getContentLength=n.fragmentToString=n.containsOnlyInlineElements=n.canSetCaret=n.calculateBaseline=n.blockElements=n.append=void 0;var e=Ct;Object.defineProperty(n,"allInputsSelector",{enumerable:!0,get:function(){return e.allInputsSelector}});var t=ue;Object.defineProperty(n,"isNativeInput",{enumerable:!0,get:function(){return t.isNativeInput}});var o=sn;Object.defineProperty(n,"append",{enumerable:!0,get:function(){return o.append}});var i=Ot;Object.defineProperty(n,"blockElements",{enumerable:!0,get:function(){return i.blockElements}});var s=rn;Object.defineProperty(n,"calculateBaseline",{enumerable:!0,get:function(){return s.calculateBaseline}});var r=an;Object.defineProperty(n,"canSetCaret",{enumerable:!0,get:function(){return r.canSetCaret}});var a=We;Object.defineProperty(n,"containsOnlyInlineElements",{enumerable:!0,get:function(){return a.containsOnlyInlineElements}});var l=dn;Object.defineProperty(n,"fragmentToString",{enumerable:!0,get:function(){return l.fragmentToString}});var c=un;Object.defineProperty(n,"getContentLength",{enumerable:!0,get:function(){return c.getContentLength}});var d=zt;Object.defineProperty(n,"getDeepestBlockElements",{enumerable:!0,get:function(){return d.getDeepestBlockElements}});var h=fn;Object.defineProperty(n,"getDeepestNode",{enumerable:!0,get:function(){return h.getDeepestNode}});var p=mn;Object.defineProperty(n,"findAllInputs",{enumerable:!0,get:function(){return p.findAllInputs}});var g=bn;Object.defineProperty(n,"isCollapsedWhitespaces",{enumerable:!0,get:function(){return g.isCollapsedWhitespaces}});var f=Lt;Object.defineProperty(n,"isContentEditable",{enumerable:!0,get:function(){return f.isContentEditable}});var v=qt;Object.defineProperty(n,"isElement",{enumerable:!0,get:function(){return v.isElement}});var O=vn;Object.defineProperty(n,"isEmpty",{enumerable:!0,get:function(){return O.isEmpty}});var S=kn;Object.defineProperty(n,"isFragment",{enumerable:!0,get:function(){return S.isFragment}});var A=yn;Object.defineProperty(n,"isHTMLString",{enumerable:!0,get:function(){return A.isHTMLString}});var G=Jt;Object.defineProperty(n,"isLeaf",{enumerable:!0,get:function(){return G.isLeaf}});var j=eo;Object.defineProperty(n,"isNodeEmpty",{enumerable:!0,get:function(){return j.isNodeEmpty}});var U=Xe;Object.defineProperty(n,"isLineBreakTag",{enumerable:!0,get:function(){return U.isLineBreakTag}});var oe=Ve;Object.defineProperty(n,"isSingleTag",{enumerable:!0,get:function(){return oe.isSingleTag}});var he=Ke;Object.defineProperty(n,"make",{enumerable:!0,get:function(){return he.make}});var k=wn;Object.defineProperty(n,"offset",{enumerable:!0,get:function(){return k.offset}});var m=En;Object.defineProperty(n,"prepend",{enumerable:!0,get:function(){return m.prepend}})}(de);var Ze={};Object.defineProperty(Ze,"__esModule",{value:!0}),Ze.getContenteditableSlice=Lr;var Ar=de;function Lr(n,e,t,o,i){var s;i===void 0&&(i=!1);var r=document.createRange();if(o==="left"?(r.setStart(n,0),r.setEnd(e,t)):(r.setStart(e,t),r.setEnd(n,n.childNodes.length)),i===!0){var a=r.extractContents();return(0,Ar.fragmentToString)(a)}var l=r.cloneContents(),c=document.createElement("div");c.appendChild(l);var d=(s=c.textContent)!==null&&s!==void 0?s:"";return d}Object.defineProperty(Ue,"__esModule",{value:!0}),Ue.checkContenteditableSliceForEmptiness=Rr;var Pr=de,Nr=Ze;function Rr(n,e,t,o){var i=(0,Nr.getContenteditableSlice)(n,e,t,o);return(0,Pr.isCollapsedWhitespaces)(i)}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.checkContenteditableSliceForEmptiness=void 0;var e=Ue;Object.defineProperty(n,"checkContenteditableSliceForEmptiness",{enumerable:!0,get:function(){return e.checkContenteditableSliceForEmptiness}})})(Bt);var xn={};(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.getContenteditableSlice=void 0;var e=Ze;Object.defineProperty(n,"getContenteditableSlice",{enumerable:!0,get:function(){return e.getContenteditableSlice}})})(xn);var Bn={},ro={};Object.defineProperty(ro,"__esModule",{value:!0}),ro.focus=Fr;var Dr=de;function Fr(n,e){var t,o;if(e===void 0&&(e=!0),(0,Dr.isNativeInput)(n)){n.focus();var i=e?0:n.value.length;n.setSelectionRange(i,i)}else{var s=document.createRange(),r=window.getSelection();if(!r)return;var a=function(p){var g=document.createTextNode("");p.appendChild(g),s.setStart(g,0),s.setEnd(g,0)},l=function(p){return p!=null},c=n.childNodes,d=e?c[0]:c[c.length-1];if(l(d)){for(;l(d)&&d.nodeType!==Node.TEXT_NODE;)d=e?d.firstChild:d.lastChild;if(l(d)&&d.nodeType===Node.TEXT_NODE){var h=(o=(t=d.textContent)===null||t===void 0?void 0:t.length)!==null&&o!==void 0?o:0,i=e?0:h;s.setStart(d,i),s.setEnd(d,i)}else a(n)}else a(n);r.removeAllRanges(),r.addRange(s)}}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.focus=void 0;var e=ro;Object.defineProperty(n,"focus",{enumerable:!0,get:function(){return e.focus}})})(Bn);var ao={},Ge={};Object.defineProperty(Ge,"__esModule",{value:!0}),Ge.getCaretNodeAndOffset=jr;function jr(){var n=window.getSelection();if(n===null)return[null,0];var e=n.focusNode,t=n.focusOffset;return e===null?[null,0]:(e.nodeType!==Node.TEXT_NODE&&e.childNodes.length>0&&(e.childNodes[t]!==void 0?(e=e.childNodes[t],t=0):(e=e.childNodes[t-1],e.textContent!==null&&(t=e.textContent.length))),[e,t])}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.getCaretNodeAndOffset=void 0;var e=Ge;Object.defineProperty(n,"getCaretNodeAndOffset",{enumerable:!0,get:function(){return e.getCaretNodeAndOffset}})})(ao);var Cn={},Je={};Object.defineProperty(Je,"__esModule",{value:!0}),Je.getRange=Hr;function Hr(){var n=window.getSelection();return n&&n.rangeCount?n.getRangeAt(0):null}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.getRange=void 0;var e=Je;Object.defineProperty(n,"getRange",{enumerable:!0,get:function(){return e.getRange}})})(Cn);var Tn={},lo={};Object.defineProperty(lo,"__esModule",{value:!0}),lo.isCaretAtEndOfInput=Ur;var Sn=de,$r=ao,zr=Bt;function Ur(n){var e=(0,Sn.getDeepestNode)(n,!0);if(e===null)return!0;if((0,Sn.isNativeInput)(e))return e.selectionEnd===e.value.length;var t=(0,$r.getCaretNodeAndOffset)(),o=t[0],i=t[1];return o===null?!1:(0,zr.checkContenteditableSliceForEmptiness)(n,o,i,"right")}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.isCaretAtEndOfInput=void 0;var e=lo;Object.defineProperty(n,"isCaretAtEndOfInput",{enumerable:!0,get:function(){return e.isCaretAtEndOfInput}})})(Tn);var In={},co={};Object.defineProperty(co,"__esModule",{value:!0}),co.isCaretAtStartOfInput=Kr;var Qe=de,Wr=Ge,Yr=Ue;function Kr(n){var e=(0,Qe.getDeepestNode)(n);if(e===null||(0,Qe.isEmpty)(n))return!0;if((0,Qe.isNativeInput)(e))return e.selectionEnd===0;if((0,Qe.isEmpty)(n))return!0;var t=(0,Wr.getCaretNodeAndOffset)(),o=t[0],i=t[1];return o===null?!1:(0,Yr.checkContenteditableSliceForEmptiness)(n,o,i,"left")}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.isCaretAtStartOfInput=void 0;var e=co;Object.defineProperty(n,"isCaretAtStartOfInput",{enumerable:!0,get:function(){return e.isCaretAtStartOfInput}})})(In);var On={},uo={};Object.defineProperty(uo,"__esModule",{value:!0}),uo.save=qr;var Xr=de,Vr=Je;function qr(){var n=(0,Vr.getRange)(),e=(0,Xr.make)("span");if(e.id="cursor",e.hidden=!0,!!n)return n.insertNode(e),function(){var o=window.getSelection();o&&(n.setStartAfter(e),n.setEndAfter(e),o.removeAllRanges(),o.addRange(n),setTimeout(function(){e.remove()},150))}}(function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.save=void 0;var e=uo;Object.defineProperty(n,"save",{enumerable:!0,get:function(){return e.save}})})(On),function(n){Object.defineProperty(n,"__esModule",{value:!0}),n.save=n.isCaretAtStartOfInput=n.isCaretAtEndOfInput=n.getRange=n.getCaretNodeAndOffset=n.focus=n.getContenteditableSlice=n.checkContenteditableSliceForEmptiness=void 0;var e=Bt;Object.defineProperty(n,"checkContenteditableSliceForEmptiness",{enumerable:!0,get:function(){return e.checkContenteditableSliceForEmptiness}});var t=xn;Object.defineProperty(n,"getContenteditableSlice",{enumerable:!0,get:function(){return t.getContenteditableSlice}});var o=Bn;Object.defineProperty(n,"focus",{enumerable:!0,get:function(){return o.focus}});var i=ao;Object.defineProperty(n,"getCaretNodeAndOffset",{enumerable:!0,get:function(){return i.getCaretNodeAndOffset}});var s=Cn;Object.defineProperty(n,"getRange",{enumerable:!0,get:function(){return s.getRange}});var r=Tn;Object.defineProperty(n,"isCaretAtEndOfInput",{enumerable:!0,get:function(){return r.isCaretAtEndOfInput}});var a=In;Object.defineProperty(n,"isCaretAtStartOfInput",{enumerable:!0,get:function(){return a.isCaretAtStartOfInput}});var l=On;Object.defineProperty(n,"save",{enumerable:!0,get:function(){return l.save}})}(nn);class Zr extends E{keydown(e){switch(this.beforeKeydownProcessing(e),e.keyCode){case y.BACKSPACE:this.backspace(e);break;case y.DELETE:this.delete(e);break;case y.ENTER:this.enter(e);break;case y.DOWN:case y.RIGHT:this.arrowRightAndDown(e);break;case y.UP:case y.LEFT:this.arrowLeftAndUp(e);break;case y.TAB:this.tabPressed(e);break}e.key==="/"&&!e.ctrlKey&&!e.metaKey&&this.slashPressed(e),e.code==="Slash"&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.commandSlashPressed())}beforeKeydownProcessing(e){this.needToolbarClosing(e)&&Eo(e.keyCode)&&(this.Editor.Toolbar.close(),e.ctrlKey||e.metaKey||e.altKey||e.shiftKey||this.Editor.BlockSelection.clearSelection(e))}keyup(e){e.shiftKey||this.Editor.UI.checkEmptiness()}dragOver(e){const t=this.Editor.BlockManager.getBlockByChildNode(e.target);t.dropTarget=!0}dragLeave(e){const t=this.Editor.BlockManager.getBlockByChildNode(e.target);t.dropTarget=!1}handleCommandC(e){const{BlockSelection:t}=this.Editor;t.anyBlockSelected&&t.copySelectedBlocks(e)}handleCommandX(e){const{BlockSelection:t,BlockManager:o,Caret:i}=this.Editor;t.anyBlockSelected&&t.copySelectedBlocks(e).then(()=>{const s=o.removeSelectedBlocks(),r=o.insertDefaultBlockAtIndex(s,!0);i.setToBlock(r,i.positions.START),t.clearSelection(e)})}tabPressed(e){const{InlineToolbar:t,Caret:o}=this.Editor;if(t.opened)return;(e.shiftKey?o.navigatePrevious(!0):o.navigateNext(!0))&&e.preventDefault()}commandSlashPressed(){this.Editor.BlockSelection.selectedBlocks.length>1||this.activateBlockSettings()}slashPressed(e){!this.Editor.UI.nodes.wrapper.contains(e.target)||!this.Editor.BlockManager.currentBlock.isEmpty||(e.preventDefault(),this.Editor.Caret.insertContentAtCaretPosition("/"),this.activateToolbox())}enter(e){const{BlockManager:t,UI:o}=this.Editor,i=t.currentBlock;if(i===void 0||i.tool.isLineBreaksEnabled||o.someToolbarOpened&&o.someFlipperButtonFocused||e.shiftKey&&!rt)return;let s=i;i.currentInput!==void 0&&$e(i.currentInput)&&!i.hasMedia?this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex):i.currentInput&&ze(i.currentInput)?s=this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex+1):s=this.Editor.BlockManager.split(),this.Editor.Caret.setToBlock(s),this.Editor.Toolbar.moveAndOpen(s),e.preventDefault()}backspace(e){const{BlockManager:t,Caret:o}=this.Editor,{currentBlock:i,previousBlock:s}=t;if(i===void 0||!b.isCollapsed||!i.currentInput||!$e(i.currentInput))return;if(e.preventDefault(),this.Editor.Toolbar.close(),!(i.currentInput===i.firstInput)){o.navigatePrevious();return}if(s===null)return;if(s.isEmpty){t.removeBlock(s);return}if(i.isEmpty){t.removeBlock(i);const l=t.currentBlock;o.setToBlock(l,o.positions.END);return}No(s,i)?this.mergeBlocks(s,i):o.setToBlock(s,o.positions.END)}delete(e){const{BlockManager:t,Caret:o}=this.Editor,{currentBlock:i,nextBlock:s}=t;if(!b.isCollapsed||!ze(i.currentInput))return;if(e.preventDefault(),this.Editor.Toolbar.close(),!(i.currentInput===i.lastInput)){o.navigateNext();return}if(s===null)return;if(s.isEmpty){t.removeBlock(s);return}if(i.isEmpty){t.removeBlock(i),o.setToBlock(s,o.positions.START);return}No(i,s)?this.mergeBlocks(i,s):o.setToBlock(s,o.positions.START)}mergeBlocks(e,t){const{BlockManager:o,Toolbar:i}=this.Editor;e.lastInput!==void 0&&(nn.focus(e.lastInput,!1),o.mergeBlocks(e,t).then(()=>{i.close()}))}arrowRightAndDown(e){const t=ae.usedKeys.includes(e.keyCode)&&(!e.shiftKey||e.keyCode===y.TAB);if(this.Editor.UI.someToolbarOpened&&t)return;this.Editor.Toolbar.close();const{currentBlock:o}=this.Editor.BlockManager,s=((o==null?void 0:o.currentInput)!==void 0?ze(o.currentInput):void 0)||this.Editor.BlockSelection.anyBlockSelected;if(e.shiftKey&&e.keyCode===y.DOWN&&s){this.Editor.CrossBlockSelection.toggleBlockSelectedState();return}if(e.keyCode===y.DOWN||e.keyCode===y.RIGHT&&!this.isRtl?this.Editor.Caret.navigateNext():this.Editor.Caret.navigatePrevious()){e.preventDefault();return}Ae(()=>{this.Editor.BlockManager.currentBlock&&this.Editor.BlockManager.currentBlock.updateCurrentInput()},20)(),this.Editor.BlockSelection.clearSelection(e)}arrowLeftAndUp(e){if(this.Editor.UI.someToolbarOpened){if(ae.usedKeys.includes(e.keyCode)&&(!e.shiftKey||e.keyCode===y.TAB))return;this.Editor.UI.closeAllToolbars()}this.Editor.Toolbar.close();const{currentBlock:t}=this.Editor.BlockManager,i=((t==null?void 0:t.currentInput)!==void 0?$e(t.currentInput):void 0)||this.Editor.BlockSelection.anyBlockSelected;if(e.shiftKey&&e.keyCode===y.UP&&i){this.Editor.CrossBlockSelection.toggleBlockSelectedState(!1);return}if(e.keyCode===y.UP||e.keyCode===y.LEFT&&!this.isRtl?this.Editor.Caret.navigatePrevious():this.Editor.Caret.navigateNext()){e.preventDefault();return}Ae(()=>{this.Editor.BlockManager.currentBlock&&this.Editor.BlockManager.currentBlock.updateCurrentInput()},20)(),this.Editor.BlockSelection.clearSelection(e)}needToolbarClosing(e){const t=e.keyCode===y.ENTER&&this.Editor.Toolbar.toolbox.opened,o=e.keyCode===y.ENTER&&this.Editor.BlockSettings.opened,i=e.keyCode===y.ENTER&&this.Editor.InlineToolbar.opened,s=e.keyCode===y.TAB;return!(e.shiftKey||s||t||o||i)}activateToolbox(){this.Editor.Toolbar.opened||this.Editor.Toolbar.moveAndOpen(),this.Editor.Toolbar.toolbox.open()}activateBlockSettings(){this.Editor.Toolbar.opened||this.Editor.Toolbar.moveAndOpen(),this.Editor.BlockSettings.opened||this.Editor.BlockSettings.open()}}class ho{constructor(e){this.blocks=[],this.workingArea=e}get length(){return this.blocks.length}get array(){return this.blocks}get nodes(){return xo(this.workingArea.children)}static set(e,t,o){return isNaN(Number(t))?(Reflect.set(e,t,o),!0):(e.insert(+t,o),!0)}static get(e,t){return isNaN(Number(t))?Reflect.get(e,t):e.get(+t)}push(e){this.blocks.push(e),this.insertToDOM(e)}swap(e,t){const o=this.blocks[t];u.swap(this.blocks[e].holder,o.holder),this.blocks[t]=this.blocks[e],this.blocks[e]=o}move(e,t){const o=this.blocks.splice(t,1)[0],i=e-1,s=Math.max(0,i),r=this.blocks[s];e>0?this.insertToDOM(o,"afterend",r):this.insertToDOM(o,"beforebegin",r),this.blocks.splice(e,0,o);const a=this.composeBlockEvent("move",{fromIndex:t,toIndex:e});o.call(te.MOVED,a)}insert(e,t,o=!1){if(!this.length){this.push(t);return}e>this.length&&(e=this.length),o&&(this.blocks[e].holder.remove(),this.blocks[e].call(te.REMOVED));const i=o?1:0;if(this.blocks.splice(e,i,t),e>0){const s=this.blocks[e-1];this.insertToDOM(t,"afterend",s)}else{const s=this.blocks[e+1];s?this.insertToDOM(t,"beforebegin",s):this.insertToDOM(t)}}replace(e,t){if(this.blocks[e]===void 0)throw Error("Incorrect index");this.blocks[e].holder.replaceWith(t.holder),this.blocks[e]=t}insertMany(e,t){const o=new DocumentFragment;for(const i of e)o.appendChild(i.holder);if(this.length>0){if(t>0){const i=Math.min(t-1,this.length-1);this.blocks[i].holder.after(o)}else t===0&&this.workingArea.prepend(o);this.blocks.splice(t,0,...e)}else this.blocks.push(...e),this.workingArea.appendChild(o);e.forEach(i=>i.call(te.RENDERED))}remove(e){isNaN(e)&&(e=this.length-1),this.blocks[e].holder.remove(),this.blocks[e].call(te.REMOVED),this.blocks.splice(e,1)}removeAll(){this.workingArea.innerHTML="",this.blocks.forEach(e=>e.call(te.REMOVED)),this.blocks.length=0}insertAfter(e,t){const o=this.blocks.indexOf(e);this.insert(o+1,t)}get(e){return this.blocks[e]}indexOf(e){return this.blocks.indexOf(e)}insertToDOM(e,t,o){t?o.holder.insertAdjacentElement(t,e.holder):this.workingArea.appendChild(e.holder),e.call(te.RENDERED)}composeBlockEvent(e,t){return new CustomEvent(e,{detail:t})}}const _n="block-removed",Mn="block-added",Gr="block-moved",An="block-changed";class Jr{constructor(){this.completed=Promise.resolve()}add(e){return new Promise((t,o)=>{this.completed=this.completed.then(e).then(t).catch(o)})}}class Qr extends E{constructor(){super(...arguments),this._currentBlockIndex=-1,this._blocks=null}get currentBlockIndex(){return this._currentBlockIndex}set currentBlockIndex(e){this._currentBlockIndex=e}get firstBlock(){return this._blocks[0]}get lastBlock(){return this._blocks[this._blocks.length-1]}get currentBlock(){return this._blocks[this.currentBlockIndex]}set currentBlock(e){this.currentBlockIndex=this.getBlockIndex(e)}get nextBlock(){return this.currentBlockIndex===this._blocks.length-1?null:this._blocks[this.currentBlockIndex+1]}get nextContentfulBlock(){return this.blocks.slice(this.currentBlockIndex+1).find(t=>!!t.inputs.length)}get previousContentfulBlock(){return this.blocks.slice(0,this.currentBlockIndex).reverse().find(t=>!!t.inputs.length)}get previousBlock(){return this.currentBlockIndex===0?null:this._blocks[this.currentBlockIndex-1]}get blocks(){return this._blocks.array}get isEditorEmpty(){return this.blocks.every(e=>e.isEmpty)}prepare(){const e=new ho(this.Editor.UI.nodes.redactor);this._blocks=new Proxy(e,{set:ho.set,get:ho.get}),this.listeners.on(document,"copy",t=>this.Editor.BlockEvents.handleCommandC(t))}toggleReadOnly(e){e?this.disableModuleBindings():this.enableModuleBindings()}composeBlock({tool:e,data:t={},id:o=void 0,tunes:i={}}){const s=this.Editor.ReadOnly.isEnabled,r=this.Editor.Tools.blockTools.get(e),a=new R({id:o,data:t,tool:r,api:this.Editor.API,readOnly:s,tunesData:i},this.eventsDispatcher);return s||window.requestIdleCallback(()=>{this.bindBlockEvents(a)},{timeout:2e3}),a}insert({id:e=void 0,tool:t=this.config.defaultBlock,data:o={},index:i,needToFocus:s=!0,replace:r=!1,tunes:a={}}={}){let l=i;l===void 0&&(l=this.currentBlockIndex+(r?0:1));const c=this.composeBlock({id:e,tool:t,data:o,tunes:a});return r&&this.blockDidMutated(_n,this.getBlockByIndex(l),{index:l}),this._blocks.insert(l,c,r),this.blockDidMutated(Mn,c,{index:l}),s?this.currentBlockIndex=l:l<=this.currentBlockIndex&&this.currentBlockIndex++,c}insertMany(e,t=0){this._blocks.insertMany(e,t)}async update(e,t,o){if(!t&&!o)return e;const i=await e.data,s=this.composeBlock({id:e.id,tool:e.name,data:Object.assign({},i,t??{}),tunes:o??e.tunes}),r=this.getBlockIndex(e);return this._blocks.replace(r,s),this.blockDidMutated(An,s,{index:r}),s}replace(e,t,o){const i=this.getBlockIndex(e);return this.insert({tool:t,data:o,index:i,replace:!0})}paste(e,t,o=!1){const i=this.insert({tool:e,replace:o});try{window.requestIdleCallback(()=>{i.call(te.ON_PASTE,t)})}catch(s){T(`${e}: onPaste callback call is failed`,"error",s)}return i}insertDefaultBlockAtIndex(e,t=!1){const o=this.composeBlock({tool:this.config.defaultBlock});return this._blocks[e]=o,this.blockDidMutated(Mn,o,{index:e}),t?this.currentBlockIndex=e:e<=this.currentBlockIndex&&this.currentBlockIndex++,o}insertAtEnd(){return this.currentBlockIndex=this.blocks.length-1,this.insert()}async mergeBlocks(e,t){let o;if(e.name===t.name&&e.mergeable){const i=await t.data;if(V(i)){console.error("Could not merge Block. Failed to extract original Block data.");return}const[s]=dt([i],e.tool.sanitizeConfig);o=s}else if(e.mergeable&&Pe(t,"export")&&Pe(e,"import")){const i=await t.exportDataAsString(),s=q(i,e.tool.sanitizeConfig);o=Ro(s,e.tool.conversionConfig)}o!==void 0&&(await e.mergeWith(o),this.removeBlock(t),this.currentBlockIndex=this._blocks.indexOf(e))}removeBlock(e,t=!0){return new Promise(o=>{const i=this._blocks.indexOf(e);if(!this.validateIndex(i))throw new Error("Can't find a Block to remove");this._blocks.remove(i),e.destroy(),this.blockDidMutated(_n,e,{index:i}),this.currentBlockIndex>=i&&this.currentBlockIndex--,this.blocks.length?i===0&&(this.currentBlockIndex=0):(this.unsetCurrentBlock(),t&&this.insert()),o()})}removeSelectedBlocks(){let e;for(let t=this.blocks.length-1;t>=0;t--)this.blocks[t].selected&&(this.removeBlock(this.blocks[t]),e=t);return e}removeAllBlocks(){for(let e=this.blocks.length-1;e>=0;e--)this._blocks.remove(e);this.unsetCurrentBlock(),this.insert(),this.currentBlock.firstInput.focus()}split(){const e=this.Editor.Caret.extractFragmentFromCaretPosition(),t=u.make("div");t.appendChild(e);const o={text:u.isEmpty(t)?"":t.innerHTML};return this.insert({data:o})}getBlockByIndex(e){return e===-1&&(e=this._blocks.length-1),this._blocks[e]}getBlockIndex(e){return this._blocks.indexOf(e)}getBlockById(e){return this._blocks.array.find(t=>t.id===e)}getBlock(e){u.isElement(e)||(e=e.parentNode);const t=this._blocks.nodes,o=e.closest(`.${R.CSS.wrapper}`),i=t.indexOf(o);if(i>=0)return this._blocks[i]}setCurrentBlockByChildNode(e){u.isElement(e)||(e=e.parentNode);const t=e.closest(`.${R.CSS.wrapper}`);if(!t)return;const o=t.closest(`.${this.Editor.UI.CSS.editorWrapper}`);if(o!=null&&o.isEqualNode(this.Editor.UI.nodes.wrapper))return this.currentBlockIndex=this._blocks.nodes.indexOf(t),this.currentBlock.updateCurrentInput(),this.currentBlock}getBlockByChildNode(e){if(!e||!(e instanceof Node))return;u.isElement(e)||(e=e.parentNode);const t=e.closest(`.${R.CSS.wrapper}`);return this.blocks.find(o=>o.holder===t)}swap(e,t){this._blocks.swap(e,t),this.currentBlockIndex=t}move(e,t=this.currentBlockIndex){if(isNaN(e)||isNaN(t)){T("Warning during 'move' call: incorrect indices provided.","warn");return}if(!this.validateIndex(e)||!this.validateIndex(t)){T("Warning during 'move' call: indices cannot be lower than 0 or greater than the amount of blocks.","warn");return}this._blocks.move(e,t),this.currentBlockIndex=e,this.blockDidMutated(Gr,this.currentBlock,{fromIndex:t,toIndex:e})}async convert(e,t,o){if(!await e.save())throw new Error("Could not convert Block. Failed to extract original Block data.");const s=this.Editor.Tools.blockTools.get(t);if(!s)throw new Error(`Could not convert Block. Tool «${t}» not found.`);const r=await e.exportDataAsString(),a=q(r,s.sanitizeConfig);let l=Ro(a,s.conversionConfig,s.settings);return o&&(l=Object.assign(l,o)),this.replace(e,s.name,l)}unsetCurrentBlock(){this.currentBlockIndex=-1}async clear(e=!1){const t=new Jr;[...this.blocks].forEach(i=>{t.add(async()=>{await this.removeBlock(i,!1)})}),await t.completed,this.unsetCurrentBlock(),e&&this.insert(),this.Editor.UI.checkEmptiness()}async destroy(){await Promise.all(this.blocks.map(e=>e.destroy()))}bindBlockEvents(e){const{BlockEvents:t}=this.Editor;this.readOnlyMutableListeners.on(e.holder,"keydown",o=>{t.keydown(o)}),this.readOnlyMutableListeners.on(e.holder,"keyup",o=>{t.keyup(o)}),this.readOnlyMutableListeners.on(e.holder,"dragover",o=>{t.dragOver(o)}),this.readOnlyMutableListeners.on(e.holder,"dragleave",o=>{t.dragLeave(o)}),e.on("didMutated",o=>this.blockDidMutated(An,o,{index:this.getBlockIndex(o)}))}disableModuleBindings(){this.readOnlyMutableListeners.clearAll()}enableModuleBindings(){this.readOnlyMutableListeners.on(document,"cut",e=>this.Editor.BlockEvents.handleCommandX(e)),this.blocks.forEach(e=>{this.bindBlockEvents(e)})}validateIndex(e){return!(e<0||e>=this._blocks.length)}blockDidMutated(e,t,o){const i=new CustomEvent(e,{detail:{target:new ee(t),...o}});return this.eventsDispatcher.emit(_o,{event:i}),t}}class ea extends E{constructor(){super(...arguments),this.anyBlockSelectedCache=null,this.needToSelectAll=!1,this.nativeInputSelected=!1,this.readyToBlockSelection=!1}get sanitizerConfig(){return{p:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},ol:{},ul:{},li:{},br:!0,img:{src:!0,width:!0,height:!0},a:{href:!0},b:{},i:{},u:{}}}get allBlocksSelected(){const{BlockManager:e}=this.Editor;return e.blocks.every(t=>t.selected===!0)}set allBlocksSelected(e){const{BlockManager:t}=this.Editor;t.blocks.forEach(o=>{o.selected=e}),this.clearCache()}get anyBlockSelected(){const{BlockManager:e}=this.Editor;return this.anyBlockSelectedCache===null&&(this.anyBlockSelectedCache=e.blocks.some(t=>t.selected===!0)),this.anyBlockSelectedCache}get selectedBlocks(){return this.Editor.BlockManager.blocks.filter(e=>e.selected)}prepare(){this.selection=new b,be.add({name:"CMD+A",handler:e=>{const{BlockManager:t,ReadOnly:o}=this.Editor;if(o.isEnabled){e.preventDefault(),this.selectAllBlocks();return}t.currentBlock&&this.handleCommandA(e)},on:this.Editor.UI.nodes.redactor})}toggleReadOnly(){b.get().removeAllRanges(),this.allBlocksSelected=!1}unSelectBlockByIndex(e){const{BlockManager:t}=this.Editor;let o;isNaN(e)?o=t.currentBlock:o=t.getBlockByIndex(e),o.selected=!1,this.clearCache()}clearSelection(e,t=!1){const{BlockManager:o,Caret:i,RectangleSelection:s}=this.Editor;this.needToSelectAll=!1,this.nativeInputSelected=!1,this.readyToBlockSelection=!1;const r=e&&e instanceof KeyboardEvent,a=r&&Eo(e.keyCode);if(this.anyBlockSelected&&r&&a&&!b.isSelectionExists){const l=o.removeSelectedBlocks();o.insertDefaultBlockAtIndex(l,!0),i.setToBlock(o.currentBlock),Ae(()=>{const c=e.key;i.insertContentAtCaretPosition(c.length>1?"":c)},20)()}if(this.Editor.CrossBlockSelection.clear(e),!this.anyBlockSelected||s.isRectActivated()){this.Editor.RectangleSelection.clearSelection();return}t&&this.selection.restore(),this.allBlocksSelected=!1}copySelectedBlocks(e){e.preventDefault();const t=u.make("div");this.selectedBlocks.forEach(s=>{const r=q(s.holder.innerHTML,this.sanitizerConfig),a=u.make("p");a.innerHTML=r,t.appendChild(a)});const o=Array.from(t.childNodes).map(s=>s.textContent).join(` + +`),i=t.innerHTML;return e.clipboardData.setData("text/plain",o),e.clipboardData.setData("text/html",i),Promise.all(this.selectedBlocks.map(s=>s.save())).then(s=>{try{e.clipboardData.setData(this.Editor.Paste.MIME_TYPE,JSON.stringify(s))}catch{}})}selectBlockByIndex(e){const{BlockManager:t}=this.Editor,o=t.getBlockByIndex(e);o!==void 0&&this.selectBlock(o)}selectBlock(e){this.selection.save(),b.get().removeAllRanges(),e.selected=!0,this.clearCache(),this.Editor.InlineToolbar.close()}unselectBlock(e){e.selected=!1,this.clearCache()}clearCache(){this.anyBlockSelectedCache=null}destroy(){be.remove(this.Editor.UI.nodes.redactor,"CMD+A")}handleCommandA(e){if(this.Editor.RectangleSelection.clearSelection(),u.isNativeInput(e.target)&&!this.readyToBlockSelection){this.readyToBlockSelection=!0;return}const t=this.Editor.BlockManager.getBlock(e.target),o=t.inputs;if(o.length>1&&!this.readyToBlockSelection){this.readyToBlockSelection=!0;return}if(o.length===1&&!this.needToSelectAll){this.needToSelectAll=!0;return}this.needToSelectAll?(e.preventDefault(),this.selectAllBlocks(),this.needToSelectAll=!1,this.readyToBlockSelection=!1):this.readyToBlockSelection&&(e.preventDefault(),this.selectBlock(t),this.needToSelectAll=!0)}selectAllBlocks(){this.selection.save(),b.get().removeAllRanges(),this.allBlocksSelected=!0,this.Editor.InlineToolbar.close()}}class et extends E{get positions(){return{START:"start",END:"end",DEFAULT:"default"}}static get CSS(){return{shadowCaret:"cdx-shadow-caret"}}setToBlock(e,t=this.positions.DEFAULT,o=0){var c;const{BlockManager:i,BlockSelection:s}=this.Editor;if(s.clearSelection(),!e.focusable){(c=window.getSelection())==null||c.removeAllRanges(),s.selectBlock(e),i.currentBlock=e;return}let r;switch(t){case this.positions.START:r=e.firstInput;break;case this.positions.END:r=e.lastInput;break;default:r=e.currentInput}if(!r)return;let a,l=o;if(t===this.positions.START)a=u.getDeepestNode(r,!1),l=0;else if(t===this.positions.END)a=u.getDeepestNode(r,!0),l=u.getContentLength(a);else{const{node:d,offset:h}=u.getNodeByOffset(r,o);d?(a=d,l=h):(a=u.getDeepestNode(r,!1),l=0)}this.set(a,l),i.setCurrentBlockByChildNode(e.holder),i.currentBlock.currentInput=r}setToInput(e,t=this.positions.DEFAULT,o=0){const{currentBlock:i}=this.Editor.BlockManager,s=u.getDeepestNode(e);switch(t){case this.positions.START:this.set(s,0);break;case this.positions.END:this.set(s,u.getContentLength(s));break;default:o&&this.set(s,o)}i.currentInput=e}set(e,t=0){const{top:i,bottom:s}=b.setCursor(e,t),{innerHeight:r}=window;i<0?window.scrollBy(0,i-30):s>r&&window.scrollBy(0,s-r+30)}setToTheLastBlock(){const e=this.Editor.BlockManager.lastBlock;if(e)if(e.tool.isDefault&&e.isEmpty)this.setToBlock(e);else{const t=this.Editor.BlockManager.insertAtEnd();this.setToBlock(t)}}extractFragmentFromCaretPosition(){const e=b.get();if(e.rangeCount){const t=e.getRangeAt(0),o=this.Editor.BlockManager.currentBlock.currentInput;if(t.deleteContents(),o)if(u.isNativeInput(o)){const i=o,s=document.createDocumentFragment(),r=i.value.substring(0,i.selectionStart),a=i.value.substring(i.selectionStart);return s.textContent=a,i.value=r,s}else{const i=t.cloneRange();return i.selectNodeContents(o),i.setStart(t.endContainer,t.endOffset),i.extractContents()}}}navigateNext(e=!1){const{BlockManager:t}=this.Editor,{currentBlock:o,nextBlock:i}=t;if(o===void 0)return!1;const{nextInput:s,currentInput:r}=o,a=r!==void 0?ze(r):void 0;let l=i;const c=e||a||!o.focusable;if(s&&c)return this.setToInput(s,this.positions.START),!0;if(l===null){if(o.tool.isDefault||!c)return!1;l=t.insertAtEnd()}return c?(this.setToBlock(l,this.positions.START),!0):!1}navigatePrevious(e=!1){const{currentBlock:t,previousBlock:o}=this.Editor.BlockManager;if(!t)return!1;const{previousInput:i,currentInput:s}=t,r=s!==void 0?$e(s):void 0,a=e||r||!t.focusable;return i&&a?(this.setToInput(i,this.positions.END),!0):o!==null&&a?(this.setToBlock(o,this.positions.END),!0):!1}createShadow(e){const t=document.createElement("span");t.classList.add(et.CSS.shadowCaret),e.insertAdjacentElement("beforeend",t)}restoreCaret(e){const t=e.querySelector(`.${et.CSS.shadowCaret}`);if(!t)return;new b().expandToTag(t);const i=document.createRange();i.selectNode(t),i.extractContents()}insertContentAtCaretPosition(e){const t=document.createDocumentFragment(),o=document.createElement("div"),i=b.get(),s=b.range;o.innerHTML=e,Array.from(o.childNodes).forEach(c=>t.appendChild(c)),t.childNodes.length===0&&t.appendChild(new Text);const r=t.lastChild;s.deleteContents(),s.insertNode(t);const a=document.createRange(),l=r.nodeType===Node.TEXT_NODE?r:r.firstChild;l!==null&&l.textContent!==null&&a.setStart(l,l.textContent.length),i.removeAllRanges(),i.addRange(a)}}class ta extends E{constructor(){super(...arguments),this.onMouseUp=()=>{this.listeners.off(document,"mouseover",this.onMouseOver),this.listeners.off(document,"mouseup",this.onMouseUp)},this.onMouseOver=e=>{const{BlockManager:t,BlockSelection:o}=this.Editor;if(e.relatedTarget===null&&e.target===null)return;const i=t.getBlockByChildNode(e.relatedTarget)||this.lastSelectedBlock,s=t.getBlockByChildNode(e.target);if(!(!i||!s)&&s!==i){if(i===this.firstSelectedBlock){b.get().removeAllRanges(),i.selected=!0,s.selected=!0,o.clearCache();return}if(s===this.firstSelectedBlock){i.selected=!1,s.selected=!1,o.clearCache();return}this.Editor.InlineToolbar.close(),this.toggleBlocksSelectedState(i,s),this.lastSelectedBlock=s}}}async prepare(){this.listeners.on(document,"mousedown",e=>{this.enableCrossBlockSelection(e)})}watchSelection(e){if(e.button!==Xn.LEFT)return;const{BlockManager:t}=this.Editor;this.firstSelectedBlock=t.getBlock(e.target),this.lastSelectedBlock=this.firstSelectedBlock,this.listeners.on(document,"mouseover",this.onMouseOver),this.listeners.on(document,"mouseup",this.onMouseUp)}get isCrossBlockSelectionStarted(){return!!this.firstSelectedBlock&&!!this.lastSelectedBlock&&this.firstSelectedBlock!==this.lastSelectedBlock}toggleBlockSelectedState(e=!0){const{BlockManager:t,BlockSelection:o}=this.Editor;this.lastSelectedBlock||(this.lastSelectedBlock=this.firstSelectedBlock=t.currentBlock),this.firstSelectedBlock===this.lastSelectedBlock&&(this.firstSelectedBlock.selected=!0,o.clearCache(),b.get().removeAllRanges());const i=t.blocks.indexOf(this.lastSelectedBlock)+(e?1:-1),s=t.blocks[i];s&&(this.lastSelectedBlock.selected!==s.selected?(s.selected=!0,o.clearCache()):(this.lastSelectedBlock.selected=!1,o.clearCache()),this.lastSelectedBlock=s,this.Editor.InlineToolbar.close(),s.holder.scrollIntoView({block:"nearest"}))}clear(e){const{BlockManager:t,BlockSelection:o,Caret:i}=this.Editor,s=t.blocks.indexOf(this.firstSelectedBlock),r=t.blocks.indexOf(this.lastSelectedBlock);if(o.anyBlockSelected&&s>-1&&r>-1&&e&&e instanceof KeyboardEvent)switch(e.keyCode){case y.DOWN:case y.RIGHT:i.setToBlock(t.blocks[Math.max(s,r)],i.positions.END);break;case y.UP:case y.LEFT:i.setToBlock(t.blocks[Math.min(s,r)],i.positions.START);break;default:i.setToBlock(t.blocks[Math.max(s,r)],i.positions.END)}this.firstSelectedBlock=this.lastSelectedBlock=null}enableCrossBlockSelection(e){const{UI:t}=this.Editor;b.isCollapsed||this.Editor.BlockSelection.clearSelection(e),t.nodes.redactor.contains(e.target)?this.watchSelection(e):this.Editor.BlockSelection.clearSelection(e)}toggleBlocksSelectedState(e,t){const{BlockManager:o,BlockSelection:i}=this.Editor,s=o.blocks.indexOf(e),r=o.blocks.indexOf(t),a=e.selected!==t.selected;for(let l=Math.min(s,r);l<=Math.max(s,r);l++){const c=o.blocks[l];c!==this.firstSelectedBlock&&c!==(a?e:t)&&(o.blocks[l].selected=!o.blocks[l].selected,i.clearCache())}}}class oa extends E{constructor(){super(...arguments),this.isStartedAtEditor=!1}toggleReadOnly(e){e?this.disableModuleBindings():this.enableModuleBindings()}enableModuleBindings(){const{UI:e}=this.Editor;this.readOnlyMutableListeners.on(e.nodes.holder,"drop",async t=>{await this.processDrop(t)},!0),this.readOnlyMutableListeners.on(e.nodes.holder,"dragstart",()=>{this.processDragStart()}),this.readOnlyMutableListeners.on(e.nodes.holder,"dragover",t=>{this.processDragOver(t)},!0)}disableModuleBindings(){this.readOnlyMutableListeners.clearAll()}async processDrop(e){const{BlockManager:t,Paste:o,Caret:i}=this.Editor;e.preventDefault(),t.blocks.forEach(r=>{r.dropTarget=!1}),b.isAtEditor&&!b.isCollapsed&&this.isStartedAtEditor&&document.execCommand("delete"),this.isStartedAtEditor=!1;const s=t.setCurrentBlockByChildNode(e.target);if(s)this.Editor.Caret.setToBlock(s,i.positions.END);else{const r=t.setCurrentBlockByChildNode(t.lastBlock.holder);this.Editor.Caret.setToBlock(r,i.positions.END)}await o.processDataTransfer(e.dataTransfer,!0)}processDragStart(){b.isAtEditor&&!b.isCollapsed&&(this.isStartedAtEditor=!0),this.Editor.InlineToolbar.close()}processDragOver(e){e.preventDefault()}}const na=180,ia=400;class sa extends E{constructor({config:e,eventsDispatcher:t}){super({config:e,eventsDispatcher:t}),this.disabled=!1,this.batchingTimeout=null,this.batchingOnChangeQueue=new Map,this.batchTime=ia,this.mutationObserver=new MutationObserver(o=>{this.redactorChanged(o)}),this.eventsDispatcher.on(_o,o=>{this.particularBlockChanged(o.event)}),this.eventsDispatcher.on(Mo,()=>{this.disable()}),this.eventsDispatcher.on(Ao,()=>{this.enable()})}enable(){this.mutationObserver.observe(this.Editor.UI.nodes.redactor,{childList:!0,subtree:!0,characterData:!0,attributes:!0}),this.disabled=!1}disable(){this.mutationObserver.disconnect(),this.disabled=!0}particularBlockChanged(e){this.disabled||!M(this.config.onChange)||(this.batchingOnChangeQueue.set(`block:${e.detail.target.id}:event:${e.type}`,e),this.batchingTimeout&&clearTimeout(this.batchingTimeout),this.batchingTimeout=setTimeout(()=>{let t;this.batchingOnChangeQueue.size===1?t=this.batchingOnChangeQueue.values().next().value:t=Array.from(this.batchingOnChangeQueue.values()),this.config.onChange&&this.config.onChange(this.Editor.API.methods,t),this.batchingOnChangeQueue.clear()},this.batchTime))}redactorChanged(e){this.eventsDispatcher.emit(at,{mutations:e})}}const Ln=class Wn extends E{constructor(){super(...arguments),this.MIME_TYPE="application/x-editor-js",this.toolsTags={},this.tagsByTool={},this.toolsPatterns=[],this.toolsFiles={},this.exceptionList=[],this.processTool=e=>{try{const t=e.create({},{},!1);if(e.pasteConfig===!1){this.exceptionList.push(e.name);return}if(!M(t.onPaste))return;this.getTagsConfig(e),this.getFilesConfig(e),this.getPatternsConfig(e)}catch(t){T(`Paste handling for «${e.name}» Tool hasn't been set up because of the error`,"warn",t)}},this.handlePasteEvent=async e=>{const{BlockManager:t,Toolbar:o}=this.Editor,i=t.setCurrentBlockByChildNode(e.target);!i||this.isNativeBehaviour(e.target)&&!e.clipboardData.types.includes("Files")||i&&this.exceptionList.includes(i.name)||(e.preventDefault(),this.processDataTransfer(e.clipboardData),o.close())}}async prepare(){this.processTools()}toggleReadOnly(e){e?this.unsetCallback():this.setCallback()}async processDataTransfer(e,t=!1){const{Tools:o}=this.Editor,i=e.types;if((i.includes?i.includes("Files"):i.contains("Files"))&&!V(this.toolsFiles)){await this.processFiles(e.files);return}const r=e.getData(this.MIME_TYPE),a=e.getData("text/plain");let l=e.getData("text/html");if(r)try{this.insertEditorJSData(JSON.parse(r));return}catch{}t&&a.trim()&&l.trim()&&(l="

    "+(l.trim()?l:a)+"

    ");const c=Object.keys(this.toolsTags).reduce((p,g)=>(p[g.toLowerCase()]=this.toolsTags[g].sanitizationConfig??{},p),{}),d=Object.assign({},c,o.getAllInlineToolsSanitizeConfig(),{br:{}}),h=q(l,d);!h.trim()||h.trim()===a||!u.isHTMLString(h)?await this.processText(a):await this.processText(h,!0)}async processText(e,t=!1){const{Caret:o,BlockManager:i}=this.Editor,s=t?this.processHTML(e):this.processPlain(e);if(!s.length)return;if(s.length===1){s[0].isBlock?this.processSingleBlock(s.pop()):this.processInlinePaste(s.pop());return}const a=i.currentBlock&&i.currentBlock.tool.isDefault&&i.currentBlock.isEmpty;s.map(async(l,c)=>this.insertBlock(l,c===0&&a)),i.currentBlock&&o.setToBlock(i.currentBlock,o.positions.END)}setCallback(){this.listeners.on(this.Editor.UI.nodes.holder,"paste",this.handlePasteEvent)}unsetCallback(){this.listeners.off(this.Editor.UI.nodes.holder,"paste",this.handlePasteEvent)}processTools(){const e=this.Editor.Tools.blockTools;Array.from(e.values()).forEach(this.processTool)}collectTagNames(e){return Q(e)?[e]:N(e)?Object.keys(e):[]}getTagsConfig(e){if(e.pasteConfig===!1)return;const t=e.pasteConfig.tags||[],o=[];t.forEach(i=>{const s=this.collectTagNames(i);o.push(...s),s.forEach(r=>{if(Object.prototype.hasOwnProperty.call(this.toolsTags,r)){T(`Paste handler for «${e.name}» Tool on «${r}» tag is skipped because it is already used by «${this.toolsTags[r].tool.name}» Tool.`,"warn");return}const a=N(i)?i[r]:null;this.toolsTags[r.toUpperCase()]={tool:e,sanitizationConfig:a}})}),this.tagsByTool[e.name]=o.map(i=>i.toUpperCase())}getFilesConfig(e){if(e.pasteConfig===!1)return;const{files:t={}}=e.pasteConfig;let{extensions:o,mimeTypes:i}=t;!o&&!i||(o&&!Array.isArray(o)&&(T(`«extensions» property of the onDrop config for «${e.name}» Tool should be an array`),o=[]),i&&!Array.isArray(i)&&(T(`«mimeTypes» property of the onDrop config for «${e.name}» Tool should be an array`),i=[]),i&&(i=i.filter(s=>Jn(s)?!0:(T(`MIME type value «${s}» for the «${e.name}» Tool is not a valid MIME type`,"warn"),!1))),this.toolsFiles[e.name]={extensions:o||[],mimeTypes:i||[]})}getPatternsConfig(e){e.pasteConfig===!1||!e.pasteConfig.patterns||V(e.pasteConfig.patterns)||Object.entries(e.pasteConfig.patterns).forEach(([t,o])=>{o instanceof RegExp||T(`Pattern ${o} for «${e.name}» Tool is skipped because it should be a Regexp instance.`,"warn"),this.toolsPatterns.push({key:t,pattern:o,tool:e})})}isNativeBehaviour(e){return u.isNativeInput(e)}async processFiles(e){const{BlockManager:t}=this.Editor;let o;o=await Promise.all(Array.from(e).map(r=>this.processFile(r))),o=o.filter(r=>!!r);const s=t.currentBlock.tool.isDefault&&t.currentBlock.isEmpty;o.forEach((r,a)=>{t.paste(r.type,r.event,a===0&&s)})}async processFile(e){const t=Gn(e),o=Object.entries(this.toolsFiles).find(([r,{mimeTypes:a,extensions:l}])=>{const[c,d]=e.type.split("/"),h=l.find(g=>g.toLowerCase()===t.toLowerCase()),p=a.find(g=>{const[f,v]=g.split("/");return f===c&&(v===d||v==="*")});return!!h||!!p});if(!o)return;const[i]=o;return{event:this.composePasteEvent("file",{file:e}),type:i}}processHTML(e){const{Tools:t}=this.Editor,o=u.make("DIV");return o.innerHTML=e,this.getNodes(o).map(s=>{let r,a=t.defaultTool,l=!1;switch(s.nodeType){case Node.DOCUMENT_FRAGMENT_NODE:r=u.make("div"),r.appendChild(s);break;case Node.ELEMENT_NODE:r=s,l=!0,this.toolsTags[r.tagName]&&(a=this.toolsTags[r.tagName].tool);break}const{tags:c}=a.pasteConfig||{tags:[]},d=c.reduce((g,f)=>(this.collectTagNames(f).forEach(O=>{const S=N(f)?f[O]:null;g[O.toLowerCase()]=S||{}}),g),{}),h=Object.assign({},d,a.baseSanitizeConfig);if(r.tagName.toLowerCase()==="table"){const g=q(r.outerHTML,h);r=u.make("div",void 0,{innerHTML:g}).firstChild}else r.innerHTML=q(r.innerHTML,h);const p=this.composePasteEvent("tag",{data:r});return{content:r,isBlock:l,tool:a.name,event:p}}).filter(s=>{const r=u.isEmpty(s.content),a=u.isSingleTag(s.content);return!r||a})}processPlain(e){const{defaultBlock:t}=this.config;if(!e)return[];const o=t;return e.split(/\r?\n/).filter(i=>i.trim()).map(i=>{const s=u.make("div");s.textContent=i;const r=this.composePasteEvent("tag",{data:s});return{content:s,tool:o,isBlock:!1,event:r}})}async processSingleBlock(e){const{Caret:t,BlockManager:o}=this.Editor,{currentBlock:i}=o;if(!i||e.tool!==i.name||!u.containsOnlyInlineElements(e.content.innerHTML)){this.insertBlock(e,(i==null?void 0:i.tool.isDefault)&&i.isEmpty);return}t.insertContentAtCaretPosition(e.content.innerHTML)}async processInlinePaste(e){const{BlockManager:t,Caret:o}=this.Editor,{content:i}=e;if(t.currentBlock&&t.currentBlock.tool.isDefault&&i.textContent.length{const s=i.pattern.exec(e);return s?e===s.shift():!1});return t?{event:this.composePasteEvent("pattern",{key:t.key,data:e}),tool:t.tool.name}:void 0}insertBlock(e,t=!1){const{BlockManager:o,Caret:i}=this.Editor,{currentBlock:s}=o;let r;if(t&&s&&s.isEmpty){r=o.paste(e.tool,e.event,!0),i.setToBlock(r,i.positions.END);return}r=o.paste(e.tool,e.event),i.setToBlock(r,i.positions.END)}insertEditorJSData(e){const{BlockManager:t,Caret:o,Tools:i}=this.Editor;dt(e,r=>i.blockTools.get(r).sanitizeConfig).forEach(({tool:r,data:a},l)=>{let c=!1;l===0&&(c=t.currentBlock&&t.currentBlock.tool.isDefault&&t.currentBlock.isEmpty);const d=t.insert({tool:r,data:a,replace:c});o.setToBlock(d,o.positions.END)})}processElementNode(e,t,o){const i=Object.keys(this.toolsTags),s=e,{tool:r}=this.toolsTags[s.tagName]||{},a=this.tagsByTool[r==null?void 0:r.name]||[],l=i.includes(s.tagName),c=u.blockElements.includes(s.tagName.toLowerCase()),d=Array.from(s.children).some(({tagName:p})=>i.includes(p)&&!a.includes(p)),h=Array.from(s.children).some(({tagName:p})=>u.blockElements.includes(p.toLowerCase()));if(!c&&!l&&!d)return o.appendChild(s),[...t,o];if(l&&!d||c&&!h&&!d)return[...t,o,s]}getNodes(e){const t=Array.from(e.childNodes);let o;const i=(s,r)=>{if(u.isEmpty(r)&&!u.isSingleTag(r))return s;const a=s[s.length-1];let l=new DocumentFragment;switch(a&&u.isFragment(a)&&(l=s.pop()),r.nodeType){case Node.ELEMENT_NODE:if(o=this.processElementNode(r,s,l),o)return o;break;case Node.TEXT_NODE:return l.appendChild(r),[...s,l];default:return[...s,l]}return[...s,...Array.from(r.childNodes).reduce(i,[])]};return t.reduce(i,[])}composePasteEvent(e,t){return new CustomEvent(e,{detail:t})}};Ln.PATTERN_PROCESSING_MAX_LENGTH=450;let ra=Ln;class aa extends E{constructor(){super(...arguments),this.toolsDontSupportReadOnly=[],this.readOnlyEnabled=!1}get isEnabled(){return this.readOnlyEnabled}async prepare(){const{Tools:e}=this.Editor,{blockTools:t}=e,o=[];Array.from(t.entries()).forEach(([i,s])=>{s.isReadOnlySupported||o.push(i)}),this.toolsDontSupportReadOnly=o,this.config.readOnly&&o.length>0&&this.throwCriticalError(),this.toggle(this.config.readOnly,!0)}async toggle(e=!this.readOnlyEnabled,t=!1){e&&this.toolsDontSupportReadOnly.length>0&&this.throwCriticalError();const o=this.readOnlyEnabled;this.readOnlyEnabled=e;for(const s in this.Editor)this.Editor[s].toggleReadOnly&&this.Editor[s].toggleReadOnly(e);if(o===e)return this.readOnlyEnabled;if(t)return this.readOnlyEnabled;this.Editor.ModificationsObserver.disable();const i=await this.Editor.Saver.save();return await this.Editor.BlockManager.clear(),await this.Editor.Renderer.render(i.blocks),this.Editor.ModificationsObserver.enable(),this.readOnlyEnabled}throwCriticalError(){throw new Oo(`To enable read-only mode all connected tools should support it. Tools ${this.toolsDontSupportReadOnly.join(", ")} don't support read-only mode.`)}}class Ie extends E{constructor(){super(...arguments),this.isRectSelectionActivated=!1,this.SCROLL_SPEED=3,this.HEIGHT_OF_SCROLL_ZONE=40,this.BOTTOM_SCROLL_ZONE=1,this.TOP_SCROLL_ZONE=2,this.MAIN_MOUSE_BUTTON=0,this.mousedown=!1,this.isScrolling=!1,this.inScrollZone=null,this.startX=0,this.startY=0,this.mouseX=0,this.mouseY=0,this.stackOfSelected=[],this.listenerIds=[]}static get CSS(){return{overlay:"codex-editor-overlay",overlayContainer:"codex-editor-overlay__container",rect:"codex-editor-overlay__rectangle",topScrollZone:"codex-editor-overlay__scroll-zone--top",bottomScrollZone:"codex-editor-overlay__scroll-zone--bottom"}}prepare(){this.enableModuleBindings()}startSelection(e,t){const o=document.elementFromPoint(e-window.pageXOffset,t-window.pageYOffset);o.closest(`.${this.Editor.Toolbar.CSS.toolbar}`)||(this.Editor.BlockSelection.allBlocksSelected=!1,this.clearSelection(),this.stackOfSelected=[]);const s=[`.${R.CSS.content}`,`.${this.Editor.Toolbar.CSS.toolbar}`,`.${this.Editor.InlineToolbar.CSS.inlineToolbar}`],r=o.closest("."+this.Editor.UI.CSS.editorWrapper),a=s.some(l=>!!o.closest(l));!r||a||(this.mousedown=!0,this.startX=e,this.startY=t)}endSelection(){this.mousedown=!1,this.startX=0,this.startY=0,this.overlayRectangle.style.display="none"}isRectActivated(){return this.isRectSelectionActivated}clearSelection(){this.isRectSelectionActivated=!1}enableModuleBindings(){const{container:e}=this.genHTML();this.listeners.on(e,"mousedown",t=>{this.processMouseDown(t)},!1),this.listeners.on(document.body,"mousemove",ot(t=>{this.processMouseMove(t)},10),{passive:!0}),this.listeners.on(document.body,"mouseleave",()=>{this.processMouseLeave()}),this.listeners.on(window,"scroll",ot(t=>{this.processScroll(t)},10),{passive:!0}),this.listeners.on(document.body,"mouseup",()=>{this.processMouseUp()},!1)}processMouseDown(e){if(e.button!==this.MAIN_MOUSE_BUTTON)return;e.target.closest(u.allInputsSelector)!==null||this.startSelection(e.pageX,e.pageY)}processMouseMove(e){this.changingRectangle(e),this.scrollByZones(e.clientY)}processMouseLeave(){this.clearSelection(),this.endSelection()}processScroll(e){this.changingRectangle(e)}processMouseUp(){this.clearSelection(),this.endSelection()}scrollByZones(e){if(this.inScrollZone=null,e<=this.HEIGHT_OF_SCROLL_ZONE&&(this.inScrollZone=this.TOP_SCROLL_ZONE),document.documentElement.clientHeight-e<=this.HEIGHT_OF_SCROLL_ZONE&&(this.inScrollZone=this.BOTTOM_SCROLL_ZONE),!this.inScrollZone){this.isScrolling=!1;return}this.isScrolling||(this.scrollVertical(this.inScrollZone===this.TOP_SCROLL_ZONE?-this.SCROLL_SPEED:this.SCROLL_SPEED),this.isScrolling=!0)}genHTML(){const{UI:e}=this.Editor,t=e.nodes.holder.querySelector("."+e.CSS.editorWrapper),o=u.make("div",Ie.CSS.overlay,{}),i=u.make("div",Ie.CSS.overlayContainer,{}),s=u.make("div",Ie.CSS.rect,{});return i.appendChild(s),o.appendChild(i),t.appendChild(o),this.overlayRectangle=s,{container:t,overlay:o}}scrollVertical(e){if(!(this.inScrollZone&&this.mousedown))return;const t=window.pageYOffset;window.scrollBy(0,e),this.mouseY+=window.pageYOffset-t,setTimeout(()=>{this.scrollVertical(e)},0)}changingRectangle(e){if(!this.mousedown)return;e.pageY!==void 0&&(this.mouseX=e.pageX,this.mouseY=e.pageY);const{rightPos:t,leftPos:o,index:i}=this.genInfoForMouseSelection(),s=this.startX>t&&this.mouseX>t,r=this.startX=this.startY?(this.overlayRectangle.style.top=`${this.startY-window.pageYOffset}px`,this.overlayRectangle.style.bottom=`calc(100% - ${this.mouseY-window.pageYOffset}px`):(this.overlayRectangle.style.bottom=`calc(100% - ${this.startY-window.pageYOffset}px`,this.overlayRectangle.style.top=`${this.mouseY-window.pageYOffset}px`),this.mouseX>=this.startX?(this.overlayRectangle.style.left=`${this.startX-window.pageXOffset}px`,this.overlayRectangle.style.right=`calc(100% - ${this.mouseX-window.pageXOffset}px`):(this.overlayRectangle.style.right=`calc(100% - ${this.startX-window.pageXOffset}px`,this.overlayRectangle.style.left=`${this.mouseX-window.pageXOffset}px`)}genInfoForMouseSelection(){const t=document.body.offsetWidth/2,o=this.mouseY-window.pageYOffset,i=document.elementFromPoint(t,o),s=this.Editor.BlockManager.getBlockByChildNode(i);let r;s!==void 0&&(r=this.Editor.BlockManager.blocks.findIndex(h=>h.holder===s.holder));const a=this.Editor.BlockManager.lastBlock.holder.querySelector("."+R.CSS.content),l=Number.parseInt(window.getComputedStyle(a).width,10)/2,c=t-l,d=t+l;return{index:r,leftPos:c,rightPos:d}}addBlockInSelection(e){this.rectCrossesBlocks&&this.Editor.BlockSelection.selectBlockByIndex(e),this.stackOfSelected.push(e)}trySelectNextBlock(e){const t=this.stackOfSelected[this.stackOfSelected.length-1]===e,o=this.stackOfSelected.length,i=1,s=-1,r=0;if(t)return;const a=this.stackOfSelected[o-1]-this.stackOfSelected[o-2]>0;let l=r;o>1&&(l=a?i:s);const c=e>this.stackOfSelected[o-1]&&l===i,d=ethis.stackOfSelected[o-1]||this.stackOfSelected[o-1]===void 0)){let v=this.stackOfSelected[o-1]+1||e;for(v;v<=e;v++)this.addBlockInSelection(v);return}if(!p&&e=e;v--)this.addBlockInSelection(v);return}if(!p)return;let g=o-1,f;for(e>this.stackOfSelected[o-1]?f=()=>e>this.stackOfSelected[g]:f=()=>e{const{Tools:o,BlockManager:i}=this.Editor;if(e.length===0)i.insert();else{const s=e.map(({type:r,data:a,tunes:l,id:c})=>{o.available.has(r)===!1&&(X(`Tool «${r}» is not found. Check 'tools' property at the Editor.js config.`,"warn"),a=this.composeStubDataForTool(r,a,c),r=o.stubTool);let d;try{d=i.composeBlock({id:c,tool:r,data:a,tunes:l})}catch(h){T(`Block «${r}» skipped because of plugins error`,"error",{data:a,error:h}),a=this.composeStubDataForTool(r,a,c),r=o.stubTool,d=i.composeBlock({id:c,tool:r,data:a,tunes:l})}return d});i.insertMany(s)}window.requestIdleCallback(()=>{t()},{timeout:2e3})})}composeStubDataForTool(e,t,o){const{Tools:i}=this.Editor;let s=e;if(i.unavailable.has(e)){const r=i.unavailable.get(e).toolbox;r!==void 0&&r[0].title!==void 0&&(s=r[0].title)}return{savedData:{id:o,type:e,data:t},title:s}}}class ca extends E{async save(){const{BlockManager:e,Tools:t}=this.Editor,o=e.blocks,i=[];try{o.forEach(a=>{i.push(this.getSavedData(a))});const s=await Promise.all(i),r=await dt(s,a=>t.blockTools.get(a).sanitizeConfig);return this.makeOutput(r)}catch(s){X("Saving failed due to the Error %o","error",s)}}async getSavedData(e){const t=await e.save(),o=t&&await e.validate(t.data);return{...t,isValid:o}}makeOutput(e){const t=[];return e.forEach(({id:o,tool:i,data:s,tunes:r,isValid:a})=>{if(!a){T(`Block «${i}» skipped because saved data is invalid`);return}if(i===this.Editor.Tools.stubTool){t.push(s);return}const l={id:o,type:i,data:s,...!V(r)&&{tunes:r}};t.push(l)}),{time:+new Date,blocks:t,version:"2.31.1"}}}(function(){try{if(typeof document<"u"){var n=document.createElement("style");n.appendChild(document.createTextNode(".ce-paragraph{line-height:1.6em;outline:none}.ce-block:only-of-type .ce-paragraph[data-placeholder-active]:empty:before,.ce-block:only-of-type .ce-paragraph[data-placeholder-active][data-empty=true]:before{content:attr(data-placeholder-active)}.ce-paragraph p:first-of-type{margin-top:0}.ce-paragraph p:last-of-type{margin-bottom:0}")),document.head.appendChild(n)}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();const da='';function ua(n){const e=document.createElement("div");e.innerHTML=n.trim();const t=document.createDocumentFragment();return t.append(...Array.from(e.childNodes)),t}/** + * Base Paragraph Block for the Editor.js. + * Represents a regular text block + * + * @author CodeX (team@codex.so) + * @copyright CodeX 2018 + * @license The MIT License (MIT) + */class po{static get DEFAULT_PLACEHOLDER(){return""}constructor({data:e,config:t,api:o,readOnly:i}){this.api=o,this.readOnly=i,this._CSS={block:this.api.styles.block,wrapper:"ce-paragraph"},this.readOnly||(this.onKeyUp=this.onKeyUp.bind(this)),this._placeholder=t.placeholder?t.placeholder:po.DEFAULT_PLACEHOLDER,this._data=e??{},this._element=null,this._preserveBlank=t.preserveBlank??!1}onKeyUp(e){if(e.code!=="Backspace"&&e.code!=="Delete"||!this._element)return;const{textContent:t}=this._element;t===""&&(this._element.innerHTML="")}drawView(){const e=document.createElement("DIV");return e.classList.add(this._CSS.wrapper,this._CSS.block),e.contentEditable="false",e.dataset.placeholderActive=this.api.i18n.t(this._placeholder),this._data.text&&(e.innerHTML=this._data.text),this.readOnly||(e.contentEditable="true",e.addEventListener("keyup",this.onKeyUp)),e}render(){return this._element=this.drawView(),this._element}merge(e){if(!this._element)return;this._data.text+=e.text;const t=ua(e.text);this._element.appendChild(t),this._element.normalize()}validate(e){return!(e.text.trim()===""&&!this._preserveBlank)}save(e){return{text:e.innerHTML}}onPaste(e){const t={text:e.detail.data.innerHTML};this._data=t,window.requestAnimationFrame(()=>{this._element&&(this._element.innerHTML=this._data.text||"")})}static get conversionConfig(){return{export:"text",import:"text"}}static get sanitize(){return{text:{br:!0}}}static get isReadOnlySupported(){return!0}static get pasteConfig(){return{tags:["P"]}}static get toolbox(){return{icon:da,title:"Text"}}}class fo{constructor(){this.commandName="bold"}static get sanitize(){return{b:{}}}render(){return{icon:Hi,name:"bold",onActivate:()=>{document.execCommand(this.commandName)},isActive:()=>document.queryCommandState(this.commandName)}}get shortcut(){return"CMD+B"}}fo.isInline=!0,fo.title="Bold";class go{constructor(){this.commandName="italic",this.CSS={button:"ce-inline-tool",buttonActive:"ce-inline-tool--active",buttonModifier:"ce-inline-tool--italic"},this.nodes={button:null}}static get sanitize(){return{i:{}}}render(){return this.nodes.button=document.createElement("button"),this.nodes.button.type="button",this.nodes.button.classList.add(this.CSS.button,this.CSS.buttonModifier),this.nodes.button.innerHTML=Xi,this.nodes.button}surround(){document.execCommand(this.commandName)}checkState(){const e=document.queryCommandState(this.commandName);return this.nodes.button.classList.toggle(this.CSS.buttonActive,e),e}get shortcut(){return"CMD+I"}}go.isInline=!0,go.title="Italic";class mo{constructor({api:e}){this.commandLink="createLink",this.commandUnlink="unlink",this.ENTER_KEY=13,this.CSS={button:"ce-inline-tool",buttonActive:"ce-inline-tool--active",buttonModifier:"ce-inline-tool--link",buttonUnlink:"ce-inline-tool--unlink",input:"ce-inline-tool-input",inputShowed:"ce-inline-tool-input--showed"},this.nodes={button:null,input:null},this.inputOpened=!1,this.toolbar=e.toolbar,this.inlineToolbar=e.inlineToolbar,this.notifier=e.notifier,this.i18n=e.i18n,this.selection=new b}static get sanitize(){return{a:{href:!0,target:"_blank",rel:"nofollow"}}}render(){return this.nodes.button=document.createElement("button"),this.nodes.button.type="button",this.nodes.button.classList.add(this.CSS.button,this.CSS.buttonModifier),this.nodes.button.innerHTML=zo,this.nodes.button}renderActions(){return this.nodes.input=document.createElement("input"),this.nodes.input.placeholder=this.i18n.t("Add a link"),this.nodes.input.enterKeyHint="done",this.nodes.input.classList.add(this.CSS.input),this.nodes.input.addEventListener("keydown",e=>{e.keyCode===this.ENTER_KEY&&this.enterPressed(e)}),this.nodes.input}surround(e){if(e){this.inputOpened?(this.selection.restore(),this.selection.removeFakeBackground()):(this.selection.setFakeBackground(),this.selection.save());const t=this.selection.findParentTag("A");if(t){this.selection.expandToTag(t),this.unlink(),this.closeActions(),this.checkState(),this.toolbar.close();return}}this.toggleActions()}checkState(){const e=this.selection.findParentTag("A");if(e){this.nodes.button.innerHTML=Gi,this.nodes.button.classList.add(this.CSS.buttonUnlink),this.nodes.button.classList.add(this.CSS.buttonActive),this.openActions();const t=e.getAttribute("href");this.nodes.input.value=t!=="null"?t:"",this.selection.save()}else this.nodes.button.innerHTML=zo,this.nodes.button.classList.remove(this.CSS.buttonUnlink),this.nodes.button.classList.remove(this.CSS.buttonActive);return!!e}clear(){this.closeActions()}get shortcut(){return"CMD+K"}toggleActions(){this.inputOpened?this.closeActions(!1):this.openActions(!0)}openActions(e=!1){this.nodes.input.classList.add(this.CSS.inputShowed),e&&this.nodes.input.focus(),this.inputOpened=!0}closeActions(e=!0){if(this.selection.isFakeBackgroundEnabled){const t=new b;t.save(),this.selection.restore(),this.selection.removeFakeBackground(),t.restore()}this.nodes.input.classList.remove(this.CSS.inputShowed),this.nodes.input.value="",e&&this.selection.clearSaved(),this.inputOpened=!1}enterPressed(e){let t=this.nodes.input.value||"";if(!t.trim()){this.selection.restore(),this.unlink(),e.preventDefault(),this.closeActions();return}if(!this.validateURL(t)){this.notifier.show({message:"Pasted link is not valid.",style:"error"}),T("Incorrect Link pasted","warn",t);return}t=this.prepareLink(t),this.selection.restore(),this.selection.removeFakeBackground(),this.insertLink(t),e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),this.selection.collapseToEnd(),this.inlineToolbar.close()}validateURL(e){return!/\s/.test(e)}prepareLink(e){return e=e.trim(),e=this.addProtocol(e),e}addProtocol(e){if(/^(\w+):(\/\/)?/.test(e))return e;const t=/^\/[^/\s]/.test(e),o=e.substring(0,1)==="#",i=/^\/\/[^/\s]/.test(e);return!t&&!o&&!i&&(e="http://"+e),e}insertLink(e){const t=this.selection.findParentTag("A");t&&this.selection.expandToTag(t),document.execCommand(this.commandLink,!1,e)}unlink(){document.execCommand(this.commandUnlink)}}mo.isInline=!0,mo.title="Link";class Pn{constructor({api:e}){this.i18nAPI=e.i18n,this.blocksAPI=e.blocks,this.selectionAPI=e.selection,this.toolsAPI=e.tools,this.caretAPI=e.caret}async render(){const e=b.get(),t=this.blocksAPI.getBlockByElement(e.anchorNode);if(t===void 0)return[];const o=this.toolsAPI.getBlockTools(),i=await Po(t,o);if(i.length===0)return[];const s=i.reduce((c,d)=>{var h;return(h=d.toolbox)==null||h.forEach(p=>{c.push({icon:p.icon,title:H.t(W.toolNames,p.title),name:d.name,closeOnActivate:!0,onActivate:async()=>{const g=await this.blocksAPI.convert(t.id,d.name,p.data);this.caretAPI.setToBlock(g,"end")}})}),c},[]),r=await t.getActiveToolboxEntry(),a=r!==void 0?r.icon:Uo,l=!ge();return{icon:a,name:"convert-to",hint:{title:this.i18nAPI.t("Convert to")},children:{searchable:l,items:s,onOpen:()=>{l&&(this.selectionAPI.setFakeBackground(),this.selectionAPI.save())},onClose:()=>{l&&(this.selectionAPI.restore(),this.selectionAPI.removeFakeBackground())}}}}}Pn.isInline=!0;class Nn{constructor({data:e,api:t}){this.CSS={wrapper:"ce-stub",info:"ce-stub__info",title:"ce-stub__title",subtitle:"ce-stub__subtitle"},this.api=t,this.title=e.title||this.api.i18n.t("Error"),this.subtitle=this.api.i18n.t("The block can not be displayed correctly."),this.savedData=e.savedData,this.wrapper=this.make()}render(){return this.wrapper}save(){return this.savedData}make(){const e=u.make("div",this.CSS.wrapper),t=Ji,o=u.make("div",this.CSS.info),i=u.make("div",this.CSS.title,{textContent:this.title}),s=u.make("div",this.CSS.subtitle,{textContent:this.subtitle});return e.innerHTML=t,o.appendChild(i),o.appendChild(s),e.appendChild(o),e}}Nn.isReadOnlySupported=!0;class ha extends xt{constructor(){super(...arguments),this.type=ce.Inline}get title(){return this.constructable[He.Title]}create(){return new this.constructable({api:this.api,config:this.settings})}get isReadOnlySupported(){return this.constructable[He.IsReadOnlySupported]??!1}}class pa extends xt{constructor(){super(...arguments),this.type=ce.Tune}create(e,t){return new this.constructable({api:this.api,config:this.settings,block:t,data:e})}}class F extends Map{get blockTools(){const e=Array.from(this.entries()).filter(([,t])=>t.isBlock());return new F(e)}get inlineTools(){const e=Array.from(this.entries()).filter(([,t])=>t.isInline());return new F(e)}get blockTunes(){const e=Array.from(this.entries()).filter(([,t])=>t.isTune());return new F(e)}get internalTools(){const e=Array.from(this.entries()).filter(([,t])=>t.isInternal);return new F(e)}get externalTools(){const e=Array.from(this.entries()).filter(([,t])=>!t.isInternal);return new F(e)}}var fa=Object.defineProperty,ga=Object.getOwnPropertyDescriptor,Rn=(n,e,t,o)=>{for(var i=o>1?void 0:o?ga(e,t):e,s=n.length-1,r;s>=0;s--)(r=n[s])&&(i=(o?r(e,t,i):r(i))||i);return o&&i&&fa(e,t,i),i};class bo extends xt{constructor(){super(...arguments),this.type=ce.Block,this.inlineTools=new F,this.tunes=new F}create(e,t,o){return new this.constructable({data:e,block:t,readOnly:o,api:this.api,config:this.settings})}get isReadOnlySupported(){return this.constructable[ve.IsReadOnlySupported]===!0}get isLineBreaksEnabled(){return this.constructable[ve.IsEnabledLineBreaks]}get toolbox(){const e=this.constructable[ve.Toolbox],t=this.config[je.Toolbox];if(!V(e)&&t!==!1)return t?Array.isArray(e)?Array.isArray(t)?t.map((o,i)=>{const s=e[i];return s?{...s,...o}:o}):[t]:Array.isArray(t)?t:[{...e,...t}]:Array.isArray(e)?e:[e]}get conversionConfig(){return this.constructable[ve.ConversionConfig]}get enabledInlineTools(){return this.config[je.EnabledInlineTools]||!1}get enabledBlockTunes(){return this.config[je.EnabledBlockTunes]}get pasteConfig(){return this.constructable[ve.PasteConfig]??{}}get sanitizeConfig(){const e=super.sanitizeConfig,t=this.baseSanitizeConfig;if(V(e))return t;const o={};for(const i in e)if(Object.prototype.hasOwnProperty.call(e,i)){const s=e[i];N(s)?o[i]=Object.assign({},t,s):o[i]=s}return o}get baseSanitizeConfig(){const e={};return Array.from(this.inlineTools.values()).forEach(t=>Object.assign(e,t.sanitizeConfig)),Array.from(this.tunes.values()).forEach(t=>Object.assign(e,t.sanitizeConfig)),e}}Rn([fe],bo.prototype,"sanitizeConfig",1),Rn([fe],bo.prototype,"baseSanitizeConfig",1);class ma{constructor(e,t,o){this.api=o,this.config=e,this.editorConfig=t}get(e){const{class:t,isInternal:o=!1,...i}=this.config[e],s=this.getConstructor(t),r=t[Et.IsTune];return new s({name:e,constructable:t,config:i,api:this.api.getMethodsForTool(e,r),isDefault:e===this.editorConfig.defaultBlock,defaultPlaceholder:this.editorConfig.placeholder,isInternal:o})}getConstructor(e){switch(!0){case e[He.IsInline]:return ha;case e[Et.IsTune]:return pa;default:return bo}}}class Dn{constructor({api:e}){this.CSS={animation:"wobble"},this.api=e}render(){return{icon:$i,title:this.api.i18n.t("Move down"),onActivate:()=>this.handleClick(),name:"move-down"}}handleClick(){const e=this.api.blocks.getCurrentBlockIndex(),t=this.api.blocks.getBlockByIndex(e+1);if(!t)throw new Error("Unable to move Block down since it is already the last");const o=t.holder,i=o.getBoundingClientRect();let s=Math.abs(window.innerHeight-o.offsetHeight);i.topthis.handleClick()}}}handleClick(){this.api.blocks.delete()}}Fn.isTune=!0;class jn{constructor({api:e}){this.CSS={animation:"wobble"},this.api=e}render(){return{icon:Wi,title:this.api.i18n.t("Move up"),onActivate:()=>this.handleClick(),name:"move-up"}}handleClick(){const e=this.api.blocks.getCurrentBlockIndex(),t=this.api.blocks.getBlockByIndex(e),o=this.api.blocks.getBlockByIndex(e-1);if(e===0||!t||!o)throw new Error("Unable to move Block up since it is already the first");const i=t.holder,s=o.holder,r=i.getBoundingClientRect(),a=s.getBoundingClientRect();let l;a.top>0?l=Math.abs(r.top)-Math.abs(a.top):l=Math.abs(r.top)+a.height,window.scrollBy(0,-1*l),this.api.blocks.move(e-1),this.api.toolbar.toggleBlockSettings(!0)}}jn.isTune=!0;var ba=Object.defineProperty,va=Object.getOwnPropertyDescriptor,ka=(n,e,t,o)=>{for(var i=o>1?void 0:o?va(e,t):e,s=n.length-1,r;s>=0;s--)(r=n[s])&&(i=(o?r(e,t,i):r(i))||i);return o&&i&&ba(e,t,i),i};class Hn extends E{constructor(){super(...arguments),this.stubTool="stub",this.toolsAvailable=new F,this.toolsUnavailable=new F}get available(){return this.toolsAvailable}get unavailable(){return this.toolsUnavailable}get inlineTools(){return this.available.inlineTools}get blockTools(){return this.available.blockTools}get blockTunes(){return this.available.blockTunes}get defaultTool(){return this.blockTools.get(this.config.defaultBlock)}get internal(){return this.available.internalTools}async prepare(){if(this.validateTools(),this.config.tools=nt({},this.internalTools,this.config.tools),!Object.prototype.hasOwnProperty.call(this.config,"tools")||Object.keys(this.config.tools).length===0)throw Error("Can't start without tools");const e=this.prepareConfig();this.factory=new ma(e,this.config,this.Editor.API);const t=this.getListOfPrepareFunctions(e);if(t.length===0)return Promise.resolve();await Zn(t,o=>{this.toolPrepareMethodSuccess(o)},o=>{this.toolPrepareMethodFallback(o)}),this.prepareBlockTools()}getAllInlineToolsSanitizeConfig(){const e={};return Array.from(this.inlineTools.values()).forEach(t=>{Object.assign(e,t.sanitizeConfig)}),e}destroy(){Object.values(this.available).forEach(async e=>{M(e.reset)&&await e.reset()})}get internalTools(){return{convertTo:{class:Pn,isInternal:!0},link:{class:mo,isInternal:!0},bold:{class:fo,isInternal:!0},italic:{class:go,isInternal:!0},paragraph:{class:po,inlineToolbar:!0,isInternal:!0},stub:{class:Nn,isInternal:!0},moveUp:{class:jn,isInternal:!0},delete:{class:Fn,isInternal:!0},moveDown:{class:Dn,isInternal:!0}}}toolPrepareMethodSuccess(e){const t=this.factory.get(e.toolName);if(t.isInline()){const i=["render"].filter(s=>!t.create()[s]);if(i.length){T(`Incorrect Inline Tool: ${t.name}. Some of required methods is not implemented %o`,"warn",i),this.toolsUnavailable.set(t.name,t);return}}this.toolsAvailable.set(t.name,t)}toolPrepareMethodFallback(e){this.toolsUnavailable.set(e.toolName,this.factory.get(e.toolName))}getListOfPrepareFunctions(e){const t=[];return Object.entries(e).forEach(([o,i])=>{t.push({function:M(i.class.prepare)?i.class.prepare:()=>{},data:{toolName:o,config:i.config}})}),t}prepareBlockTools(){Array.from(this.blockTools.values()).forEach(e=>{this.assignInlineToolsToBlockTool(e),this.assignBlockTunesToBlockTool(e)})}assignInlineToolsToBlockTool(e){if(this.config.inlineToolbar!==!1){if(e.enabledInlineTools===!0){e.inlineTools=new F(Array.isArray(this.config.inlineToolbar)?this.config.inlineToolbar.map(t=>[t,this.inlineTools.get(t)]):Array.from(this.inlineTools.entries()));return}Array.isArray(e.enabledInlineTools)&&(e.inlineTools=new F(["convertTo",...e.enabledInlineTools].map(t=>[t,this.inlineTools.get(t)])))}}assignBlockTunesToBlockTool(e){if(e.enabledBlockTunes!==!1){if(Array.isArray(e.enabledBlockTunes)){const t=new F(e.enabledBlockTunes.map(o=>[o,this.blockTunes.get(o)]));e.tunes=new F([...t,...this.blockTunes.internalTools]);return}if(Array.isArray(this.config.tunes)){const t=new F(this.config.tunes.map(o=>[o,this.blockTunes.get(o)]));e.tunes=new F([...t,...this.blockTunes.internalTools]);return}e.tunes=this.blockTunes.internalTools}}validateTools(){for(const e in this.config.tools)if(Object.prototype.hasOwnProperty.call(this.config.tools,e)){if(e in this.internalTools)return;const t=this.config.tools[e];if(!M(t)&&!M(t.class))throw Error(`Tool «${e}» must be a constructor function or an object with function in the «class» property`)}}prepareConfig(){const e={};for(const t in this.config.tools)N(this.config.tools[t])?e[t]=this.config.tools[t]:e[t]={class:this.config.tools[t]};return e}}ka([fe],Hn.prototype,"getAllInlineToolsSanitizeConfig",1);const ya=`:root{--selectionColor: #e1f2ff;--inlineSelectionColor: #d4ecff;--bg-light: #eff2f5;--grayText: #707684;--color-dark: #1D202B;--color-active-icon: #388AE5;--color-gray-border: rgba(201, 201, 204, .48);--content-width: 650px;--narrow-mode-right-padding: 50px;--toolbox-buttons-size: 26px;--toolbox-buttons-size--mobile: 36px;--icon-size: 20px;--icon-size--mobile: 28px;--block-padding-vertical: .4em;--color-line-gray: #EFF0F1 }.codex-editor{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;z-index:1}.codex-editor .hide{display:none}.codex-editor__redactor [contenteditable]:empty:after{content:"\\feff"}@media (min-width: 651px){.codex-editor--narrow .codex-editor__redactor{margin-right:50px}}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .codex-editor__redactor{margin-left:50px;margin-right:0}}@media (min-width: 651px){.codex-editor--narrow .ce-toolbar__actions{right:-5px}}.codex-editor-copyable{position:absolute;height:1px;width:1px;top:-400%;opacity:.001}.codex-editor-overlay{position:fixed;top:0;left:0;right:0;bottom:0;z-index:999;pointer-events:none;overflow:hidden}.codex-editor-overlay__container{position:relative;pointer-events:auto;z-index:0}.codex-editor-overlay__rectangle{position:absolute;pointer-events:none;background-color:#2eaadc33;border:1px solid transparent}.codex-editor svg{max-height:100%}.codex-editor path{stroke:currentColor}.codex-editor ::-moz-selection{background-color:#d4ecff}.codex-editor ::selection{background-color:#d4ecff}.codex-editor--toolbox-opened [contentEditable=true][data-placeholder]:focus:before{opacity:0!important}.ce-scroll-locked{overflow:hidden}.ce-scroll-locked--hard{overflow:hidden;top:calc(-1 * var(--window-scroll-offset));position:fixed;width:100%}.ce-toolbar{position:absolute;left:0;right:0;top:0;-webkit-transition:opacity .1s ease;transition:opacity .1s ease;will-change:opacity,top;display:none}.ce-toolbar--opened{display:block}.ce-toolbar__content{max-width:650px;margin:0 auto;position:relative}.ce-toolbar__plus{color:#1d202b;cursor:pointer;width:26px;height:26px;border-radius:7px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-ms-flex-negative:0;flex-shrink:0}@media (max-width: 650px){.ce-toolbar__plus{width:36px;height:36px}}@media (hover: hover){.ce-toolbar__plus:hover{background-color:#eff2f5}}.ce-toolbar__plus--active{background-color:#eff2f5;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.ce-toolbar__plus-shortcut{opacity:.6;word-spacing:-2px;margin-top:5px}@media (max-width: 650px){.ce-toolbar__plus{position:absolute;background-color:#fff;border:1px solid #E8E8EB;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px #0d142121;border-radius:6px;z-index:2;position:static}.ce-toolbar__plus--left-oriented:before{left:15px;margin-left:0}.ce-toolbar__plus--right-oriented:before{left:auto;right:15px;margin-left:0}}.ce-toolbar__actions{position:absolute;right:100%;opacity:0;display:-webkit-box;display:-ms-flexbox;display:flex;padding-right:5px}.ce-toolbar__actions--opened{opacity:1}@media (max-width: 650px){.ce-toolbar__actions{right:auto}}.ce-toolbar__settings-btn{color:#1d202b;width:26px;height:26px;border-radius:7px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;margin-left:3px;cursor:pointer;user-select:none}@media (max-width: 650px){.ce-toolbar__settings-btn{width:36px;height:36px}}@media (hover: hover){.ce-toolbar__settings-btn:hover{background-color:#eff2f5}}.ce-toolbar__settings-btn--active{background-color:#eff2f5;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@media (min-width: 651px){.ce-toolbar__settings-btn{width:24px}}.ce-toolbar__settings-btn--hidden{display:none}@media (max-width: 650px){.ce-toolbar__settings-btn{position:absolute;background-color:#fff;border:1px solid #E8E8EB;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px #0d142121;border-radius:6px;z-index:2;position:static}.ce-toolbar__settings-btn--left-oriented:before{left:15px;margin-left:0}.ce-toolbar__settings-btn--right-oriented:before{left:auto;right:15px;margin-left:0}}.ce-toolbar__plus svg,.ce-toolbar__settings-btn svg{width:24px;height:24px}@media (min-width: 651px){.codex-editor--narrow .ce-toolbar__plus{left:5px}}@media (min-width: 651px){.codex-editor--narrow .ce-toolbox .ce-popover{right:0;left:auto;left:initial}}.ce-inline-toolbar{--y-offset: 8px;--color-background-icon-active: rgba(56, 138, 229, .1);--color-text-icon-active: #388AE5;--color-text-primary: black;position:absolute;visibility:hidden;-webkit-transition:opacity .25s ease;transition:opacity .25s ease;will-change:opacity,left,top;top:0;left:0;z-index:3;opacity:1;visibility:visible}.ce-inline-toolbar [hidden]{display:none!important}.ce-inline-toolbar__toggler-and-button-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;padding:0 6px}.ce-inline-toolbar__buttons{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-inline-toolbar__dropdown{display:-webkit-box;display:-ms-flexbox;display:flex;padding:6px;margin:0 6px 0 -6px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;cursor:pointer;border-right:1px solid rgba(201,201,204,.48);-webkit-box-sizing:border-box;box-sizing:border-box}@media (hover: hover){.ce-inline-toolbar__dropdown:hover{background:#eff2f5}}.ce-inline-toolbar__dropdown--hidden{display:none}.ce-inline-toolbar__dropdown-content,.ce-inline-toolbar__dropdown-arrow{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-inline-toolbar__dropdown-content svg,.ce-inline-toolbar__dropdown-arrow svg{width:20px;height:20px}.ce-inline-toolbar__shortcut{opacity:.6;word-spacing:-3px;margin-top:3px}.ce-inline-tool{color:var(--color-text-primary);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:0;border-radius:4px;line-height:normal;height:100%;padding:0;width:28px;background-color:transparent;cursor:pointer}@media (max-width: 650px){.ce-inline-tool{width:36px;height:36px}}@media (hover: hover){.ce-inline-tool:hover{background-color:#f8f8f8}}.ce-inline-tool svg{display:block;width:20px;height:20px}@media (max-width: 650px){.ce-inline-tool svg{width:28px;height:28px}}.ce-inline-tool--link .icon--unlink,.ce-inline-tool--unlink .icon--link{display:none}.ce-inline-tool--unlink .icon--unlink{display:inline-block;margin-bottom:-1px}.ce-inline-tool-input{background:#F8F8F8;border:1px solid rgba(226,226,229,.2);border-radius:6px;padding:4px 8px;font-size:14px;line-height:22px;outline:none;margin:0;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box;display:none;font-weight:500;-webkit-appearance:none;font-family:inherit}@media (max-width: 650px){.ce-inline-tool-input{font-size:15px;font-weight:500}}.ce-inline-tool-input::-webkit-input-placeholder{color:#707684}.ce-inline-tool-input::-moz-placeholder{color:#707684}.ce-inline-tool-input:-ms-input-placeholder{color:#707684}.ce-inline-tool-input::-ms-input-placeholder{color:#707684}.ce-inline-tool-input::placeholder{color:#707684}.ce-inline-tool-input--showed{display:block}.ce-inline-tool--active{background:var(--color-background-icon-active);color:var(--color-text-icon-active)}@-webkit-keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.ce-block{-webkit-animation:fade-in .3s ease;animation:fade-in .3s ease;-webkit-animation-fill-mode:none;animation-fill-mode:none;-webkit-animation-fill-mode:initial;animation-fill-mode:initial}.ce-block:first-of-type{margin-top:0}.ce-block--selected .ce-block__content{background:#e1f2ff}.ce-block--selected .ce-block__content [contenteditable]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ce-block--selected .ce-block__content img,.ce-block--selected .ce-block__content .ce-stub{opacity:.55}.ce-block--stretched .ce-block__content{max-width:none}.ce-block__content{position:relative;max-width:650px;margin:0 auto;-webkit-transition:background-color .15s ease;transition:background-color .15s ease}.ce-block--drop-target .ce-block__content:before{content:"";position:absolute;top:100%;left:-20px;margin-top:-1px;height:8px;width:8px;border:solid #388AE5;border-width:1px 1px 0 0;-webkit-transform-origin:right;transform-origin:right;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ce-block--drop-target .ce-block__content:after{content:"";position:absolute;top:100%;height:1px;width:100%;color:#388ae5;background:repeating-linear-gradient(90deg,#388AE5,#388AE5 1px,#fff 1px,#fff 6px)}.ce-block a{cursor:pointer;-webkit-text-decoration:underline;text-decoration:underline}.ce-block b{font-weight:700}.ce-block i{font-style:italic}@-webkit-keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}20%{-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}60%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}20%{-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}60%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@-webkit-keyframes selectionBounce{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}50%{-webkit-transform:scale3d(1.01,1.01,1.01);transform:scale3d(1.01,1.01,1.01)}70%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes selectionBounce{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}50%{-webkit-transform:scale3d(1.01,1.01,1.01);transform:scale3d(1.01,1.01,1.01)}70%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@-webkit-keyframes buttonClicked{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.95,.95,.95);transform:scale3d(.95,.95,.95)}60%{-webkit-transform:scale3d(1.02,1.02,1.02);transform:scale3d(1.02,1.02,1.02)}80%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes buttonClicked{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.95,.95,.95);transform:scale3d(.95,.95,.95)}60%{-webkit-transform:scale3d(1.02,1.02,1.02);transform:scale3d(1.02,1.02,1.02)}80%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}.cdx-block{padding:.4em 0}.cdx-block::-webkit-input-placeholder{line-height:normal!important}.cdx-input{border:1px solid rgba(201,201,204,.48);-webkit-box-shadow:inset 0 1px 2px 0 rgba(35,44,72,.06);box-shadow:inset 0 1px 2px #232c480f;border-radius:3px;padding:10px 12px;outline:none;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.cdx-input[data-placeholder]:before{position:static!important}.cdx-input[data-placeholder]:before{display:inline-block;width:0;white-space:nowrap;pointer-events:none}.cdx-settings-button{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:3px;cursor:pointer;border:0;outline:none;background-color:transparent;vertical-align:bottom;color:inherit;margin:0;min-width:26px;min-height:26px}.cdx-settings-button--focused{background:rgba(34,186,255,.08)!important}.cdx-settings-button--focused{-webkit-box-shadow:inset 0 0 0px 1px rgba(7,161,227,.08);box-shadow:inset 0 0 0 1px #07a1e314}.cdx-settings-button--focused-animated{-webkit-animation-name:buttonClicked;animation-name:buttonClicked;-webkit-animation-duration:.25s;animation-duration:.25s}.cdx-settings-button--active{color:#388ae5}.cdx-settings-button svg{width:auto;height:auto}@media (max-width: 650px){.cdx-settings-button svg{width:28px;height:28px}}@media (max-width: 650px){.cdx-settings-button{width:36px;height:36px;border-radius:8px}}@media (hover: hover){.cdx-settings-button:hover{background-color:#eff2f5}}.cdx-loader{position:relative;border:1px solid rgba(201,201,204,.48)}.cdx-loader:before{content:"";position:absolute;left:50%;top:50%;width:18px;height:18px;margin:-11px 0 0 -11px;border:2px solid rgba(201,201,204,.48);border-left-color:#388ae5;border-radius:50%;-webkit-animation:cdxRotation 1.2s infinite linear;animation:cdxRotation 1.2s infinite linear}@-webkit-keyframes cdxRotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes cdxRotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.cdx-button{padding:13px;border-radius:3px;border:1px solid rgba(201,201,204,.48);font-size:14.9px;background:#fff;-webkit-box-shadow:0 2px 2px 0 rgba(18,30,57,.04);box-shadow:0 2px 2px #121e390a;color:#707684;text-align:center;cursor:pointer}@media (hover: hover){.cdx-button:hover{background:#FBFCFE;-webkit-box-shadow:0 1px 3px 0 rgba(18,30,57,.08);box-shadow:0 1px 3px #121e3914}}.cdx-button svg{height:20px;margin-right:.2em;margin-top:-2px}.ce-stub{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:12px 18px;margin:10px 0;border-radius:10px;background:#eff2f5;border:1px solid #EFF0F1;color:#707684;font-size:14px}.ce-stub svg{width:20px;height:20px}.ce-stub__info{margin-left:14px}.ce-stub__title{font-weight:500;text-transform:capitalize}.codex-editor.codex-editor--rtl{direction:rtl}.codex-editor.codex-editor--rtl .cdx-list{padding-left:0;padding-right:40px}.codex-editor.codex-editor--rtl .ce-toolbar__plus{right:-26px;left:auto}.codex-editor.codex-editor--rtl .ce-toolbar__actions{right:auto;left:-26px}@media (max-width: 650px){.codex-editor.codex-editor--rtl .ce-toolbar__actions{margin-left:0;margin-right:auto;padding-right:0;padding-left:10px}}.codex-editor.codex-editor--rtl .ce-settings{left:5px;right:auto}.codex-editor.codex-editor--rtl .ce-settings:before{right:auto;left:25px}.codex-editor.codex-editor--rtl .ce-settings__button:not(:nth-child(3n+3)){margin-left:3px;margin-right:0}.codex-editor.codex-editor--rtl .ce-conversion-tool__icon{margin-right:0;margin-left:10px}.codex-editor.codex-editor--rtl .ce-inline-toolbar__dropdown{border-right:0px solid transparent;border-left:1px solid rgba(201,201,204,.48);margin:0 -6px 0 6px}.codex-editor.codex-editor--rtl .ce-inline-toolbar__dropdown .icon--toggler-down{margin-left:0;margin-right:4px}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .ce-toolbar__plus{left:0;right:5px}}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .ce-toolbar__actions{left:-5px}}.cdx-search-field{--icon-margin-right: 10px;background:#F8F8F8;border:1px solid rgba(226,226,229,.2);border-radius:6px;padding:2px;display:grid;grid-template-columns:auto auto 1fr;grid-template-rows:auto}.cdx-search-field__icon{width:26px;height:26px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-right:var(--icon-margin-right)}.cdx-search-field__icon svg{width:20px;height:20px;color:#707684}.cdx-search-field__input{font-size:14px;outline:none;font-weight:500;font-family:inherit;border:0;background:transparent;margin:0;padding:0;line-height:22px;min-width:calc(100% - 26px - var(--icon-margin-right))}.cdx-search-field__input::-webkit-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::-moz-placeholder{color:#707684;font-weight:500}.cdx-search-field__input:-ms-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::-ms-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::placeholder{color:#707684;font-weight:500}.ce-popover{--border-radius: 6px;--width: 200px;--max-height: 270px;--padding: 6px;--offset-from-target: 8px;--color-border: #EFF0F1;--color-shadow: rgba(13, 20, 33, .1);--color-background: white;--color-text-primary: black;--color-text-secondary: #707684;--color-border-icon: rgba(201, 201, 204, .48);--color-border-icon-disabled: #EFF0F1;--color-text-icon-active: #388AE5;--color-background-icon-active: rgba(56, 138, 229, .1);--color-background-item-focus: rgba(34, 186, 255, .08);--color-shadow-item-focus: rgba(7, 161, 227, .08);--color-background-item-hover: #F8F8F8;--color-background-item-confirm: #E24A4A;--color-background-item-confirm-hover: #CE4343;--popover-top: calc(100% + var(--offset-from-target));--popover-left: 0;--nested-popover-overlap: 4px;--icon-size: 20px;--item-padding: 3px;--item-height: calc(var(--icon-size) + 2 * var(--item-padding))}.ce-popover__container{min-width:var(--width);width:var(--width);max-height:var(--max-height);border-radius:var(--border-radius);overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:0px 3px 15px -3px var(--color-shadow);box-shadow:0 3px 15px -3px var(--color-shadow);position:absolute;left:var(--popover-left);top:var(--popover-top);background:var(--color-background);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;z-index:4;opacity:0;max-height:0;pointer-events:none;padding:0;border:none}.ce-popover--opened>.ce-popover__container{opacity:1;padding:var(--padding);max-height:var(--max-height);pointer-events:auto;-webkit-animation:panelShowing .1s ease;animation:panelShowing .1s ease;border:1px solid var(--color-border)}@media (max-width: 650px){.ce-popover--opened>.ce-popover__container{-webkit-animation:panelShowingMobile .25s ease;animation:panelShowingMobile .25s ease}}.ce-popover--open-top .ce-popover__container{--popover-top: calc(-1 * (var(--offset-from-target) + var(--popover-height)))}.ce-popover--open-left .ce-popover__container{--popover-left: calc(-1 * var(--width) + 100%)}.ce-popover__items{overflow-y:auto;-ms-scroll-chaining:none;overscroll-behavior:contain}@media (max-width: 650px){.ce-popover__overlay{position:fixed;top:0;bottom:0;left:0;right:0;background:#1D202B;z-index:3;opacity:.5;-webkit-transition:opacity .12s ease-in;transition:opacity .12s ease-in;will-change:opacity;visibility:visible}}.ce-popover__overlay--hidden{display:none}@media (max-width: 650px){.ce-popover .ce-popover__container{--offset: 5px;position:fixed;max-width:none;min-width:calc(100% - var(--offset) * 2);left:var(--offset);right:var(--offset);bottom:calc(var(--offset) + env(safe-area-inset-bottom));top:auto;border-radius:10px}}.ce-popover__search{margin-bottom:5px}.ce-popover__nothing-found-message{color:#707684;display:none;cursor:default;padding:3px;font-size:14px;line-height:20px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ce-popover__nothing-found-message--displayed{display:block}.ce-popover--nested .ce-popover__container{--popover-left: calc(var(--nesting-level) * (var(--width) - var(--nested-popover-overlap)));top:calc(var(--trigger-item-top) - var(--nested-popover-overlap));position:absolute}.ce-popover--open-top.ce-popover--nested .ce-popover__container{top:calc(var(--trigger-item-top) - var(--popover-height) + var(--item-height) + var(--offset-from-target) + var(--nested-popover-overlap))}.ce-popover--open-left .ce-popover--nested .ce-popover__container{--popover-left: calc(-1 * (var(--nesting-level) + 1) * var(--width) + 100%)}.ce-popover-item-separator{padding:4px 3px}.ce-popover-item-separator--hidden{display:none}.ce-popover-item-separator__line{height:1px;background:var(--color-border);width:100%}.ce-popover-item-html--hidden{display:none}.ce-popover-item{--border-radius: 6px;border-radius:var(--border-radius);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:var(--item-padding);color:var(--color-text-primary);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:none;background:transparent}@media (max-width: 650px){.ce-popover-item{padding:4px}}.ce-popover-item:not(:last-of-type){margin-bottom:1px}.ce-popover-item__icon{width:26px;height:26px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ce-popover-item__icon svg{width:20px;height:20px}@media (max-width: 650px){.ce-popover-item__icon{width:36px;height:36px;border-radius:8px}.ce-popover-item__icon svg{width:28px;height:28px}}.ce-popover-item__icon--tool{margin-right:4px}.ce-popover-item__title{font-size:14px;line-height:20px;font-weight:500;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-right:auto}@media (max-width: 650px){.ce-popover-item__title{font-size:16px}}.ce-popover-item__secondary-title{color:var(--color-text-secondary);font-size:12px;white-space:nowrap;letter-spacing:-.1em;padding-right:5px;opacity:.6}@media (max-width: 650px){.ce-popover-item__secondary-title{display:none}}.ce-popover-item--active{background:var(--color-background-icon-active);color:var(--color-text-icon-active)}.ce-popover-item--disabled{color:var(--color-text-secondary);cursor:default;pointer-events:none}.ce-popover-item--focused:not(.ce-popover-item--no-focus){background:var(--color-background-item-focus)!important}.ce-popover-item--hidden{display:none}@media (hover: hover){.ce-popover-item:hover{cursor:pointer}.ce-popover-item:hover:not(.ce-popover-item--no-hover){background-color:var(--color-background-item-hover)}}.ce-popover-item--confirmation{background:var(--color-background-item-confirm)}.ce-popover-item--confirmation .ce-popover-item__title,.ce-popover-item--confirmation .ce-popover-item__icon{color:#fff}@media (hover: hover){.ce-popover-item--confirmation:not(.ce-popover-item--no-hover):hover{background:var(--color-background-item-confirm-hover)}}.ce-popover-item--confirmation:not(.ce-popover-item--no-focus).ce-popover-item--focused{background:var(--color-background-item-confirm-hover)!important}@-webkit-keyframes panelShowing{0%{opacity:0;-webkit-transform:translateY(-8px) scale(.9);transform:translateY(-8px) scale(.9)}70%{opacity:1;-webkit-transform:translateY(2px);transform:translateY(2px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes panelShowing{0%{opacity:0;-webkit-transform:translateY(-8px) scale(.9);transform:translateY(-8px) scale(.9)}70%{opacity:1;-webkit-transform:translateY(2px);transform:translateY(2px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes panelShowingMobile{0%{opacity:0;-webkit-transform:translateY(14px) scale(.98);transform:translateY(14px) scale(.98)}70%{opacity:1;-webkit-transform:translateY(-4px);transform:translateY(-4px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes panelShowingMobile{0%{opacity:0;-webkit-transform:translateY(14px) scale(.98);transform:translateY(14px) scale(.98)}70%{opacity:1;-webkit-transform:translateY(-4px);transform:translateY(-4px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.wobble{-webkit-animation-name:wobble;animation-name:wobble;-webkit-animation-duration:.4s;animation-duration:.4s}@-webkit-keyframes wobble{0%{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-9%,0,0);transform:translate3d(-9%,0,0)}30%{-webkit-transform:translate3d(9%,0,0);transform:translate3d(9%,0,0)}45%{-webkit-transform:translate3d(-4%,0,0);transform:translate3d(-4%,0,0)}60%{-webkit-transform:translate3d(4%,0,0);transform:translate3d(4%,0,0)}75%{-webkit-transform:translate3d(-1%,0,0);transform:translate3d(-1%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}@keyframes wobble{0%{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-9%,0,0);transform:translate3d(-9%,0,0)}30%{-webkit-transform:translate3d(9%,0,0);transform:translate3d(9%,0,0)}45%{-webkit-transform:translate3d(-4%,0,0);transform:translate3d(-4%,0,0)}60%{-webkit-transform:translate3d(4%,0,0);transform:translate3d(4%,0,0)}75%{-webkit-transform:translate3d(-1%,0,0);transform:translate3d(-1%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}.ce-popover-header{margin-bottom:8px;margin-top:4px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ce-popover-header__text{font-size:18px;font-weight:600}.ce-popover-header__back-button{border:0;background:transparent;width:36px;height:36px;color:var(--color-text-primary)}.ce-popover-header__back-button svg{display:block;width:28px;height:28px}.ce-popover--inline{--height: 38px;--height-mobile: 46px;--container-padding: 4px;position:relative}.ce-popover--inline .ce-popover__custom-content{margin-bottom:0}.ce-popover--inline .ce-popover__items{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-popover--inline .ce-popover__container{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;padding:var(--container-padding);height:var(--height);top:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;width:-webkit-max-content;width:-moz-max-content;width:max-content;-webkit-animation:none;animation:none}@media (max-width: 650px){.ce-popover--inline .ce-popover__container{height:var(--height-mobile);position:absolute}}.ce-popover--inline .ce-popover-item-separator{padding:0 4px}.ce-popover--inline .ce-popover-item-separator__line{height:100%;width:1px}.ce-popover--inline .ce-popover-item{border-radius:4px;padding:4px}.ce-popover--inline .ce-popover-item__icon--tool{-webkit-box-shadow:none;box-shadow:none;background:transparent;margin-right:0}.ce-popover--inline .ce-popover-item__icon{width:auto;width:initial;height:auto;height:initial}.ce-popover--inline .ce-popover-item__icon svg{width:20px;height:20px}@media (max-width: 650px){.ce-popover--inline .ce-popover-item__icon svg{width:28px;height:28px}}.ce-popover--inline .ce-popover-item:not(:last-of-type){margin-bottom:0;margin-bottom:initial}.ce-popover--inline .ce-popover-item-html{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ce-popover--inline .ce-popover-item__icon--chevron-right{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.ce-popover--inline .ce-popover--nested-level-1 .ce-popover__container{--offset: 3px;left:0;top:calc(var(--height) + var(--offset))}@media (max-width: 650px){.ce-popover--inline .ce-popover--nested-level-1 .ce-popover__container{top:calc(var(--height-mobile) + var(--offset))}}.ce-popover--inline .ce-popover--nested .ce-popover__container{min-width:var(--width);width:var(--width);height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;padding:6px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ce-popover--inline .ce-popover--nested .ce-popover__items{display:block;width:100%}.ce-popover--inline .ce-popover--nested .ce-popover-item{border-radius:6px;padding:3px}@media (max-width: 650px){.ce-popover--inline .ce-popover--nested .ce-popover-item{padding:4px}}.ce-popover--inline .ce-popover--nested .ce-popover-item__icon--tool{margin-right:4px}.ce-popover--inline .ce-popover--nested .ce-popover-item__icon{width:26px;height:26px}.ce-popover--inline .ce-popover--nested .ce-popover-item-separator{padding:4px 3px}.ce-popover--inline .ce-popover--nested .ce-popover-item-separator__line{width:100%;height:1px}.codex-editor [data-placeholder]:empty:before,.codex-editor [data-placeholder][data-empty=true]:before{pointer-events:none;color:#707684;cursor:text;content:attr(data-placeholder)}.codex-editor [data-placeholder-active]:empty:before,.codex-editor [data-placeholder-active][data-empty=true]:before{pointer-events:none;color:#707684;cursor:text}.codex-editor [data-placeholder-active]:empty:focus:before,.codex-editor [data-placeholder-active][data-empty=true]:focus:before{content:attr(data-placeholder-active)} +`;class wa extends E{constructor(){super(...arguments),this.isMobile=!1,this.contentRectCache=null,this.resizeDebouncer=Bo(()=>{this.windowResize()},200),this.selectionChangeDebounced=Bo(()=>{this.selectionChanged()},na),this.documentTouchedListener=e=>{this.documentTouched(e)}}get CSS(){return{editorWrapper:"codex-editor",editorWrapperNarrow:"codex-editor--narrow",editorZone:"codex-editor__redactor",editorZoneHidden:"codex-editor__redactor--hidden",editorEmpty:"codex-editor--empty",editorRtlFix:"codex-editor--rtl"}}get contentRect(){if(this.contentRectCache!==null)return this.contentRectCache;const e=this.nodes.wrapper.querySelector(`.${R.CSS.content}`);return e?(this.contentRectCache=e.getBoundingClientRect(),this.contentRectCache):{width:650,left:0,right:0}}async prepare(){this.setIsMobile(),this.make(),this.loadStyles()}toggleReadOnly(e){e?this.unbindReadOnlySensitiveListeners():window.requestIdleCallback(()=>{this.bindReadOnlySensitiveListeners()},{timeout:2e3})}checkEmptiness(){const{BlockManager:e}=this.Editor;this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty,e.isEditorEmpty)}get someToolbarOpened(){const{Toolbar:e,BlockSettings:t,InlineToolbar:o}=this.Editor;return!!(t.opened||o.opened||e.toolbox.opened)}get someFlipperButtonFocused(){return this.Editor.Toolbar.toolbox.hasFocus()?!0:Object.entries(this.Editor).filter(([e,t])=>t.flipper instanceof ae).some(([e,t])=>t.flipper.hasFocus())}destroy(){this.nodes.holder.innerHTML="",this.unbindReadOnlyInsensitiveListeners()}closeAllToolbars(){const{Toolbar:e,BlockSettings:t,InlineToolbar:o}=this.Editor;t.close(),o.close(),e.toolbox.close()}setIsMobile(){const e=window.innerWidth{this.redactorClicked(e)},!1),this.readOnlyMutableListeners.on(document,"keydown",e=>{this.documentKeydown(e)},!0),this.readOnlyMutableListeners.on(document,"mousedown",e=>{this.documentClicked(e)},!0),this.watchBlockHoveredEvents(),this.enableInputsEmptyMark()}watchBlockHoveredEvents(){let e;this.readOnlyMutableListeners.on(this.nodes.redactor,"mousemove",ot(t=>{const o=t.target.closest(".ce-block");this.Editor.BlockSelection.anyBlockSelected||o&&e!==o&&(e=o,this.eventsDispatcher.emit(Qo,{block:this.Editor.BlockManager.getBlockByChildNode(o)}))},20),{passive:!0})}unbindReadOnlySensitiveListeners(){this.readOnlyMutableListeners.clearAll()}windowResize(){this.contentRectCache=null,this.setIsMobile()}documentKeydown(e){switch(e.keyCode){case y.ENTER:this.enterPressed(e);break;case y.BACKSPACE:case y.DELETE:this.backspacePressed(e);break;case y.ESC:this.escapePressed(e);break;default:this.defaultBehaviour(e);break}}defaultBehaviour(e){const{currentBlock:t}=this.Editor.BlockManager,o=e.target.closest(`.${this.CSS.editorWrapper}`),i=e.altKey||e.ctrlKey||e.metaKey||e.shiftKey;if(t!==void 0&&o===null){this.Editor.BlockEvents.keydown(e);return}o||t&&i||(this.Editor.BlockManager.unsetCurrentBlock(),this.Editor.Toolbar.close())}backspacePressed(e){const{BlockManager:t,BlockSelection:o,Caret:i}=this.Editor;if(o.anyBlockSelected&&!b.isSelectionExists){const s=t.removeSelectedBlocks(),r=t.insertDefaultBlockAtIndex(s,!0);i.setToBlock(r,i.positions.START),o.clearSelection(e),e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation()}}escapePressed(e){this.Editor.BlockSelection.clearSelection(e),this.Editor.Toolbar.toolbox.opened?(this.Editor.Toolbar.toolbox.close(),this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock,this.Editor.Caret.positions.END)):this.Editor.BlockSettings.opened?this.Editor.BlockSettings.close():this.Editor.InlineToolbar.opened?this.Editor.InlineToolbar.close():this.Editor.Toolbar.close()}enterPressed(e){const{BlockManager:t,BlockSelection:o}=this.Editor;if(this.someToolbarOpened)return;const i=t.currentBlockIndex>=0;if(o.anyBlockSelected&&!b.isSelectionExists){o.clearSelection(e),e.preventDefault(),e.stopImmediatePropagation(),e.stopPropagation();return}if(!this.someToolbarOpened&&i&&e.target.tagName==="BODY"){const s=this.Editor.BlockManager.insert();e.preventDefault(),this.Editor.Caret.setToBlock(s),this.Editor.Toolbar.moveAndOpen(s)}this.Editor.BlockSelection.clearSelection(e)}documentClicked(e){var a,l;if(!e.isTrusted)return;const t=e.target;this.nodes.holder.contains(t)||b.isAtEditor||(this.Editor.BlockManager.unsetCurrentBlock(),this.Editor.Toolbar.close());const i=(a=this.Editor.BlockSettings.nodes.wrapper)==null?void 0:a.contains(t),s=(l=this.Editor.Toolbar.nodes.settingsToggler)==null?void 0:l.contains(t),r=i||s;if(this.Editor.BlockSettings.opened&&!r){this.Editor.BlockSettings.close();const c=this.Editor.BlockManager.getBlockByChildNode(t);this.Editor.Toolbar.moveAndOpen(c)}this.Editor.BlockSelection.clearSelection(e)}documentTouched(e){let t=e.target;if(t===this.nodes.redactor){const o=e instanceof MouseEvent?e.clientX:e.touches[0].clientX,i=e instanceof MouseEvent?e.clientY:e.touches[0].clientY;t=document.elementFromPoint(o,i)}try{this.Editor.BlockManager.setCurrentBlockByChildNode(t)}catch{this.Editor.RectangleSelection.isRectActivated()||this.Editor.Caret.setToTheLastBlock()}this.Editor.ReadOnly.isEnabled||this.Editor.Toolbar.moveAndOpen()}redactorClicked(e){if(!b.isCollapsed)return;const t=e.target,o=e.metaKey||e.ctrlKey;if(u.isAnchor(t)&&o){e.stopImmediatePropagation(),e.stopPropagation();const i=t.getAttribute("href"),s=ei(i);oi(s);return}this.processBottomZoneClick(e)}processBottomZoneClick(e){const t=this.Editor.BlockManager.getBlockByIndex(-1),o=u.offset(t.holder).bottom,i=e.pageY,{BlockSelection:s}=this.Editor;if(e.target instanceof Element&&e.target.isEqualNode(this.nodes.redactor)&&!s.anyBlockSelected&&o{t=i,o=s}),Promise.resolve().then(async()=>{this.configuration=e,this.validate(),this.init(),await this.start(),await this.render();const{BlockManager:i,Caret:s,UI:r,ModificationsObserver:a}=this.moduleInstances;r.checkEmptiness(),a.enable(),this.configuration.autofocus===!0&&this.configuration.readOnly!==!0&&s.setToBlock(i.blocks[0],s.positions.START),t()}).catch(i=>{T(`Editor.js is not ready because of ${i}`,"error"),o(i)})}set configuration(e){var o,i;N(e)?this.config={...e}:this.config={holder:e},st(!!this.config.holderId,"config.holderId","config.holder"),this.config.holderId&&!this.config.holder&&(this.config.holder=this.config.holderId,this.config.holderId=null),this.config.holder==null&&(this.config.holder="editorjs"),this.config.logLevel||(this.config.logLevel=ko.VERBOSE),Vn(this.config.logLevel),st(!!this.config.initialBlock,"config.initialBlock","config.defaultBlock"),this.config.defaultBlock=this.config.defaultBlock||this.config.initialBlock||"paragraph",this.config.minHeight=this.config.minHeight!==void 0?this.config.minHeight:300;const t={type:this.config.defaultBlock,data:{}};this.config.placeholder=this.config.placeholder||!1,this.config.sanitizer=this.config.sanitizer||{p:!0,b:!0,a:!0},this.config.hideToolbar=this.config.hideToolbar?this.config.hideToolbar:!1,this.config.tools=this.config.tools||{},this.config.i18n=this.config.i18n||{},this.config.data=this.config.data||{blocks:[]},this.config.onReady=this.config.onReady||(()=>{}),this.config.onChange=this.config.onChange||(()=>{}),this.config.inlineToolbar=this.config.inlineToolbar!==void 0?this.config.inlineToolbar:!0,(V(this.config.data)||!this.config.data.blocks||this.config.data.blocks.length===0)&&(this.config.data={blocks:[t]}),this.config.readOnly=this.config.readOnly||!1,(o=this.config.i18n)!=null&&o.messages&&H.setDictionary(this.config.i18n.messages),this.config.i18n.direction=((i=this.config.i18n)==null?void 0:i.direction)||"ltr"}get configuration(){return this.config}validate(){const{holderId:e,holder:t}=this.config;if(e&&t)throw Error("«holderId» and «holder» param can't assign at the same time.");if(Q(t)&&!u.get(t))throw Error(`element with ID «${t}» is missing. Pass correct holder's ID.`);if(t&&N(t)&&!u.isElement(t))throw Error("«holder» value must be an Element node")}init(){this.constructModules(),this.configureModules()}async start(){await["Tools","UI","BlockManager","Paste","BlockSelection","RectangleSelection","CrossBlockSelection","ReadOnly"].reduce((t,o)=>t.then(async()=>{try{await this.moduleInstances[o].prepare()}catch(i){if(i instanceof Oo)throw new Error(i.message);T(`Module ${o} was skipped because of %o`,"warn",i)}}),Promise.resolve())}render(){return this.moduleInstances.Renderer.render(this.config.data.blocks)}constructModules(){Object.entries(Ea).forEach(([e,t])=>{try{this.moduleInstances[e]=new t({config:this.configuration,eventsDispatcher:this.eventsDispatcher})}catch(o){T("[constructModules]",`Module ${e} skipped because`,"error",o)}})}configureModules(){for(const e in this.moduleInstances)Object.prototype.hasOwnProperty.call(this.moduleInstances,e)&&(this.moduleInstances[e].state=this.getModulesDiff(e))}getModulesDiff(e){const t={};for(const o in this.moduleInstances)o!==e&&(t[o]=this.moduleInstances[o]);return t}}/** + * Editor.js + * + * @license Apache-2.0 + * @see Editor.js + * @author CodeX Team + */class Ba{static get version(){return"2.31.1"}constructor(e){let t=()=>{};N(e)&&M(e.onReady)&&(t=e.onReady);const o=new xa(e);this.isReady=o.isReady.then(()=>{this.exportAPI(o),t()})}exportAPI(e){const t=["configuration"],o=()=>{Object.values(e.moduleInstances).forEach(s=>{M(s.destroy)&&s.destroy(),s.listeners.removeAll()}),Ri(),e=null;for(const s in this)Object.prototype.hasOwnProperty.call(this,s)&&delete this[s];Object.setPrototypeOf(this,null)};t.forEach(s=>{this[s]=e[s]}),this.destroy=o,Object.setPrototypeOf(this,e.moduleInstances.API.methods),delete this.exportAPI,Object.entries({blocks:{clear:"clear",render:"render"},caret:{focus:"focus"},events:{on:"on",off:"off",emit:"emit"},saver:{save:"save"}}).forEach(([s,r])=>{Object.entries(r).forEach(([a,l])=>{this[l]=e.moduleInstances.API.methods[s][a]})})}}return Ba}); diff --git a/web/vendor/editorjs/tools/checklist.umd.js b/web/vendor/editorjs/tools/checklist.umd.js new file mode 100644 index 0000000..4ab4fc4 --- /dev/null +++ b/web/vendor/editorjs/tools/checklist.umd.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Checklist=t():e.Checklist=t()}(window,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=6)}([function(e,t,n){var r=n(1);"string"==typeof r&&(r=[[e.i,r,""]]);var i={hmr:!0,transform:void 0,insertInto:void 0};n(3)(r,i);r.locals&&(e.exports=r.locals)},function(e,t,n){(e.exports=n(2)(!1)).push([e.i,".cdx-checklist {\n gap: 6px;\n display: flex;\n flex-direction: column;\n}\n\n .cdx-checklist__item {\n display: flex;\n box-sizing: content-box;\n align-items: flex-start;\n }\n\n .cdx-checklist__item-text {\n outline: none;\n flex-grow: 1;\n line-height: 1.57em;\n }\n\n .cdx-checklist__item-checkbox {\n width: 22px;\n height: 22px;\n display: flex;\n align-items: center;\n margin-right: 8px;\n margin-top: calc(1.57em/2 - 22px/2);\n cursor: pointer;\n }\n\n .cdx-checklist__item-checkbox svg {\n opacity: 0;\n height: 20px;\n width: 20px;\n position: absolute;\n left: -1px;\n top: -1px;\n max-height: 20px;\n }\n\n @media (hover: hover) {\n .cdx-checklist__item-checkbox:not(.cdx-checklist__item-checkbox--no-hover):hover .cdx-checklist__item-checkbox-check svg {\n opacity: 1;\n }\n }\n\n .cdx-checklist__item-checkbox-check {\n cursor: pointer;\n display: inline-block;\n flex-shrink: 0;\n position: relative;\n width: 20px;\n height: 20px;\n box-sizing: border-box;\n margin-left: 0;\n border-radius: 5px;\n border: 1px solid #C9C9C9;\n background: #fff;\n }\n\n .cdx-checklist__item-checkbox-check::before {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n border-radius: 100%;\n background-color: #369FFF;\n visibility: hidden;\n pointer-events: none;\n transform: scale(1);\n transition: transform 400ms ease-out, opacity 400ms;\n }\n\n @media (hover: hover) {\n .cdx-checklist__item--checked .cdx-checklist__item-checkbox:not(.cdx-checklist__item--checked .cdx-checklist__item-checkbox--no-hover):hover .cdx-checklist__item-checkbox-check {\n background: #0059AB;\n border-color: #0059AB;\n }\n }\n\n .cdx-checklist__item--checked .cdx-checklist__item-checkbox-check {\n background: #369FFF;\n border-color: #369FFF;\n }\n\n .cdx-checklist__item--checked .cdx-checklist__item-checkbox-check svg {\n opacity: 1;\n }\n\n .cdx-checklist__item--checked .cdx-checklist__item-checkbox-check svg path {\n stroke: #fff;\n }\n\n .cdx-checklist__item--checked .cdx-checklist__item-checkbox-check::before {\n opacity: 0;\n visibility: visible;\n transform: scale(2.5);\n }\n",""])},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var i=(c=r,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(c))))+" */"),o=r.sources.map((function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"}));return[n].concat(o).concat([i]).join("\n")}var c;return[n].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n})).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},i=0;i=0&&d.splice(t,1)}function b(e){var t=document.createElement("style");return void 0===e.attrs.type&&(e.attrs.type="text/css"),k(t,e.attrs),m(e,t),t}function k(e,t){Object.keys(t).forEach((function(n){e.setAttribute(n,t[n])}))}function x(e,t){var n,r,i,o;if(t.transform&&e.css){if(!(o=t.transform(e.css)))return function(){};e.css=o}if(t.singleton){var c=u++;n=l||(l=b(t)),r=w.bind(null,n,c,!1),i=w.bind(null,n,c,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",k(t,e.attrs),m(e,t),t}(t),r=_.bind(null,n,t),i=function(){v(n),n.href&&URL.revokeObjectURL(n.href)}):(n=b(t),r=S.bind(null,n),i=function(){v(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else i()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=c()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=p(e,t);return f(n,t),function(e){for(var r=[],i=0;ie.length)&&(t=e.length);for(var n=0,r=new Array(t);n1&&void 0!==arguments[1]?arguments[1]:null,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=document.createElement(e);Array.isArray(n)?(t=o.classList).add.apply(t,i(n)):n&&o.classList.add(n);for(var c in r)o[c]=r[c];return o}function a(e){return e.innerHTML.replace("
    "," ").trim()}function l(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,r=document.createRange(),i=window.getSelection();r.selectNodeContents(e),void 0!==n&&(r.setStart(e,n),r.setEnd(e,n)),r.collapse(t),i.removeAllRanges(),i.addRange(r)}n(5);function u(e,t){for(var n=0;n',title:"Checklist"}}},{key:"conversionConfig",get:function(){return{export:function(e){return e.items.map((function(e){return e.text})).join(". ")},import:function(e){return{items:[{text:e,checked:!1}]}}}}}]),d(e,[{key:"render",value:function(){var e=this;return this._elements.wrapper=s("div",[this.CSS.baseBlock,this.CSS.wrapper]),this.data.items||(this.data.items=[{text:"",checked:!1}]),this.data.items.forEach((function(t){var n=e.createChecklistItem(t);e._elements.wrapper.appendChild(n)})),this.readOnly||(this._elements.wrapper.addEventListener("keydown",(function(t){switch(t.keyCode){case 13:e.enterPressed(t);break;case 8:e.backspace(t)}}),!1),this._elements.wrapper.addEventListener("click",(function(t){e.toggleCheckbox(t)}))),this._elements.wrapper}},{key:"save",value:function(){var e=this,t=this.items.map((function(t){return{text:a(e.getItemInput(t)),checked:t.classList.contains(e.CSS.itemChecked)}}));return{items:t=t.filter((function(e){return 0!==e.text.trim().length}))}}},{key:"validate",value:function(e){return!!e.items.length}},{key:"toggleCheckbox",value:function(e){var t=this,n=e.target.closest(".".concat(this.CSS.item)),r=n.querySelector(".".concat(this.CSS.checkboxContainer));r.contains(e.target)&&(n.classList.toggle(this.CSS.itemChecked),r.classList.add(this.CSS.noHover),r.addEventListener("mouseleave",(function(){return t.removeSpecialHoverBehavior(r)}),{once:!0}))}},{key:"createChecklistItem",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=s("div",this.CSS.item),n=s("span",this.CSS.checkbox),i=s("div",this.CSS.checkboxContainer),o=s("div",this.CSS.textField,{innerHTML:e.text?e.text:"",contentEditable:!this.readOnly});return e.checked&&t.classList.add(this.CSS.itemChecked),n.innerHTML=r,i.appendChild(n),t.appendChild(i),t.appendChild(o),t}},{key:"enterPressed",value:function(e){e.preventDefault();var t=this.items,n=document.activeElement.closest(".".concat(this.CSS.item));if(t.indexOf(n)===t.length-1&&0===a(this.getItemInput(n)).length){var r=this.api.blocks.getCurrentBlockIndex();return n.remove(),this.api.blocks.insert(),void this.api.caret.setToBlock(r+1)}var i,o,s=c(),u=(i=s,(o=document.createElement("div")).appendChild(i),o.innerHTML),d=this.createChecklistItem({text:u,checked:!1});this._elements.wrapper.insertBefore(d,n.nextSibling),l(this.getItemInput(d),!0)}},{key:"backspace",value:function(e){var t=e.target.closest(".".concat(this.CSS.item)),n=this.items.indexOf(t),r=this.items[n-1];if(r&&0===window.getSelection().focusOffset){e.preventDefault();var i=c(),o=this.getItemInput(r),s=o.childNodes.length;o.appendChild(i),l(o,void 0,s),t.remove()}}},{key:"removeSpecialHoverBehavior",value:function(e){e.classList.remove(this.CSS.noHover)}},{key:"getItemInput",value:function(e){return e.querySelector(".".concat(this.CSS.textField))}},{key:"CSS",get:function(){return{baseBlock:this.api.styles.block,wrapper:"cdx-checklist",item:"cdx-checklist__item",itemChecked:"cdx-checklist__item--checked",noHover:"cdx-checklist__item-checkbox--no-hover",checkbox:"cdx-checklist__item-checkbox-check",textField:"cdx-checklist__item-text",checkboxContainer:"cdx-checklist__item-checkbox"}}},{key:"items",get:function(){return Array.from(this._elements.wrapper.querySelectorAll(".".concat(this.CSS.item)))}}]),e}()}]).default})); \ No newline at end of file diff --git a/web/vendor/editorjs/tools/columns.js b/web/vendor/editorjs/tools/columns.js new file mode 100644 index 0000000..233465e --- /dev/null +++ b/web/vendor/editorjs/tools/columns.js @@ -0,0 +1,8 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/@calumk/editorjs-columns@0.3.2/dist/editorjs-columns.bundle.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +/*! For license information please see editorjs-columns.bundle.js.LICENSE.txt */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.editorjsColumns=e():t.editorjsColumns=e()}(self,(()=>(()=>{var t={745:(t,e,o)=>{"use strict";o.d(e,{Z:()=>r});var n=o(81),a=o.n(n),s=o(645),i=o.n(s)()(a());i.push([t.id,".ce-editorjsColumns_col{flex:50%}.ce-editorjsColumns_wrapper{display:flex;width:100%;gap:10px;margin-bottom:10px;flex-direction:row}.ce-editorjsColumns_wrapper .ce-toolbar__actions{z-index:0}.ce-editorjsColumns_wrapper .ce-toolbar{z-index:4}.ce-editorjsColumns_wrapper .ce-popover{z-index:4000}@media(max-width: 800px){.ce-editorjsColumns_wrapper{flex-direction:column;padding:10px;border:1px solid #ccc;border-radius:4px}}.ce-inline-toolbar{z-index:1000}.ce-block__content,.ce-toolbar__content{max-width:calc(100% - 50px)}.ce-toolbar__actions{right:calc(100% + 30px);background-color:rgba(255,255,255,.5);border-radius:4px}.codex-editor--narrow .codex-editor__redactor{margin:0}.ce-toolbar{z-index:4}.codex-editor{z-index:auto !important}",""]);const r=i},645:t=>{"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var o="",n=void 0!==e[5];return e[4]&&(o+="@supports (".concat(e[4],") {")),e[2]&&(o+="@media ".concat(e[2]," {")),n&&(o+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),o+=t(e),n&&(o+="}"),e[2]&&(o+="}"),e[4]&&(o+="}"),o})).join("")},e.i=function(t,o,n,a,s){"string"==typeof t&&(t=[[null,t,void 0]]);var i={};if(n)for(var r=0;r0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=s),o&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=o):d[2]=o),a&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=a):d[4]="".concat(a)),e.push(d))}},e}},81:t=>{"use strict";t.exports=function(t){return t[1]}},379:t=>{"use strict";var e=[];function o(t){for(var o=-1,n=0;n{"use strict";var e={};t.exports=function(t,o){var n=function(t){if(void 0===e[t]){var o=document.querySelector(t);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(t){o=null}e[t]=o}return e[t]}(t);if(!n)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");n.appendChild(o)}},216:t=>{"use strict";t.exports=function(t){var e=document.createElement("style");return t.setAttributes(e,t.attributes),t.insert(e,t.options),e}},565:(t,e,o)=>{"use strict";t.exports=function(t){var e=o.nc;e&&t.setAttribute("nonce",e)}},795:t=>{"use strict";t.exports=function(t){var e=t.insertStyleElement(t);return{update:function(o){!function(t,e,o){var n="";o.supports&&(n+="@supports (".concat(o.supports,") {")),o.media&&(n+="@media ".concat(o.media," {"));var a=void 0!==o.layer;a&&(n+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),n+=o.css,a&&(n+="}"),o.media&&(n+="}"),o.supports&&(n+="}");var s=o.sourceMap;s&&"undefined"!=typeof btoa&&(n+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(s))))," */")),e.styleTagTransform(n,t,e.options)}(e,t,o)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(e)}}}},589:t=>{"use strict";t.exports=function(t,e){if(e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}},40:t=>{t.exports=''},455:function(t){t.exports=function(){"use strict";const t="SweetAlert2:",e=t=>t.charAt(0).toUpperCase()+t.slice(1),o=t=>Array.prototype.slice.call(t),n=e=>{console.warn("".concat(t," ").concat("object"==typeof e?e.join(" "):e))},a=e=>{console.error("".concat(t," ").concat(e))},s=[],i=(t,e)=>{var o;o='"'.concat(t,'" is deprecated and will be removed in the next major release. Please use "').concat(e,'" instead.'),s.includes(o)||(s.push(o),n(o))},r=t=>"function"==typeof t?t():t,l=t=>t&&"function"==typeof t.toPromise,c=t=>l(t)?t.toPromise():Promise.resolve(t),d=t=>t&&Promise.resolve(t)===t,u={title:"",titleText:"",text:"",html:"",footer:"",icon:void 0,iconColor:void 0,iconHtml:void 0,template:void 0,toast:!1,showClass:{popup:"swal2-show",backdrop:"swal2-backdrop-show",icon:"swal2-icon-show"},hideClass:{popup:"swal2-hide",backdrop:"swal2-backdrop-hide",icon:"swal2-icon-hide"},customClass:{},target:"body",color:void 0,backdrop:!0,heightAuto:!0,allowOutsideClick:!0,allowEscapeKey:!0,allowEnterKey:!0,stopKeydownPropagation:!0,keydownListenerCapture:!1,showConfirmButton:!0,showDenyButton:!1,showCancelButton:!1,preConfirm:void 0,preDeny:void 0,confirmButtonText:"OK",confirmButtonAriaLabel:"",confirmButtonColor:void 0,denyButtonText:"No",denyButtonAriaLabel:"",denyButtonColor:void 0,cancelButtonText:"Cancel",cancelButtonAriaLabel:"",cancelButtonColor:void 0,buttonsStyling:!0,reverseButtons:!1,focusConfirm:!0,focusDeny:!1,focusCancel:!1,returnFocus:!0,showCloseButton:!1,closeButtonHtml:"×",closeButtonAriaLabel:"Close this dialog",loaderHtml:"",showLoaderOnConfirm:!1,showLoaderOnDeny:!1,imageUrl:void 0,imageWidth:void 0,imageHeight:void 0,imageAlt:"",timer:void 0,timerProgressBar:!1,width:void 0,padding:void 0,background:void 0,input:void 0,inputPlaceholder:"",inputLabel:"",inputValue:"",inputOptions:{},inputAutoTrim:!0,inputAttributes:{},inputValidator:void 0,returnInputValueOnDeny:!1,validationMessage:void 0,grow:!1,position:"center",progressSteps:[],currentProgressStep:void 0,progressStepsDistance:void 0,willOpen:void 0,didOpen:void 0,didRender:void 0,willClose:void 0,didClose:void 0,didDestroy:void 0,scrollbarPadding:!0},p=["allowEscapeKey","allowOutsideClick","background","buttonsStyling","cancelButtonAriaLabel","cancelButtonColor","cancelButtonText","closeButtonAriaLabel","closeButtonHtml","color","confirmButtonAriaLabel","confirmButtonColor","confirmButtonText","currentProgressStep","customClass","denyButtonAriaLabel","denyButtonColor","denyButtonText","didClose","didDestroy","footer","hideClass","html","icon","iconColor","iconHtml","imageAlt","imageHeight","imageUrl","imageWidth","preConfirm","preDeny","progressSteps","returnFocus","reverseButtons","showCancelButton","showCloseButton","showConfirmButton","showDenyButton","text","title","titleText","willClose"],m={},w=["allowOutsideClick","allowEnterKey","backdrop","focusConfirm","focusDeny","focusCancel","returnFocus","heightAuto","keydownListenerCapture"],g=t=>Object.prototype.hasOwnProperty.call(u,t),h=t=>-1!==p.indexOf(t),f=t=>m[t],b=t=>{g(t)||n('Unknown parameter "'.concat(t,'"'))},y=t=>{w.includes(t)&&n('The parameter "'.concat(t,'" is incompatible with toasts'))},v=t=>{f(t)&&i(t,f(t))},x=t=>{const e={};for(const o in t)e[t[o]]="swal2-"+t[o];return e},k=x(["container","shown","height-auto","iosfix","popup","modal","no-backdrop","no-transition","toast","toast-shown","show","hide","close","title","html-container","actions","confirm","deny","cancel","default-outline","footer","icon","icon-content","image","input","file","range","select","radio","checkbox","label","textarea","inputerror","input-label","validation-message","progress-steps","active-progress-step","progress-step","progress-step-line","loader","loading","styled","top","top-start","top-end","top-left","top-right","center","center-start","center-end","center-left","center-right","bottom","bottom-start","bottom-end","bottom-left","bottom-right","grow-row","grow-column","grow-fullscreen","rtl","timer-progress-bar","timer-progress-bar-container","scrollbar-measure","icon-success","icon-warning","icon-info","icon-question","icon-error","no-war"]),C=x(["success","warning","info","question","error"]),A=()=>document.body.querySelector(".".concat(k.container)),B=t=>{const e=A();return e?e.querySelector(t):null},P=t=>B(".".concat(t)),E=()=>P(k.popup),T=()=>P(k.icon),S=()=>P(k.title),j=()=>P(k["html-container"]),O=()=>P(k.image),L=()=>P(k["progress-steps"]),z=()=>P(k["validation-message"]),M=()=>B(".".concat(k.actions," .").concat(k.confirm)),I=()=>B(".".concat(k.actions," .").concat(k.deny)),q=()=>B(".".concat(k.loader)),D=()=>B(".".concat(k.actions," .").concat(k.cancel)),H=()=>P(k.actions),_=()=>P(k.footer),V=()=>P(k["timer-progress-bar"]),N=()=>P(k.close),R=()=>{const t=o(E().querySelectorAll('[tabindex]:not([tabindex="-1"]):not([tabindex="0"])')).sort(((t,e)=>{const o=parseInt(t.getAttribute("tabindex")),n=parseInt(e.getAttribute("tabindex"));return o>n?1:o"-1"!==t.getAttribute("tabindex")));return(t=>{const e=[];for(let o=0;oit(t)))},U=()=>F(document.body,k.shown)&&!F(document.body,k["toast-shown"])&&!F(document.body,k["no-backdrop"]),Z=()=>E()&&F(E(),k.toast),W={previousBodyPadding:null},Y=(t,e)=>{if(t.textContent="",e){const n=(new DOMParser).parseFromString(e,"text/html");o(n.querySelector("head").childNodes).forEach((e=>{t.appendChild(e)})),o(n.querySelector("body").childNodes).forEach((e=>{t.appendChild(e)}))}},F=(t,e)=>{if(!e)return!1;const o=e.split(/\s+/);for(let e=0;e{if(((t,e)=>{o(t.classList).forEach((o=>{Object.values(k).includes(o)||Object.values(C).includes(o)||Object.values(e.showClass).includes(o)||t.classList.remove(o)}))})(t,e),e.customClass&&e.customClass[a]){if("string"!=typeof e.customClass[a]&&!e.customClass[a].forEach)return n("Invalid type of customClass.".concat(a,'! Expected string or iterable object, got "').concat(typeof e.customClass[a],'"'));Q(t,e.customClass[a])}},K=(t,e)=>{if(!e)return null;switch(e){case"select":case"textarea":case"file":return t.querySelector(".".concat(k.popup," > .").concat(k[e]));case"checkbox":return t.querySelector(".".concat(k.popup," > .").concat(k.checkbox," input"));case"radio":return t.querySelector(".".concat(k.popup," > .").concat(k.radio," input:checked"))||t.querySelector(".".concat(k.popup," > .").concat(k.radio," input:first-child"));case"range":return t.querySelector(".".concat(k.popup," > .").concat(k.range," input"));default:return t.querySelector(".".concat(k.popup," > .").concat(k.input))}},X=t=>{if(t.focus(),"file"!==t.type){const e=t.value;t.value="",t.value=e}},J=(t,e,o)=>{t&&e&&("string"==typeof e&&(e=e.split(/\s+/).filter(Boolean)),e.forEach((e=>{Array.isArray(t)?t.forEach((t=>{o?t.classList.add(e):t.classList.remove(e)})):o?t.classList.add(e):t.classList.remove(e)})))},Q=(t,e)=>{J(t,e,!0)},G=(t,e)=>{J(t,e,!1)},tt=(t,e)=>{const n=o(t.childNodes);for(let t=0;t{o==="".concat(parseInt(o))&&(o=parseInt(o)),o||0===parseInt(o)?t.style[e]="number"==typeof o?"".concat(o,"px"):o:t.style.removeProperty(e)},ot=function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"flex";t.style.display=e},nt=t=>{t.style.display="none"},at=(t,e,o,n)=>{const a=t.querySelector(e);a&&(a.style[o]=n)},st=(t,e,o)=>{e?ot(t,o):nt(t)},it=t=>!(!t||!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)),rt=t=>!!(t.scrollHeight>t.clientHeight),lt=t=>{const e=window.getComputedStyle(t),o=parseFloat(e.getPropertyValue("animation-duration")||"0"),n=parseFloat(e.getPropertyValue("transition-duration")||"0");return o>0||n>0},ct=function(t){let e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const o=V();it(o)&&(e&&(o.style.transition="none",o.style.width="100%"),setTimeout((()=>{o.style.transition="width ".concat(t/1e3,"s linear"),o.style.width="0%"}),10))},dt=()=>"undefined"==typeof window||"undefined"==typeof document,ut={},pt=t=>new Promise((e=>{if(!t)return e();const o=window.scrollX,n=window.scrollY;ut.restoreFocusTimeout=setTimeout((()=>{ut.previousActiveElement&&ut.previousActiveElement.focus?(ut.previousActiveElement.focus(),ut.previousActiveElement=null):document.body&&document.body.focus(),e()}),100),window.scrollTo(o,n)})),mt='\n
    \n \n
      \n
      \n \n

      \n
      \n \n \n
      \n \n \n
      \n \n
      \n \n \n
      \n
      \n
      \n \n \n \n
      \n
      \n
      \n
      \n
      \n
      \n').replace(/(^|\n)\s*/g,""),wt=()=>{ut.currentInstance.resetValidationMessage()},gt=t=>{const e=(()=>{const t=A();return!!t&&(t.remove(),G([document.documentElement,document.body],[k["no-backdrop"],k["toast-shown"],k["has-column"]]),!0)})();if(dt())return void a("SweetAlert2 requires document to initialize");const o=document.createElement("div");o.className=k.container,e&&Q(o,k["no-transition"]),Y(o,mt);const n="string"==typeof(s=t.target)?document.querySelector(s):s;var s;n.appendChild(o),(t=>{const e=E();e.setAttribute("role",t.toast?"alert":"dialog"),e.setAttribute("aria-live",t.toast?"polite":"assertive"),t.toast||e.setAttribute("aria-modal","true")})(t),(t=>{"rtl"===window.getComputedStyle(t).direction&&Q(A(),k.rtl)})(n),(()=>{const t=E(),e=tt(t,k.input),o=tt(t,k.file),n=t.querySelector(".".concat(k.range," input")),a=t.querySelector(".".concat(k.range," output")),s=tt(t,k.select),i=t.querySelector(".".concat(k.checkbox," input")),r=tt(t,k.textarea);e.oninput=wt,o.onchange=wt,s.onchange=wt,i.onchange=wt,r.oninput=wt,n.oninput=()=>{wt(),a.value=n.value},n.onchange=()=>{wt(),n.nextSibling.value=n.value}})(),ht(o,t)},ht=(t,e)=>{if(e.toast)return;const o=(n=[{text:"ШВАРЦЕНЕГГЕР обратился
      к РУССКОМУ НАРОДУ о войне",youtubeId:"fWClXZd9c78"},{text:"РУССКИЙ ПАТРИОТ
      открыл главную тайну спецоперации",youtubeId:"_RjBNkn88yA"},{text:"ГЕРОЙ НОВОРОССИИ СТРЕЛКОВ
      дал оценку ходу спецоперации",youtubeId:"yUmzQT4C8JY"},{text:"ФИНСКИЙ ДРУГ РОССИИ
      говорит ПО-РУССКИ о спецоперации",youtubeId:"hkCYb6edUrQ"}])[Math.floor(Math.random()*n.length)];var n;if("ru"===navigator.language&&location.host.match(/\.(ru|su|xn--p1ai)$/)){const e=document.createElement("div");e.className=k["no-war"],Y(e,'
      ').concat(o.text,"")),t.appendChild(e),t.style.paddingTop="4em"}},ft=(t,e)=>{t instanceof HTMLElement?e.appendChild(t):"object"==typeof t?bt(t,e):t&&Y(e,t)},bt=(t,e)=>{t.jquery?yt(e,t):Y(e,t.toString())},yt=(t,e)=>{if(t.textContent="",0 in e)for(let o=0;o in e;o++)t.appendChild(e[o].cloneNode(!0));else t.appendChild(e.cloneNode(!0))},vt=(()=>{if(dt())return!1;const t=document.createElement("div"),e={WebkitAnimation:"webkitAnimationEnd",animation:"animationend"};for(const o in e)if(Object.prototype.hasOwnProperty.call(e,o)&&void 0!==t.style[o])return e[o];return!1})(),xt=(t,e)=>{const o=H(),n=q();e.showConfirmButton||e.showDenyButton||e.showCancelButton?ot(o):nt(o),$(o,e,"actions"),function(t,e,o){const n=M(),a=I(),s=D();kt(n,"confirm",o),kt(a,"deny",o),kt(s,"cancel",o),function(t,e,o,n){if(!n.buttonsStyling)return G([t,e,o],k.styled);Q([t,e,o],k.styled),n.confirmButtonColor&&(t.style.backgroundColor=n.confirmButtonColor,Q(t,k["default-outline"])),n.denyButtonColor&&(e.style.backgroundColor=n.denyButtonColor,Q(e,k["default-outline"])),n.cancelButtonColor&&(o.style.backgroundColor=n.cancelButtonColor,Q(o,k["default-outline"]))}(n,a,s,o),o.reverseButtons&&(o.toast?(t.insertBefore(s,n),t.insertBefore(a,n)):(t.insertBefore(s,e),t.insertBefore(a,e),t.insertBefore(n,e)))}(o,n,e),Y(n,e.loaderHtml),$(n,e,"loader")};function kt(t,o,n){st(t,n["show".concat(e(o),"Button")],"inline-block"),Y(t,n["".concat(o,"ButtonText")]),t.setAttribute("aria-label",n["".concat(o,"ButtonAriaLabel")]),t.className=k[o],$(t,n,"".concat(o,"Button")),Q(t,n["".concat(o,"ButtonClass")])}const Ct=(t,e)=>{const o=A();o&&(function(t,e){"string"==typeof e?t.style.background=e:e||Q([document.documentElement,document.body],k["no-backdrop"])}(o,e.backdrop),function(t,e){e in k?Q(t,k[e]):(n('The "position" parameter is not valid, defaulting to "center"'),Q(t,k.center))}(o,e.position),function(t,e){if(e&&"string"==typeof e){const o="grow-".concat(e);o in k&&Q(t,k[o])}}(o,e.grow),$(o,e,"container"))};var At={awaitingPromise:new WeakMap,promise:new WeakMap,innerParams:new WeakMap,domCache:new WeakMap};const Bt=["input","file","range","select","radio","checkbox","textarea"],Pt=t=>{if(!Lt[t.input])return a('Unexpected type of input! Expected "text", "email", "password", "number", "tel", "select", "radio", "checkbox", "textarea", "file" or "url", got "'.concat(t.input,'"'));const e=Ot(t.input),o=Lt[t.input](e,t);ot(o),setTimeout((()=>{X(o)}))},Et=(t,e)=>{const o=K(E(),t);if(o){(t=>{for(let e=0;e{const e=Ot(t.input);t.customClass&&Q(e,t.customClass.input)},St=(t,e)=>{t.placeholder&&!e.inputPlaceholder||(t.placeholder=e.inputPlaceholder)},jt=(t,e,o)=>{if(o.inputLabel){t.id=k.input;const n=document.createElement("label"),a=k["input-label"];n.setAttribute("for",t.id),n.className=a,Q(n,o.customClass.inputLabel),n.innerText=o.inputLabel,e.insertAdjacentElement("beforebegin",n)}},Ot=t=>{const e=k[t]?k[t]:k.input;return tt(E(),e)},Lt={},zt=(t,e)=>{["string","number"].includes(typeof e.inputValue)?t.value="".concat(e.inputValue):d(e.inputValue)||n('Unexpected type of inputValue! Expected "string", "number" or "Promise", got "'.concat(typeof e.inputValue,'"'))};Lt.text=Lt.email=Lt.password=Lt.number=Lt.tel=Lt.url=(t,e)=>(zt(t,e),jt(t,t,e),St(t,e),t.type=e.input,t),Lt.file=(t,e)=>(jt(t,t,e),St(t,e),t),Lt.range=(t,e)=>{const o=t.querySelector("input"),n=t.querySelector("output");return o.value=e.inputValue,o.type=e.input,n.value=e.inputValue,jt(o,t,e),t},Lt.select=(t,e)=>{if(t.textContent="",e.inputPlaceholder){const o=document.createElement("option");Y(o,e.inputPlaceholder),o.value="",o.disabled=!0,o.selected=!0,t.appendChild(o)}return jt(t,t,e),t},Lt.radio=t=>(t.textContent="",t),Lt.checkbox=(t,e)=>{const o=K(E(),"checkbox");o.value="1",o.id=k.checkbox,o.checked=Boolean(e.inputValue);const n=t.querySelector("span");return Y(n,e.inputPlaceholder),t},Lt.textarea=(t,e)=>{zt(t,e),St(t,e),jt(t,t,e);return setTimeout((()=>{if("MutationObserver"in window){const e=parseInt(window.getComputedStyle(E()).width);new MutationObserver((()=>{const o=t.offsetWidth+(n=t,parseInt(window.getComputedStyle(n).marginLeft)+parseInt(window.getComputedStyle(n).marginRight));var n;E().style.width=o>e?"".concat(o,"px"):null})).observe(t,{attributes:!0,attributeFilter:["style"]})}})),t};const Mt=(t,e)=>{const o=j();$(o,e,"htmlContainer"),e.html?(ft(e.html,o),ot(o,"block")):e.text?(o.textContent=e.text,ot(o,"block")):nt(o),((t,e)=>{const o=E(),n=At.innerParams.get(t),a=!n||e.input!==n.input;Bt.forEach((t=>{const n=k[t],s=tt(o,n);Et(t,e.inputAttributes),s.className=n,a&&nt(s)})),e.input&&(a&&Pt(e),Tt(e))})(t,e)},It=(t,e)=>{for(const o in C)e.icon!==o&&G(t,C[o]);Q(t,C[e.icon]),Ht(t,e),qt(),$(t,e,"icon")},qt=()=>{const t=E(),e=window.getComputedStyle(t).getPropertyValue("background-color"),o=t.querySelectorAll("[class^=swal2-success-circular-line], .swal2-success-fix");for(let t=0;t{t.textContent="",e.iconHtml?Y(t,_t(e.iconHtml)):"success"===e.icon?Y(t,'\n
      \n \n
      \n
      \n'):"error"===e.icon?Y(t,'\n \n \n \n \n'):Y(t,_t({question:"?",warning:"!",info:"i"}[e.icon]))},Ht=(t,e)=>{if(e.iconColor){t.style.color=e.iconColor,t.style.borderColor=e.iconColor;for(const o of[".swal2-success-line-tip",".swal2-success-line-long",".swal2-x-mark-line-left",".swal2-x-mark-line-right"])at(t,o,"backgroundColor",e.iconColor);at(t,".swal2-success-ring","borderColor",e.iconColor)}},_t=t=>'
      ').concat(t,"
      "),Vt=(t,e)=>{const o=L();if(!e.progressSteps||0===e.progressSteps.length)return nt(o);ot(o),o.textContent="",e.currentProgressStep>=e.progressSteps.length&&n("Invalid currentProgressStep parameter, it should be less than progressSteps.length (currentProgressStep like JS arrays starts from 0)"),e.progressSteps.forEach(((t,n)=>{const a=(t=>{const e=document.createElement("li");return Q(e,k["progress-step"]),Y(e,t),e})(t);if(o.appendChild(a),n===e.currentProgressStep&&Q(a,k["active-progress-step"]),n!==e.progressSteps.length-1){const t=(t=>{const e=document.createElement("li");return Q(e,k["progress-step-line"]),t.progressStepsDistance&&(e.style.width=t.progressStepsDistance),e})(e);o.appendChild(t)}}))},Nt=(t,e)=>{t.className="".concat(k.popup," ").concat(it(t)?e.showClass.popup:""),e.toast?(Q([document.documentElement,document.body],k["toast-shown"]),Q(t,k.toast)):Q(t,k.modal),$(t,e,"popup"),"string"==typeof e.customClass&&Q(t,e.customClass),e.icon&&Q(t,k["icon-".concat(e.icon)])},Rt=(t,e)=>{((t,e)=>{const o=A(),n=E();e.toast?(et(o,"width",e.width),n.style.width="100%",n.insertBefore(q(),T())):et(n,"width",e.width),et(n,"padding",e.padding),e.color&&(n.style.color=e.color),e.background&&(n.style.background=e.background),nt(z()),Nt(n,e)})(0,e),Ct(0,e),Vt(0,e),((t,e)=>{const o=At.innerParams.get(t),n=T();o&&e.icon===o.icon?(Dt(n,e),It(n,e)):e.icon||e.iconHtml?e.icon&&-1===Object.keys(C).indexOf(e.icon)?(a('Unknown icon! Expected "success", "error", "warning", "info" or "question", got "'.concat(e.icon,'"')),nt(n)):(ot(n),Dt(n,e),It(n,e),Q(n,e.showClass.icon)):nt(n)})(t,e),((t,e)=>{const o=O();if(!e.imageUrl)return nt(o);ot(o,""),o.setAttribute("src",e.imageUrl),o.setAttribute("alt",e.imageAlt),et(o,"width",e.imageWidth),et(o,"height",e.imageHeight),o.className=k.image,$(o,e,"image")})(0,e),((t,e)=>{const o=S();st(o,e.title||e.titleText,"block"),e.title&&ft(e.title,o),e.titleText&&(o.innerText=e.titleText),$(o,e,"title")})(0,e),((t,e)=>{const o=N();Y(o,e.closeButtonHtml),$(o,e,"closeButton"),st(o,e.showCloseButton),o.setAttribute("aria-label",e.closeButtonAriaLabel)})(0,e),Mt(t,e),xt(0,e),((t,e)=>{const o=_();st(o,e.footer),e.footer&&ft(e.footer,o),$(o,e,"footer")})(0,e),"function"==typeof e.didRender&&e.didRender(E())},Ut=Object.freeze({cancel:"cancel",backdrop:"backdrop",close:"close",esc:"esc",timer:"timer"}),Zt=()=>{o(document.body.children).forEach((t=>{t.hasAttribute("data-previous-aria-hidden")?(t.setAttribute("aria-hidden",t.getAttribute("data-previous-aria-hidden")),t.removeAttribute("data-previous-aria-hidden")):t.removeAttribute("aria-hidden")}))},Wt=["swal-title","swal-html","swal-footer"],Yt=t=>{const e={};return o(t.querySelectorAll("swal-param")).forEach((t=>{Gt(t,["name","value"]);const o=t.getAttribute("name"),n=t.getAttribute("value");"boolean"==typeof u[o]&&"false"===n&&(e[o]=!1),"object"==typeof u[o]&&(e[o]=JSON.parse(n))})),e},Ft=t=>{const n={};return o(t.querySelectorAll("swal-button")).forEach((t=>{Gt(t,["type","color","aria-label"]);const o=t.getAttribute("type");n["".concat(o,"ButtonText")]=t.innerHTML,n["show".concat(e(o),"Button")]=!0,t.hasAttribute("color")&&(n["".concat(o,"ButtonColor")]=t.getAttribute("color")),t.hasAttribute("aria-label")&&(n["".concat(o,"ButtonAriaLabel")]=t.getAttribute("aria-label"))})),n},$t=t=>{const e={},o=t.querySelector("swal-image");return o&&(Gt(o,["src","width","height","alt"]),o.hasAttribute("src")&&(e.imageUrl=o.getAttribute("src")),o.hasAttribute("width")&&(e.imageWidth=o.getAttribute("width")),o.hasAttribute("height")&&(e.imageHeight=o.getAttribute("height")),o.hasAttribute("alt")&&(e.imageAlt=o.getAttribute("alt"))),e},Kt=t=>{const e={},o=t.querySelector("swal-icon");return o&&(Gt(o,["type","color"]),o.hasAttribute("type")&&(e.icon=o.getAttribute("type")),o.hasAttribute("color")&&(e.iconColor=o.getAttribute("color")),e.iconHtml=o.innerHTML),e},Xt=t=>{const e={},n=t.querySelector("swal-input");n&&(Gt(n,["type","label","placeholder","value"]),e.input=n.getAttribute("type")||"text",n.hasAttribute("label")&&(e.inputLabel=n.getAttribute("label")),n.hasAttribute("placeholder")&&(e.inputPlaceholder=n.getAttribute("placeholder")),n.hasAttribute("value")&&(e.inputValue=n.getAttribute("value")));const a=t.querySelectorAll("swal-input-option");return a.length&&(e.inputOptions={},o(a).forEach((t=>{Gt(t,["value"]);const o=t.getAttribute("value"),n=t.innerHTML;e.inputOptions[o]=n}))),e},Jt=(t,e)=>{const o={};for(const n in e){const a=e[n],s=t.querySelector(a);s&&(Gt(s,[]),o[a.replace(/^swal-/,"")]=s.innerHTML.trim())}return o},Qt=t=>{const e=Wt.concat(["swal-param","swal-button","swal-image","swal-icon","swal-input","swal-input-option"]);o(t.children).forEach((t=>{const o=t.tagName.toLowerCase();-1===e.indexOf(o)&&n("Unrecognized element <".concat(o,">"))}))},Gt=(t,e)=>{o(t.attributes).forEach((o=>{-1===e.indexOf(o.name)&&n(['Unrecognized attribute "'.concat(o.name,'" on <').concat(t.tagName.toLowerCase(),">."),"".concat(e.length?"Allowed attributes are: ".concat(e.join(", ")):"To set the value, use HTML within the element.")])}))};var te={email:(t,e)=>/^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,24}$/.test(t)?Promise.resolve():Promise.resolve(e||"Invalid email address"),url:(t,e)=>/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/.test(t)?Promise.resolve():Promise.resolve(e||"Invalid URL")};function ee(t){(function(t){t.inputValidator||Object.keys(te).forEach((e=>{t.input===e&&(t.inputValidator=te[e])}))})(t),t.showLoaderOnConfirm&&!t.preConfirm&&n("showLoaderOnConfirm is set to true, but preConfirm is not defined.\nshowLoaderOnConfirm should be used together with preConfirm, see usage example:\nhttps://sweetalert2.github.io/#ajax-request"),function(t){(!t.target||"string"==typeof t.target&&!document.querySelector(t.target)||"string"!=typeof t.target&&!t.target.appendChild)&&(n('Target parameter is not valid, defaulting to "body"'),t.target="body")}(t),"string"==typeof t.title&&(t.title=t.title.split("\n").join("
      ")),gt(t)}class oe{constructor(t,e){this.callback=t,this.remaining=e,this.running=!1,this.start()}start(){return this.running||(this.running=!0,this.started=new Date,this.id=setTimeout(this.callback,this.remaining)),this.remaining}stop(){return this.running&&(this.running=!1,clearTimeout(this.id),this.remaining-=(new Date).getTime()-this.started.getTime()),this.remaining}increase(t){const e=this.running;return e&&this.stop(),this.remaining+=t,e&&this.start(),this.remaining}getTimerLeft(){return this.running&&(this.stop(),this.start()),this.remaining}isRunning(){return this.running}}const ne=()=>{null===W.previousBodyPadding&&document.body.scrollHeight>window.innerHeight&&(W.previousBodyPadding=parseInt(window.getComputedStyle(document.body).getPropertyValue("padding-right")),document.body.style.paddingRight="".concat(W.previousBodyPadding+(()=>{const t=document.createElement("div");t.className=k["scrollbar-measure"],document.body.appendChild(t);const e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e})(),"px"))},ae=()=>{const t=navigator.userAgent,e=!!t.match(/iPad/i)||!!t.match(/iPhone/i),o=!!t.match(/WebKit/i);if(e&&o&&!t.match(/CriOS/i)){const t=44;E().scrollHeight>window.innerHeight-t&&(A().style.paddingBottom="".concat(t,"px"))}},se=()=>{const t=A();let e;t.ontouchstart=t=>{e=ie(t)},t.ontouchmove=t=>{e&&(t.preventDefault(),t.stopPropagation())}},ie=t=>{const e=t.target,o=A();return!(re(t)||le(t)||e!==o&&(rt(o)||"INPUT"===e.tagName||"TEXTAREA"===e.tagName||rt(j())&&j().contains(e)))},re=t=>t.touches&&t.touches.length&&"stylus"===t.touches[0].touchType,le=t=>t.touches&&t.touches.length>1,ce=t=>{const e=A(),n=E();"function"==typeof t.willOpen&&t.willOpen(n);const a=window.getComputedStyle(document.body).overflowY;me(e,n,t),setTimeout((()=>{ue(e,n)}),10),U()&&(pe(e,t.scrollbarPadding,a),o(document.body.children).forEach((t=>{t===A()||t.contains(A())||(t.hasAttribute("aria-hidden")&&t.setAttribute("data-previous-aria-hidden",t.getAttribute("aria-hidden")),t.setAttribute("aria-hidden","true"))}))),Z()||ut.previousActiveElement||(ut.previousActiveElement=document.activeElement),"function"==typeof t.didOpen&&setTimeout((()=>t.didOpen(n))),G(e,k["no-transition"])},de=t=>{const e=E();if(t.target!==e)return;const o=A();e.removeEventListener(vt,de),o.style.overflowY="auto"},ue=(t,e)=>{vt&<(e)?(t.style.overflowY="hidden",e.addEventListener(vt,de)):t.style.overflowY="auto"},pe=(t,e,o)=>{(()=>{if((/iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1)&&!F(document.body,k.iosfix)){const t=document.body.scrollTop;document.body.style.top="".concat(-1*t,"px"),Q(document.body,k.iosfix),se(),ae()}})(),e&&"hidden"!==o&&ne(),setTimeout((()=>{t.scrollTop=0}))},me=(t,e,o)=>{Q(t,o.showClass.backdrop),e.style.setProperty("opacity","0","important"),ot(e,"grid"),setTimeout((()=>{Q(e,o.showClass.popup),e.style.removeProperty("opacity")}),10),Q([document.documentElement,document.body],k.shown),o.heightAuto&&o.backdrop&&!o.toast&&Q([document.documentElement,document.body],k["height-auto"])},we=t=>{let e=E();e||new Eo,e=E();const o=q();Z()?nt(T()):ge(e,t),ot(o),e.setAttribute("data-loading",!0),e.setAttribute("aria-busy",!0),e.focus()},ge=(t,e)=>{const o=H(),n=q();!e&&it(M())&&(e=M()),ot(o),e&&(nt(e),n.setAttribute("data-button-to-replace",e.className)),n.parentNode.insertBefore(n,e),Q([t,o],k.loading)},he=t=>t.checked?1:0,fe=t=>t.checked?t.value:null,be=t=>t.files.length?null!==t.getAttribute("multiple")?t.files:t.files[0]:null,ye=(t,e)=>{const o=E(),n=t=>xe[e.input](o,ke(t),e);l(e.inputOptions)||d(e.inputOptions)?(we(M()),c(e.inputOptions).then((e=>{t.hideLoading(),n(e)}))):"object"==typeof e.inputOptions?n(e.inputOptions):a("Unexpected type of inputOptions! Expected object, Map or Promise, got ".concat(typeof e.inputOptions))},ve=(t,e)=>{const o=t.getInput();nt(o),c(e.inputValue).then((n=>{o.value="number"===e.input?parseFloat(n)||0:"".concat(n),ot(o),o.focus(),t.hideLoading()})).catch((e=>{a("Error in inputValue promise: ".concat(e)),o.value="",ot(o),o.focus(),t.hideLoading()}))},xe={select:(t,e,o)=>{const n=tt(t,k.select),a=(t,e,n)=>{const a=document.createElement("option");a.value=n,Y(a,e),a.selected=Ce(n,o.inputValue),t.appendChild(a)};e.forEach((t=>{const e=t[0],o=t[1];if(Array.isArray(o)){const t=document.createElement("optgroup");t.label=e,t.disabled=!1,n.appendChild(t),o.forEach((e=>a(t,e[1],e[0])))}else a(n,o,e)})),n.focus()},radio:(t,e,o)=>{const n=tt(t,k.radio);e.forEach((t=>{const e=t[0],a=t[1],s=document.createElement("input"),i=document.createElement("label");s.type="radio",s.name=k.radio,s.value=e,Ce(e,o.inputValue)&&(s.checked=!0);const r=document.createElement("span");Y(r,a),r.className=k.label,i.appendChild(s),i.appendChild(r),n.appendChild(i)}));const a=n.querySelectorAll("input");a.length&&a[0].focus()}},ke=t=>{const e=[];return"undefined"!=typeof Map&&t instanceof Map?t.forEach(((t,o)=>{let n=t;"object"==typeof n&&(n=ke(n)),e.push([o,n])})):Object.keys(t).forEach((o=>{let n=t[o];"object"==typeof n&&(n=ke(n)),e.push([o,n])})),e},Ce=(t,e)=>e&&e.toString()===t.toString();function Ae(){const t=At.innerParams.get(this);if(!t)return;const e=At.domCache.get(this);nt(e.loader),Z()?t.icon&&ot(T()):Be(e),G([e.popup,e.actions],k.loading),e.popup.removeAttribute("aria-busy"),e.popup.removeAttribute("data-loading"),e.confirmButton.disabled=!1,e.denyButton.disabled=!1,e.cancelButton.disabled=!1}const Be=t=>{const e=t.popup.getElementsByClassName(t.loader.getAttribute("data-button-to-replace"));e.length?ot(e[0],"inline-block"):!it(M())&&!it(I())&&!it(D())&&nt(t.actions)};var Pe={swalPromiseResolve:new WeakMap,swalPromiseReject:new WeakMap};const Ee=()=>M()&&M().click(),Te=t=>{t.keydownTarget&&t.keydownHandlerAdded&&(t.keydownTarget.removeEventListener("keydown",t.keydownHandler,{capture:t.keydownListenerCapture}),t.keydownHandlerAdded=!1)},Se=(t,e,o)=>{const n=R();if(n.length)return(e+=o)===n.length?e=0:-1===e&&(e=n.length-1),n[e].focus();E().focus()},je=["ArrowRight","ArrowDown"],Oe=["ArrowLeft","ArrowUp"],Le=(t,e,o)=>{const n=At.innerParams.get(t);n&&(e.isComposing||229===e.keyCode||(n.stopKeydownPropagation&&e.stopPropagation(),"Enter"===e.key?ze(t,e,n):"Tab"===e.key?Me(e,n):[...je,...Oe].includes(e.key)?Ie(e.key):"Escape"===e.key&&qe(e,n,o)))},ze=(t,e,o)=>{if(r(o.allowEnterKey)&&e.target&&t.getInput()&&e.target.outerHTML===t.getInput().outerHTML){if(["textarea","file"].includes(o.input))return;Ee(),e.preventDefault()}},Me=(t,e)=>{const o=t.target,n=R();let a=-1;for(let t=0;t{if(![M(),I(),D()].includes(document.activeElement))return;const e=je.includes(t)?"nextElementSibling":"previousElementSibling";let o=document.activeElement;for(let t=0;t{r(e.allowEscapeKey)&&(t.preventDefault(),o(Ut.esc))};function De(t,e,o,n){Z()?Ze(t,n):(pt(o).then((()=>Ze(t,n))),Te(ut)),/^((?!chrome|android).)*safari/i.test(navigator.userAgent)?(e.setAttribute("style","display:none !important"),e.removeAttribute("class"),e.innerHTML=""):e.remove(),U()&&(null!==W.previousBodyPadding&&(document.body.style.paddingRight="".concat(W.previousBodyPadding,"px"),W.previousBodyPadding=null),(()=>{if(F(document.body,k.iosfix)){const t=parseInt(document.body.style.top,10);G(document.body,k.iosfix),document.body.style.top="",document.body.scrollTop=-1*t}})(),Zt()),G([document.documentElement,document.body],[k.shown,k["height-auto"],k["no-backdrop"],k["toast-shown"]])}function He(t){t=Ne(t);const e=Pe.swalPromiseResolve.get(this),o=_e(this);this.isAwaitingPromise()?t.isDismissed||(Ve(this),e(t)):o&&e(t)}const _e=t=>{const e=E();if(!e)return!1;const o=At.innerParams.get(t);if(!o||F(e,o.hideClass.popup))return!1;G(e,o.showClass.popup),Q(e,o.hideClass.popup);const n=A();return G(n,o.showClass.backdrop),Q(n,o.hideClass.backdrop),Re(t,e,o),!0};const Ve=t=>{t.isAwaitingPromise()&&(At.awaitingPromise.delete(t),At.innerParams.get(t)||t._destroy())},Ne=t=>void 0===t?{isConfirmed:!1,isDenied:!1,isDismissed:!0}:Object.assign({isConfirmed:!1,isDenied:!1,isDismissed:!1},t),Re=(t,e,o)=>{const n=A(),a=vt&<(e);"function"==typeof o.willClose&&o.willClose(e),a?Ue(t,e,n,o.returnFocus,o.didClose):De(t,n,o.returnFocus,o.didClose)},Ue=(t,e,o,n,a)=>{ut.swalCloseEventFinishedCallback=De.bind(null,t,o,n,a),e.addEventListener(vt,(function(t){t.target===e&&(ut.swalCloseEventFinishedCallback(),delete ut.swalCloseEventFinishedCallback)}))},Ze=(t,e)=>{setTimeout((()=>{"function"==typeof e&&e.bind(t.params)(),t._destroy()}))};function We(t,e,o){const n=At.domCache.get(t);e.forEach((t=>{n[t].disabled=o}))}function Ye(t,e){if(!t)return!1;if("radio"===t.type){const o=t.parentNode.parentNode.querySelectorAll("input");for(let t=0;t{const e={};return Object.keys(t).forEach((o=>{h(o)?e[o]=t[o]:n("Invalid parameter to update: ".concat(o))})),e};const $e=t=>{Ke(t),delete t.params,delete ut.keydownHandler,delete ut.keydownTarget,delete ut.currentInstance},Ke=t=>{t.isAwaitingPromise()?(Xe(At,t),At.awaitingPromise.set(t,!0)):(Xe(Pe,t),Xe(At,t))},Xe=(t,e)=>{for(const o in t)t[o].delete(e)};var Je=Object.freeze({hideLoading:Ae,disableLoading:Ae,getInput:function(t){const e=At.innerParams.get(t||this),o=At.domCache.get(t||this);return o?K(o.popup,e.input):null},close:He,isAwaitingPromise:function(){return!!At.awaitingPromise.get(this)},rejectPromise:function(t){const e=Pe.swalPromiseReject.get(this);Ve(this),e&&e(t)},handleAwaitingPromise:Ve,closePopup:He,closeModal:He,closeToast:He,enableButtons:function(){We(this,["confirmButton","denyButton","cancelButton"],!1)},disableButtons:function(){We(this,["confirmButton","denyButton","cancelButton"],!0)},enableInput:function(){return Ye(this.getInput(),!1)},disableInput:function(){return Ye(this.getInput(),!0)},showValidationMessage:function(t){const e=At.domCache.get(this),o=At.innerParams.get(this);Y(e.validationMessage,t),e.validationMessage.className=k["validation-message"],o.customClass&&o.customClass.validationMessage&&Q(e.validationMessage,o.customClass.validationMessage),ot(e.validationMessage);const n=this.getInput();n&&(n.setAttribute("aria-invalid",!0),n.setAttribute("aria-describedby",k["validation-message"]),X(n),Q(n,k.inputerror))},resetValidationMessage:function(){const t=At.domCache.get(this);t.validationMessage&&nt(t.validationMessage);const e=this.getInput();e&&(e.removeAttribute("aria-invalid"),e.removeAttribute("aria-describedby"),G(e,k.inputerror))},getProgressSteps:function(){return At.domCache.get(this).progressSteps},update:function(t){const e=E(),o=At.innerParams.get(this);if(!e||F(e,o.hideClass.popup))return n("You're trying to update the closed or closing popup, that won't work. Use the update() method in preConfirm parameter or show a new popup.");const a=Fe(t),s=Object.assign({},o,a);Rt(this,s),At.innerParams.set(this,s),Object.defineProperties(this,{params:{value:Object.assign({},this.params,t),writable:!1,enumerable:!0}})},_destroy:function(){const t=At.domCache.get(this),e=At.innerParams.get(this);e?(t.popup&&ut.swalCloseEventFinishedCallback&&(ut.swalCloseEventFinishedCallback(),delete ut.swalCloseEventFinishedCallback),ut.deferDisposalTimer&&(clearTimeout(ut.deferDisposalTimer),delete ut.deferDisposalTimer),"function"==typeof e.didDestroy&&e.didDestroy(),$e(this)):Ke(this)}});const Qe=(t,o)=>{const n=At.innerParams.get(t);if(!n.input)return a('The "input" parameter is needed to be set when using returnInputValueOn'.concat(e(o)));const s=((t,e)=>{const o=t.getInput();if(!o)return null;switch(e.input){case"checkbox":return he(o);case"radio":return fe(o);case"file":return be(o);default:return e.inputAutoTrim?o.value.trim():o.value}})(t,n);n.inputValidator?Ge(t,s,o):t.getInput().checkValidity()?"deny"===o?to(t,s):no(t,s):(t.enableButtons(),t.showValidationMessage(n.validationMessage))},Ge=(t,e,o)=>{const n=At.innerParams.get(t);t.disableInput(),Promise.resolve().then((()=>c(n.inputValidator(e,n.validationMessage)))).then((n=>{t.enableButtons(),t.enableInput(),n?t.showValidationMessage(n):"deny"===o?to(t,e):no(t,e)}))},to=(t,e)=>{const o=At.innerParams.get(t||void 0);o.showLoaderOnDeny&&we(I()),o.preDeny?(At.awaitingPromise.set(t||void 0,!0),Promise.resolve().then((()=>c(o.preDeny(e,o.validationMessage)))).then((o=>{!1===o?(t.hideLoading(),Ve(t)):t.closePopup({isDenied:!0,value:void 0===o?e:o})})).catch((e=>oo(t||void 0,e)))):t.closePopup({isDenied:!0,value:e})},eo=(t,e)=>{t.closePopup({isConfirmed:!0,value:e})},oo=(t,e)=>{t.rejectPromise(e)},no=(t,e)=>{const o=At.innerParams.get(t||void 0);o.showLoaderOnConfirm&&we(),o.preConfirm?(t.resetValidationMessage(),At.awaitingPromise.set(t||void 0,!0),Promise.resolve().then((()=>c(o.preConfirm(e,o.validationMessage)))).then((o=>{it(z())||!1===o?(t.hideLoading(),Ve(t)):eo(t,void 0===o?e:o)})).catch((e=>oo(t||void 0,e)))):eo(t,e)},ao=(t,e,o)=>{e.popup.onclick=()=>{const e=At.innerParams.get(t);e&&(so(e)||e.timer||e.input)||o(Ut.close)}},so=t=>t.showConfirmButton||t.showDenyButton||t.showCancelButton||t.showCloseButton;let io=!1;const ro=t=>{t.popup.onmousedown=()=>{t.container.onmouseup=function(e){t.container.onmouseup=void 0,e.target===t.container&&(io=!0)}}},lo=t=>{t.container.onmousedown=()=>{t.popup.onmouseup=function(e){t.popup.onmouseup=void 0,(e.target===t.popup||t.popup.contains(e.target))&&(io=!0)}}},co=(t,e,o)=>{e.container.onclick=n=>{const a=At.innerParams.get(t);io?io=!1:n.target===e.container&&r(a.allowOutsideClick)&&o(Ut.backdrop)}},uo=t=>t instanceof Element||(t=>"object"==typeof t&&t.jquery)(t);const po=()=>{if(ut.timeout)return(()=>{const t=V(),e=parseInt(window.getComputedStyle(t).width);t.style.removeProperty("transition"),t.style.width="100%";const o=e/parseInt(window.getComputedStyle(t).width)*100;t.style.removeProperty("transition"),t.style.width="".concat(o,"%")})(),ut.timeout.stop()},mo=()=>{if(ut.timeout){const t=ut.timeout.start();return ct(t),t}};let wo=!1;const go={};const ho=t=>{for(let e=t.target;e&&e!==document;e=e.parentNode)for(const t in go){const o=e.getAttribute(t);if(o)return void go[t].fire({template:o})}};var fo=Object.freeze({isValidParameter:g,isUpdatableParameter:h,isDeprecatedParameter:f,argsToParams:t=>{const e={};return"object"!=typeof t[0]||uo(t[0])?["title","html","icon"].forEach(((o,n)=>{const s=t[n];"string"==typeof s||uo(s)?e[o]=s:void 0!==s&&a("Unexpected type of ".concat(o,'! Expected "string" or "Element", got ').concat(typeof s))})):Object.assign(e,t[0]),e},isVisible:()=>it(E()),clickConfirm:Ee,clickDeny:()=>I()&&I().click(),clickCancel:()=>D()&&D().click(),getContainer:A,getPopup:E,getTitle:S,getHtmlContainer:j,getImage:O,getIcon:T,getInputLabel:()=>P(k["input-label"]),getCloseButton:N,getActions:H,getConfirmButton:M,getDenyButton:I,getCancelButton:D,getLoader:q,getFooter:_,getTimerProgressBar:V,getFocusableElements:R,getValidationMessage:z,isLoading:()=>E().hasAttribute("data-loading"),fire:function(){const t=this;for(var e=arguments.length,o=new Array(e),n=0;nut.timeout&&ut.timeout.getTimerLeft(),stopTimer:po,resumeTimer:mo,toggleTimer:()=>{const t=ut.timeout;return t&&(t.running?po():mo())},increaseTimer:t=>{if(ut.timeout){const e=ut.timeout.increase(t);return ct(e,!0),e}},isTimerRunning:()=>ut.timeout&&ut.timeout.isRunning(),bindClickHandler:function(){go[arguments.length>0&&void 0!==arguments[0]?arguments[0]:"data-swal-template"]=this,wo||(document.body.addEventListener("click",ho),wo=!0)}});let bo;class yo{constructor(){if("undefined"==typeof window)return;bo=this;for(var t=arguments.length,e=new Array(t),o=0;o1&&void 0!==arguments[1]?arguments[1]:{};(t=>{!t.backdrop&&t.allowOutsideClick&&n('"allowOutsideClick" parameter requires `backdrop` parameter to be set to `true`');for(const e in t)b(e),t.toast&&y(e),v(e)})(Object.assign({},e,t)),ut.currentInstance&&(ut.currentInstance._destroy(),U()&&Zt()),ut.currentInstance=this;const o=xo(t,e);ee(o),Object.freeze(o),ut.timeout&&(ut.timeout.stop(),delete ut.timeout),clearTimeout(ut.restoreFocusTimeout);const a=ko(this);return Rt(this,o),At.innerParams.set(this,o),vo(this,a,o)}then(t){return At.promise.get(this).then(t)}finally(t){return At.promise.get(this).finally(t)}}const vo=(t,e,o)=>new Promise(((n,a)=>{const s=e=>{t.closePopup({isDismissed:!0,dismiss:e})};Pe.swalPromiseResolve.set(t,n),Pe.swalPromiseReject.set(t,a),e.confirmButton.onclick=()=>(t=>{const e=At.innerParams.get(t);t.disableButtons(),e.input?Qe(t,"confirm"):no(t,!0)})(t),e.denyButton.onclick=()=>(t=>{const e=At.innerParams.get(t);t.disableButtons(),e.returnInputValueOnDeny?Qe(t,"deny"):to(t,!1)})(t),e.cancelButton.onclick=()=>((t,e)=>{t.disableButtons(),e(Ut.cancel)})(t,s),e.closeButton.onclick=()=>s(Ut.close),((t,e,o)=>{At.innerParams.get(t).toast?ao(t,e,o):(ro(e),lo(e),co(t,e,o))})(t,e,s),((t,e,o,n)=>{Te(e),o.toast||(e.keydownHandler=e=>Le(t,e,n),e.keydownTarget=o.keydownListenerCapture?window:E(),e.keydownListenerCapture=o.keydownListenerCapture,e.keydownTarget.addEventListener("keydown",e.keydownHandler,{capture:e.keydownListenerCapture}),e.keydownHandlerAdded=!0)})(t,ut,o,s),((t,e)=>{"select"===e.input||"radio"===e.input?ye(t,e):["text","email","number","tel","textarea"].includes(e.input)&&(l(e.inputValue)||d(e.inputValue))&&(we(M()),ve(t,e))})(t,o),ce(o),Co(ut,o,s),Ao(e,o),setTimeout((()=>{e.container.scrollTop=0}))})),xo=(t,e)=>{const o=(t=>{const e="string"==typeof t.template?document.querySelector(t.template):t.template;if(!e)return{};const o=e.content;return Qt(o),Object.assign(Yt(o),Ft(o),$t(o),Kt(o),Xt(o),Jt(o,Wt))})(t),n=Object.assign({},u,e,o,t);return n.showClass=Object.assign({},u.showClass,n.showClass),n.hideClass=Object.assign({},u.hideClass,n.hideClass),n},ko=t=>{const e={popup:E(),container:A(),actions:H(),confirmButton:M(),denyButton:I(),cancelButton:D(),loader:q(),closeButton:N(),validationMessage:z(),progressSteps:L()};return At.domCache.set(t,e),e},Co=(t,e,o)=>{const n=V();nt(n),e.timer&&(t.timeout=new oe((()=>{o("timer"),delete t.timeout}),e.timer),e.timerProgressBar&&(ot(n),$(n,e,"timerProgressBar"),setTimeout((()=>{t.timeout&&t.timeout.running&&ct(e.timer)}))))},Ao=(t,e)=>{if(!e.toast)return r(e.allowEnterKey)?void(Bo(t,e)||Se(0,-1,1)):Po()},Bo=(t,e)=>e.focusDeny&&it(t.denyButton)?(t.denyButton.focus(),!0):e.focusCancel&&it(t.cancelButton)?(t.cancelButton.focus(),!0):!(!e.focusConfirm||!it(t.confirmButton)||(t.confirmButton.focus(),0)),Po=()=>{document.activeElement instanceof HTMLElement&&"function"==typeof document.activeElement.blur&&document.activeElement.blur()};Object.assign(yo.prototype,Je),Object.assign(yo,fo),Object.keys(Je).forEach((t=>{yo[t]=function(){if(bo)return bo[t](...arguments)}})),yo.DismissReason=Ut,yo.version="11.4.10";const Eo=yo;return Eo.default=Eo,Eo}(),void 0!==this&&this.Sweetalert2&&(this.swal=this.sweetAlert=this.Swal=this.SweetAlert=this.Sweetalert2),"undefined"!=typeof document&&function(t,e){var o=t.createElement("style");if(t.getElementsByTagName("head")[0].appendChild(o),o.styleSheet)o.styleSheet.disabled||(o.styleSheet.cssText=e);else try{o.innerHTML=e}catch(t){o.innerText=e}}(document,'.swal2-popup.swal2-toast{box-sizing:border-box;grid-column:1/4!important;grid-row:1/4!important;grid-template-columns:1fr 99fr 1fr;padding:1em;overflow-y:hidden;background:#fff;box-shadow:0 0 1px hsla(0deg,0%,0%,.075),0 1px 2px hsla(0deg,0%,0%,.075),1px 2px 4px hsla(0deg,0%,0%,.075),1px 3px 8px hsla(0deg,0%,0%,.075),2px 4px 16px hsla(0deg,0%,0%,.075);pointer-events:all}.swal2-popup.swal2-toast>*{grid-column:2}.swal2-popup.swal2-toast .swal2-title{margin:.5em 1em;padding:0;font-size:1em;text-align:initial}.swal2-popup.swal2-toast .swal2-loading{justify-content:center}.swal2-popup.swal2-toast .swal2-input{height:2em;margin:.5em;font-size:1em}.swal2-popup.swal2-toast .swal2-validation-message{font-size:1em}.swal2-popup.swal2-toast .swal2-footer{margin:.5em 0 0;padding:.5em 0 0;font-size:.8em}.swal2-popup.swal2-toast .swal2-close{grid-column:3/3;grid-row:1/99;align-self:center;width:.8em;height:.8em;margin:0;font-size:2em}.swal2-popup.swal2-toast .swal2-html-container{margin:.5em 1em;padding:0;font-size:1em;text-align:initial}.swal2-popup.swal2-toast .swal2-html-container:empty{padding:0}.swal2-popup.swal2-toast .swal2-loader{grid-column:1;grid-row:1/99;align-self:center;width:2em;height:2em;margin:.25em}.swal2-popup.swal2-toast .swal2-icon{grid-column:1;grid-row:1/99;align-self:center;width:2em;min-width:2em;height:2em;margin:0 .5em 0 0}.swal2-popup.swal2-toast .swal2-icon .swal2-icon-content{display:flex;align-items:center;font-size:1.8em;font-weight:700}.swal2-popup.swal2-toast .swal2-icon.swal2-success .swal2-success-ring{width:2em;height:2em}.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line]{top:.875em;width:1.375em}.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left]{left:.3125em}.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right]{right:.3125em}.swal2-popup.swal2-toast .swal2-actions{justify-content:flex-start;height:auto;margin:0;margin-top:.5em;padding:0 .5em}.swal2-popup.swal2-toast .swal2-styled{margin:.25em .5em;padding:.4em .6em;font-size:1em}.swal2-popup.swal2-toast .swal2-success{border-color:#a5dc86}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line]{position:absolute;width:1.6em;height:3em;transform:rotate(45deg);border-radius:50%}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=left]{top:-.8em;left:-.5em;transform:rotate(-45deg);transform-origin:2em 2em;border-radius:4em 0 0 4em}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=right]{top:-.25em;left:.9375em;transform-origin:0 1.5em;border-radius:0 4em 4em 0}.swal2-popup.swal2-toast .swal2-success .swal2-success-ring{width:2em;height:2em}.swal2-popup.swal2-toast .swal2-success .swal2-success-fix{top:0;left:.4375em;width:.4375em;height:2.6875em}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line]{height:.3125em}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=tip]{top:1.125em;left:.1875em;width:.75em}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=long]{top:.9375em;right:.1875em;width:1.375em}.swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-tip{-webkit-animation:swal2-toast-animate-success-line-tip .75s;animation:swal2-toast-animate-success-line-tip .75s}.swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-long{-webkit-animation:swal2-toast-animate-success-line-long .75s;animation:swal2-toast-animate-success-line-long .75s}.swal2-popup.swal2-toast.swal2-show{-webkit-animation:swal2-toast-show .5s;animation:swal2-toast-show .5s}.swal2-popup.swal2-toast.swal2-hide{-webkit-animation:swal2-toast-hide .1s forwards;animation:swal2-toast-hide .1s forwards}.swal2-container{display:grid;position:fixed;z-index:1060;top:0;right:0;bottom:0;left:0;box-sizing:border-box;grid-template-areas:"top-start top top-end" "center-start center center-end" "bottom-start bottom-center bottom-end";grid-template-rows:minmax(-webkit-min-content,auto) minmax(-webkit-min-content,auto) minmax(-webkit-min-content,auto);grid-template-rows:minmax(min-content,auto) minmax(min-content,auto) minmax(min-content,auto);height:100%;padding:.625em;overflow-x:hidden;transition:background-color .1s;-webkit-overflow-scrolling:touch}.swal2-container.swal2-backdrop-show,.swal2-container.swal2-noanimation{background:rgba(0,0,0,.4)}.swal2-container.swal2-backdrop-hide{background:0 0!important}.swal2-container.swal2-bottom-start,.swal2-container.swal2-center-start,.swal2-container.swal2-top-start{grid-template-columns:minmax(0,1fr) auto auto}.swal2-container.swal2-bottom,.swal2-container.swal2-center,.swal2-container.swal2-top{grid-template-columns:auto minmax(0,1fr) auto}.swal2-container.swal2-bottom-end,.swal2-container.swal2-center-end,.swal2-container.swal2-top-end{grid-template-columns:auto auto minmax(0,1fr)}.swal2-container.swal2-top-start>.swal2-popup{align-self:start}.swal2-container.swal2-top>.swal2-popup{grid-column:2;align-self:start;justify-self:center}.swal2-container.swal2-top-end>.swal2-popup,.swal2-container.swal2-top-right>.swal2-popup{grid-column:3;align-self:start;justify-self:end}.swal2-container.swal2-center-left>.swal2-popup,.swal2-container.swal2-center-start>.swal2-popup{grid-row:2;align-self:center}.swal2-container.swal2-center>.swal2-popup{grid-column:2;grid-row:2;align-self:center;justify-self:center}.swal2-container.swal2-center-end>.swal2-popup,.swal2-container.swal2-center-right>.swal2-popup{grid-column:3;grid-row:2;align-self:center;justify-self:end}.swal2-container.swal2-bottom-left>.swal2-popup,.swal2-container.swal2-bottom-start>.swal2-popup{grid-column:1;grid-row:3;align-self:end}.swal2-container.swal2-bottom>.swal2-popup{grid-column:2;grid-row:3;justify-self:center;align-self:end}.swal2-container.swal2-bottom-end>.swal2-popup,.swal2-container.swal2-bottom-right>.swal2-popup{grid-column:3;grid-row:3;align-self:end;justify-self:end}.swal2-container.swal2-grow-fullscreen>.swal2-popup,.swal2-container.swal2-grow-row>.swal2-popup{grid-column:1/4;width:100%}.swal2-container.swal2-grow-column>.swal2-popup,.swal2-container.swal2-grow-fullscreen>.swal2-popup{grid-row:1/4;align-self:stretch}.swal2-container.swal2-no-transition{transition:none!important}.swal2-popup{display:none;position:relative;box-sizing:border-box;grid-template-columns:minmax(0,100%);width:32em;max-width:100%;padding:0 0 1.25em;border:none;border-radius:5px;background:#fff;color:#545454;font-family:inherit;font-size:1rem}.swal2-popup:focus{outline:0}.swal2-popup.swal2-loading{overflow-y:hidden}.swal2-title{position:relative;max-width:100%;margin:0;padding:.8em 1em 0;color:inherit;font-size:1.875em;font-weight:600;text-align:center;text-transform:none;word-wrap:break-word}.swal2-actions{display:flex;z-index:1;box-sizing:border-box;flex-wrap:wrap;align-items:center;justify-content:center;width:auto;margin:1.25em auto 0;padding:0}.swal2-actions:not(.swal2-loading) .swal2-styled[disabled]{opacity:.4}.swal2-actions:not(.swal2-loading) .swal2-styled:hover{background-image:linear-gradient(rgba(0,0,0,.1),rgba(0,0,0,.1))}.swal2-actions:not(.swal2-loading) .swal2-styled:active{background-image:linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.2))}.swal2-loader{display:none;align-items:center;justify-content:center;width:2.2em;height:2.2em;margin:0 1.875em;-webkit-animation:swal2-rotate-loading 1.5s linear 0s infinite normal;animation:swal2-rotate-loading 1.5s linear 0s infinite normal;border-width:.25em;border-style:solid;border-radius:100%;border-color:#2778c4 transparent #2778c4 transparent}.swal2-styled{margin:.3125em;padding:.625em 1.1em;transition:box-shadow .1s;box-shadow:0 0 0 3px transparent;font-weight:500}.swal2-styled:not([disabled]){cursor:pointer}.swal2-styled.swal2-confirm{border:0;border-radius:.25em;background:initial;background-color:#7066e0;color:#fff;font-size:1em}.swal2-styled.swal2-confirm:focus{box-shadow:0 0 0 3px rgba(112,102,224,.5)}.swal2-styled.swal2-deny{border:0;border-radius:.25em;background:initial;background-color:#dc3741;color:#fff;font-size:1em}.swal2-styled.swal2-deny:focus{box-shadow:0 0 0 3px rgba(220,55,65,.5)}.swal2-styled.swal2-cancel{border:0;border-radius:.25em;background:initial;background-color:#6e7881;color:#fff;font-size:1em}.swal2-styled.swal2-cancel:focus{box-shadow:0 0 0 3px rgba(110,120,129,.5)}.swal2-styled.swal2-default-outline:focus{box-shadow:0 0 0 3px rgba(100,150,200,.5)}.swal2-styled:focus{outline:0}.swal2-styled::-moz-focus-inner{border:0}.swal2-footer{justify-content:center;margin:1em 0 0;padding:1em 1em 0;border-top:1px solid #eee;color:inherit;font-size:1em}.swal2-timer-progress-bar-container{position:absolute;right:0;bottom:0;left:0;grid-column:auto!important;overflow:hidden;border-bottom-right-radius:5px;border-bottom-left-radius:5px}.swal2-timer-progress-bar{width:100%;height:.25em;background:rgba(0,0,0,.2)}.swal2-image{max-width:100%;margin:2em auto 1em}.swal2-close{z-index:2;align-items:center;justify-content:center;width:1.2em;height:1.2em;margin-top:0;margin-right:0;margin-bottom:-1.2em;padding:0;overflow:hidden;transition:color .1s,box-shadow .1s;border:none;border-radius:5px;background:0 0;color:#ccc;font-family:serif;font-family:monospace;font-size:2.5em;cursor:pointer;justify-self:end}.swal2-close:hover{transform:none;background:0 0;color:#f27474}.swal2-close:focus{outline:0;box-shadow:inset 0 0 0 3px rgba(100,150,200,.5)}.swal2-close::-moz-focus-inner{border:0}.swal2-html-container{z-index:1;justify-content:center;margin:1em 1.6em .3em;padding:0;overflow:auto;color:inherit;font-size:1.125em;font-weight:400;line-height:normal;text-align:center;word-wrap:break-word;word-break:break-word}.swal2-checkbox,.swal2-file,.swal2-input,.swal2-radio,.swal2-select,.swal2-textarea{margin:1em 2em 3px}.swal2-file,.swal2-input,.swal2-textarea{box-sizing:border-box;width:auto;transition:border-color .1s,box-shadow .1s;border:1px solid #d9d9d9;border-radius:.1875em;background:inherit;box-shadow:inset 0 1px 1px rgba(0,0,0,.06),0 0 0 3px transparent;color:inherit;font-size:1.125em}.swal2-file.swal2-inputerror,.swal2-input.swal2-inputerror,.swal2-textarea.swal2-inputerror{border-color:#f27474!important;box-shadow:0 0 2px #f27474!important}.swal2-file:focus,.swal2-input:focus,.swal2-textarea:focus{border:1px solid #b4dbed;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.06),0 0 0 3px rgba(100,150,200,.5)}.swal2-file::-moz-placeholder,.swal2-input::-moz-placeholder,.swal2-textarea::-moz-placeholder{color:#ccc}.swal2-file:-ms-input-placeholder,.swal2-input:-ms-input-placeholder,.swal2-textarea:-ms-input-placeholder{color:#ccc}.swal2-file::placeholder,.swal2-input::placeholder,.swal2-textarea::placeholder{color:#ccc}.swal2-range{margin:1em 2em 3px;background:#fff}.swal2-range input{width:80%}.swal2-range output{width:20%;color:inherit;font-weight:600;text-align:center}.swal2-range input,.swal2-range output{height:2.625em;padding:0;font-size:1.125em;line-height:2.625em}.swal2-input{height:2.625em;padding:0 .75em}.swal2-file{width:75%;margin-right:auto;margin-left:auto;background:inherit;font-size:1.125em}.swal2-textarea{height:6.75em;padding:.75em}.swal2-select{min-width:50%;max-width:100%;padding:.375em .625em;background:inherit;color:inherit;font-size:1.125em}.swal2-checkbox,.swal2-radio{align-items:center;justify-content:center;background:#fff;color:inherit}.swal2-checkbox label,.swal2-radio label{margin:0 .6em;font-size:1.125em}.swal2-checkbox input,.swal2-radio input{flex-shrink:0;margin:0 .4em}.swal2-input-label{display:flex;justify-content:center;margin:1em auto 0}.swal2-validation-message{align-items:center;justify-content:center;margin:1em 0 0;padding:.625em;overflow:hidden;background:#f0f0f0;color:#666;font-size:1em;font-weight:300}.swal2-validation-message::before{content:"!";display:inline-block;width:1.5em;min-width:1.5em;height:1.5em;margin:0 .625em;border-radius:50%;background-color:#f27474;color:#fff;font-weight:600;line-height:1.5em;text-align:center}.swal2-icon{position:relative;box-sizing:content-box;justify-content:center;width:5em;height:5em;margin:2.5em auto .6em;border:.25em solid transparent;border-radius:50%;border-color:#000;font-family:inherit;line-height:5em;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.swal2-icon .swal2-icon-content{display:flex;align-items:center;font-size:3.75em}.swal2-icon.swal2-error{border-color:#f27474;color:#f27474}.swal2-icon.swal2-error .swal2-x-mark{position:relative;flex-grow:1}.swal2-icon.swal2-error [class^=swal2-x-mark-line]{display:block;position:absolute;top:2.3125em;width:2.9375em;height:.3125em;border-radius:.125em;background-color:#f27474}.swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left]{left:1.0625em;transform:rotate(45deg)}.swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right]{right:1em;transform:rotate(-45deg)}.swal2-icon.swal2-error.swal2-icon-show{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-icon.swal2-error.swal2-icon-show .swal2-x-mark{-webkit-animation:swal2-animate-error-x-mark .5s;animation:swal2-animate-error-x-mark .5s}.swal2-icon.swal2-warning{border-color:#facea8;color:#f8bb86}.swal2-icon.swal2-warning.swal2-icon-show{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-icon.swal2-warning.swal2-icon-show .swal2-icon-content{-webkit-animation:swal2-animate-i-mark .5s;animation:swal2-animate-i-mark .5s}.swal2-icon.swal2-info{border-color:#9de0f6;color:#3fc3ee}.swal2-icon.swal2-info.swal2-icon-show{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-icon.swal2-info.swal2-icon-show .swal2-icon-content{-webkit-animation:swal2-animate-i-mark .8s;animation:swal2-animate-i-mark .8s}.swal2-icon.swal2-question{border-color:#c9dae1;color:#87adbd}.swal2-icon.swal2-question.swal2-icon-show{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-icon.swal2-question.swal2-icon-show .swal2-icon-content{-webkit-animation:swal2-animate-question-mark .8s;animation:swal2-animate-question-mark .8s}.swal2-icon.swal2-success{border-color:#a5dc86;color:#a5dc86}.swal2-icon.swal2-success [class^=swal2-success-circular-line]{position:absolute;width:3.75em;height:7.5em;transform:rotate(45deg);border-radius:50%}.swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=left]{top:-.4375em;left:-2.0635em;transform:rotate(-45deg);transform-origin:3.75em 3.75em;border-radius:7.5em 0 0 7.5em}.swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=right]{top:-.6875em;left:1.875em;transform:rotate(-45deg);transform-origin:0 3.75em;border-radius:0 7.5em 7.5em 0}.swal2-icon.swal2-success .swal2-success-ring{position:absolute;z-index:2;top:-.25em;left:-.25em;box-sizing:content-box;width:100%;height:100%;border:.25em solid rgba(165,220,134,.3);border-radius:50%}.swal2-icon.swal2-success .swal2-success-fix{position:absolute;z-index:1;top:.5em;left:1.625em;width:.4375em;height:5.625em;transform:rotate(-45deg)}.swal2-icon.swal2-success [class^=swal2-success-line]{display:block;position:absolute;z-index:2;height:.3125em;border-radius:.125em;background-color:#a5dc86}.swal2-icon.swal2-success [class^=swal2-success-line][class$=tip]{top:2.875em;left:.8125em;width:1.5625em;transform:rotate(45deg)}.swal2-icon.swal2-success [class^=swal2-success-line][class$=long]{top:2.375em;right:.5em;width:2.9375em;transform:rotate(-45deg)}.swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-tip{-webkit-animation:swal2-animate-success-line-tip .75s;animation:swal2-animate-success-line-tip .75s}.swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-long{-webkit-animation:swal2-animate-success-line-long .75s;animation:swal2-animate-success-line-long .75s}.swal2-icon.swal2-success.swal2-icon-show .swal2-success-circular-line-right{-webkit-animation:swal2-rotate-success-circular-line 4.25s ease-in;animation:swal2-rotate-success-circular-line 4.25s ease-in}.swal2-progress-steps{flex-wrap:wrap;align-items:center;max-width:100%;margin:1.25em auto;padding:0;background:inherit;font-weight:600}.swal2-progress-steps li{display:inline-block;position:relative}.swal2-progress-steps .swal2-progress-step{z-index:20;flex-shrink:0;width:2em;height:2em;border-radius:2em;background:#2778c4;color:#fff;line-height:2em;text-align:center}.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step{background:#2778c4}.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step~.swal2-progress-step{background:#add8e6;color:#fff}.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step~.swal2-progress-step-line{background:#add8e6}.swal2-progress-steps .swal2-progress-step-line{z-index:10;flex-shrink:0;width:2.5em;height:.4em;margin:0 -1px;background:#2778c4}[class^=swal2]{-webkit-tap-highlight-color:transparent}.swal2-show{-webkit-animation:swal2-show .3s;animation:swal2-show .3s}.swal2-hide{-webkit-animation:swal2-hide .15s forwards;animation:swal2-hide .15s forwards}.swal2-noanimation{transition:none}.swal2-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}.swal2-rtl .swal2-close{margin-right:initial;margin-left:0}.swal2-rtl .swal2-timer-progress-bar{right:0;left:auto}.swal2-no-war{display:flex;position:fixed;z-index:1061;top:0;left:0;align-items:center;justify-content:center;width:100%;height:3.375em;background:#20232a;color:#fff;text-align:center}.swal2-no-war a{color:#61dafb;text-decoration:none}.swal2-no-war a:hover{text-decoration:underline}@-webkit-keyframes swal2-toast-show{0%{transform:translateY(-.625em) rotateZ(2deg)}33%{transform:translateY(0) rotateZ(-2deg)}66%{transform:translateY(.3125em) rotateZ(2deg)}100%{transform:translateY(0) rotateZ(0)}}@keyframes swal2-toast-show{0%{transform:translateY(-.625em) rotateZ(2deg)}33%{transform:translateY(0) rotateZ(-2deg)}66%{transform:translateY(.3125em) rotateZ(2deg)}100%{transform:translateY(0) rotateZ(0)}}@-webkit-keyframes swal2-toast-hide{100%{transform:rotateZ(1deg);opacity:0}}@keyframes swal2-toast-hide{100%{transform:rotateZ(1deg);opacity:0}}@-webkit-keyframes swal2-toast-animate-success-line-tip{0%{top:.5625em;left:.0625em;width:0}54%{top:.125em;left:.125em;width:0}70%{top:.625em;left:-.25em;width:1.625em}84%{top:1.0625em;left:.75em;width:.5em}100%{top:1.125em;left:.1875em;width:.75em}}@keyframes swal2-toast-animate-success-line-tip{0%{top:.5625em;left:.0625em;width:0}54%{top:.125em;left:.125em;width:0}70%{top:.625em;left:-.25em;width:1.625em}84%{top:1.0625em;left:.75em;width:.5em}100%{top:1.125em;left:.1875em;width:.75em}}@-webkit-keyframes swal2-toast-animate-success-line-long{0%{top:1.625em;right:1.375em;width:0}65%{top:1.25em;right:.9375em;width:0}84%{top:.9375em;right:0;width:1.125em}100%{top:.9375em;right:.1875em;width:1.375em}}@keyframes swal2-toast-animate-success-line-long{0%{top:1.625em;right:1.375em;width:0}65%{top:1.25em;right:.9375em;width:0}84%{top:.9375em;right:0;width:1.125em}100%{top:.9375em;right:.1875em;width:1.375em}}@-webkit-keyframes swal2-show{0%{transform:scale(.7)}45%{transform:scale(1.05)}80%{transform:scale(.95)}100%{transform:scale(1)}}@keyframes swal2-show{0%{transform:scale(.7)}45%{transform:scale(1.05)}80%{transform:scale(.95)}100%{transform:scale(1)}}@-webkit-keyframes swal2-hide{0%{transform:scale(1);opacity:1}100%{transform:scale(.5);opacity:0}}@keyframes swal2-hide{0%{transform:scale(1);opacity:1}100%{transform:scale(.5);opacity:0}}@-webkit-keyframes swal2-animate-success-line-tip{0%{top:1.1875em;left:.0625em;width:0}54%{top:1.0625em;left:.125em;width:0}70%{top:2.1875em;left:-.375em;width:3.125em}84%{top:3em;left:1.3125em;width:1.0625em}100%{top:2.8125em;left:.8125em;width:1.5625em}}@keyframes swal2-animate-success-line-tip{0%{top:1.1875em;left:.0625em;width:0}54%{top:1.0625em;left:.125em;width:0}70%{top:2.1875em;left:-.375em;width:3.125em}84%{top:3em;left:1.3125em;width:1.0625em}100%{top:2.8125em;left:.8125em;width:1.5625em}}@-webkit-keyframes swal2-animate-success-line-long{0%{top:3.375em;right:2.875em;width:0}65%{top:3.375em;right:2.875em;width:0}84%{top:2.1875em;right:0;width:3.4375em}100%{top:2.375em;right:.5em;width:2.9375em}}@keyframes swal2-animate-success-line-long{0%{top:3.375em;right:2.875em;width:0}65%{top:3.375em;right:2.875em;width:0}84%{top:2.1875em;right:0;width:3.4375em}100%{top:2.375em;right:.5em;width:2.9375em}}@-webkit-keyframes swal2-rotate-success-circular-line{0%{transform:rotate(-45deg)}5%{transform:rotate(-45deg)}12%{transform:rotate(-405deg)}100%{transform:rotate(-405deg)}}@keyframes swal2-rotate-success-circular-line{0%{transform:rotate(-45deg)}5%{transform:rotate(-45deg)}12%{transform:rotate(-405deg)}100%{transform:rotate(-405deg)}}@-webkit-keyframes swal2-animate-error-x-mark{0%{margin-top:1.625em;transform:scale(.4);opacity:0}50%{margin-top:1.625em;transform:scale(.4);opacity:0}80%{margin-top:-.375em;transform:scale(1.15)}100%{margin-top:0;transform:scale(1);opacity:1}}@keyframes swal2-animate-error-x-mark{0%{margin-top:1.625em;transform:scale(.4);opacity:0}50%{margin-top:1.625em;transform:scale(.4);opacity:0}80%{margin-top:-.375em;transform:scale(1.15)}100%{margin-top:0;transform:scale(1);opacity:1}}@-webkit-keyframes swal2-animate-error-icon{0%{transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0);opacity:1}}@keyframes swal2-animate-error-icon{0%{transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0);opacity:1}}@-webkit-keyframes swal2-rotate-loading{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes swal2-rotate-loading{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@-webkit-keyframes swal2-animate-question-mark{0%{transform:rotateY(-360deg)}100%{transform:rotateY(0)}}@keyframes swal2-animate-question-mark{0%{transform:rotateY(-360deg)}100%{transform:rotateY(0)}}@-webkit-keyframes swal2-animate-i-mark{0%{transform:rotateZ(45deg);opacity:0}25%{transform:rotateZ(-25deg);opacity:.4}50%{transform:rotateZ(15deg);opacity:.8}75%{transform:rotateZ(-5deg);opacity:1}100%{transform:rotateX(0);opacity:1}}@keyframes swal2-animate-i-mark{0%{transform:rotateZ(45deg);opacity:0}25%{transform:rotateZ(-25deg);opacity:.4}50%{transform:rotateZ(15deg);opacity:.8}75%{transform:rotateZ(-5deg);opacity:1}100%{transform:rotateX(0);opacity:1}}body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown){overflow:hidden}body.swal2-height-auto{height:auto!important}body.swal2-no-backdrop .swal2-container{background-color:transparent!important;pointer-events:none}body.swal2-no-backdrop .swal2-container .swal2-popup{pointer-events:all}body.swal2-no-backdrop .swal2-container .swal2-modal{box-shadow:0 0 10px rgba(0,0,0,.4)}@media print{body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown){overflow-y:scroll!important}body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown)>[aria-hidden=true]{display:none}body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) .swal2-container{position:static!important}}body.swal2-toast-shown .swal2-container{box-sizing:border-box;width:360px;max-width:100%;background-color:transparent;pointer-events:none}body.swal2-toast-shown .swal2-container.swal2-top{top:0;right:auto;bottom:auto;left:50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-top-end,body.swal2-toast-shown .swal2-container.swal2-top-right{top:0;right:0;bottom:auto;left:auto}body.swal2-toast-shown .swal2-container.swal2-top-left,body.swal2-toast-shown .swal2-container.swal2-top-start{top:0;right:auto;bottom:auto;left:0}body.swal2-toast-shown .swal2-container.swal2-center-left,body.swal2-toast-shown .swal2-container.swal2-center-start{top:50%;right:auto;bottom:auto;left:0;transform:translateY(-50%)}body.swal2-toast-shown .swal2-container.swal2-center{top:50%;right:auto;bottom:auto;left:50%;transform:translate(-50%,-50%)}body.swal2-toast-shown .swal2-container.swal2-center-end,body.swal2-toast-shown .swal2-container.swal2-center-right{top:50%;right:0;bottom:auto;left:auto;transform:translateY(-50%)}body.swal2-toast-shown .swal2-container.swal2-bottom-left,body.swal2-toast-shown .swal2-container.swal2-bottom-start{top:auto;right:auto;bottom:0;left:0}body.swal2-toast-shown .swal2-container.swal2-bottom{top:auto;right:auto;bottom:0;left:50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-bottom-end,body.swal2-toast-shown .swal2-container.swal2-bottom-right{top:auto;right:0;bottom:0;left:auto}')}},e={};function o(n){var a=e[n];if(void 0!==a)return a.exports;var s=e[n]={id:n,exports:{}};return t[n].call(s.exports,s,s.exports,o),s.exports}o.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return o.d(e,{a:e}),e},o.d=(t,e)=>{for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.nc=void 0;var n={};return(()=>{"use strict";var t;o.d(n,{default:()=>T});var e=new Uint8Array(16);function a(){if(!t&&!(t="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return t(e)}const s=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,i=function(t){return"string"==typeof t&&s.test(t)};for(var r=[],l=0;l<256;++l)r.push((l+256).toString(16).substr(1));const c=function(t,e,o){var n=(t=t||{}).random||(t.rng||a)();if(n[6]=15&n[6]|64,n[8]=63&n[8]|128,e){o=o||0;for(var s=0;s<16;++s)e[o+s]=n[s];return e}return function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,o=(r[t[e+0]]+r[t[e+1]]+r[t[e+2]]+r[t[e+3]]+"-"+r[t[e+4]]+r[t[e+5]]+"-"+r[t[e+6]]+r[t[e+7]]+"-"+r[t[e+8]]+r[t[e+9]]+"-"+r[t[e+10]]+r[t[e+11]]+r[t[e+12]]+r[t[e+13]]+r[t[e+14]]+r[t[e+15]]).toLowerCase();if(!i(o))throw TypeError("Stringified UUID is invalid");return o}(n)};var d=o(455),u=o.n(d),p=o(40),m=o.n(p),w=o(379),g=o.n(w),h=o(795),f=o.n(h),b=o(569),y=o.n(b),v=o(565),x=o.n(v),k=o(216),C=o.n(k),A=o(589),B=o.n(A),P=o(745),E={};E.styleTagTransform=B(),E.setAttributes=x(),E.insert=y().bind(null,"head"),E.domAPI=f(),E.insertStyleElement=C(),g()(P.Z,E),P.Z&&P.Z.locals&&P.Z.locals;class T{static get enableLineBreaks(){return!0}constructor({data:t,config:e,api:o,readOnly:n}){this.api=o,this.readOnly=n,this.config=e||{},this._CSS={block:this.api.styles.block,wrapper:"ce-EditorJsColumns"},this.readOnly||(this.onKeyUp=this.onKeyUp.bind(this)),this._data={},this.editors={},this.colWrapper=void 0,this.editors.cols=[],this.data=t,Array.isArray(this.data.cols)?this.editors.numberOfColumns=this.data.cols.length:(this.data.cols=[],this.editors.numberOfColumns=2)}static get isReadOnlySupported(){return!0}onKeyUp(t){"Backspace"===t.code||t.code}get CSS(){return{settingsButton:this.api.styles.settingsButton,settingsButtonActive:this.api.styles.settingsButtonActive}}renderSettings(){return[{icon:"2",label:"2 Columns",onActivate:()=>{this._updateCols(2)}},{icon:"3",label:"3 Columns",onActivate:()=>{this._updateCols(3)}},{icon:"R",label:"Roll Colls",onActivate:()=>{this._rollCols()}}]}_rollCols(){this.data.cols.unshift(this.data.cols.pop()),this.editors.cols.unshift(this.editors.cols.pop()),this._rerender()}async _updateCols(t){2==t&&3==this.editors.numberOfColumns&&(await u().fire({title:"Are you sure?",text:"This will delete Column 3!",icon:"warning",showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:"Yes, delete it!"})).isConfirmed&&(this.editors.numberOfColumns=2,this.data.cols.pop(),this.editors.cols.pop(),this._rerender()),3==t&&(this.editors.numberOfColumns=3,this._rerender())}async _rerender(){await this.save();for(let t=0;t{t.stopPropagation()}),!0),this.colWrapper.addEventListener("keydown",(t=>{"Enter"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),t.stopPropagation()),"Tab"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),t.stopPropagation())}));for(let t=0;t=0&&f.splice(t,1)}function b(e){var t=document.createElement("style");return void 0===e.attrs.type&&(e.attrs.type="text/css"),m(t,e.attrs),v(e,t),t}function m(e,t){Object.keys(t).forEach((function(n){e.setAttribute(n,t[n])}))}function y(e,t){var n,r,o,i;if(t.transform&&e.css){if(!(i=t.transform(e.css)))return function(){};e.css=i}if(t.singleton){var a=c++;n=u||(u=b(t)),r=L.bind(null,n,a,!1),o=L.bind(null,n,a,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",m(t,e.attrs),v(e,t),t}(t),r=x.bind(null,n,t),o=function(){g(n),n.href&&URL.revokeObjectURL(n.href)}):(n=b(t),r=M.bind(null,n),o=function(){g(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else o()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=a()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=h(e,t);return p(n,t),function(e){for(var r=[],o=0;o',title:"Heading"}}}],(n=[{key:"normalizeData",value:function(e){var t={};return"object"!==r(e)&&(e={}),t.text=e.text||"",t.level=parseInt(e.level)||this.defaultLevel.number,t}},{key:"render",value:function(){return this._element}},{key:"renderSettings",value:function(){var e=this;return this.levels.map((function(t){return{icon:t.svg,label:e.api.i18n.t("Heading ".concat(t.number)),onActivate:function(){return e.setLevel(t.number)},closeOnActivate:!0,isActive:e.currentLevel.number===t.number}}))}},{key:"setLevel",value:function(e){this.data={level:e,text:this.data.text}}},{key:"merge",value:function(e){var t={text:this.data.text+e.text,level:this.data.level};this.data=t}},{key:"validate",value:function(e){return""!==e.text.trim()}},{key:"save",value:function(e){return{text:e.innerHTML,level:this.currentLevel.number}}},{key:"getTag",value:function(){var e=document.createElement(this.currentLevel.tag);return e.innerHTML=this._data.text||"",e.classList.add(this._CSS.wrapper),e.contentEditable=this.readOnly?"false":"true",e.dataset.placeholder=this.api.i18n.t(this._settings.placeholder||""),e}},{key:"onPaste",value:function(e){var t=e.detail.data,n=this.defaultLevel.number;switch(t.tagName){case"H1":n=1;break;case"H2":n=2;break;case"H3":n=3;break;case"H4":n=4;break;case"H5":n=5;break;case"H6":n=6}this._settings.levels&&(n=this._settings.levels.reduce((function(e,t){return Math.abs(t-n)'},{number:2,tag:"H2",svg:''},{number:3,tag:"H3",svg:''},{number:4,tag:"H4",svg:''},{number:5,tag:"H5",svg:''},{number:6,tag:"H6",svg:''}];return this._settings.levels?t.filter((function(t){return e._settings.levels.includes(t.number)})):t}}])&&o(t.prototype,n),i&&o(t,i),e}()}]).default})); \ No newline at end of file diff --git a/web/vendor/editorjs/tools/list.umd.js b/web/vendor/editorjs/tools/list.umd.js new file mode 100644 index 0000000..c8257ec --- /dev/null +++ b/web/vendor/editorjs/tools/list.umd.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.List=t():e.List=t()}(window,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=4)}([function(e,t,n){var r=n(1),i=n(2);"string"==typeof(i=i.__esModule?i.default:i)&&(i=[[e.i,i,""]]);var o={insert:"head",singleton:!1};r(i,o);e.exports=i.locals||{}},function(e,t,n){"use strict";var r,i=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},o=function(){var e={};return function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}e[t]=n}return e[t]}}(),a=[];function s(e){for(var t=-1,n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n',default:"ordered"===i.defaultStyle||!0}],this._data={style:this.settings.find((function(e){return!0===e.default})).name,items:[]},this.data=n}return u(e,null,[{key:"isReadOnlySupported",get:function(){return!0}},{key:"enableLineBreaks",get:function(){return!0}},{key:"toolbox",get:function(){return{icon:r,title:"List"}}}]),u(e,[{key:"render",value:function(){var e=this;return this._elements.wrapper=this.makeMainTag(this._data.style),this._data.items.length?this._data.items.forEach((function(t){e._elements.wrapper.appendChild(e._make("li",e.CSS.item,{innerHTML:t}))})):this._elements.wrapper.appendChild(this._make("li",this.CSS.item)),this.readOnly||this._elements.wrapper.addEventListener("keydown",(function(t){switch(t.keyCode){case 13:e.getOutofList(t);break;case 8:e.backspace(t)}}),!1),this._elements.wrapper}},{key:"save",value:function(){return this.data}},{key:"renderSettings",value:function(){var e=this;return this.settings.map((function(t){return s(s({},t),{},{isActive:e._data.style===t.name,closeOnActivate:!0,onActivate:function(){return e.toggleTune(t.name)}})}))}},{key:"onPaste",value:function(e){var t=e.detail.data;this.data=this.pasteHandler(t)}},{key:"makeMainTag",value:function(e){var t="ordered"===e?this.CSS.wrapperOrdered:this.CSS.wrapperUnordered,n="ordered"===e?"ol":"ul";return this._make(n,[this.CSS.baseBlock,this.CSS.wrapper,t],{contentEditable:!this.readOnly})}},{key:"toggleTune",value:function(e){for(var t=this.makeMainTag(e);this._elements.wrapper.hasChildNodes();)t.appendChild(this._elements.wrapper.firstChild);this._elements.wrapper.replaceWith(t),this._elements.wrapper=t,this._data.style=e}},{key:"_make",value:function(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=document.createElement(e);Array.isArray(n)?(t=o.classList).add.apply(t,i(n)):n&&o.classList.add(n);for(var a in r)o[a]=r[a];return o}},{key:"getOutofList",value:function(e){var t=this._elements.wrapper.querySelectorAll("."+this.CSS.item);if(!(t.length<2)){var n=t[t.length-1],r=this.currentItem;r!==n||n.textContent.trim().length||(r.parentElement.removeChild(r),this.api.blocks.insert(),this.api.caret.setToBlock(this.api.blocks.getCurrentBlockIndex()),e.preventDefault(),e.stopPropagation())}}},{key:"backspace",value:function(e){var t=this._elements.wrapper.querySelectorAll("."+this.CSS.item),n=t[0];n&&t.length<2&&!n.innerHTML.replace("
      "," ").trim()&&e.preventDefault()}},{key:"selectItem",value:function(e){e.preventDefault();var t=window.getSelection(),n=t.anchorNode.parentNode.closest("."+this.CSS.item),r=new Range;r.selectNodeContents(n),t.removeAllRanges(),t.addRange(r)}},{key:"pasteHandler",value:function(e){var t,n=e.tagName;switch(n){case"OL":t="ordered";break;case"UL":case"LI":t="unordered"}var r={style:t,items:[]};if("LI"===n)r.items=[e.innerHTML];else{var i=Array.from(e.querySelectorAll("LI"));r.items=i.map((function(e){return e.innerHTML})).filter((function(e){return!!e.trim()}))}return r}},{key:"CSS",get:function(){return{baseBlock:this.api.styles.block,wrapper:"cdx-list",wrapperOrdered:"cdx-list--ordered",wrapperUnordered:"cdx-list--unordered",item:"cdx-list__item"}}},{key:"data",set:function(e){e||(e={}),this._data.style=e.style||this.settings.find((function(e){return!0===e.default})).name,this._data.items=e.items||[];var t=this._elements.wrapper;t&&t.parentNode.replaceChild(this.render(),t)},get:function(){this._data.items=[];for(var e=this._elements.wrapper.querySelectorAll(".".concat(this.CSS.item)),t=0;t"," ").trim()&&this._data.items.push(e[t].innerHTML)}return this._data}},{key:"currentItem",get:function(){var e=window.getSelection().anchorNode;return e.nodeType!==Node.ELEMENT_NODE&&(e=e.parentNode),e.closest(".".concat(this.CSS.item))}}],[{key:"conversionConfig",get:function(){return{export:function(e){return e.items.join(". ")},import:function(e){return{items:[e],style:"unordered"}}}}},{key:"sanitize",get:function(){return{style:{},items:{br:!0}}}},{key:"pasteConfig",get:function(){return{tags:["OL","UL","LI"]}}}]),e}()}]).default})); \ No newline at end of file diff --git a/web/vendor/editorjs/tools/marker.umd.js b/web/vendor/editorjs/tools/marker.umd.js new file mode 100644 index 0000000..759bde8 --- /dev/null +++ b/web/vendor/editorjs/tools/marker.umd.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Marker=e():t.Marker=e()}(window,function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/",n(n.s=5)}([function(t,e,n){var r=n(1);"string"==typeof r&&(r=[[t.i,r,""]]);var o={hmr:!0,transform:void 0,insertInto:void 0};n(3)(r,o);r.locals&&(t.exports=r.locals)},function(t,e,n){(t.exports=n(2)(!1)).push([t.i,".cdx-marker {\n background: rgba(245,235,111,0.29);\n padding: 3px 0;\n}",""])},function(t,e){t.exports=function(t){var e=[];return e.toString=function(){return this.map(function(e){var n=function(t,e){var n=t[1]||"",r=t[3];if(!r)return n;if(e&&"function"==typeof btoa){var o=(a=r,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(a))))+" */"),i=r.sources.map(function(t){return"/*# sourceURL="+r.sourceRoot+t+" */"});return[n].concat(i).concat([o]).join("\n")}var a;return[n].join("\n")}(e,t);return e[2]?"@media "+e[2]+"{"+n+"}":n}).join("")},e.i=function(t,n){"string"==typeof t&&(t=[[null,t,""]]);for(var r={},o=0;o=0&&f.splice(e,1)}function b(t){var e=document.createElement("style");return void 0===t.attrs.type&&(t.attrs.type="text/css"),y(e,t.attrs),h(t,e),e}function y(t,e){Object.keys(e).forEach(function(n){t.setAttribute(n,e[n])})}function g(t,e){var n,r,o,i;if(e.transform&&t.css){if(!(i=e.transform(t.css)))return function(){};t.css=i}if(e.singleton){var a=c++;n=u||(u=b(e)),r=x.bind(null,n,a,!1),o=x.bind(null,n,a,!0)}else t.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(t){var e=document.createElement("link");return void 0===t.attrs.type&&(t.attrs.type="text/css"),t.attrs.rel="stylesheet",y(e,t.attrs),h(t,e),e}(e),r=function(t,e,n){var r=n.css,o=n.sourceMap,i=void 0===e.convertToAbsoluteUrls&&o;(e.convertToAbsoluteUrls||i)&&(r=l(r));o&&(r+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */");var a=new Blob([r],{type:"text/css"}),s=t.href;t.href=URL.createObjectURL(a),s&&URL.revokeObjectURL(s)}.bind(null,n,e),o=function(){v(n),n.href&&URL.revokeObjectURL(n.href)}):(n=b(e),r=function(t,e){var n=e.css,r=e.media;r&&t.setAttribute("media",r);if(t.styleSheet)t.styleSheet.cssText=n;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(n))}}.bind(null,n),o=function(){v(n)});return r(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;r(t=e)}else o()}}t.exports=function(t,e){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(e=e||{}).attrs="object"==typeof e.attrs?e.attrs:{},e.singleton||"boolean"==typeof e.singleton||(e.singleton=a()),e.insertInto||(e.insertInto="head"),e.insertAt||(e.insertAt="bottom");var n=d(t,e);return p(n,e),function(t){for(var r=[],o=0;o'}}],[{key:"isInline",get:function(){return!0}},{key:"sanitize",get:function(){return{mark:{class:t.CSS}}}}]),t}()}]).default}); \ No newline at end of file diff --git a/web/vendor/editorjs/tools/table.umd.js b/web/vendor/editorjs/tools/table.umd.js new file mode 100644 index 0000000..2a2ebe6 --- /dev/null +++ b/web/vendor/editorjs/tools/table.umd.js @@ -0,0 +1,8 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/@editorjs/table@2.4.5/dist/table.umd.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +(function(){var r;"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.nonce=(r=document.head.querySelector("meta[property=csp-nonce]"))==null?void 0:r.content,o.appendChild(document.createTextNode('.tc-wrap{--color-background:#f9f9fb;--color-text-secondary:#7b7e89;--color-border:#e8e8eb;--cell-size:34px;--toolbox-icon-size:18px;--toolbox-padding:6px;--toolbox-aiming-field-size:calc(var(--toolbox-icon-size) + var(--toolbox-padding)*2);border-left:0;position:relative;height:100%;width:100%;margin-top:var(--toolbox-icon-size);box-sizing:border-box;display:grid;grid-template-columns:calc(100% - var(--cell-size)) var(--cell-size);z-index:0}.tc-wrap--readonly{grid-template-columns:100% var(--cell-size)}.tc-wrap svg{vertical-align:top}@media print{.tc-wrap{border-left-color:var(--color-border);border-left-style:solid;border-left-width:1px;grid-template-columns:100% var(--cell-size)}}@media print{.tc-wrap .tc-row:after{display:none}}.tc-table{position:relative;width:100%;height:100%;display:grid;font-size:14px;border-top:1px solid var(--color-border);line-height:1.4}.tc-table:after{width:calc(var(--cell-size));height:100%;left:calc(var(--cell-size)*-1);top:0}.tc-table:after,.tc-table:before{position:absolute;content:""}.tc-table:before{width:100%;height:var(--toolbox-aiming-field-size);top:calc(var(--toolbox-aiming-field-size)*-1);left:0}.tc-table--heading .tc-row:first-child{font-weight:600;border-bottom:2px solid var(--color-border);position:sticky;top:0;z-index:2;background:var(--color-background)}.tc-table--heading .tc-row:first-child [contenteditable]:empty:before{content:attr(heading);color:var(--color-text-secondary)}.tc-table--heading .tc-row:first-child:after{bottom:-2px;border-bottom:2px solid var(--color-border)}.tc-add-column,.tc-add-row{display:flex;color:var(--color-text-secondary)}@media print{.tc-add{display:none}}.tc-add-column{display:grid;border-top:1px solid var(--color-border);grid-template-columns:var(--cell-size);grid-auto-rows:var(--cell-size);place-items:center}.tc-add-column svg{padding:5px;position:sticky;top:0;background-color:var(--color-background)}.tc-add-column--disabled{visibility:hidden}@media print{.tc-add-column{display:none}}.tc-add-row{height:var(--cell-size);align-items:center;padding-left:4px;position:relative}.tc-add-row--disabled{display:none}.tc-add-row:before{content:"";position:absolute;right:calc(var(--cell-size)*-1);width:var(--cell-size);height:100%}@media print{.tc-add-row{display:none}}.tc-add-column,.tc-add-row{transition:0s;cursor:pointer;will-change:background-color}.tc-add-column:hover,.tc-add-row:hover{transition:background-color .1s ease;background-color:var(--color-background)}.tc-add-row{margin-top:1px}.tc-add-row:hover:before{transition:.1s;background-color:var(--color-background)}.tc-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(10px,1fr));position:relative;border-bottom:1px solid var(--color-border)}.tc-row:after{content:"";pointer-events:none;position:absolute;width:var(--cell-size);height:100%;bottom:-1px;right:calc(var(--cell-size)*-1);border-bottom:1px solid var(--color-border)}.tc-row--selected{background:var(--color-background)}.tc-row--selected:after{background:var(--color-background)}.tc-cell{border-right:1px solid var(--color-border);padding:6px 12px;overflow:hidden;outline:none;line-break:normal}.tc-cell--selected{background:var(--color-background)}.tc-wrap--readonly .tc-row:after{display:none}.tc-toolbox{--toolbox-padding:6px;--popover-margin:30px;--toggler-click-zone-size:30px;--toggler-dots-color:#7b7e89;--toggler-dots-color-hovered:#1d202b;position:absolute;cursor:pointer;z-index:1;opacity:0;transition:opacity .1s;will-change:left,opacity}.tc-toolbox--column{top:calc(var(--toggler-click-zone-size)*-1);transform:translate(calc(var(--toggler-click-zone-size)*-1/2));will-change:left,opacity}.tc-toolbox--row{left:calc(var(--popover-margin)*-1);transform:translateY(calc(var(--toggler-click-zone-size)*-1/2));margin-top:-1px;will-change:top,opacity}.tc-toolbox--showed{opacity:1}.tc-toolbox .tc-popover{position:absolute;top:0;left:var(--popover-margin)}.tc-toolbox__toggler{display:flex;align-items:center;justify-content:center;width:var(--toggler-click-zone-size);height:var(--toggler-click-zone-size);color:var(--toggler-dots-color);opacity:0;transition:opacity .15s ease;will-change:opacity}.tc-toolbox__toggler:hover{color:var(--toggler-dots-color-hovered)}.tc-toolbox__toggler svg{fill:currentColor}.tc-wrap:hover .tc-toolbox__toggler{opacity:1}.tc-settings .cdx-settings-button{width:50%;margin:0}.tc-popover{--color-border:#eaeaea;--color-background:#fff;--color-background-hover:rgba(232,232,235,.49);--color-background-confirm:#e24a4a;--color-background-confirm-hover:#d54040;--color-text-confirm:#fff;background:var(--color-background);border:1px solid var(--color-border);box-shadow:0 3px 15px -3px #0d142121;border-radius:6px;padding:6px;display:none;will-change:opacity,transform}.tc-popover--opened{display:block;animation:menuShowing .1s cubic-bezier(.215,.61,.355,1) forwards}.tc-popover__item{display:flex;align-items:center;padding:2px 14px 2px 2px;border-radius:5px;cursor:pointer;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;user-select:none}.tc-popover__item:hover{background:var(--color-background-hover)}.tc-popover__item:not(:last-of-type){margin-bottom:2px}.tc-popover__item-icon{display:inline-flex;width:26px;height:26px;align-items:center;justify-content:center;background:var(--color-background);border-radius:5px;border:1px solid var(--color-border);margin-right:8px}.tc-popover__item-label{line-height:22px;font-size:14px;font-weight:500}.tc-popover__item--confirm{background:var(--color-background-confirm);color:var(--color-text-confirm)}.tc-popover__item--confirm:hover{background-color:var(--color-background-confirm-hover)}.tc-popover__item--confirm .tc-popover__item-icon{background:var(--color-background-confirm);border-color:#0000001a}.tc-popover__item--confirm .tc-popover__item-icon svg{transition:transform .2s ease-in;transform:rotate(90deg) scale(1.2)}.tc-popover__item--hidden{display:none}@keyframes menuShowing{0%{opacity:0;transform:translateY(-8px) scale(.9)}70%{opacity:1;transform:translateY(2px)}to{transform:translateY(0)}}')),document.head.appendChild(o)}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})(); +(function(d,p){typeof exports=="object"&&typeof module<"u"?module.exports=p():typeof define=="function"&&define.amd?define(p):(d=typeof globalThis<"u"?globalThis:d||self,d.Table=p())})(this,function(){"use strict";function d(a,t,e={}){const o=document.createElement(a);Array.isArray(t)?o.classList.add(...t):t&&o.classList.add(t);for(const i in e)Object.prototype.hasOwnProperty.call(e,i)&&(o[i]=e[i]);return o}function p(a){const t=a.getBoundingClientRect();return{y1:Math.floor(t.top+window.pageYOffset),x1:Math.floor(t.left+window.pageXOffset),x2:Math.floor(t.right+window.pageXOffset),y2:Math.floor(t.bottom+window.pageYOffset)}}function g(a,t){const e=p(a),o=p(t);return{fromTopBorder:o.y1-e.y1,fromLeftBorder:o.x1-e.x1,fromRightBorder:e.x2-o.x2,fromBottomBorder:e.y2-o.y2}}function k(a,t){const e=a.getBoundingClientRect(),{width:o,height:i,x:n,y:r}=e,{clientX:h,clientY:l}=t;return{width:o,height:i,x:h-n,y:l-r}}function m(a,t){return t.parentNode.insertBefore(a,t)}function C(a,t=!0){const e=document.createRange(),o=window.getSelection();e.selectNodeContents(a),e.collapse(t),o.removeAllRanges(),o.addRange(e)}class c{constructor({items:t}){this.items=t,this.wrapper=void 0,this.itemEls=[]}static get CSS(){return{popover:"tc-popover",popoverOpened:"tc-popover--opened",item:"tc-popover__item",itemHidden:"tc-popover__item--hidden",itemConfirmState:"tc-popover__item--confirm",itemIcon:"tc-popover__item-icon",itemLabel:"tc-popover__item-label"}}render(){return this.wrapper=d("div",c.CSS.popover),this.items.forEach((t,e)=>{const o=d("div",c.CSS.item),i=d("div",c.CSS.itemIcon,{innerHTML:t.icon}),n=d("div",c.CSS.itemLabel,{textContent:t.label});o.dataset.index=e,o.appendChild(i),o.appendChild(n),this.wrapper.appendChild(o),this.itemEls.push(o)}),this.wrapper.addEventListener("click",t=>{this.popoverClicked(t)}),this.wrapper}popoverClicked(t){const e=t.target.closest(`.${c.CSS.item}`);if(!e)return;const o=e.dataset.index,i=this.items[o];if(i.confirmationRequired&&!this.hasConfirmationState(e)){this.setConfirmationState(e);return}i.onClick()}setConfirmationState(t){t.classList.add(c.CSS.itemConfirmState)}clearConfirmationState(t){t.classList.remove(c.CSS.itemConfirmState)}hasConfirmationState(t){return t.classList.contains(c.CSS.itemConfirmState)}get opened(){return this.wrapper.classList.contains(c.CSS.popoverOpened)}open(){this.items.forEach((t,e)=>{typeof t.hideIf=="function"&&this.itemEls[e].classList.toggle(c.CSS.itemHidden,t.hideIf())}),this.wrapper.classList.add(c.CSS.popoverOpened)}close(){this.wrapper.classList.remove(c.CSS.popoverOpened),this.itemEls.forEach(t=>{this.clearConfirmationState(t)})}}const R='',b='',x='',S='',y='',L='',M='',v='',O='',T='',H='',A='';class w{constructor({api:t,items:e,onOpen:o,onClose:i,cssModifier:n=""}){this.api=t,this.items=e,this.onOpen=o,this.onClose=i,this.cssModifier=n,this.popover=null,this.wrapper=this.createToolbox()}static get CSS(){return{toolbox:"tc-toolbox",toolboxShowed:"tc-toolbox--showed",toggler:"tc-toolbox__toggler"}}get element(){return this.wrapper}createToolbox(){const t=d("div",[w.CSS.toolbox,this.cssModifier?`${w.CSS.toolbox}--${this.cssModifier}`:""]);t.dataset.mutationFree="true";const e=this.createPopover(),o=this.createToggler();return t.appendChild(o),t.appendChild(e),t}createToggler(){const t=d("div",w.CSS.toggler,{innerHTML:M});return t.addEventListener("click",()=>{this.togglerClicked()}),t}createPopover(){return this.popover=new c({items:this.items}),this.popover.render()}togglerClicked(){this.popover.opened?(this.popover.close(),this.onClose()):(this.popover.open(),this.onOpen())}show(t){const e=t();Object.entries(e).forEach(([o,i])=>{this.wrapper.style[o]=i}),this.wrapper.classList.add(w.CSS.toolboxShowed)}hide(){this.popover.close(),this.wrapper.classList.remove(w.CSS.toolboxShowed)}}function B(a,t){let e=0;return function(...o){const i=new Date().getTime();if(!(i-e{const r=n.target.closest(`.${s.table}`)!==null,h=n.target.closest(`.${s.wrapper}`)===null;(r||h)&&this.hideToolboxes();const u=n.target.closest(`.${s.addRow}`),f=n.target.closest(`.${s.addColumn}`);u&&u.parentNode===this.wrapper?(this.addRow(void 0,!0),this.hideToolboxes()):f&&f.parentNode===this.wrapper&&(this.addColumn(void 0,!0),this.hideToolboxes())},this.readOnly||this.bindEvents()}getWrapper(){return this.wrapper}bindEvents(){document.addEventListener("click",this.documentClicked),this.table.addEventListener("mousemove",B(150,t=>this.onMouseMoveInTable(t)),{passive:!0}),this.table.onkeypress=t=>this.onKeyPressListener(t),this.table.addEventListener("keydown",t=>this.onKeyDownListener(t)),this.table.addEventListener("focusin",t=>this.focusInTableListener(t))}createColumnToolbox(){return new w({api:this.api,cssModifier:"column",items:[{label:this.api.i18n.t("Add column to left"),icon:S,hideIf:()=>this.numberOfColumns===this.config.maxcols,onClick:()=>{this.addColumn(this.selectedColumn,!0),this.hideToolboxes()}},{label:this.api.i18n.t("Add column to right"),icon:y,hideIf:()=>this.numberOfColumns===this.config.maxcols,onClick:()=>{this.addColumn(this.selectedColumn+1,!0),this.hideToolboxes()}},{label:this.api.i18n.t("Delete column"),icon:b,hideIf:()=>this.numberOfColumns===1,confirmationRequired:!0,onClick:()=>{this.deleteColumn(this.selectedColumn),this.hideToolboxes()}}],onOpen:()=>{this.selectColumn(this.hoveredColumn),this.hideRowToolbox()},onClose:()=>{this.unselectColumn()}})}createRowToolbox(){return new w({api:this.api,cssModifier:"row",items:[{label:this.api.i18n.t("Add row above"),icon:L,hideIf:()=>this.numberOfRows===this.config.maxrows,onClick:()=>{this.addRow(this.selectedRow,!0),this.hideToolboxes()}},{label:this.api.i18n.t("Add row below"),icon:x,hideIf:()=>this.numberOfRows===this.config.maxrows,onClick:()=>{this.addRow(this.selectedRow+1,!0),this.hideToolboxes()}},{label:this.api.i18n.t("Delete row"),icon:b,hideIf:()=>this.numberOfRows===1,confirmationRequired:!0,onClick:()=>{this.deleteRow(this.selectedRow),this.hideToolboxes()}}],onOpen:()=>{this.selectRow(this.hoveredRow),this.hideColumnToolbox()},onClose:()=>{this.unselectRow()}})}moveCursorToNextRow(){this.focusedCell.row!==this.numberOfRows?(this.focusedCell.row+=1,this.focusCell(this.focusedCell)):(this.addRow(),this.focusedCell.row+=1,this.focusCell(this.focusedCell),this.updateToolboxesPosition(0,0))}getCell(t,e){return this.table.querySelectorAll(`.${s.row}:nth-child(${t}) .${s.cell}`)[e-1]}getRow(t){return this.table.querySelector(`.${s.row}:nth-child(${t})`)}getRowByCell(t){return t.parentElement}getRowFirstCell(t){return t.querySelector(`.${s.cell}:first-child`)}setCellContent(t,e,o){const i=this.getCell(t,e);i.innerHTML=o}addColumn(t=-1,e=!1){var n;let o=this.numberOfColumns;if(this.config&&this.config.maxcols&&this.numberOfColumns>=this.config.maxcols)return;for(let r=1;r<=this.numberOfRows;r++){let h;const l=this.createCell();if(t>0&&t<=o?(h=this.getCell(r,t),m(l,h)):h=this.getRow(r).appendChild(l),r===1){const u=this.getCell(r,t>0?t:o+1);u&&e&&C(u)}}const i=this.wrapper.querySelector(`.${s.addColumn}`);(n=this.config)!=null&&n.maxcols&&this.numberOfColumns>this.config.maxcols-1&&i&&i.classList.add(s.addColumnDisabled),this.addHeadingAttrToFirstRow()}addRow(t=-1,e=!1){let o,i=d("div",s.row);this.tunes.withHeadings&&this.removeHeadingAttrFromFirstRow();let n=this.numberOfColumns;if(this.config&&this.config.maxrows&&this.numberOfRows>=this.config.maxrows&&h)return;if(t>0&&t<=this.numberOfRows){let l=this.getRow(t);o=m(i,l)}else o=this.table.appendChild(i);this.fillRow(o,n),this.tunes.withHeadings&&this.addHeadingAttrToFirstRow();const r=this.getRowFirstCell(o);r&&e&&C(r);const h=this.wrapper.querySelector(`.${s.addRow}`);return this.config&&this.config.maxrows&&this.numberOfRows>=this.config.maxrows&&h&&h.classList.add(s.addRowDisabled),o}deleteColumn(t){for(let o=1;o<=this.numberOfRows;o++){const i=this.getCell(o,t);if(!i)return;i.remove()}const e=this.wrapper.querySelector(`.${s.addColumn}`);e&&e.classList.remove(s.addColumnDisabled)}deleteRow(t){this.getRow(t).remove();const e=this.wrapper.querySelector(`.${s.addRow}`);e&&e.classList.remove(s.addRowDisabled),this.addHeadingAttrToFirstRow()}createTableWrapper(){if(this.wrapper=d("div",s.wrapper),this.table=d("div",s.table),this.readOnly&&this.wrapper.classList.add(s.wrapperReadOnly),this.wrapper.appendChild(this.toolboxRow.element),this.wrapper.appendChild(this.toolboxColumn.element),this.wrapper.appendChild(this.table),!this.readOnly){const t=d("div",s.addColumn,{innerHTML:v}),e=d("div",s.addRow,{innerHTML:v});this.wrapper.appendChild(t),this.wrapper.appendChild(e)}}computeInitialSize(){const t=this.data&&this.data.content,e=Array.isArray(t),o=e?t.length:!1,i=e?t.length:void 0,n=o?t[0].length:void 0,r=Number.parseInt(this.config&&this.config.rows),h=Number.parseInt(this.config&&this.config.cols),l=!isNaN(r)&&r>0?r:void 0,u=!isNaN(h)&&h>0?h:void 0;return{rows:i||l||2,cols:n||u||2}}resize(){const{rows:t,cols:e}=this.computeInitialSize();for(let o=0;o0&&e<=this.numberOfColumns&&this.toolboxColumn.show(()=>({left:`calc((100% - var(--cell-size)) / (${this.numberOfColumns} * 2) * (1 + (${e} - 1) * 2))`})),this.isRowMenuShowing||t>0&&t<=this.numberOfRows&&this.toolboxRow.show(()=>{const o=this.getRow(t),{fromTopBorder:i}=g(this.table,o),{height:n}=o.getBoundingClientRect();return{top:`${Math.ceil(i+n/2)}px`}})}setHeadingsSetting(t){this.tunes.withHeadings=t,t?(this.table.classList.add(s.withHeadings),this.addHeadingAttrToFirstRow()):(this.table.classList.remove(s.withHeadings),this.removeHeadingAttrFromFirstRow())}addHeadingAttrToFirstRow(){for(let t=1;t<=this.numberOfColumns;t++){let e=this.getCell(1,t);e&&e.setAttribute("heading",this.api.i18n.t("Heading"))}}removeHeadingAttrFromFirstRow(){for(let t=1;t<=this.numberOfColumns;t++){let e=this.getCell(1,t);e&&e.removeAttribute("heading")}}selectRow(t){const e=this.getRow(t);e&&(this.selectedRow=t,e.classList.add(s.rowSelected))}unselectRow(){if(this.selectedRow<=0)return;const t=this.table.querySelector(`.${s.rowSelected}`);t&&t.classList.remove(s.rowSelected),this.selectedRow=0}selectColumn(t){for(let e=1;e<=this.numberOfRows;e++){const o=this.getCell(e,t);o&&o.classList.add(s.cellSelected)}this.selectedColumn=t}unselectColumn(){if(this.selectedColumn<=0)return;let t=this.table.querySelectorAll(`.${s.cellSelected}`);Array.from(t).forEach(e=>{e.classList.remove(s.cellSelected)}),this.selectedColumn=0}getHoveredCell(t){let e=this.hoveredRow,o=this.hoveredColumn;const{width:i,height:n,x:r,y:h}=k(this.table,t);return r>=0&&(o=this.binSearch(this.numberOfColumns,l=>this.getCell(1,l),({fromLeftBorder:l})=>rr>i-l)),h>=0&&(e=this.binSearch(this.numberOfRows,l=>this.getCell(l,1),({fromTopBorder:l})=>hh>n-l)),{row:e||this.hoveredRow,column:o||this.hoveredColumn}}binSearch(t,e,o,i){let n=0,r=t+1,h=0,l;for(;n!r.textContent.trim())||t.push(i.map(r=>r.innerHTML))}return t}destroy(){document.removeEventListener("click",this.documentClicked)}}class ${static get isReadOnlySupported(){return!0}static get enableLineBreaks(){return!0}constructor({data:t,config:e,api:o,readOnly:i,block:n}){this.api=o,this.readOnly=i,this.config=e,this.data={withHeadings:this.getConfig("withHeadings",!1,t),stretched:this.getConfig("stretched",!1,t),content:t&&t.content?t.content:[]},this.table=null,this.block=n}static get toolbox(){return{icon:A,title:"Table"}}render(){return this.table=new E(this.readOnly,this.api,this.data,this.config),this.container=d("div",this.api.styles.block),this.container.appendChild(this.table.getWrapper()),this.table.setHeadingsSetting(this.data.withHeadings),this.container}renderSettings(){return[{label:this.api.i18n.t("With headings"),icon:T,isActive:this.data.withHeadings,closeOnActivate:!0,toggle:!0,onActivate:()=>{this.data.withHeadings=!0,this.table.setHeadingsSetting(this.data.withHeadings)}},{label:this.api.i18n.t("Without headings"),icon:H,isActive:!this.data.withHeadings,closeOnActivate:!0,toggle:!0,onActivate:()=>{this.data.withHeadings=!1,this.table.setHeadingsSetting(this.data.withHeadings)}},{label:this.data.stretched?this.api.i18n.t("Collapse"):this.api.i18n.t("Stretch"),icon:this.data.stretched?R:O,closeOnActivate:!0,toggle:!0,onActivate:()=>{this.data.stretched=!this.data.stretched,this.block.stretched=this.data.stretched}}]}save(){const t=this.table.getData();return{withHeadings:this.data.withHeadings,stretched:this.data.stretched,content:t}}destroy(){this.table.destroy()}getConfig(t,e=void 0,o=void 0){const i=this.data||o;return i?i[t]?i[t]:e:this.config&&this.config[t]?this.config[t]:e}static get pasteConfig(){return{tags:["TABLE","TR","TH","TD"]}}onPaste(t){const e=t.detail.data,o=e.querySelector(":scope > thead, tr:first-of-type th"),n=Array.from(e.querySelectorAll("tr")).map(r=>Array.from(r.querySelectorAll("th, td")).map(l=>l.innerHTML));this.data={withHeadings:o!==null,content:n},this.table.wrapper&&this.table.wrapper.replaceWith(this.render())}}const I="";return $}); diff --git a/web/vendor/fslightbox/README.md b/web/vendor/fslightbox/README.md new file mode 100644 index 0000000..b06db4d --- /dev/null +++ b/web/vendor/fslightbox/README.md @@ -0,0 +1,55 @@ +# Vanilla JavaScript Fullscreen Lightbox Pro + +## Description +A vanilla JavaScript plug-in without production dependencies for displaying images, videos, or, through custom sources, anything you want in a clean overlying box. +The project's website: https://fslightbox.com. + +## Basic usage +```html + + Open the first slide (an image) + + + Open the second slide (a YouTube video) + + + Open the third slide (an HTML video) + + + Open the fourth slide (a Vimeo video—a custom source) + + + + +``` + +## Documentation +Available at: https://fslightbox.com/javascript/documentation. + +## Browser Compatibility + +| Browser | Works? | +| --- | --- | +| Chrome | Yes | +| Firefox | Yes | +| Opera | Yes | +| Safari | Yes | +| Edge | Yes | +| IE 11 | Yes | diff --git a/web/vendor/fslightbox/fslightbox.js b/web/vendor/fslightbox/fslightbox.js new file mode 100644 index 0000000..a06a899 --- /dev/null +++ b/web/vendor/fslightbox/fslightbox.js @@ -0,0 +1 @@ +!function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n=e();for(var o in n)("object"==typeof exports?exports:t)[o]=n[o]}}(self,(()=>(()=>{"use strict";var t={};(t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})})(t);var e,n="fslightbox-",i="".concat(n,"styles"),s="".concat(n,"full-dimension"),r="".concat(n,"flex-centered"),a="".concat(n,"open"),c="".concat(n,"absoluted"),u="".concat(n,"opacity-1"),l="".concat(n,"slide-btn"),d="".concat(l,"-container"),h="".concat(n,"fade-in"),f="".concat(n,"fade-out"),p=h+"-strong",m=f+"-strong",b=("".concat(n,"caption"),"".concat(n,"thumb")),g=b+"s",v="".concat(g,"-loader"),x="".concat(g,"-cursorer"),w="".concat(g,"-inner"),y=b+"-wrapper",C=b+"-invalid";function L(t){return L="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},L(t)}function T(t){var e=t.c,n=t.componentsServices,o=t.core.thumbsRenderDispatcher,i=t.data,s=t.ea,r=t.la,a=(t.stageIndexes,t.ui),c=t.z;function u(){for(var t=0;tt&&c>t?i(t-a):a>t?i(innerWidth-e.thumbsInnerWidth-9):c>t&&i(0)},this.runToThinThumbsActions=function(){S(n.thumbsContainer,r),i(0)};var i=function(t){e.thumbsTransform=t,n.thumbsInner.style.transform="translateX(".concat(t,"px)")}}function I(t){var e=this,n=t.core,o=n.eventsDispatcher,i=n.globalEventsController,s=n.scrollbarRecompensor,r=t.data,c=t.elements,u=t.fs,l=t.p,d=t.props,h=t.qs,f=t.ss,p=t.t,b=t.thumbsSwipingProps,g=t.z;this.isLightboxFadingOut=!1,this.runActions=function(){e.isLightboxFadingOut=!0,c.container.classList.add(m),i.removeListeners(),f.r(),d.exitFullscreenOnClose&&r.ifs&&u.x(),g.r(),p((function(){e.isLightboxFadingOut=!1;for(var n=0;n0?c(s.previous,"ne"):void 0!==s.next&&o.swipedX<0&&c(s.next,"p")},this.zs=function(t){o.swipedX=(t.screenX-o.downScreenX)/a,o.swipedY=(t.screenY-o.downScreenY)/a,i[s.current].v(o.ux+o.swipedX,o.uy+o.swipedY).z()}}function B(t,e){var n=t.c,o=t.dss,i=t.p,s=t.r,r=t.zv,a=s(k);if(i.isPinching)return a.a(e),void a.p();2!==i.pc&&(1===r?1===n||o?i.swipedX=1:(a.a(e),a.s()):(a.a(e),a.zs(e)))}function W(t){var e=t.core,n=e.clickZoomer,o=e.slideIndexChanger,i=t.p,s=t.smw,r=t.stageIndexes,a=t.sws,c=t.zv;function u(t){var e=s[r.current];e.a(),e[t]()}function l(t,e){void 0!==t&&(s[t].s(),s[t][e]())}this.p=function(){var t=r.previous;if(void 0===t)u("z");else{u("p");var e=r.next;o.changeTo(t);var n=r.previous;a.d(n),a.b(e),u("z"),l(n,"ne")}},this.n=function(){var t=r.next;if(void 0===t)u("z");else{u("ne");var e=r.previous;o.changeTo(t);var n=r.next;a.d(n),a.b(e),u("z"),l(n,"p")}},this.s=function(){var t=s[r.current];i.ux=t.gx(),i.uy=t.gy()},this.d=function(){c<=1?n.zoomIn():n.zoomOut()}}function M(t,e){t.contains(e)&&t.removeChild(e)}function H(t){t.componentsServices;var e=t.core,n=e.lightboxCloser,o=e.pointeringBucket,i=t.dss,s=t.elements,r=t.p,a=t.props.disableBackgroundClose,c=t.r,u=t.swc,l=(t.ui,t.zv),d=c(W);this.a=function(){M(s.container,t.h),r.isPinching=!1,r.pinchedHypot=0,o.runSwipingTopActionsForPropsAndEvent(r),u.classList.remove("fslightboxswcp")},this.s=function(){1===l?i||(r.swipedX>0?d.p():d.n()):d.s()},this.n=function(t){"VIDEO"!==t.target.tagName&&(r.sd?d.d():a||n.close())}}function D(t,e){var n=t.p;n.p[e.pointerId]={screenX:e.screenX,screenY:e.screenY};var o=Object.keys(n.p).length;return n.pc=o,o<=2}function O(t){var e=t.core.pointeringBucket,n=t.data,o=t.elements,i=t.thumbsSwipingProps;this.runActions=function(t){e.runSwipingMoveActionsForPropsAndEvent(i,t),o.thumbsInner.style.transform="translateX(".concat(n.thumbsTransform+i.swipedX,"px)"),o.thumbsContainer.contains(o.thumbsCursorer)||o.thumbsContainer.appendChild(o.thumbsCursorer)}}function P(t){var e=t.data,n=t.resolve,o=t.thumbsSwipingProps,i=n(O),s=window.innerWidth;this.listener=function(t){e.thumbsInnerWidth>s&&o.i&&i.runActions(t)}}function R(t){var e=t.data,n=t.core,o=n.slideIndexChanger,i=n.thumbsTransformTransitioner,s=n.pointeringBucket,r=t.elements,a=t.thumbsSwipingProps,c=r.thumbsWrappers;this.runNoSwipeActionsForEvent=function(t){M(r.thumbsContainer,r.thumbsCursorer),a.i=!1;for(var e=0;e0)return u(0);e.thumbsTransform0)return;n.b()}var i=.1*o,s=o;e.deltaY<0?s+=i:(s-=i)<1&&(s=1),n.z(s),1===s&&n.e()}(t,e))}}function Y(t,e){var n=t.core,i=n.clickZoomer,s=n.lightboxCloser,r=n.slideChangeFacade,a=n.thumbsToggler,c=t.fs,u=(t.middleware,t.props),l=t.ss;if(t.ui.qps(),"Space"!==e.code)switch(e.key){case"Escape":s.close();break;case"ArrowLeft":r.changeToPrevious();break;case"ArrowRight":r.changeToNext();break;case"t":u.disableThumbs||a.toggleThumbs();break;case"+":o.p.i||i.zoomIn();break;case"-":o.p.i||i.zoomOut();break;case"F11":e.preventDefault(),c.t()}else l.t()}function j(t,e,o,i,s){var r=document.createElementNS("http://www.w3.org/2000/svg","svg");r.setAttributeNS(null,"width",e),r.setAttributeNS(null,"height",e),r.setAttributeNS(null,"viewBox",i);var a=document.createElementNS("http://www.w3.org/2000/svg","path");return a.setAttributeNS(null,"class","".concat(n,"svg-path")),a.setAttributeNS(null,"d",s),r.appendChild(a),t.appendChild(r),r}function V(t,e){var o=document.createElement("div");return o.className="".concat(n,"toolbar-button ").concat(r),o.title=e,t.appendChild(o),o}function U(t,e,n){var o=V(t,e.title);o.onclick=n,j(o,e.width,e.height,e.viewBox,e.d)}function Z(t){var e=t.props.sources,o=t.elements,i=document.createElement("div");o.nav=i,i.className="".concat(n,"nav"),o.container.appendChild(i),function(t,e){var o=t.core,i=o.clickZoomer,s=i.zoomIn,r=i.zoomOut,a=o.lightboxCloser.close,c=o.thumbsToggler,u=t.props,l=u.customToolbarButtons,d=u.disableThumbs,h=u.toolbarButtons,f=document.createElement("div");f.className="".concat(n,"toolbar"),e.appendChild(f);for(var p=function(e){U(f,l[e],(function(){return l[e].onClick(t)}))},m=0;m1&&function(t,e){var o=t.componentsServices,i=t.props.sources,s=document.createElement("div");s.className="".concat(n,"slide-number-container");var a=document.createElement("div");a.className=r;var c=document.createElement("span");o.setSlideNumber=function(t){return c.innerHTML=t};var u=document.createElement("span");u.className="".concat(n,"slash");var l=document.createElement("div");l.innerHTML=i.length,s.appendChild(a),a.appendChild(c),a.appendChild(u),a.appendChild(l),e.appendChild(s),setTimeout((function(){a.offsetWidth>55&&(s.style.justifyContent="flex-start")}))}(t,i)}function _(t,e){var n=t.c,o=t.core.pointeringBucket,i=t.elements.sources,s=t.p,r=t.smw,a=t.stageIndexes,c=t.swc,u=t.z,l=t.zv;if("touch"!==e.pointerType&&"IMG"===e.target.tagName&&e.preventDefault(),o.runSwipingDownActionsForPropsAndEvent(s,e),s.downScreenY=e.screenY,2===s.pc)s.isPinching=!0,s.pinchedHypot=N(s),c.classList.add("fslightboxswcp"),1===l&&u.b();else for(var d=0;d0&&void 0!==arguments[0]?arguments[0]:t.mh,i=t.mw;return(a=i/r)e?e:o)*r,a]},this.d=function(){return c}}function ct(t,e){var n=this,o=t.elements.sources,i=t.isl,s=t.la,r=t.props.initialAnimation,a=t.resolve,c=t.saw,l=t.sew,d=t.sz;function h(t,n){d[e]=a(at,[e,t,n]),d[e].s()}this.a=function(t,a){i[e]=!0,o[e].classList.add(u),c[e].classList.add(r),c[e].removeChild(c[e].firstChild),requestAnimationFrame((function(){requestAnimationFrame((function(){l[e].classList.add("fslightboxtt")}))})),h(t,a),s.s(e),s.t(e),n.a=h}}function ut(t,e){var n,o=this,i=t.elements.sources,s=t.props,r=(0,t.resolve)(ct,[e]);this.handleImageLoad=function(t){var e=t.target,n=e.naturalWidth,o=e.naturalHeight;r.a(n,o)},this.handleVideoLoad=function(t){var e=t.target,o=e.videoWidth,i=e.videoHeight;n=!0,r.a(o,i)},this.handleNotMetaDatedVideoLoad=function(){n||o.handleYoutubeLoad()},this.handleYoutubeLoad=function(){var t=1920,e=1080;s.maxYoutubeDimensions&&(t=s.maxYoutubeDimensions.width,e=s.maxYoutubeDimensions.height),r.a(t,e)},this.handleCustomLoad=function(){var t=i[e];t.offsetWidth&&t.offsetHeight?r.a(t.offsetWidth,t.offsetHeight):setTimeout(o.handleCustomLoad)}}function lt(t,e){var n=t.elements.sources,o=t.props.customAttributes,i=n[e];for(var s in o[e]){var r=o[e][s];"class"!==s?i.setAttribute(s,r):i.classList.add("a")}}function dt(t,e){var n=t.collections.sourceLoadHandlers,o=t.elements.sources,i=t.props.sources,s=t.saw,r=document.createElement("img");o[e]=r,r.className="fslightboxs",r.src=i[e],r.onload=n[e].handleImageLoad,lt(t,e),s[e].appendChild(r)}function ht(t,e){var n=t.collections.sourceLoadHandlers,o=t.elements.sources,i=t.props.sources,s=t.saw,r=document.createElement("video"),a=document.createElement("source");o[e]=r,r.className="fslightboxs",r.src=i[e],r.onloadedmetadata=function(t){n[e].handleVideoLoad(t)},r.controls=!0,lt(t,e),r.appendChild(a),setTimeout(n[e].handleNotMetaDatedVideoLoad,3e3),s[e].appendChild(r)}function ft(t,e){var n=t.collections.sourceLoadHandlers,o=t.elements,i=o.sources,s=o.saw,r=t.props.sources,a=(s=t.saw,document.createElement("iframe")),c=r[e],u=c.split("?")[1];i[e]=a,a.className="fslightboxs fslightboxyt",a.src="https://www.youtube.com/embed/".concat(c.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/)[2],"?").concat(u||""),a.allowFullscreen=!0,lt(t,e),s[e].appendChild(a),n[e].handleYoutubeLoad()}function pt(t,e){var n=t.collections.sourceLoadHandlers,o=t.elements.sources,i=t.props.sources,s=t.saw,r=i[e];o[e]=r,r.classList.add("fslightboxs"),lt(t,e),s[e].appendChild(r),n[e].handleCustomLoad()}function mt(t,e){t.data.isSourceLoaded;var n,o,i=t.elements.sources,s=t.props,a=s.initialAnimation;return s.sources,n=t.saw,o=document.createElement("div"),n=n[e],o.className="fslightboxin ".concat(r),o.innerHTML="Invalid source",n.removeChild(n.firstChild),i[e]=o,n.classList.add(a),void n.appendChild(o)}function bt(t,e,n){var o=t.props.thumbsIcons;if(o[n]){e.appendChild(o[n].cloneNode(!0));var i=document.createElement("div");i.className="fslightboxtd",e.appendChild(i)}}function gt(t,e,n){var o=t.elements,i=o.thumbsWrappers,s=o.thumbsInner;i[e]=document.createElement("div"),i[e].className=y,bt(t,i[e],e),function(t,e,n,o){var i=t.core.thumbLoadHandler.handleLoad,s=t.elements.thumbs,r=t.stageIndexes.current;s[n]=document.createElement("img"),s[n].src=o;var a=b;r===n&&(a+=" fslightboxta"),s[n].className=a,s[n].onload=i,e.appendChild(s[n])}(t,i[e],e,n),s.appendChild(i[e])}function vt(t){var e=t.core.thumbsRenderDispatcher,n=t.data,o=t.props,i=o.showThumbsOnMount,s=o.sources,a=o.thumbs;this.buildThumbForTypeAndIndex=function(o,c){var u;u=a[c]?function(){return gt(t,c,a[c])}:o===nt?function(){return gt(t,c,s[c])}:function(){return function(t,e){var n=t.elements,o=n.thumbsWrappers,i=n.thumbsInner;o[e]=document.createElement("div"),o[e].className="".concat(C," ").concat(y),bt(t,o[e],e),function(t,e,n){var o=t.core.thumbLoadHandler.handleLoad,i=t.elements.thumbs,s=t.stageIndexes.current;i[n]=document.createElement("div");var a="".concat(b," ").concat(r);s===n&&(a+=" fslightboxta"),i[n].className=a,j(i[n],"22px",0,"0 0 30 30","M15,3C8.373,3,3,8.373,3,15c0,6.627,5.373,12,12,12s12-5.373,12-12C27,8.373,21.627,3,15,3z M16.212,8l-0.2,9h-2.024l-0.2-9 H16.212z M15.003,22.189c-0.828,0-1.323-0.441-1.323-1.182c0-0.755,0.494-1.196,1.323-1.196c0.822,0,1.316,0.441,1.316,1.196 C16.319,21.748,15.825,22.189,15.003,22.189z"),e.appendChild(i[n]),setTimeout(o)}(t,o[e],e),i.appendChild(o[e])}(t,c)},e.addFunctionToToBeRenderedAtIndex(u,c),(i||n.isThumbing)&&e.renderThumbsIfNotYetAndAllTypesDetected()}}function xt(t){var e,n=t.collections,o=n.sourceLoadHandlers,i=n.sourcesRenderFunctions,s=t.core.sourceDisplayFacade,r=t.props.disableThumbs,a=t.resolve;r||(e=a(vt)),this.runActionsForSourceTypeAndIndex=function(n,c){var u;switch(n!==rt&&(o[c]=a(ut,[c])),n){case nt:u=dt;break;case ot:u=ht;break;case it:u=ft;break;case st:u=pt;break;default:u=mt}i[c]=function(){return u(t,c)},s.displaySourcesWhichShouldBeDisplayed(),r||e.buildThumbForTypeAndIndex(n,c)}}function wt(t,e,n){var o=t.props,i=o.types,s=o.type,r=o.sources;this.getTypeSetByClientForIndex=function(t){var e;return i&&i[t]?e=i[t]:s&&(e=s),e},this.retrieveTypeWithXhrForIndex=function(t){!function(t,e){var n=document.createElement("a");n.href=t;var o=n.hostname;if("www.youtube.com"===o||"youtu.be"===o)return e(it);var i=new XMLHttpRequest;i.onreadystatechange=function(){if(4!==i.readyState){if(2===i.readyState){var t,n=i.getResponseHeader("content-type");switch(n.slice(0,n.indexOf("/"))){case"image":t=nt;break;case"video":t=ot;break;default:t=rt}i.onreadystatechange=null,i.abort(),e(t)}}else e(rt)},i.open("GET",t),i.send()}(r[t],(function(o){e.handleReceivedSourceTypeForUrl(o,r[t]),n.runActionsForSourceTypeAndIndex(o,t)}))}}function yt(t){var e=t.props.sources,n=t.st,o=t.stageIndexes,i=e.length-1;n.getPreviousSlideIndex=function(){return 0===o.current?i:o.current-1},n.getNextSlideIndex=function(){return o.current===i?0:o.current+1},n.u=0===i?function(){}:1===i?function(){0===o.current?(o.next=1,delete o.previous):(o.previous=0,delete o.next)}:function(){o.previous=n.getPreviousSlideIndex(),o.next=n.getNextSlideIndex()},n.i=i<=2?function(){return!0}:function(t){var e=o.current;if(0===e&&t===i||e===i&&0===t)return!0;var n=e-t;return-1===n||0===n||1===n}}function Ct(t){var e=t.componentsServices,o=t.core,i=o.eventsDispatcher,r=(o.lightboxOpener,o.globalEventsController),l=o.scrollbarRecompensor,d=o.sourceDisplayFacade,h=t.data,m=t.ea,b=t.elements,y=t.la,C=t.smw,L=t.st,N=t.stageIndexes,k=t.sws,B=t.ui,W=!1;function M(){var e,o=t.props,r=o.disableThumbs,a=o.showThumbsOnMount,l=o.sources;W=!0,function(t){var e=t.props;t.dss=e.disableSlideSwiping,t.dt=e.disableThumbs,t.c=e.sources.length,t.tc=e.showThumbsWithCaptions}(t),h.scrollbarWidth=function(){var t=document.createElement("div"),e=t.style,n=document.createElement("div");e.visibility="hidden",e.width="100px",e.msOverflowStyle="scrollbar",e.overflow="scroll",n.style.width="100%",document.body.appendChild(t);var o=t.offsetWidth;t.appendChild(n);var i=n.offsetWidth;return document.body.removeChild(t),o-i}(),h.unloadedThumbsCount=l.length,r||(h.isThumbing=a,function(t){var e=t.core,n=t.data,o=t.elements,i=t.props;n.isThumbing=i.showThumbsOnMount,n.thumbsInnerWidth=null,n.thumbsTransform=0,n.thumbedSourceEnhancementWrapperScale=null,n.thumbedSourceEnhancementWrapperTranslateY=null,n.unloadedThumbsCount=i.sources.length,t.thumbsSwipingProps={i:!1,downScreenX:null,swipedX:null},e.thumbLoadHandler={},e.thumbsRenderDispatcher={},e.thumbsSwipingDown={},e.thumbsToggler={},e.thumbsTransformer={},e.thumbsTransformTransitioner={},o.thumbsContainer=null,o.thumbs=[],o.thumbsWrappers=[],o.thumbsComponents=[],o.thumbsInner=null,function(t){var e=t.core.thumbLoadHandler,n=t.componentsServices,o=t.data,i=t.elements.thumbsWrappers,s=t.la;e.handleLoad=function(){if(o.unloadedThumbsCount--,0===o.unloadedThumbsCount){for(var t=0;tinnerWidth?s.runActions():s.runToThinThumbsActions()},n.transformToCurrentWithTransition=function(){i.thumbsInnerWidth>innerWidth&&o.callActionWithTransition(s.runActions)}}(t),function(t){var e=t.core.thumbsTransformTransitioner,n=t.elements,o=(0,t.q)((function(){n.thumbsInner.classList.remove("fslightboxtt")}),300);e.callActionWithTransition=function(t){n.thumbsInner.classList.add("fslightboxtt"),t(),o()}}(t)}(t)),function(t){!function(t){var e=t.core,n=e.classFacade,o=e.st,i=t.elements;n.removeFromEachElementClassIfContains=function(t,e){for(var n=0;nwindow.innerHeight&&(document.body.style.marginRight=e.scrollbarWidth+"px")};n.removeRecompense=function(){document.body.style.removeProperty("margin-right")}}(t),function(t){var e=t.c,n=t.core.thumbsTransformer,o=t.data,i=t.dt,s=t.elements,r=t.f,a=t.isl,c=t.la,u=t.props.sourceMargin,l=t.sew,d=t.smw,h=t.sz,f=t.stageIndexes,p=t.tc,m=s.captions,b=s.thumbs,g=1-2*u,v=1-u,x=[],w=[],y=[],C=[],L=[],T=0;function z(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1;l[arguments.length>2?arguments[2]:void 0].style.transform="translateY(".concat(t,"px) scale(").concat(e,")")}c.r=function(){innerWidth>992?t.mw=g*innerWidth:t.mw=innerWidth,t.mh=g*innerHeight,0===o.unloadedThumbsCount&&c.rt(),void 0!==f.previous&&d[f.previous].ne(),void 0!==f.next&&d[f.next].p(),r((function(t){var e=h[t],n=m[t];e&&e.s(),n&&(x[t]=n.offsetHeight),i||(T=s.thumbsContainer.offsetHeight),c.s(t),d[t].d(),c.t(t)}))},c.s=function(t){var e=h[t];if(e){var n=e.d(),o=n[0]+n[1];if(x[t]){var s=e.g(innerHeight*v-x[t]);w[t]=(s[0]+s[1])/o,L[t]=-(x[t]-innerHeight/2+s[1]/2),L[t]>0&&(L[t]=0)}if(!i){if(p&&x[t]){var r=T+x[t];a=e.g((innerHeight-r)*(1-1.4*u)+34),y[t]=(a[0]+a[1])/o,C[t]=-(r-34-innerHeight/2+a[1]/2)}else{var a=e.g(innerHeight*v-T);y[t]=(a[0]+a[1])/o,C[t]=-(T-innerHeight/2+a[1]/2)}C[t]>0&&(C[t]=0)}}},c.t=function(e){a[e]&&(1!==t.zv?z(0,1,e):o.isThumbing?z(C[e],y[e],e):z(L[e],w[e],e))},c.ut=function(){r((function(t){a[t]&&z(L[t],w[t],t)}))},c.rt=function(){o.thumbsInnerWidth=0;for(var t=0;t1?(n.changeToPrevious=function(){o.jumpTo(s.getPreviousSlideIndex())},n.changeToNext=function(){o.jumpTo(s.getNextSlideIndex())}):(n.changeToPrevious=function(){},n.changeToNext=function(){})}(t),function(t){var e=t.componentsServices,n=t.core,o=n.eventsDispatcher,i=n.slideIndexChanger,s=n.sourceDisplayFacade,r=n.thumbsTransformer,a=t.ea,c=t.isl,u=t.props.disableThumbs,l=t.resolve,d=t.smw,h=t.st,f=t.stageIndexes,p=t.sws,m=t.z;i.changeTo=function(t){var n=f.current;m.r(),a.c(n,t),f.current=t,h.u(),e.setSlideNumber(t+1),u||(a.t(n,t),r.transformToCurrentWithTransition()),s.displaySourcesWhichShouldBeDisplayed(),o.dispatch("onSlideChange")},i.jumpTo=function(t){var e=f.current;if(e!==t){var n=l(E,[{previous:f.previous,current:e,next:f.next},c[e],c[t]]);i.changeTo(t);for(var o=0;o1&&(Q(e=t,"previous"),Q(e,"next")),r||function(t){var e=t.componentsServices,n=t.elements,o=t.data;n.thumbsContainer=document.createElement("div");var i,r,a=g;function u(){r=!0,(i=J(n.thumbsContainer)).classList.add(v)}o.isThumbing?u():a+=" fslightboxx",e.appendThumbsLoaderIfNotYet=function(){r||u()},e.hideThumbsLoader=function(){n.thumbsContainer.removeChild(i)},n.thumbsContainer.className=a,n.container.appendChild(n.thumbsContainer),function(t){var e=t.elements;e.thumbsCursorer=document.createElement("div"),e.thumbsCursorer.className="".concat(x," ").concat(s," ").concat(c)}(t),function(t){var e=t.core.thumbsSwipingDown.listener,n=t.elements;n.thumbsInner=document.createElement("div"),n.thumbsInner.className=w,n.thumbsInner.addEventListener("pointerdown",e),n.thumbsContainer.appendChild(n.thumbsInner)}(t)}(t),function(t){for(var e=t.props.sources,n=t.resolve,o=n(et),i=n(xt),s=n(wt,[o,i]),r=0;r0&&void 0!==arguments[0]?arguments[0]:0,o=N.previous,s=N.current,c=N.next;N.current=n,W||yt(t),L.u(),W?(k.c(),k.a(),k.b(o),k.b(s),k.b(c),m.c(s,N.current),m.t(s,N.current),i.dispatch("onShow")):M(),d.displaySourcesWhichShouldBeDisplayed(),e.setSlideNumber(n+1),document.body.appendChild(b.container),document.documentElement.classList.add(a),l.addRecompense(),r.addListeners(),y.r(),C[N.current].n(),B.q(),i.dispatch("onOpen")}}function Lt(t,e,n){return Lt=Tt()?Reflect.construct.bind():function(t,e,n){var o=[null];o.push.apply(o,e);var i=new(Function.bind.apply(t,o));return n&&zt(i,n.prototype),i},Lt.apply(null,arguments)}function Tt(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function zt(t,e){return zt=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},zt(t,e)}function St(t){return function(t){if(Array.isArray(t))return At(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return At(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?At(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function At(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n1&&void 0!==arguments[1]?arguments[1]:[];return n.unshift(t),Lt(e,St(n))},this.r=this.resolve,this.collections={sourceLoadHandlers:[],sourcesRenderFunctions:[]},this.sz=[],this.core={classFacade:{},clickZoomer:{},eventsDispatcher:{},globalEventsController:{},lightboxCloser:{},lightboxUpdater:{},pointeringBucket:{},scrollbarRecompensor:{},slideChangeFacade:{},slideIndexChanger:{},sourceDisplayFacade:{},swipingActioner:{}},this.ea={},this.fs={},this.la={},this.ss={},this.st={},this.sws={},this.ui={},this.z={},this.t=function(e,n){var o=t.ts.push(setTimeout((function(){delete t.ts[o-1],e()}),n))},this.q=function(e,n){var o=t.qs.push(0)-1;return function(){t.qs[o]++,t.t((function(){t.qs[o]--,t.qs[o]||e()}),n)}},Ct(this),this.close=function(){return t.core.lightboxCloser.close()}},window.fsLightboxInstances={},It(),window.refreshFsLightbox=function(){for(var t in fsLightboxInstances){var e=fsLightboxInstances[t].props;fsLightboxInstances[t]=new FsLightbox,fsLightboxInstances[t].props=e,fsLightboxInstances[t].props.sources=[],fsLightboxInstances[t].elements.a=[]}It()},t})())); \ No newline at end of file diff --git a/web/vendor/fslightbox/package.json b/web/vendor/fslightbox/package.json new file mode 100644 index 0000000..d605ec7 --- /dev/null +++ b/web/vendor/fslightbox/package.json @@ -0,0 +1,22 @@ +{ + "name": "fslightbox", + "version": "3.6.2", + "description": "A vanilla JavaScript plug-in without production dependencies for dispalying images, videos, or, through custom sources, anything you want in a clean overlying box.", + "author": "Piotr Zdziarski", + "homepage": "https://fslightbox.com", + "main": "fslightbox.js", + "scripts": { + "w": "webpack-dev-server --host 0.0.0.0", + "p": "webpack --config ./webpack.prod.config.js" + }, + "devDependencies": { + "@babel/core": "^7.4.4", + "@babel/preset-env": "^7.4.4", + "@babel/register": "^7.4.4", + "babel-loader": "^8.0.5", + "html-webpack-plugin": "^5.0.0", + "webpack": "^5.0.0", + "webpack-cli": "^4.0.0", + "webpack-dev-server": "^4.0.0" + } +} \ No newline at end of file diff --git a/web/vendor/gridjs/gridjs.umd.js b/web/vendor/gridjs/gridjs.umd.js new file mode 100644 index 0000000..31761d7 --- /dev/null +++ b/web/vendor/gridjs/gridjs.umd.js @@ -0,0 +1,2 @@ +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t||self).gridjs={})}(this,function(t){function n(t,n){for(var e=0;et.length)&&(n=t.length);for(var e=0,r=new Array(n);e=t.length?{done:!0}:{done:!1,value:t[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var l;!function(t){t[t.Init=0]="Init",t[t.Loading=1]="Loading",t[t.Loaded=2]="Loaded",t[t.Rendered=3]="Rendered",t[t.Error=4]="Error"}(l||(l={}));var c,f,p,d,h,_,m,v={},g=[],y=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function b(t,n){for(var e in n)t[e]=n[e];return t}function w(t){var n=t.parentNode;n&&n.removeChild(t)}function x(t,n,e){var r,o,i,u={};for(i in n)"key"==i?r=n[i]:"ref"==i?o=n[i]:u[i]=n[i];if(arguments.length>2&&(u.children=arguments.length>3?c.call(arguments,2):e),"function"==typeof t&&null!=t.defaultProps)for(i in t.defaultProps)void 0===u[i]&&(u[i]=t.defaultProps[i]);return k(t,u,r,o,null)}function k(t,n,e,r,o){var i={type:t,props:n,key:e,ref:r,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==o?++p:o};return null==o&&null!=f.vnode&&f.vnode(i),i}function P(t){return t.children}function S(t,n){this.props=t,this.context=n}function N(t,n){if(null==n)return t.__?N(t.__,t.__.__k.indexOf(t)+1):null;for(var e;n0?k(d.type,d.props,d.key,d.ref?d.ref:null,d.__v):d)){if(d.__=e,d.__b=e.__b+1,null===(p=y[c])||p&&d.key==p.key&&d.type===p.type)y[c]=void 0;else for(f=0;f0&&(this.callbacks[e].forEach(function(t){return t.apply(void 0,[].slice.call(n,1))}),!0)},t}();function Y(t,n){if(typeof t!=typeof n)return!1;if(null===t&&null===n)return!0;if("object"!=typeof t)return t===n;if(Array.isArray(t)&&Array.isArray(n)){if(t.length!==n.length)return!1;for(var e=0;e=e.__.length&&e.__.push({__V:ft}),e.__[t]}function gt(t){return lt=1,function(t,n,e){var r=vt(ot++,2);if(r.t=t,!r.__c&&(r.__=[Et(void 0,n),function(t){var n=r.__N?r.__N[0]:r.__[0],e=r.t(n,t);n!==e&&(r.__N=[e,r.__[1]],r.__c.setState({}))}],r.__c=it,!it.u)){it.u=!0;var o=it.shouldComponentUpdate;it.shouldComponentUpdate=function(t,n,e){if(!r.__c.__H)return!0;var i=r.__c.__H.__.filter(function(t){return t.__c});if(i.every(function(t){return!t.__N}))return!o||o.call(this,t,n,e);var u=!1;return i.forEach(function(t){if(t.__N){var n=t.__[0];t.__=t.__N,t.__N=void 0,n!==t.__[0]&&(u=!0)}}),!(!u&&r.__c.props===t)&&(!o||o.call(this,t,n,e))}}return r.__N||r.__}(Et,t)}function yt(t,n){var e=vt(ot++,3);!f.__s&&Ct(e.__H,n)&&(e.__=t,e.i=n,it.__H.__h.push(e))}function bt(t){return lt=5,wt(function(){return{current:t}},[])}function wt(t,n){var e=vt(ot++,7);return Ct(e.__H,n)?(e.__V=t(),e.i=n,e.__h=t,e.__V):e.__}function xt(){for(var t;t=ct.shift();)if(t.__P&&t.__H)try{t.__H.__h.forEach(St),t.__H.__h.forEach(Nt),t.__H.__h=[]}catch(n){t.__H.__h=[],f.__e(n,t.__v)}}f.__b=function(t){it=null,pt&&pt(t)},f.__r=function(t){dt&&dt(t),ot=0;var n=(it=t.__c).__H;n&&(ut===it?(n.__h=[],it.__h=[],n.__.forEach(function(t){t.__N&&(t.__=t.__N),t.__V=ft,t.__N=t.i=void 0})):(n.__h.forEach(St),n.__h.forEach(Nt),n.__h=[])),ut=it},f.diffed=function(t){ht&&ht(t);var n=t.__c;n&&n.__H&&(n.__H.__h.length&&(1!==ct.push(n)&&st===f.requestAnimationFrame||((st=f.requestAnimationFrame)||Pt)(xt)),n.__H.__.forEach(function(t){t.i&&(t.__H=t.i),t.__V!==ft&&(t.__=t.__V),t.i=void 0,t.__V=ft})),ut=it=null},f.__c=function(t,n){n.some(function(t){try{t.__h.forEach(St),t.__h=t.__h.filter(function(t){return!t.__||Nt(t)})}catch(e){n.some(function(t){t.__h&&(t.__h=[])}),n=[],f.__e(e,t.__v)}}),_t&&_t(t,n)},f.unmount=function(t){mt&&mt(t);var n,e=t.__c;e&&e.__H&&(e.__H.__.forEach(function(t){try{St(t)}catch(t){n=t}}),e.__H=void 0,n&&f.__e(n,e.__v))};var kt="function"==typeof requestAnimationFrame;function Pt(t){var n,e=function(){clearTimeout(r),kt&&cancelAnimationFrame(n),setTimeout(t)},r=setTimeout(e,100);kt&&(n=requestAnimationFrame(e))}function St(t){var n=it,e=t.__c;"function"==typeof e&&(t.__c=void 0,e()),it=n}function Nt(t){var n=it;t.__c=t.__(),it=n}function Ct(t,n){return!t||t.length!==n.length||n.some(function(n,e){return n!==t[e]})}function Et(t,n){return"function"==typeof n?n(t):n}function It(){return function(t){var n=it.context[t.__c],e=vt(ot++,9);return e.c=t,n?(null==e.__&&(e.__=!0,n.sub(it)),n.props.value):t.__}(fn)}var Tt={search:{placeholder:"Type a keyword..."},sort:{sortAsc:"Sort column ascending",sortDesc:"Sort column descending"},pagination:{previous:"Previous",next:"Next",navigate:function(t,n){return"Page "+t+" of "+n},page:function(t){return"Page "+t},showing:"Showing",of:"of",to:"to",results:"results"},loading:"Loading...",noRecordsFound:"No matching records found",error:"An error happened while fetching the data"},Lt=/*#__PURE__*/function(){function t(t){this._language=void 0,this._defaultLanguage=void 0,this._language=t,this._defaultLanguage=Tt}var n=t.prototype;return n.getString=function(t,n){if(!n||!t)return null;var e=t.split("."),r=e[0];if(n[r]){var o=n[r];return"string"==typeof o?function(){return o}:"function"==typeof o?o:this.getString(e.slice(1).join("."),o)}return null},n.translate=function(t){var n,e=this.getString(t,this._language);return(n=e||this.getString(t,this._defaultLanguage))?n.apply(void 0,[].slice.call(arguments,1)):t},t}();function At(){var t=It();return function(n){var e;return(e=t.translator).translate.apply(e,[n].concat([].slice.call(arguments,1)))}}var Ot=function(t){return function(n){return r({},n,{search:{keyword:t}})}};function jt(){return It().store}function Ht(t){var n=jt(),e=gt(t(n.getState())),r=e[0],o=e[1];return yt(function(){return n.subscribe(function(){var e=t(n.getState());r!==e&&o(e)})},[]),r}function Dt(){var t,n=gt(void 0),e=n[0],r=n[1],o=It(),i=o.search,u=At(),s=jt().dispatch,a=Ht(function(t){return t.search});yt(function(){e&&e.setProps({keyword:null==a?void 0:a.keyword})},[a,e]),yt(function(){r(i.server?new at({keyword:i.keyword,url:i.server.url,body:i.server.body}):new nt({keyword:i.keyword,columns:o.header&&o.header.columns,ignoreHiddenColumns:i.ignoreHiddenColumns||void 0===i.ignoreHiddenColumns,selector:i.selector})),i.keyword&&s(Ot(i.keyword))},[i]),yt(function(){if(e)return o.pipeline.register(e),function(){return o.pipeline.unregister(e)}},[o,e]);var l,c,f,p=function(t,n){return lt=8,wt(function(){return t},n)}((l=function(t){t.target instanceof HTMLInputElement&&s(Ot(t.target.value))},c=e instanceof at?i.debounceTimeout||250:0,function(){var t=arguments;return new Promise(function(n){f&&clearTimeout(f),f=setTimeout(function(){return n(l.apply(void 0,[].slice.call(t)))},c)})}),[i,e]);return x("div",{className:et(rt("search",null==(t=o.className)?void 0:t.search))},x("input",{type:"search",placeholder:u("search.placeholder"),"aria-label":u("search.placeholder"),onInput:p,className:rt(et("input"),et("search","input")),defaultValue:(null==a?void 0:a.keyword)||""}))}var Mt=/*#__PURE__*/function(t){function n(){return t.apply(this,arguments)||this}o(n,t);var r=n.prototype;return r.validateProps=function(){if(isNaN(Number(this.props.limit))||isNaN(Number(this.props.page)))throw Error("Invalid parameters passed")},r._process=function(t){var n=this.props.page;return new J(t.rows.slice(n*this.props.limit,(n+1)*this.props.limit))},e(n,[{key:"type",get:function(){return K.Limit}}]),n}(tt),Rt=/*#__PURE__*/function(t){function n(){return t.apply(this,arguments)||this}return o(n,t),n.prototype._process=function(t){var n={};return this.props.url&&(n.url=this.props.url(t.url,this.props.page,this.props.limit)),this.props.body&&(n.body=this.props.body(t.body,this.props.page,this.props.limit)),r({},t,n)},e(n,[{key:"type",get:function(){return K.ServerLimit}}]),n}(tt);function Ft(){var t=It(),n=t.pagination,e=n.server,r=n.summary,o=void 0===r||r,i=n.nextButton,u=void 0===i||i,s=n.prevButton,a=void 0===s||s,l=n.buttonsCount,c=void 0===l?3:l,f=n.limit,p=void 0===f?10:f,d=n.page,h=void 0===d?0:d,_=n.resetPageOnUpdate,m=void 0===_||_,v=bt(null),g=gt(h),y=g[0],b=g[1],w=gt(0),k=w[0],S=w[1],N=At();yt(function(){return e?(v.current=new Rt({limit:p,page:y,url:e.url,body:e.body}),t.pipeline.register(v.current)):(v.current=new Mt({limit:p,page:y}),t.pipeline.register(v.current)),v.current instanceof Rt?t.pipeline.on("afterProcess",function(t){return S(t.length)}):v.current instanceof Mt&&v.current.on("beforeProcess",function(t){return S(t.length)}),t.pipeline.on("updated",C),t.pipeline.on("error",function(){S(0),b(0)}),function(){t.pipeline.unregister(v.current),t.pipeline.off("updated",C)}},[]);var C=function(t){m&&t!==v.current&&(b(0),0!==v.current.props.page&&v.current.setProps({page:0}))},E=function(){return Math.ceil(k/p)},I=function(t){if(t>=E()||t<0||t===y)return null;b(t),v.current.setProps({page:t})};return x("div",{className:rt(et("pagination"),t.className.pagination)},x(P,null,o&&k>0&&x("div",{role:"status","aria-live":"polite",className:rt(et("summary"),t.className.paginationSummary),title:N("pagination.navigate",y+1,E())},N("pagination.showing")," ",x("b",null,N(""+(y*p+1)))," ",N("pagination.to")," ",x("b",null,N(""+Math.min((y+1)*p,k)))," ",N("pagination.of")," ",x("b",null,N(""+k))," ",N("pagination.results"))),x("div",{className:et("pages")},a&&x("button",{tabIndex:0,role:"button",disabled:0===y,onClick:function(){return I(y-1)},title:N("pagination.previous"),"aria-label":N("pagination.previous"),className:rt(t.className.paginationButton,t.className.paginationButtonPrev)},N("pagination.previous")),function(){if(c<=0)return null;var n=Math.min(E(),c),e=Math.min(y,Math.floor(n/2));return y+Math.floor(n/2)>=E()&&(e=n-(E()-y)),x(P,null,E()>n&&y-e>0&&x(P,null,x("button",{tabIndex:0,role:"button",onClick:function(){return I(0)},title:N("pagination.firstPage"),"aria-label":N("pagination.firstPage"),className:t.className.paginationButton},N("1")),x("button",{tabIndex:-1,className:rt(et("spread"),t.className.paginationButton)},"...")),Array.from(Array(n).keys()).map(function(t){return y+(t-e)}).map(function(n){return x("button",{tabIndex:0,role:"button",onClick:function(){return I(n)},className:rt(y===n?rt(et("currentPage"),t.className.paginationButtonCurrent):null,t.className.paginationButton),title:N("pagination.page",n+1),"aria-label":N("pagination.page",n+1)},N(""+(n+1)))}),E()>n&&E()>y+e+1&&x(P,null,x("button",{tabIndex:-1,className:rt(et("spread"),t.className.paginationButton)},"..."),x("button",{tabIndex:0,role:"button",onClick:function(){return I(E()-1)},title:N("pagination.page",E()),"aria-label":N("pagination.page",E()),className:t.className.paginationButton},N(""+E()))))}(),u&&x("button",{tabIndex:0,role:"button",disabled:E()===y+1||0===E(),onClick:function(){return I(y+1)},title:N("pagination.next"),"aria-label":N("pagination.next"),className:rt(t.className.paginationButton,t.className.paginationButtonNext)},N("pagination.next"))))}function Ut(t,n){return"string"==typeof t?t.indexOf("%")>-1?n/100*parseInt(t,10):parseInt(t,10):t}function Wt(t){return t?Math.floor(t)+"px":""}function Bt(t){var n=t.tableRef.cloneNode(!0);return n.style.position="absolute",n.style.width="100%",n.style.zIndex="-2147483640",n.style.visibility="hidden",x("div",{ref:function(t){t&&t.appendChild(n)}})}function qt(t){if(!t)return"";var n=t.split(" ");return 1===n.length&&/([a-z][A-Z])+/g.test(t)?t:n.map(function(t,n){return 0==n?t.toLowerCase():t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()}).join("")}var zt,Vt=new(/*#__PURE__*/function(){function t(){}var n=t.prototype;return n.format=function(t,n){return"[Grid.js] ["+n.toUpperCase()+"]: "+t},n.error=function(t,n){void 0===n&&(n=!1);var e=this.format(t,"error");if(n)throw Error(e);console.error(e)},n.warn=function(t){console.warn(this.format(t,"warn"))},n.info=function(t){console.info(this.format(t,"info"))},t}());t.PluginPosition=void 0,(zt=t.PluginPosition||(t.PluginPosition={}))[zt.Header=0]="Header",zt[zt.Footer=1]="Footer",zt[zt.Cell=2]="Cell";var $t=/*#__PURE__*/function(){function t(){this.plugins=void 0,this.plugins=[]}var n=t.prototype;return n.get=function(t){return this.plugins.find(function(n){return n.id===t})},n.add=function(t){return t.id?this.get(t.id)?(Vt.error("Duplicate plugin ID: "+t.id),this):(this.plugins.push(t),this):(Vt.error("Plugin ID cannot be empty"),this)},n.remove=function(t){var n=this.get(t);return n&&this.plugins.splice(this.plugins.indexOf(n),1),this},n.list=function(t){var n;return n=null!=t||null!=t?this.plugins.filter(function(n){return n.position===t}):this.plugins,n.sort(function(t,n){return t.order&&n.order?t.order-n.order:1})},t}();function Gt(t){var n=this,e=It();if(t.pluginId){var o=e.plugin.get(t.pluginId);return o?x(P,{},x(o.component,r({plugin:o},t.props))):null}return void 0!==t.position?x(P,{},e.plugin.list(t.position).map(function(t){return x(t.component,r({plugin:t},n.props.props))})):null}var Kt=/*#__PURE__*/function(n){function i(){var t;return(t=n.call(this)||this)._columns=void 0,t._columns=[],t}o(i,n);var u=i.prototype;return u.adjustWidth=function(t,n,e){var o=t.container,u=t.autoWidth;if(!o)return this;var s=o.clientWidth,l={};n.current&&u&&(q(x(Bt,{tableRef:n.current}),e.current),l=function(t){var n=t.querySelector("table");if(!n)return{};var e=n.className,o=n.style.cssText;n.className=e+" "+et("shadowTable"),n.style.tableLayout="auto",n.style.width="auto",n.style.padding="0",n.style.margin="0",n.style.border="none",n.style.outline="none";var i=Array.from(n.parentNode.querySelectorAll("thead th")).reduce(function(t,n){var e;return n.style.width=n.clientWidth+"px",r(((e={})[n.getAttribute("data-column-id")]={minWidth:n.clientWidth},e),t)},{});return n.className=e,n.style.cssText=o,n.style.tableLayout="auto",Array.from(n.parentNode.querySelectorAll("thead th")).reduce(function(t,n){return t[n.getAttribute("data-column-id")].width=n.clientWidth,t},i)}(e.current));for(var c,f=a(i.tabularFormat(this.columns).reduce(function(t,n){return t.concat(n)},[]));!(c=f()).done;){var p=c.value;p.columns&&p.columns.length>0||(!p.width&&u?p.id in l&&(p.width=Wt(l[p.id].width),p.minWidth=Wt(l[p.id].minWidth)):p.width=Wt(Ut(p.width,s)))}return n.current&&u&&q(null,e.current),this},u.setSort=function(t,n){for(var e,o=a(n||this.columns||[]);!(e=o()).done;){var i=e.value;i.columns&&i.columns.length>0?i.sort=void 0:void 0===i.sort&&t?i.sort={}:i.sort?"object"==typeof i.sort&&(i.sort=r({},i.sort)):i.sort=void 0,i.columns&&this.setSort(t,i.columns)}},u.setResizable=function(t,n){for(var e,r=a(n||this.columns||[]);!(e=r()).done;){var o=e.value;void 0===o.resizable&&(o.resizable=t),o.columns&&this.setResizable(t,o.columns)}},u.setID=function(t){for(var n,e=a(t||this.columns||[]);!(n=e()).done;){var r=n.value;r.id||"string"!=typeof r.name||(r.id=qt(r.name)),r.id||Vt.error('Could not find a valid ID for one of the columns. Make sure a valid "id" is set for all columns.'),r.columns&&this.setID(r.columns)}},u.populatePlugins=function(n,e){for(var o,i=a(e);!(o=i()).done;){var u=o.value;void 0!==u.plugin&&n.add(r({id:u.id},u.plugin,{position:t.PluginPosition.Cell}))}},i.fromColumns=function(t){for(var n,e=new i,r=a(t);!(n=r()).done;){var o=n.value;if("string"==typeof o||d(o))e.columns.push({name:o});else if("object"==typeof o){var u=o;u.columns&&(u.columns=i.fromColumns(u.columns).columns),"object"==typeof u.plugin&&void 0===u.data&&(u.data=null),e.columns.push(o)}}return e},i.createFromConfig=function(t){var n=new i;return t.from?n.columns=i.fromHTMLTable(t.from).columns:t.columns?n.columns=i.fromColumns(t.columns).columns:!t.data||"object"!=typeof t.data[0]||t.data[0]instanceof Array||(n.columns=Object.keys(t.data[0]).map(function(t){return{name:t}})),n.columns.length?(n.setID(),n.setSort(t.sort),n.setResizable(t.resizable),n.populatePlugins(t.plugin,n.columns),n):null},i.fromHTMLTable=function(t){for(var n,e=new i,r=a(t.querySelector("thead").querySelectorAll("th"));!(n=r()).done;){var o=n.value;e.columns.push({name:o.innerHTML,width:o.width})}return e},i.tabularFormat=function(t){var n=[],e=t||[],r=[];if(e&&e.length){n.push(e);for(var o,i=a(e);!(o=i()).done;){var u=o.value;u.columns&&u.columns.length&&(r=r.concat(u.columns))}r.length&&(n=n.concat(this.tabularFormat(r)))}return n},i.leafColumns=function(t){var n=[],e=t||[];if(e&&e.length)for(var r,o=a(e);!(r=o()).done;){var i=r.value;i.columns&&0!==i.columns.length||n.push(i),i.columns&&(n=n.concat(this.leafColumns(i.columns)))}return n},i.maximumDepth=function(t){return this.tabularFormat([t]).length-1},e(i,[{key:"columns",get:function(){return this._columns},set:function(t){this._columns=t}},{key:"visibleColumns",get:function(){return this._columns.filter(function(t){return!t.hidden})}}]),i}(V),Xt=function(){},Zt=/*#__PURE__*/function(t){function n(n){var e;return(e=t.call(this)||this).data=void 0,e.set(n),e}o(n,t);var e=n.prototype;return e.get=function(){try{return Promise.resolve(this.data()).then(function(t){return{data:t,total:t.length}})}catch(t){return Promise.reject(t)}},e.set=function(t){return t instanceof Array?this.data=function(){return t}:t instanceof Function&&(this.data=t),this},n}(Xt),Jt=/*#__PURE__*/function(t){function n(n){var e;return(e=t.call(this)||this).options=void 0,e.options=n,e}o(n,t);var e=n.prototype;return e.handler=function(t){return"function"==typeof this.options.handle?this.options.handle(t):t.ok?t.json():(Vt.error("Could not fetch data: "+t.status+" - "+t.statusText,!0),null)},e.get=function(t){var n=r({},this.options,t);return"function"==typeof n.data?n.data(n):fetch(n.url,n).then(this.handler.bind(this)).then(function(t){return{data:n.then(t),total:"function"==typeof n.total?n.total(t):void 0}})},n}(Xt),Qt=/*#__PURE__*/function(){function t(){}return t.createFromConfig=function(t){var n=null;return t.data&&(n=new Zt(t.data)),t.from&&(n=new Zt(this.tableElementToArray(t.from)),t.from.style.display="none"),t.server&&(n=new Jt(t.server)),n||Vt.error("Could not determine the storage type",!0),n},t.tableElementToArray=function(t){for(var n,e,r=[],o=a(t.querySelector("tbody").querySelectorAll("tr"));!(n=o()).done;){for(var i,u=[],s=a(n.value.querySelectorAll("td"));!(i=s()).done;){var l=i.value;1===l.childNodes.length&&l.childNodes[0].nodeType===Node.TEXT_NODE?u.push((e=l.innerHTML,(new DOMParser).parseFromString(e,"text/html").documentElement.textContent)):u.push(G(l.innerHTML))}r.push(u)}return r},t}(),Yt="undefined"!=typeof Symbol?Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator")):"@@iterator";function tn(t,n,e){if(!t.s){if(e instanceof nn){if(!e.s)return void(e.o=tn.bind(null,t,n));1&n&&(n=e.s),e=e.v}if(e&&e.then)return void e.then(tn.bind(null,t,n),tn.bind(null,t,2));t.s=n,t.v=e;var r=t.o;r&&r(t)}}var nn=/*#__PURE__*/function(){function t(){}return t.prototype.then=function(n,e){var r=new t,o=this.s;if(o){var i=1&o?n:e;if(i){try{tn(r,1,i(this.v))}catch(t){tn(r,2,t)}return r}return this}return this.o=function(t){try{var o=t.v;1&t.s?tn(r,1,n?n(o):o):e?tn(r,1,e(o)):tn(r,2,o)}catch(t){tn(r,2,t)}},r},t}();function en(t){return t instanceof nn&&1&t.s}var rn=/*#__PURE__*/function(t){function n(n){var e;return(e=t.call(this)||this)._steps=new Map,e.cache=new Map,e.lastProcessorIndexUpdated=-1,n&&n.forEach(function(t){return e.register(t)}),e}o(n,t);var r=n.prototype;return r.clearCache=function(){this.cache=new Map,this.lastProcessorIndexUpdated=-1},r.register=function(t,n){if(void 0===n&&(n=null),!t)throw Error("Processor is not defined");if(null===t.type)throw Error("Processor type is not defined");if(this.findProcessorIndexByID(t.id)>-1)throw Error("Processor ID "+t.id+" is already defined");return t.on("propsUpdated",this.processorPropsUpdated.bind(this)),this.addProcessorByPriority(t,n),this.afterRegistered(t),t},r.tryRegister=function(t,n){void 0===n&&(n=null);try{return this.register(t,n)}catch(t){}},r.unregister=function(t){if(t&&-1!==this.findProcessorIndexByID(t.id)){var n=this._steps.get(t.type);n&&n.length&&(this._steps.set(t.type,n.filter(function(n){return n!=t})),this.emit("updated",t))}},r.addProcessorByPriority=function(t,n){var e=this._steps.get(t.type);if(!e){var r=[];this._steps.set(t.type,r),e=r}if(null===n||n<0)e.push(t);else if(e[n]){var o=e.slice(0,n-1),i=e.slice(n+1);this._steps.set(t.type,o.concat(t).concat(i))}else e[n]=t},r.getStepsByType=function(t){return this.steps.filter(function(n){return n.type===t})},r.getSortedProcessorTypes=function(){return Object.keys(K).filter(function(t){return!isNaN(Number(t))}).map(function(t){return Number(t)})},r.process=function(t){try{var n=function(t){return e.lastProcessorIndexUpdated=o.length,e.emit("afterProcess",i),i},e=this,r=e.lastProcessorIndexUpdated,o=e.steps,i=t,u=function(t,n){try{var u=function(t,n,e){if("function"==typeof t[Yt]){var r,o,i,u=t[Yt]();if(function t(e){try{for(;!(r=u.next()).done;)if((e=n(r.value))&&e.then){if(!en(e))return void e.then(t,i||(i=tn.bind(null,o=new nn,2)));e=e.v}o?tn(o,1,e):o=e}catch(t){tn(o||(o=new nn),2,t)}}(),u.return){var s=function(t){try{r.done||u.return()}catch(t){}return t};if(o&&o.then)return o.then(s,function(t){throw s(t)});s()}return o}if(!("length"in t))throw new TypeError("Object is not iterable");for(var a=[],l=0;l=r)return Promise.resolve(t.process(i)).then(function(n){e.cache.set(t.id,i=n)});i=e.cache.get(t.id)}();if(o&&o.then)return o.then(function(){})})}catch(t){return n(t)}return u&&u.then?u.then(void 0,n):u}(0,function(t){throw Vt.error(t),e.emit("error",i),t});return Promise.resolve(u&&u.then?u.then(n):n())}catch(t){return Promise.reject(t)}},r.findProcessorIndexByID=function(t){return this.steps.findIndex(function(n){return n.id==t})},r.setLastProcessorIndex=function(t){var n=this.findProcessorIndexByID(t.id);this.lastProcessorIndexUpdated>n&&(this.lastProcessorIndexUpdated=n)},r.processorPropsUpdated=function(t){this.setLastProcessorIndex(t),this.emit("propsUpdated"),this.emit("updated",t)},r.afterRegistered=function(t){this.setLastProcessorIndex(t),this.emit("afterRegister"),this.emit("updated",t)},e(n,[{key:"steps",get:function(){for(var t,n=[],e=a(this.getSortedProcessorTypes());!(t=e()).done;){var r=this._steps.get(t.value);r&&r.length&&(n=n.concat(r))}return n.filter(function(t){return t})}}]),n}(Q),on=/*#__PURE__*/function(t){function n(){return t.apply(this,arguments)||this}return o(n,t),n.prototype._process=function(t){try{return Promise.resolve(this.props.storage.get(t))}catch(t){return Promise.reject(t)}},e(n,[{key:"type",get:function(){return K.Extractor}}]),n}(tt),un=/*#__PURE__*/function(t){function n(){return t.apply(this,arguments)||this}return o(n,t),n.prototype._process=function(t){var n=J.fromArray(t.data);return n.length=t.total,n},e(n,[{key:"type",get:function(){return K.Transformer}}]),n}(tt),sn=/*#__PURE__*/function(t){function n(){return t.apply(this,arguments)||this}return o(n,t),n.prototype._process=function(){return Object.entries(this.props.serverStorageOptions).filter(function(t){return"function"!=typeof t[1]}).reduce(function(t,n){var e;return r({},t,((e={})[n[0]]=n[1],e))},{})},e(n,[{key:"type",get:function(){return K.Initiator}}]),n}(tt),an=/*#__PURE__*/function(t){function n(){return t.apply(this,arguments)||this}o(n,t);var r=n.prototype;return r.castData=function(t){if(!t||!t.length)return[];if(!this.props.header||!this.props.header.columns)return t;var n=Kt.leafColumns(this.props.header.columns);return t[0]instanceof Array?t.map(function(t){var e=0;return n.map(function(n,r){return void 0!==n.data?(e++,"function"==typeof n.data?n.data(t):n.data):t[r-e]})}):"object"!=typeof t[0]||t[0]instanceof Array?[]:t.map(function(t){return n.map(function(n,e){return void 0!==n.data?"function"==typeof n.data?n.data(t):n.data:n.id?t[n.id]:(Vt.error("Could not find the correct cell for column at position "+e+".\n Make sure either 'id' or 'selector' is defined for all columns."),null)})})},r._process=function(t){return{data:this.castData(t.data),total:t.total}},e(n,[{key:"type",get:function(){return K.Transformer}}]),n}(tt),ln=/*#__PURE__*/function(){function t(){}return t.createFromConfig=function(t){var n=new rn;return t.storage instanceof Jt&&n.register(new sn({serverStorageOptions:t.server})),n.register(new on({storage:t.storage})),n.register(new an({header:t.header})),n.register(new un),n},t}(),cn=function(t){var n=this;this.state=void 0,this.listeners=[],this.isDispatching=!1,this.getState=function(){return n.state},this.getListeners=function(){return n.listeners},this.dispatch=function(t){if("function"!=typeof t)throw new Error("Reducer is not a function");if(n.isDispatching)throw new Error("Reducers may not dispatch actions");n.isDispatching=!0;var e=n.state;try{n.state=t(n.state)}finally{n.isDispatching=!1}for(var r,o=a(n.listeners);!(r=o()).done;)(0,r.value)(n.state,e);return n.state},this.subscribe=function(t){if("function"!=typeof t)throw new Error("Listener is not a function");return n.listeners=[].concat(n.listeners,[t]),function(){return n.listeners=n.listeners.filter(function(n){return n!==t})}},this.state=t},fn=function(t,n){var e={__c:n="__cC"+m++,__:null,Consumer:function(t,n){return t.children(n)},Provider:function(t){var e,r;return this.getChildContext||(e=[],(r={})[n]=this,this.getChildContext=function(){return r},this.shouldComponentUpdate=function(t){this.props.value!==t.value&&e.some(E)},this.sub=function(t){e.push(t);var n=t.componentWillUnmount;t.componentWillUnmount=function(){e.splice(e.indexOf(t),1),n&&n.call(t)}}),t.children}};return e.Provider.__=e.Consumer.contextType=e}(),pn=/*#__PURE__*/function(){function n(){Object.assign(this,n.defaultConfig())}var e=n.prototype;return e.assign=function(t){return Object.assign(this,t)},e.update=function(t){return t?(this.assign(n.fromPartialConfig(r({},this,t))),this):this},n.defaultConfig=function(){return{store:new cn({status:l.Init,header:void 0,data:null}),plugin:new $t,tableRef:{current:null},width:"100%",height:"auto",processingThrottleMs:100,autoWidth:!0,style:{},className:{}}},n.fromPartialConfig=function(e){var r=(new n).assign(e);return"boolean"==typeof e.sort&&e.sort&&r.assign({sort:{multiColumn:!0}}),r.assign({header:Kt.createFromConfig(r)}),r.assign({storage:Qt.createFromConfig(r)}),r.assign({pipeline:ln.createFromConfig(r)}),r.assign({translator:new Lt(r.language)}),r.plugin=new $t,r.search&&r.plugin.add({id:"search",position:t.PluginPosition.Header,component:Dt}),r.pagination&&r.plugin.add({id:"pagination",position:t.PluginPosition.Footer,component:Ft}),r.plugins&&r.plugins.forEach(function(t){return r.plugin.add(t)}),r},n}();function dn(t){var n,e=It();return x("td",r({role:t.role,colSpan:t.colSpan,"data-column-id":t.column&&t.column.id,className:rt(et("td"),t.className,e.className.td),style:r({},t.style,e.style.td),onClick:function(n){t.messageCell||e.eventEmitter.emit("cellClick",n,t.cell,t.column,t.row)}},(n=t.column)?"function"==typeof n.attributes?n.attributes(t.cell.data,t.row,t.column):n.attributes:{}),t.column&&"function"==typeof t.column.formatter?t.column.formatter(t.cell.data,t.row,t.column):t.column&&t.column.plugin?x(Gt,{pluginId:t.column.id,props:{column:t.column,cell:t.cell,row:t.row}}):t.cell.data)}function hn(t){var n=It(),e=Ht(function(t){return t.header});return x("tr",{className:rt(et("tr"),n.className.tr),onClick:function(e){t.messageRow||n.eventEmitter.emit("rowClick",e,t.row)}},t.children?t.children:t.row.cells.map(function(n,r){var o=function(t){if(e){var n=Kt.leafColumns(e.columns);if(n)return n[t]}return null}(r);return o&&o.hidden?null:x(dn,{key:n.id,cell:n,row:t.row,column:o})}))}function _n(t){return x(hn,{messageRow:!0},x(dn,{role:"alert",colSpan:t.colSpan,messageCell:!0,cell:new X(t.message),className:rt(et("message"),t.className?t.className:null)}))}function mn(){var t=It(),n=Ht(function(t){return t.data}),e=Ht(function(t){return t.status}),r=Ht(function(t){return t.header}),o=At(),i=function(){return r?r.visibleColumns.length:0};return x("tbody",{className:rt(et("tbody"),t.className.tbody)},n&&n.rows.map(function(t){return x(hn,{key:t.id,row:t})}),e===l.Loading&&(!n||0===n.length)&&x(_n,{message:o("loading"),colSpan:i(),className:rt(et("loading"),t.className.loading)}),e===l.Rendered&&n&&0===n.length&&x(_n,{message:o("noRecordsFound"),colSpan:i(),className:rt(et("notfound"),t.className.notfound)}),e===l.Error&&x(_n,{message:o("error"),colSpan:i(),className:rt(et("error"),t.className.error)}))}var vn=/*#__PURE__*/function(t){function n(){return t.apply(this,arguments)||this}o(n,t);var r=n.prototype;return r.validateProps=function(){for(var t,n=a(this.props.columns);!(t=n()).done;){var e=t.value;void 0===e.direction&&(e.direction=1),1!==e.direction&&-1!==e.direction&&Vt.error("Invalid sort direction "+e.direction)}},r.compare=function(t,n){return t>n?1:t1&&(f=!0,c=!0):0===a?c=!0:a>0&&!e?(c=!0,f=!0):a>0&&e&&(c=!0),f&&(s=[]),c)s.push({index:t,direction:n,compare:o});else if(d){var h=s.indexOf(l);s[h].direction=n}else if(p){var _=s.indexOf(l);s.splice(_,1)}return r({},i,{sort:{columns:s}})}},yn=function(t,n,e){return function(o){var i=(o.sort?[].concat(o.sort.columns):[]).find(function(n){return n.index===t});return r({},o,i?gn(t,1===i.direction?-1:1,n,e)(o):gn(t,1,n,e)(o))}},bn=/*#__PURE__*/function(t){function n(){return t.apply(this,arguments)||this}return o(n,t),n.prototype._process=function(t){var n={};return this.props.url&&(n.url=this.props.url(t.url,this.props.columns)),this.props.body&&(n.body=this.props.body(t.body,this.props.columns)),r({},t,n)},e(n,[{key:"type",get:function(){return K.ServerSort}}]),n}(tt);function wn(t){var n=It(),e=jt().dispatch,o=At(),i=gt(0),u=i[0],s=i[1],a=n.sort,l=Ht(function(t){return t.sort}),c="object"==typeof(null==a?void 0:a.server)?K.ServerSort:K.Sort,f=function(){var t=n.pipeline.getStepsByType(c);if(t.length)return t[0]};return yt(function(){var t=f()||(c===K.ServerSort?new bn(r({columns:l?l.columns:[]},a.server)):new vn({columns:l?l.columns:[]}));return n.pipeline.tryRegister(t),function(){return n.pipeline.unregister(t)}},[n]),yt(function(){if(l){var n,e=l.columns.find(function(n){return n.index===t.index});e?(0===u&&(e.direction=null!=(n=t.direction)?n:1),s(e.direction)):s(0)}},[l]),yt(function(){var t=f();t&&l&&t.setProps({columns:l.columns})},[l]),x("button",{tabIndex:-1,"aria-label":o("sort.sort"+(1===u?"Desc":"Asc")),title:o("sort.sort"+(1===u?"Desc":"Asc")),className:rt(et("sort"),et("sort",function(t){return 1===t?"asc":-1===t?"desc":"neutral"}(u)),n.className.sort),onClick:function(n){n.preventDefault(),n.stopPropagation(),e(yn(t.index,!0===n.shiftKey&&a.multiColumn,t.compare))}})}var xn=function(t,n){var e;void 0===n&&(n=100);var r=Date.now(),o=function(){r=Date.now(),t.apply(void 0,[].slice.call(arguments))};return function(){var t=[].slice.call(arguments),i=Date.now(),u=i-r;u>=n?o.apply(void 0,t):(e&&clearTimeout(e),e=setTimeout(function(){o.apply(void 0,t),e=null},n-u))}};function kn(t){var n,e=function(t){return t instanceof MouseEvent?Math.floor(t.pageX):Math.floor(t.changedTouches[0].pageX)},r=function(r){r.stopPropagation();var u=parseInt(t.thRef.current.style.width,10)-e(r);n=xn(function(t){return o(t,u)},10),document.addEventListener("mouseup",i),document.addEventListener("touchend",i),document.addEventListener("mousemove",n),document.addEventListener("touchmove",n)},o=function(n,r){n.stopPropagation();var o=t.thRef.current;r+e(n)>=parseInt(o.style.minWidth,10)&&(o.style.width=r+e(n)+"px")},i=function t(e){e.stopPropagation(),document.removeEventListener("mouseup",t),document.removeEventListener("mousemove",n),document.removeEventListener("touchmove",n),document.removeEventListener("touchend",t)};return x("div",{className:rt(et("th"),et("resizable")),onMouseDown:r,onTouchStart:r,onClick:function(t){return t.stopPropagation()}})}function Pn(t){var n=It(),e=bt(null),o=gt({}),i=o[0],u=o[1],s=jt().dispatch;yt(function(){if(n.fixedHeader&&e.current){var t=e.current.offsetTop;"number"==typeof t&&u({top:t})}},[e]);var a,l=function(){return null!=t.column.sort},c=function(e){e.stopPropagation(),l()&&s(yn(t.index,!0===e.shiftKey&&n.sort.multiColumn,t.column.sort.compare))};return x("th",r({ref:e,"data-column-id":t.column&&t.column.id,className:rt(et("th"),l()?et("th","sort"):null,n.fixedHeader?et("th","fixed"):null,n.className.th),onClick:c,style:r({},n.style.th,{minWidth:t.column.minWidth,width:t.column.width},i,t.style),onKeyDown:function(t){l()&&13===t.which&&c(t)},rowSpan:t.rowSpan>1?t.rowSpan:void 0,colSpan:t.colSpan>1?t.colSpan:void 0},(a=t.column)?"function"==typeof a.attributes?a.attributes(null,null,t.column):a.attributes:{},l()?{tabIndex:0}:{}),x("div",{className:et("th","content")},void 0!==t.column.name?t.column.name:void 0!==t.column.plugin?x(Gt,{pluginId:t.column.plugin.id,props:{column:t.column}}):null),l()&&x(wn,r({index:t.index},t.column.sort)),t.column.resizable&&t.index0?(Vt.error("The container element "+t+" is not empty. Make sure the container is empty and call render() again"),this):(this.config.container=t,q(this.createElement(),t),this)},n}(Q);t.Cell=X,t.Component=S,t.Config=pn,t.Grid=Ln,t.Row=Z,t.className=et,t.createElement=x,t.createRef=function(){return{current:null}},t.h=x,t.html=G,t.useConfig=It,t.useEffect=yt,t.useRef=bt,t.useSelector=Ht,t.useState=gt,t.useStore=jt,t.useTranslator=At}); +//# sourceMappingURL=gridjs.umd.js.map diff --git a/web/vendor/gridjs/mermaid.min.css b/web/vendor/gridjs/mermaid.min.css new file mode 100644 index 0000000..21332c7 --- /dev/null +++ b/web/vendor/gridjs/mermaid.min.css @@ -0,0 +1 @@ +.gridjs-footer button,.gridjs-head button{background-color:transparent;background-image:none;border:none;cursor:pointer;margin:0;outline:none;padding:0}.gridjs-temp{position:relative}.gridjs-head{margin-bottom:5px;padding:5px 1px;width:100%}.gridjs-head:after{clear:both;content:"";display:block}.gridjs-head:empty{border:none;padding:0}.gridjs-container{color:#000;display:inline-block;overflow:hidden;padding:2px;position:relative;z-index:0}.gridjs-footer{background-color:#fff;border-bottom-width:1px;border-color:#e5e7eb;border-radius:0 0 8px 8px;border-top:1px solid #e5e7eb;box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.26);display:block;padding:12px 24px;position:relative;width:100%;z-index:5}.gridjs-footer:empty{border:none;padding:0}input.gridjs-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border:1px solid #d2d6dc;border-radius:5px;font-size:14px;line-height:1.45;outline:none;padding:10px 13px}input.gridjs-input:focus{border-color:#9bc2f7;box-shadow:0 0 0 3px rgba(149,189,243,.5)}.gridjs-pagination{color:#3d4044}.gridjs-pagination:after{clear:both;content:"";display:block}.gridjs-pagination .gridjs-summary{float:left;margin-top:5px}.gridjs-pagination .gridjs-pages{float:right}.gridjs-pagination .gridjs-pages button{background-color:#fff;border:1px solid #d2d6dc;border-right:none;outline:none;padding:5px 14px;-webkit-user-select:none;-moz-user-select:none;user-select:none}.gridjs-pagination .gridjs-pages button:focus{border-right:1px solid #d2d6dc;box-shadow:0 0 0 2px rgba(149,189,243,.5);margin-right:-1px;position:relative}.gridjs-pagination .gridjs-pages button:hover{background-color:#f7f7f7;color:#3c4257;outline:none}.gridjs-pagination .gridjs-pages button:disabled,.gridjs-pagination .gridjs-pages button:hover:disabled,.gridjs-pagination .gridjs-pages button[disabled]{background-color:#fff;color:#6b7280;cursor:default}.gridjs-pagination .gridjs-pages button.gridjs-spread{background-color:#fff;box-shadow:none;cursor:default}.gridjs-pagination .gridjs-pages button.gridjs-currentPage{background-color:#f7f7f7;font-weight:700}.gridjs-pagination .gridjs-pages button:last-child{border-bottom-right-radius:6px;border-right:1px solid #d2d6dc;border-top-right-radius:6px}.gridjs-pagination .gridjs-pages button:first-child{border-bottom-left-radius:6px;border-top-left-radius:6px}.gridjs-pagination .gridjs-pages button:last-child:focus{margin-right:0}button.gridjs-sort{background-color:transparent;background-position-x:center;background-repeat:no-repeat;background-size:contain;border:none;cursor:pointer;float:right;height:24px;margin:0;outline:none;padding:0;width:13px}button.gridjs-sort-neutral{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDEuOTk4IiBoZWlnaHQ9IjQwMS45OTgiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQwMS45OTggNDAxLjk5OCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTczLjA5MiAxNjQuNDUyaDI1NS44MTNjNC45NDkgMCA5LjIzMy0xLjgwNyAxMi44NDgtNS40MjQgMy42MTMtMy42MTYgNS40MjctNy44OTggNS40MjctMTIuODQ3cy0xLjgxMy05LjIyOS01LjQyNy0xMi44NUwyMTMuODQ2IDUuNDI0QzIxMC4yMzIgMS44MTIgMjA1Ljk1MSAwIDIwMC45OTkgMHMtOS4yMzMgMS44MTItMTIuODUgNS40MjRMNjAuMjQyIDEzMy4zMzFjLTMuNjE3IDMuNjE3LTUuNDI0IDcuOTAxLTUuNDI0IDEyLjg1IDAgNC45NDggMS44MDcgOS4yMzEgNS40MjQgMTIuODQ3IDMuNjIxIDMuNjE3IDcuOTAyIDUuNDI0IDEyLjg1IDUuNDI0ek0zMjguOTA1IDIzNy41NDlINzMuMDkyYy00Ljk1MiAwLTkuMjMzIDEuODA4LTEyLjg1IDUuNDIxLTMuNjE3IDMuNjE3LTUuNDI0IDcuODk4LTUuNDI0IDEyLjg0N3MxLjgwNyA5LjIzMyA1LjQyNCAxMi44NDhMMTg4LjE0OSAzOTYuNTdjMy42MjEgMy42MTcgNy45MDIgNS40MjggMTIuODUgNS40MjhzOS4yMzMtMS44MTEgMTIuODQ3LTUuNDI4bDEyNy45MDctMTI3LjkwNmMzLjYxMy0zLjYxNCA1LjQyNy03Ljg5OCA1LjQyNy0xMi44NDggMC00Ljk0OC0xLjgxMy05LjIyOS01LjQyNy0xMi44NDctMy42MTQtMy42MTYtNy44OTktNS40Mi0xMi44NDgtNS40MnoiLz48L3N2Zz4=");background-position-y:center;opacity:.3}button.gridjs-sort-asc{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTIuMzYyIiBoZWlnaHQ9IjI5Mi4zNjEiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI5Mi4zNjIgMjkyLjM2MSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTI4Ni45MzUgMTk3LjI4NyAxNTkuMDI4IDY5LjM4MWMtMy42MTMtMy42MTctNy44OTUtNS40MjQtMTIuODQ3LTUuNDI0cy05LjIzMyAxLjgwNy0xMi44NSA1LjQyNEw1LjQyNCAxOTcuMjg3QzEuODA3IDIwMC45MDQgMCAyMDUuMTg2IDAgMjEwLjEzNHMxLjgwNyA5LjIzMyA1LjQyNCAxMi44NDdjMy42MjEgMy42MTcgNy45MDIgNS40MjUgMTIuODUgNS40MjVoMjU1LjgxM2M0Ljk0OSAwIDkuMjMzLTEuODA4IDEyLjg0OC01LjQyNSAzLjYxMy0zLjYxMyA1LjQyNy03Ljg5OCA1LjQyNy0xMi44NDdzLTEuODE0LTkuMjMtNS40MjctMTIuODQ3eiIvPjwvc3ZnPg==");background-position-y:35%;background-size:10px}button.gridjs-sort-desc{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTIuMzYyIiBoZWlnaHQ9IjI5Mi4zNjIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI5Mi4zNjIgMjkyLjM2MiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTI4Ni45MzUgNjkuMzc3Yy0zLjYxNC0zLjYxNy03Ljg5OC01LjQyNC0xMi44NDgtNS40MjRIMTguMjc0Yy00Ljk1MiAwLTkuMjMzIDEuODA3LTEyLjg1IDUuNDI0QzEuODA3IDcyLjk5OCAwIDc3LjI3OSAwIDgyLjIyOGMwIDQuOTQ4IDEuODA3IDkuMjI5IDUuNDI0IDEyLjg0N2wxMjcuOTA3IDEyNy45MDdjMy42MjEgMy42MTcgNy45MDIgNS40MjggMTIuODUgNS40MjhzOS4yMzMtMS44MTEgMTIuODQ3LTUuNDI4TDI4Ni45MzUgOTUuMDc0YzMuNjEzLTMuNjE3IDUuNDI3LTcuODk4IDUuNDI3LTEyLjg0NyAwLTQuOTQ4LTEuODE0LTkuMjI5LTUuNDI3LTEyLjg1eiIvPjwvc3ZnPg==");background-position-y:65%;background-size:10px}button.gridjs-sort:focus{outline:none}table.gridjs-table{border-collapse:collapse;display:table;margin:0;max-width:100%;overflow:auto;padding:0;table-layout:fixed;text-align:left;width:100%}.gridjs-tbody,td.gridjs-td{background-color:#fff}td.gridjs-td{border:1px solid #e5e7eb;box-sizing:content-box;padding:12px 24px}td.gridjs-td:first-child{border-left:none}td.gridjs-td:last-child{border-right:none}td.gridjs-message{text-align:center}th.gridjs-th{background-color:#f9fafb;border:1px solid #e5e7eb;border-top:none;box-sizing:border-box;color:#6b7280;outline:none;padding:14px 24px;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;white-space:nowrap}th.gridjs-th .gridjs-th-content{float:left;overflow:hidden;text-overflow:ellipsis;width:100%}th.gridjs-th-sort{cursor:pointer}th.gridjs-th-sort .gridjs-th-content{width:calc(100% - 15px)}th.gridjs-th-sort:focus,th.gridjs-th-sort:hover{background-color:#e5e7eb}th.gridjs-th-fixed{box-shadow:0 1px 0 0 #e5e7eb;position:sticky}@supports (-moz-appearance:none){th.gridjs-th-fixed{box-shadow:0 0 0 1px #e5e7eb}}th.gridjs-th:first-child{border-left:none}th.gridjs-th:last-child{border-right:none}.gridjs-tr{border:none}.gridjs-tr-selected td{background-color:#ebf5ff}.gridjs-tr:last-child td{border-bottom:0}.gridjs *,.gridjs :after,.gridjs :before{box-sizing:border-box}.gridjs-wrapper{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;border-color:#e5e7eb;border-radius:8px 8px 0 0;border-top-width:1px;box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.26);display:block;overflow:auto;position:relative;width:100%;z-index:1}.gridjs-wrapper:nth-last-of-type(2){border-bottom-width:1px;border-radius:8px}.gridjs-search{float:left}.gridjs-search-input{width:250px}.gridjs-loading-bar{background-color:#fff;opacity:.5;z-index:10}.gridjs-loading-bar,.gridjs-loading-bar:after{bottom:0;left:0;position:absolute;right:0;top:0}.gridjs-loading-bar:after{animation:shimmer 2s infinite;background-image:linear-gradient(90deg,hsla(0,0%,80%,0),hsla(0,0%,80%,.2) 20%,hsla(0,0%,80%,.5) 60%,hsla(0,0%,80%,0));content:"";transform:translateX(-100%)}@keyframes shimmer{to{transform:translateX(100%)}}.gridjs-td .gridjs-checkbox{cursor:pointer;display:block;margin:auto}.gridjs-resizable{bottom:0;position:absolute;right:0;top:0;width:5px}.gridjs-resizable:hover{background-color:#9bc2f7;cursor:ew-resize} \ No newline at end of file diff --git a/web/vendor/gridjs/plugins/selection/selection.umd.js b/web/vendor/gridjs/plugins/selection/selection.umd.js new file mode 100644 index 0000000..5c61aac --- /dev/null +++ b/web/vendor/gridjs/plugins/selection/selection.umd.js @@ -0,0 +1,2 @@ +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("gridjs")):"function"==typeof define&&define.amd?define(["exports","gridjs"],n):n(((e||self).gridjs=e.gridjs||{},e.gridjs.plugins=e.gridjs.plugins||{},e.gridjs.plugins.selection={}),e.gridjs)}(this,function(e,n){var t,r,o;function l(e,n,t,l,i){var c={type:e,props:n,key:t,ref:l,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==i?++o:i};return null==i&&null!=r.vnode&&r.vnode(c),c}function i(){return i=Object.assign?Object.assign.bind():function(e){for(var n=1;n-1?n:i({},n,{rowSelection:{rowIds:[e].concat(r)}})}},u=function(e){return function(n){var t,r=(null==(t=n.rowSelection)?void 0:t.rowIds)||[],o=r.indexOf(e);if(-1===o)return n;var l=[].concat(r);return l.splice(o,1),i({},n,{rowSelection:{rowIds:l}})}},s={__proto__:null,CheckRow:c,UncheckRow:u};e.RowSelection=function(e){var r=this,o=n.useStore().dispatch,i=n.useSelector(function(e){return e.rowSelection}),s=n.useState(!1),a=s[0],d=s[1],f=n.className("tr","selected"),p=n.className("checkbox"),_=function(e){return void 0!==e.row};n.useEffect(function(){var n;null!=(n=e.cell)&&n.data&&_(e)&&v()},[]),n.useEffect(function(){var n=r.base&&r.base.parentElement&&r.base.parentElement.parentElement;if(n){var t=((null==i?void 0:i.rowIds)||[]).indexOf(e.row.id)>-1;d(t),t?n.classList.add(f):n.classList.remove(f)}},[i]);var v=function(){var n;o(c(e.row.id)),null==(n=e.cell)||n.update(!0)};return _(e)?function(e,n,r){var o,i,c,u={};for(c in n)"key"==c?o=n[c]:"ref"==c?i=n[c]:u[c]=n[c];if(arguments.length>2&&(u.children=arguments.length>3?t.call(arguments,2):r),"function"==typeof e&&null!=e.defaultProps)for(c in e.defaultProps)void 0===u[c]&&(u[c]=e.defaultProps[c]);return l(e,u,o,i,null)}("input",{type:"checkbox",checked:a,onChange:function(){var n;a?(o(u(e.row.id)),null==(n=e.cell)||n.update(!1)):v()},className:p}):null},e.RowSelectionActions=s}); +//# sourceMappingURL=selection.umd.js.map diff --git a/web/vendor/multi-select/MultiSelect.css b/web/vendor/multi-select/MultiSelect.css new file mode 100644 index 0000000..e6f1fe8 --- /dev/null +++ b/web/vendor/multi-select/MultiSelect.css @@ -0,0 +1,206 @@ +:root { + --spacing-smaller: 3px; + --spacing-small: 5px; + --spacing-medium: 7px; + --spacing-large: 12px; + --font-size: 12px; + --font-size-large: 14px; + --font-size-larger: 16px; + --line-height: 16px; + --line-height-larger: 20px; + --primary-color: #40c979; + --text-color-dark: #212529; + --text-color: #585858; + --text-color-light: #65727e; + --border-color: #bebebe; + --border-color-light: #f1f3f5; + --input-placeholder: #65727e; + --input-background: #e9e9ed; + --input-border: #dee2e6; + --input-border-active: #c1c9d0; + --input-border-invalid: #e44e4e; + --input-outline-invalid: rgba(219, 138, 138, 0.5); + --input-color: #e9e9ed; + --input-disabled: #f7f7f7; + --input-min-height: 45px; + --options-height: 40dvh; + --option-background: #f3f4f7; + --border-radius: 5px; + --icon-size: 12px; + --icon-space: 30px; + --checkbox-size: 16px; + --checkbox-radius: 4px; + --checkbox-border: #ced4da; + --checkbox-background: #fff; + --checkbox-active: #fff; + --checkbox-thickness: 2px; +} +.multi-select { + display: flex; + box-sizing: border-box; + flex-direction: column; + position: relative; + width: 100%; + user-select: none; +} +.multi-select .multi-select-header { + border: 1px solid var(--input-border); + border-radius: var(--border-radius); + padding: var(--spacing-medium) var(--spacing-large); + padding-right: var(--icon-space); + overflow: hidden; + gap: var(--spacing-medium); + min-height: var(--input-min-height); +} +.multi-select .multi-select-header::after { + content: ""; + display: block; + position: absolute; + top: 50%; + right: 15px; + transform: translateY(-50%); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23949ba3' viewBox='0 0 16 16'%3E%3Cpath d='M8 13.1l-8-8 2.1-2.2 5.9 5.9 5.9-5.9 2.1 2.2z'/%3E%3C/svg%3E"); + height: var(--icon-size); + width: var(--icon-size); +} +.multi-select .multi-select-header.multi-select-header-active { + border-color: var(--input-border-active); +} +.multi-select .multi-select-header.multi-select-header-active::after { + transform: translateY(-50%) rotate(180deg); +} +.multi-select .multi-select-header.multi-select-header-active + .multi-select-options { + display: flex; +} +.multi-select .multi-select-header .multi-select-header-placeholder { + color: var(--text-color-light); +} +.multi-select .multi-select-header .multi-select-header-option { + display: inline-flex; + align-items: center; + background-color: var(--option-background); + font-size: var(--font-size-large); + padding: var(--spacing-smaller) var(--spacing-small); + border-radius: var(--border-radius); +} +.multi-select .multi-select-header .multi-select-header-max { + font-size: var(--font-size-large); + color: var(--text-color-light); +} +.multi-select .multi-select-options { + display: none; + box-sizing: border-box; + flex-flow: wrap; + position: absolute; + top: 100%; + left: 0; + right: 0; + z-index: 999; + margin-top: var(--spacing-small); + padding: var(--spacing-small); + background-color: #fff; + border-radius: var(--border-radius); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + max-height: var(--options-height); + overflow-y: auto; + overflow-x: hidden; +} +.multi-select .multi-select-options::-webkit-scrollbar { + width: 5px; +} +.multi-select .multi-select-options::-webkit-scrollbar-track { + background: #f0f1f3; +} +.multi-select .multi-select-options::-webkit-scrollbar-thumb { + background: #cdcfd1; +} +.multi-select .multi-select-options::-webkit-scrollbar-thumb:hover { + background: #b2b6b9; +} +.multi-select .multi-select-options .multi-select-option, +.multi-select .multi-select-options .multi-select-all { + padding: var(--spacing-large); +} +.multi-select .multi-select-options .multi-select-option .multi-select-option-radio, +.multi-select .multi-select-options .multi-select-all .multi-select-option-radio { + background: var(--checkbox-background); + margin-right: var(--spacing-large); + height: var(--checkbox-size); + width: var(--checkbox-size); + border: 1px solid var(--checkbox-border); + border-radius: var(--checkbox-radius); +} +.multi-select .multi-select-options .multi-select-option .multi-select-option-text, +.multi-select .multi-select-options .multi-select-all .multi-select-option-text { + box-sizing: border-box; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: inherit; + font-size: var(--font-size-larger); + line-height: var(--line-height); +} +.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-radio, +.multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-radio { + border-color: var(--primary-color); + background-color: var(--primary-color); +} +.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-radio::after, +.multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-radio::after { + content: ""; + display: block; + width: calc(var(--checkbox-size) / 4); + height: calc(var(--checkbox-size) / 2); + border: solid var(--checkbox-active); + border-width: 0 var(--checkbox-thickness) var(--checkbox-thickness) 0; + transform: rotate(45deg) translate(50%, -25%); +} +.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-text, +.multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-text { + color: var(--text-color-dark); +} +.multi-select .multi-select-options .multi-select-option:hover, .multi-select .multi-select-options .multi-select-option:active, +.multi-select .multi-select-options .multi-select-all:hover, .multi-select .multi-select-options .multi-select-all:active { + background-color: var(--option-background); +} +.multi-select .multi-select-options .multi-select-all { + border-bottom: 1px solid var(--border-color-light); + border-radius: 0; +} +.multi-select .multi-select-options .multi-select-search { + padding: var(--spacing-medium) var(--spacing-large); + border: 1px solid var(--input-border); + border-radius: var(--border-radius); + margin: 10px 10px 5px 10px; + width: 100%; + outline: none; + font-size: var(--font-size-larger); +} +.multi-select .multi-select-options .multi-select-search::placeholder { + color: var(--text-color-light); +} +.multi-select .multi-select-header, +.multi-select .multi-select-option, +.multi-select .multi-select-all { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + align-items: center; + border-radius: var(--border-radius); + cursor: pointer; + display: flex; + align-items: center; + width: 100%; + font-size: var(--font-size-larger); + color: var(--text-color-dark); +} +.multi-select.disabled { + opacity: 0.6; + pointer-events: none; + background-color: var(--input-disabled); +} +.multi-select.multi-select-invalid .multi-select-header { + border-color: var(--input-border-invalid); + outline: var(--input-outline-invalid) solid 1px; +} diff --git a/web/vendor/multi-select/MultiSelect.js b/web/vendor/multi-select/MultiSelect.js new file mode 100644 index 0000000..a92c0b9 --- /dev/null +++ b/web/vendor/multi-select/MultiSelect.js @@ -0,0 +1,399 @@ +/* + * Created by David Adams + * https://codeshack.io/multi-select-dropdown-html-javascript/ + * + * Released under the MIT license + */ +class MultiSelect { + + constructor(element, options = {}) { + let defaults = { + placeholder: 'Select item(s)', + max: null, + min: null, + disabled: false, + search: true, + searchPlaceholder: 'Search...', + selectAll: true, + selectAllLabel: 'Select all', + selectedLabel: 'selected', + listAll: true, + closeListOnItemSelect: false, + name: '', + width: '', + height: '', + dropdownWidth: '', + dropdownHeight: '', + data: [], + onChange: function() {}, + onSelect: function() {}, + onUnselect: function() {}, + onMaxReached: function() {} + }; + this.options = Object.assign(defaults, options); + this.selectElement = typeof element === 'string' ? document.querySelector(element) : element; + this.originalSelectElement = this.selectElement.cloneNode(true); + for(const prop in this.selectElement.dataset) { + if (this.options[prop] !== undefined) { + if (typeof this.options[prop] === 'boolean') { + this.options[prop] = this.selectElement.dataset[prop] === 'true'; + } else { + this.options[prop] = this.selectElement.dataset[prop]; + } + } + } + this.name = this.selectElement.getAttribute('name') ? this.selectElement.getAttribute('name') : 'multi-select-' + Math.floor(Math.random() * 1000000); + this.form = this.selectElement.getAttribute('form') ? this.selectElement.getAttribute('form') : ''; + if (!this.options.data.length) { + let options = this.selectElement.querySelectorAll('option'); + for (let i = 0; i < options.length; i++) { + this.options.data.push({ + value: options[i].value, + text: options[i].innerHTML, + selected: options[i].selected, + html: options[i].getAttribute('data-html') + }); + } + } + this.originalData = JSON.parse(JSON.stringify(this.options.data)); + this.element = this._template(); + this.selectElement.replaceWith(this.element); + this.outsideClickHandler = this._outsideClick.bind(this); + this._bulkSelect = false; + this._updateSelected(); + this._eventHandlers(); + if (this.options.disabled) { + this.disable(); + } + } + + _template() { + let optionsHTML = ''; + for (let i = 0; i < this.data.length; i++) { + const isSelected = this.data[i].selected; + optionsHTML += ` +
      + + ${this.data[i].html ? this.data[i].html : this.data[i].text} +
      + `; + } + let selectAllHTML = ''; + if (this.options.selectAll) { + selectAllHTML = `
      + + ${this.options.selectAllLabel} +
      `; + } + let template = ` + + `; + let element = document.createElement('div'); + element.innerHTML = template; + return element.firstElementChild; + } + + _eventHandlers() { + let headerElement = this.element.querySelector('.multi-select-header'); + const toggleDropdown = (forceClose = false) => { + if (this.element.classList.contains('disabled')) return; + if (forceClose || headerElement.classList.contains('multi-select-header-active')) { + headerElement.classList.remove('multi-select-header-active'); + this.element.setAttribute('aria-expanded', 'false'); + } else { + headerElement.classList.add('multi-select-header-active'); + this.element.setAttribute('aria-expanded', 'true'); + } + }; + this.element.querySelectorAll('.multi-select-option').forEach(option => { + option.onclick = (e) => { + e.stopPropagation(); + if (this.element.classList.contains('disabled')) return; + let selected = true; + if (!option.classList.contains('multi-select-selected')) { + if (this.options.max && this.selectedValues.length >= this.options.max) { + this.options.onMaxReached(this.options.max); + return; + } + option.classList.add('multi-select-selected'); + option.setAttribute('aria-selected', 'true'); + this.element.insertAdjacentHTML('afterbegin', ``); + this.data.find(data => data.value == option.dataset.value).selected = true; + } else { + option.classList.remove('multi-select-selected'); + option.setAttribute('aria-selected', 'false'); + this.element.querySelector(`input[value="${option.dataset.value}"]`).remove(); + this.data.find(data => data.value == option.dataset.value).selected = false; + selected = false; + } + this._updateHeader(); + if (this.options.search) { + this.element.querySelector('.multi-select-search').value = ''; + this.element.querySelectorAll('.multi-select-option').forEach(opt => opt.style.display = 'flex'); + } + if (this.options.closeListOnItemSelect) { + toggleDropdown(true); + } + this.options.onChange(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option); + if (selected) { + this.options.onSelect(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option); + } else { + this.options.onUnselect(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option); + } + this._validate(); + if (!this._bulkSelect) { + this._emitChange(); + } + }; + }); + headerElement.onclick = () => toggleDropdown(); + if (this.options.search) { + let search = this.element.querySelector('.multi-select-search'); + search.oninput = () => { + this.element.querySelectorAll('.multi-select-option').forEach(option => { + const text = option.querySelector('.multi-select-option-text').innerHTML.toLowerCase(); + option.style.display = text.includes(search.value.toLowerCase()) ? 'flex' : 'none'; + }); + }; + } + if (this.options.selectAll) { + let selectAllButton = this.element.querySelector('.multi-select-all'); + selectAllButton.onclick = (e) => { + e.stopPropagation(); + if (this.element.classList.contains('disabled')) return; + let allSelected = selectAllButton.classList.contains('multi-select-selected'); + this._bulkSelect = true; + this.element.querySelectorAll('.multi-select-option').forEach(option => { + let dataItem = this.data.find(data => data.value == option.dataset.value); + if (dataItem && ((allSelected && dataItem.selected) || (!allSelected && !dataItem.selected))) { + option.click(); + } + }); + this._bulkSelect = false; + selectAllButton.classList.toggle('multi-select-selected'); + this._emitChange(); + }; + } + if (this.selectElement.id && document.querySelector('label[for="' + this.selectElement.id + '"]')) { + document.querySelector('label[for="' + this.selectElement.id + '"]').onclick = () => { + toggleDropdown(); + }; + } + document.addEventListener('click', this.outsideClickHandler); + headerElement.addEventListener('keydown', (e) => { + if (['Enter', ' ', 'ArrowDown', 'ArrowUp'].includes(e.key)) { + e.preventDefault(); + toggleDropdown(); + const firstElement = this.element.querySelector('[role="searchbox"]') || this.element.querySelector('[role="option"]'); + if (firstElement) firstElement.focus(); + } + }); + this.element.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + toggleDropdown(true); + headerElement.focus(); + } + }); + const optionsContainer = this.element.querySelector('.multi-select-options'); + optionsContainer.addEventListener('keydown', (e) => { + const currentFocused = document.activeElement; + if (currentFocused.closest('.multi-select-options')) { + if (['ArrowDown', 'ArrowUp'].includes(e.key)) { + e.preventDefault(); + const direction = e.key === 'ArrowDown' ? 'nextElementSibling' : 'previousElementSibling'; + let nextElement = currentFocused[direction]; + while (nextElement && (nextElement.style.display === 'none' || !nextElement.matches('[role="option"], [role="searchbox"]'))) { + nextElement = nextElement[direction]; + } + if (nextElement) nextElement.focus(); + } else if (['Enter', ' '].includes(e.key) && currentFocused.matches('[role="option"]')) { + e.preventDefault(); + currentFocused.click(); + } + } + }); + } + + _updateHeader() { + this.element.querySelectorAll('.multi-select-header-option, .multi-select-header-placeholder').forEach(el => el.remove()); + if (this.selectedValues.length > 0) { + if (this.options.listAll) { + this.selectedItems.forEach(item => { + const el = document.createElement('span'); + el.className = 'multi-select-header-option'; + el.dataset.value = item.value; + el.innerHTML = item.text; + this.element.querySelector('.multi-select-header').prepend(el); + }); + } else { + const template = this.options.selectedLabel || 'selected'; + let label = template; + if (template.includes('{count}')) { + label = template.replace('{count}', this.selectedValues.length); + } else if (template.includes('%d')) { + label = template.replace('%d', this.selectedValues.length); + } else { + label = `${this.selectedValues.length} ${template}`; + } + this.element.querySelector('.multi-select-header').insertAdjacentHTML('afterbegin', `${label}`); + } + } else { + this.element.querySelector('.multi-select-header').insertAdjacentHTML('beforeend', `${this.placeholder}`); + } + if (this.options.max) { + this.element.querySelector('.multi-select-header-max').innerHTML = this.selectedValues.length + '/' + this.options.max; + } + } + + _updateSelected() { this._updateHeader(); } + + _emitChange() { + this.element.dispatchEvent(new CustomEvent('multi-select:change', { + detail: { values: this.selectedValues } + })); + } + + _validate() { + if (this.options.min && this.selectedValues.length < this.options.min) { + this.element.classList.add('multi-select-invalid'); + } else { + this.element.classList.remove('multi-select-invalid'); + } + } + + _outsideClick(event) { + if (!this.element.contains(event.target) && !event.target.closest('label[for="' + this.selectElement.id + '"]')) { + let headerElement = this.element.querySelector('.multi-select-header'); + if (headerElement.classList.contains('multi-select-header-active')) { + headerElement.classList.remove('multi-select-header-active'); + this.element.setAttribute('aria-expanded', 'false'); + } + } + } + + select(value) { + const option = this.element.querySelector(`.multi-select-option[data-value="${value}"]`); + if (option && !option.classList.contains('multi-select-selected')) { + option.click(); + } + } + + unselect(value) { + const option = this.element.querySelector(`.multi-select-option[data-value="${value}"]`); + if (option && option.classList.contains('multi-select-selected')) { + option.click(); + } + } + + setValues(values) { + this.data.forEach(item => { + item.selected = values.includes(item.value); + }); + this.refresh(); + } + + disable() { + this.element.classList.add('disabled'); + this.element.querySelector('.multi-select-header').removeAttribute('tabindex'); + const searchInput = this.element.querySelector('.multi-select-search'); + if (searchInput) searchInput.disabled = true; + } + + enable() { + this.element.classList.remove('disabled'); + this.element.querySelector('.multi-select-header').setAttribute('tabindex', '0'); + const searchInput = this.element.querySelector('.multi-select-search'); + if (searchInput) searchInput.disabled = false; + } + + destroy() { + this.element.replaceWith(this.originalSelectElement); + document.removeEventListener('click', this.outsideClickHandler); + } + + refresh() { + const newElement = this._template(); + this.element.replaceWith(newElement); + this.element = newElement; + this._updateSelected(); + this._eventHandlers(); + this._validate(); + } + + addItem(item) { + this.options.data.push(item); + this.refresh(); + } + + addItems(items) { + this.options.data.push(...items); + this.refresh(); + } + + async fetch(url, options = {}) { + const response = await fetch(url, options); + const data = await response.json(); + this.addItems(data); + if (this.options.onload) { + this.options.onload(data, this.options); + } + } + + removeItem(value) { + this.options.data = this.options.data.filter(item => item.value !== value); + this.refresh(); + } + + clear() { + this.options.data = []; + this.refresh(); + } + + reset() { + this.data = JSON.parse(JSON.stringify(this.originalData)); + this.refresh(); + } + + selectAll() { + this.data.forEach(item => item.selected = true); + this.refresh(); + } + + get selectedValues() { return this.data.filter(d => d.selected).map(d => d.value); } + get selectedItems() { return this.data.filter(d => d.selected); } + get data() { return this.options.data; } + set data(value) { this.options.data = value; } + + set selectElement(value) { this.options.selectElement = value; } + get selectElement() { return this.options.selectElement; } + + set element(value) { this.options.element = value; } + get element() { return this.options.element; } + + set placeholder(value) { this.options.placeholder = value; } + get placeholder() { return this.options.placeholder; } + + set name(value) { this.options.name = value; } + get name() { return this.options.name; } + + set width(value) { this.options.width = value; } + get width() { return this.options.width; } + + set height(value) { this.options.height = value; } + get height() { return this.options.height; } + +} +if (typeof window !== 'undefined') { + window.MultiSelect = window.MultiSelect || MultiSelect; +}